package help import ( "log" "unicode/utf8" "github.com/bwmarrin/discordgo" ) var Help = &discordgo.ApplicationCommand{ Name: "help", Description: "Get help with the bot", } // A message's embeds share a limit of 6000 characters, and one embed holds at // most 25 fields of 1024 characters. The reference is longer than that, so it // is sent as a few messages. const ( maxEmbedChars = 5000 maxFieldChars = 1024 ) func fields() []*discordgo.MessageEmbedField { return []*discordgo.MessageEmbedField{ { 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", }, } } func fieldSize(f *discordgo.MessageEmbedField) int { return utf8.RuneCountInString(f.Name) + utf8.RuneCountInString(f.Value) } // embeds splits the reference into embeds that fit Discord's limits. 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.", Color: 0xFFA500, Author: author, } size := utf8.RuneCountInString(cur.Title) + utf8.RuneCountInString(cur.Description) + utf8.RuneCountInString(author.Name) for _, f := range fields() { if size+fieldSize(f) > maxEmbedChars || len(cur.Fields) >= 25 { out = append(out, cur) cur = &discordgo.MessageEmbed{Title: "Velox Bot Help (continued)", Color: 0xFFA500} size = utf8.RuneCountInString(cur.Title) } cur.Fields = append(cur.Fields, f) size += fieldSize(f) } return append(out, cur) } func HelpHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Check your DMs for help.", Flags: discordgo.MessageFlagsEphemeral, }, }) userID := "" // check if command is run in a guild if i.Member != nil { userID = i.Member.User.ID } else { userID = i.Interaction.User.ID } dmChannel, err := s.UserChannelCreate(userID) if err != nil { log.Printf("Error creating DM channel: %v", err) return } author := discordgo.MessageEmbedAuthor{ Name: "Velox Bot", URL: "https://gitea.fernandovideira.com/FernandoJVideira/velox-bot", } for _, e := range embeds(&author) { if _, err := s.ChannelMessageSendEmbed(dmChannel.ID, e); err != nil { log.Printf("Error sending help: %v", err) return } } }