diff --git a/internal/commands/help/help.go b/internal/commands/help/help.go index 5b7bf6d..8795221 100644 --- a/internal/commands/help/help.go +++ b/internal/commands/help/help.go @@ -20,141 +20,53 @@ const ( maxFieldChars = 1024 ) +// docsURL is where each command is explained in full, with its options and +// who may use it. +const docsURL = "https://velox.fernandovideira.com/docs/commands" + +// col is one short column of the help: a list of commands, no descriptions. +// Fields are inline, so Discord lays three of them side by side and the help +// is wide and short instead of narrow and long. +func col(name string, lines ...string) *discordgo.MessageEmbedField { + v := "" + for i, l := range lines { + if i > 0 { + v += "\n" + } + v += l + } + return &discordgo.MessageEmbedField{Name: name, Value: v, Inline: true} +} + func fields() []*discordgo.MessageEmbedField { return []*discordgo.MessageEmbedField{ + col("Fun", + "`/ping` `/joke` `/coinflip`", "`/dice` `/rps` `/rpsstats`", "`/rpsleaderboard`"), + col("Music (DJ role)", + "`/play` `/queue` `/volume`", "`/music skip` `pause` `resume`", "`/music stop` `repeat`", "`/music remove` `move`", "`/music toggle` (Manage Server)"), + col("Sessions and time", + "`/schedule create`", "`/schedule list`", "`/schedule reschedule`", "`/timezone set` `show`"), + col("Tickets", + "Open one from the panel.", "`/ticket add` `close`", "`/ticket claim` `remove` (staff)", "`/tickets setup` `settings`", "`/tickets category` `panel` `list`"), + col("Security", + "`/security status` `settings` `history`", "`/security toggle`", "`/security joingate` `words` `raid`", "`/security antinuke` `heat` `alert-dms`", "`/security trust` `release` `end-raid`", "`/security alerts` `quarantine-role`", "`/security snapshot` `restore`", "`/lockdown` `/unlock`"), + col("Moderation", + "`/moderation purge`", "`/moderation timeout` `untimeout`", "`/moderation kick` `ban` `unban`", "`/moderation slowmode`", "`/moderation bans` `timeouts`"), + col("Meeting rooms", + "`/meeting add-lobby`", "`/meeting remove-lobby` `lobbies`", "`/meeting lock` `lock-private`", "`/meeting unlock`", "`/meeting invite` `uninvite`"), + col("Leveling", + "`/rank` `/leaderboard` `/rewards`", "`/slvl toggle` `status`", "`/slvl set` `set-channel`", "`/slvl set-message`", "`/slvl set-reward` `remove-reward`"), + col("Projects", + "`/projects list`", "`/projects create`", "`/projects add-helper`", "`/projects close`"), + col("Welcome and roles", + "`/config setwelcomechannel`", "`/config setwelcomemessage`", "`/config setwelcomedm`", "`/config setwelcomegif`", "`/config setdefaultrole`"), + col("Logging and Twitch", + "`/config setlogchannel`", "`/config enablelogging` `disablelogging`", "`/config addstreamer` `removestreamer`", "`/config streamers`", "`/config toggletwitch`", "`/config settwitchnotificationchannel`"), + col("Settings and beta", + "`/config view` `clear`", "`/config beta-features` (server owner)", "Bot owner, in a DM with me:", "`/beta set` `off` `status`"), { - Name: "Fun", - Value: "" + - "`/ping` — Pong!\n" + - "`/joke [type]` — Random joke (optional category)\n" + - "`/coinflip` — Flip a coin\n" + - "`/dice ` — Roll dice (e.g. `d20`, `2d20+1`, `2#d20+1`)\n" + - "`/rps ` — Rock Paper Scissors (server only)\n" + - "`/rpsstats` — Your RPS score (server only)\n" + - "`/rpsleaderboard` — Top RPS scores (server only)", - }, - { - Name: "Music (requires **DJ** role)", - Value: "" + - "`/play ` — Play a song (search or URL)\n" + - "`/queue` — Show the current queue\n" + - "`/volume <0-150>` — Set playback volume\n" + - "`/music skip|pause|resume|stop` — Control playback\n" + - "`/music repeat `, `/music remove `, `/music move ` — Repeat and reorder\n" + - "`/music toggle` — Turn music on or off (**Manage Server**)", - }, - { - Name: "Scheduling (1:1 sessions)", - Value: "" + - "`/schedule create user:@someone datetime:\"YYYY-MM-DD HH:MM\" [description]` — Create a session\n" + - "`/schedule list` — List your upcoming sessions\n" + - "`/schedule reschedule id: datetime:\"YYYY-MM-DD HH:MM\"` — Request a new time\n\n" + - "Invites are sent via DM; react with ✅ / ❌ / 🔁 to respond.", - }, - { - Name: "Timezone", - Value: "" + - "`/timezone set ` — Set your timezone (e.g. `Europe/Lisbon`)\n" + - "`/timezone show` — Show your timezone", - }, - { - Name: "Projects", - Value: "" + - "`/projects list` — List active projects\n" + - "`/projects create name:\"...\" [description]` — Create a project (**Manage Server**)\n" + - "`/projects add-helper project-id: user:@someone` — Add helper (**Manage Server**)", - }, - { - Name: "Tickets", - Value: "" + - "Open a ticket from the panel: you get a private thread with the staff.\n" + - "`/ticket add user:@someone` — Add someone to your ticket\n" + - "`/ticket close [reason]` — Close the ticket (opener or staff)\n" + - "`/ticket claim`, `/ticket remove user:@someone` — Staff only\n" + - "`/tickets setup`, `/tickets settings`, `/tickets category add|edit|remove|list`, `/tickets panel`, `/tickets list` — Set up (**Manage Server**)", - }, - { - Name: "Security (anti-raid & anti-nuke)", - Value: "" + - "`/security status`, `/security settings`, `/security history` — What's on, every setting, what happened\n" + - "`/security toggle` — Join Gate, raid mode, anti-nuke, heat on/off (off: owner or trusted)\n" + - "`/security joingate`, `words`, `raid`, `antinuke`, `heat`, `alert-dms` — Tune thresholds and filters (owner or trusted)\n" + - "`/security alerts`, `/security quarantine-role`, `/security release`, `/security end-raid` — Owner or trusted\n" + - "`/security trust add|remove` — Owner only\n" + - "`/security snapshot` — Save roles and channels; `/security restore` — Rebuild what a nuke deleted (owner or trusted)\n" + - "`/lockdown channel|server|joins`, `/unlock …` (unlock: owner or trusted)", - }, - { - Name: "Meetings (**Manage Server**)", - Value: "" + - "`/meeting add-lobby channel:#voice` — Add a voice lobby that creates meetings (up to 10)\n" + - "`/meeting remove-lobby channel:#voice` — Stop using a voice lobby\n" + - "`/meeting lobbies` — List the voice lobbies\n" + - "`/meeting lock` — Lock your meeting room\n" + - "`/meeting lock-private` — Lock + hide your meeting room\n" + - "`/meeting unlock` — Unlock your meeting room\n" + - "`/meeting invite user:@someone` — Allow a user to join\n" + - "`/meeting uninvite user:@someone` — Remove a user's permission", - }, - { - Name: "Moderation (focused tools)", - Value: "" + - "`/moderation purge amount:<1-100>` — Delete recent messages (**Manage Messages**)\n" + - "`/moderation timeout user:@someone duration:<10m|1h|...> [reason]` — Timeout member (**Moderate Members**)\n" + - "`/moderation kick user:@someone [reason]` — Kick member (**Kick Members**)\n" + - "`/moderation ban user:@someone [delete_days] [reason]` — Ban member (**Ban Members**)\n" + - "`/moderation unban user_id: [reason]` — Unban by user ID (**Ban Members**)\n" + - "`/moderation untimeout user:@someone [reason]` — Remove timeout (**Moderate Members**)\n" + - "`/moderation slowmode channel:#channel seconds:<0-21600>` — Set slowmode (**Manage Channels**)\n" + - "`/moderation bans`, `/moderation timeouts` — Who is banned, who is timed out", - }, - { - Name: "Leveling", - Value: "" + - "`/slvl toggle` — Enable/disable leveling (**Manage Server**)\n" + - "`/slvl set user:@someone level:` — Set user level (**Manage Server**)\n" + - "`/slvl set-channel channel:#channel` — Level-up message channel (**Manage Server**)\n" + - "`/slvl set-message message:\"...\"` — Custom level-up message (**Manage Server**)\n" + - "`/slvl set-reward level: role:@role`, `/slvl remove-reward level:` — Role rewards (**Manage Server**)\n" + - "`/slvl status` — Show the leveling settings (**Manage Server**)\n" + - "`/rank [user]` — Your rank card (server only)\n" + - "`/leaderboard` — Top levels (server only)\n" + - "`/rewards` — List configured rewards (server only)", - }, - { - Name: "Twitch live notifications (**Manage Server**)", - Value: "" + - "`/config addstreamer username:` — Add a streamer\n" + - "`/config removestreamer username:` — Remove a streamer\n" + - "`/config settwitchnotificationchannel channel:#channel` — Set notification channel\n" + - "`/config toggletwitch enabled:`, `/config streamers` — Turn alerts on or off, list streamers", - }, - { - Name: "Welcome / onboarding (**Manage Server**)", - Value: "" + - "`/config setwelcomechannel channel:#channel` — Channel for welcome messages\n" + - "`/config setwelcomemessage message:\"...\"` — Welcome text (supports `{user}`, `{server}`)\n" + - "`/config setwelcomedm message:\"...\"` — DM template (supports `{user}`, `{server}`)\n" + - "`/config setwelcomegif url:` — GIF/image used in welcome embed\n" + - "`/config setdefaultrole role:@role` — Role granted to new members\n" + - "`/config view`, `/config clear setting:<...>` — See or clear these settings", - }, - { - Name: "Logging (**Manage Server**)", - Value: "" + - "`/config setlogchannel channel:#channel` — Set log output channel\n" + - "`/config enablelogging` — Enable event/moderation logging\n" + - "`/config disablelogging` — Disable event/moderation logging", - }, - { - Name: "Beta features", - Value: "" + - "`/config beta-features [enabled]` — Let testers use beta features here (server owner only)\n" + - "Bot owner: `/beta` in a DM with the bot — set the beta server and role, list or remove servers", - }, - { - Name: "Other", - Value: "`/help` — Show this help in DMs", + Name: "Everything in detail", + Value: "Options, permissions and what each command does: " + docsURL + "\nMost setup commands need **Manage Server**. `/help` shows this list again.", }, } } @@ -168,7 +80,7 @@ func embeds(author *discordgo.MessageEmbedAuthor) []*discordgo.MessageEmbed { var out []*discordgo.MessageEmbed cur := &discordgo.MessageEmbed{ Title: "Velox Bot Help", - Description: "Command reference. Some commands require server permissions or roles.", + Description: "Every command, by area. Some need server permissions or a role.", Color: 0xFFA500, Author: author, } diff --git a/internal/commands/help/help_test.go b/internal/commands/help/help_test.go index 35d83ad..eb5424b 100644 --- a/internal/commands/help/help_test.go +++ b/internal/commands/help/help_test.go @@ -38,3 +38,20 @@ func TestHelpFitsDiscordsLimits(t *testing.T) { t.Errorf("%d fields shown of %d", seen, len(fields())) } } + +// The help is meant to be wide and short: inline fields, so Discord puts +// three side by side, and it should fit in one message. +func TestHelpIsCompactAndInline(t *testing.T) { + if n := len(embeds(&discordgo.MessageEmbedAuthor{Name: "Velox Bot"})); n != 1 { + t.Errorf("the help should fit in one message, it takes %d", n) + } + inline := 0 + for _, f := range fields() { + if f.Inline { + inline++ + } + } + if inline < len(fields())-2 { + t.Errorf("only %d of %d fields are inline", inline, len(fields())) + } +}