// guide / engagement

03

Engaging your members

Member-facing features that build community.

01

Reaction roles end-to-end

Reaction roles are self-service tagging. You post an embed in a public channel, list the roles a member can claim, and pair each role with an emoji. Members react to the emoji and Discord adds the role to their account. Un-react and the role goes away. The bot watches the post for reaction events and applies the mapping you set up. No mod has to be on duty for this to work, which is the point.

  1. Pick the channel reaction-role posts should live in. This is a one-time setup per guild:
    /roles channel channel:#pick-your-roles
    Every future /roles create post lands in #pick-your-roles. Members know where to look.
  2. Create the post. Pick a clear title and body so members understand what they are signing up for:
    /roles create title:"What region?" body:"React below to pick your region. You can pick more than one."
    The bot posts the embed in the channel you configured and returns the message ID. You will need that ID for the next step, so copy it now.
  3. Add the mappings. One add-mapping call per (emoji, role) pair:
    /roles add-mapping message_id:1234567890 emoji:🦅 role:@USA
    /roles add-mapping message_id:1234567890 emoji:🇨🇦 role:@Canada
    /roles add-mapping message_id:1234567890 emoji:🇬🇧 role:@UK
    /roles add-mapping message_id:1234567890 emoji:🇪🇺 role:@Europe
    The bot reacts to the post with each emoji as you add it, so members can react without having to type the emoji themselves. It also stores the mapping so reaction events on this message route to the right role.
  4. Confirm what is wired up across the guild:
    /roles list
    The bot prints every active reaction-role post and the mappings under each one. Quick way to remember what you posted six months ago.
  5. Remove a post you no longer want:
    /roles delete message_id:1234567890
    The bot deletes the embed and clears all the mappings. Members who already claimed roles via the post keep those roles; the bot just stops handling new reactions on the dead message.

The region picker above is a common starter pattern. Pronoun pickers, game pickers ("which games do you play?"), and notification opt-ins ("ping me for events?") all use the same shape. One post per category. One emoji per role. The bot handles the rest.

Tip. The bot needs Manage Roles, and any role you map must sit BELOW the bot's own role in the role list. Discord refuses to let a bot assign a role above its own position. If a mapping silently fails to apply, that is almost always why.

02

Self-serve color roles

Color roles let members pick their own name color from a curated palette without bothering a mod. The bot ships a single command, /color, that presents the available colors as buttons. The member clicks one, the bot swaps their old color role for the new one, and that is the entire interaction. No tickets, no mod queue, no per-member role assignments.

  1. Run the command as a member:
    /color
    The bot replies with a button grid of every Color-* role that exists in the guild. The member clicks the color they want. Done.
  2. Swap colors the same way. Re-running /color and picking a different button removes the old color role and adds the new one in a single action. Members can change as often as they like.
  3. Remove the color by running /color and clicking a "no color" option, which clears every Color-* role from the member's account. Their name reverts to whatever default Discord renders.

From a mod's perspective, you never have to do anything per-member. The bot handles the whole flow. The only one-time setup is making sure the Color-* roles actually exist in the guild. The bot does not auto-create them, because picking the palette is a judgement call (how many colors, which shades, which order they sort in the member list).

Creating the roles is a one-shot mod task. The recommended path is the GSB-CreateColorRoles SSM document, which spins up a curated palette in your guild in a single call. The local equivalent is npx tsx src/scripts/create-color-roles.ts --guild <id> if you are running the bot yourself. Either way, the script is documented in docs/manual-setup.md under the color-roles entry, so check there for the current parameter set and the exact role list it creates.

Once the roles exist, you can move on. The command is permissionless on the member side (any member can run it), and the bot needs Manage Roles plus a position above every Color-* role in the role list. That is identical to the reaction-role requirement, so if reaction roles work in your guild, color roles will work too.

03

The welcome flow, deeper

The basics live in Setup: pick a channel with /welcome channel, write a template with /welcome message, test it with /welcome test. This sub-section covers the parts you only need once you are running it day-to-day. Template variables, restart safety, and inspection commands.

  1. The {user} placeholder is the only template variable, and it expands to a Discord mention of the new member at post time. That mention pings them, which is what causes their client to highlight your welcome channel as having unread activity. Drop it anywhere in the template:
    /welcome message template:"Hey {user}, welcome. Read #rules first, then grab roles in #pick-your-roles."
    You can use it multiple times in one template ("Hey {user}! {user}, here is what to do first.") but doing so pings the member twice. Usually once is plenty.
  2. Inspect the current config before you change anything:
    /welcome show
    The bot prints the configured channel, the current template, and whether the flow is enabled or disabled. Use this when you take over a guild from another mod and want to see what is already set up.
  3. Preview a real post without waiting for someone to actually join:
    /welcome test
    The bot posts the template to the configured channel exactly the way it would for a genuine guildMemberAdd event. The {user} placeholder expands to your own mention, so you can read what the message looks like and what a real new member would see.
  4. Disable temporarily without losing the config:
    /welcome disable
    The channel and template stay saved. New joins do not produce a post. Re-run /welcome channel to turn it back on later; the saved template is still there.

The flow is restart-safe by design. When the bot reboots, Discord sometimes re-fires guildMemberAdd for members who joined right before the restart. Without protection, those members would get greeted twice. The bot keeps a per-member dedup record with a 1-hour TTL, so a re-fire within an hour is suppressed. The original welcome stands; no double-post.

After the hour passes, the dedup expires. That is intentional. If a member legitimately leaves the guild and rejoins later in the day, they get a fresh welcome, which is the right behavior; they are a returning member and the greeting should reflect that.

Tip. If a user rejoins right after leaving, the 1-hour dedup window means they don't get re-greeted. After an hour, a rejoin re-greets (intended behavior). If you see only the first greeting and not the second, check whether the rejoin happened inside the window.

04

Birthday tracking

The birthday tools turn member birthdays into a recurring small-celebration moment in the guild. Members opt in by registering their birthday with the bot. Once a day, the bot scans for matches and posts a shoutout in the channel you configured. No mod has to remember anything. The whole flow is opt-in, so members who do not want a public birthday post simply do not register one.

  1. Pick the shoutout channel as a mod with Manage Server:
    /birthday channel channel:#birthdays
    Every daily shoutout lands in #birthdays. Pick a channel that fits the energy of the moment; #general works fine if you are a small guild.
  2. Members register their own birthday with the day in MM-DD format. Year is intentionally not collected:
    /birthday set day:03-14
    The bot stores the registration scoped to this guild and confirms back to the member. Members can re-run this any time to update it, which matters if someone fat-fingered the date the first time.
  3. Look up a friend's birthday if they have registered one:
    /birthday check user:@Friend
    The bot returns the friend's saved date, or a "not registered" message if they have not opted in. There is no way to see a date someone has not chosen to share.
  4. The daily scan runs once per day on a cron schedule. It queries every birthday with today's MM-DD and posts one shoutout per matching member in the configured channel. If three members share a birthday, three shoutouts post, one after the other.

Behind the scenes, the bot stores birthdays in a per-guild, per-user record keyed on MM-DD. That lets the daily scan be a single efficient query ("find all birthdays on 03-14") instead of a scan over every member's data. Year is not stored because age is sensitive information and not needed for the shoutout. The member knows how old they are; the guild does not need to.

If you set up the channel but later want to turn the feature off, re-run /birthday channel with a channel option pointing at a channel the bot cannot post in, or remove the channel entry by re-running it with no value. Members who already registered keep their registration; the daily scan just stops producing posts.

Tip. Birthdays are per-user-per-guild. The same person in two different guilds can set different display dates if they want. The data is not shared across guilds.

05

Temporary voice channels

Temporary voice channels solve the "all our rooms are either empty or full" problem. You designate one voice channel as a join-to-create hub. When a member joins it, the bot instantly spins up a personal voice channel for them, moves them into it, and deletes it again the moment the last person leaves. Members get a private room on demand without a mod making channels by hand, and your channel list never fills up with abandoned rooms.

  1. Pick the hub channel as a mod with Manage Server. Make an ordinary voice channel first (call it something like "➕ Join to Create"), then point the bot at it:
    /voice hub channel:#Join to Create
    From now on, anyone who joins that channel triggers the create-and-move flow. The hub itself stays empty; it is only a trigger.
  2. Members just join the hub. No command required. The bot creates a channel named after them ("Alex's Channel") in the same category, moves them in, and posts an owner control panel into the new channel's text chat. When everyone leaves, the bot deletes the channel automatically. If a member leaves and rejoins the hub within a few seconds, they land back in their existing room instead of getting a duplicate.
  3. The owner controls their own room from the panel the bot posts. Only the member who created the channel can use the buttons:
    • Rename (✏️) opens a text box to retitle the channel.
    • User limit (👥) caps how many people can join, from 2 up to 99, or removes the cap entirely.
    • Kick (🚪) disconnects someone from the room.
    Every response is private to the owner, so the panel does not clutter the channel.
  4. Inspect or turn it off as a mod. /voice show prints the configured hub and any live temp channels (anyone can run it). /voice disable clears the hub so no new channels are created; rooms that already exist are left alone to empty out naturally.

Everything here is per-guild and free. The only setup cost is the one-time /voice hub call. There is no per-member configuration and nothing for mods to clean up afterward.

Tip. The bot needs the Manage Channels and Move Members permissions to create rooms and move joiners. These were added when temp voice channels shipped, so if you invited the bot before that, re-invite it or grant those two permissions to its role — otherwise joining the hub silently does nothing. Discord also rate-limits channel renames to twice per ten minutes, so the Rename button can briefly refuse.

06

Voice activity stats

Voice activity stats reward the people who actually show up in voice. When you turn the feature on, the bot quietly tracks how long each member spends in voice channels and exposes a leaderboard and per-member ranks. It is opt-in per guild and off by default, so existing servers do not suddenly start counting time without you asking.

  1. Turn tracking on as a mod with Manage Server:
    /voice stats-enable
    From that point, qualifying voice time accrues for every member. Turning it back off with /voice stats-disable stops new counting but keeps every total already recorded — re-enabling later picks up where it left off.
  2. See the leaderboard (any member can run it):
    /voice leaderboard
    /voice leaderboard window:this-week
    The default window is all-time; pass window:this-week for a rolling weekly board. Either way the bot shows the top ten members by time spent in voice.
  3. Check one member's rank with the optional user option, defaulting to yourself:
    /voice rank
    /voice rank user:@Friend
    The reply is that member's total voice time and their position on the board.

Time only counts when it represents real participation. A member accrues time when they are not in the AFK channel, not self-deafened, and there is at least one other person in the channel with them. Sitting alone in a channel, or sitting deafened, does not pad anyone's numbers. All-time totals are kept permanently; the weekly window rolls off after 90 days.

Tip. Voice stats track time, not XP — they are separate from the message leveling system below. Enabling one does not enable the other. If a member insists their hours look low, the usual reason is they were alone in a channel or self-deafened, neither of which counts.

07

Leveling — message and voice XP

Message leveling turns regular chatting into a visible progression. Members earn XP for posting, level up on a curve, and can compare standings on a leaderboard. It runs entirely on Discord events and the database — no AI tokens are spent — so it is free and instant. Like voice stats it is opt-in per guild and off by default.

  1. Switch it on as a mod with Manage Server:
    /level enable
    Members start earning XP immediately. /level disable stops new XP but preserves everyone's existing totals, so toggling the feature never wipes progress.
  2. Members track their own progress:
    /level rank
    /level rank user:@Friend
    The reply shows the member's current level, total XP, how far they are into the next level, and their rank in the guild. Omit the user option to see yourself.
  3. See the standings (open to any member):
    /level leaderboard
    The bot lists the top ten members by total XP, with medals for the top three.
  4. Tune the economy as a mod. Each control is its own command, and /level show prints the current settings:
    /level xp-per-message amount:15
    /level cooldown seconds:60
    /level voice-xp-per-minute amount:10
    /level announce enabled:true
    /level channel channel:#level-ups
    The defaults are 15 XP per message, a 60-second message cooldown, and 10 XP per minute of active voice. Level-up announcements are on by default and post in the channel where the member levelled up; set /level channel to funnel them all to one place instead.

Members earn XP two ways: posting messages, and time spent genuinely active in voice. A once-a-minute sweep awards the voice-xp-per-minute amount to anyone who is not in the AFK channel, not muted or deafened, and sharing a channel with at least one other person — set it to 0 to switch voice XP off entirely. Levels follow a gently accelerating curve (each level costs a little more than the last), and a member's level is always computed from their total XP rather than stored, so changing the XP rates re-derives everyone's level consistently. The message cooldown is per member, per guild: only the first message inside each window earns XP.

Tip. Leveling stays off until a mod runs /level enable, which keeps it from surprising an established server with sudden level-up spam. XP comes from both messages and active voice time; the message cooldown means only your first message per window counts, and voice XP only accrues while you are unmuted and not alone in a channel — so chatting and actually hanging out in voice level you up, but idling, lurking muted, or spamming do not.

08

Personal reminders

Personal reminders let any member ask the bot to ping them later — no permissions, no setup. You can set them relative to now or at an exact date and time, and the bot delivers a DM when they fire, falling back to a mention in the channel if your DMs are closed. Scheduled channel announcements for mods are a separate tool covered in the admin stage.

  1. Remind me in a while with a relative duration like 10m, 2h, 3d, or 1w:
    /remind in duration:2h message:"stretch and drink water"
    The bot confirms and fires the reminder two hours later.
  2. Remind me at a specific time using YYYY-MM-DD HH:MM in your saved timezone:
    /remind at when:"2026-06-12 19:30" message:"raid night"
    Set your timezone first so the bot knows what 19:30 means for you:
    /util set-timezone tz:America/New_York
    The timezone field has autocomplete, so start typing a city or region and pick from the list.
  3. Snooze or dismiss when the reminder arrives. The delivery comes with buttons — +10m, +1h, +1d, and Done — so you can push it back with one tap or mark it handled. Only you can use the buttons on your own reminder.
  4. Manage your reminders any time:
    /remind list
    /remind edit id:<id> when:"2026-06-13 20:00"
    /remind cancel id:<id>
    /remind list shows your active reminders with their short IDs; edit and cancel take that ID (with autocomplete) and let you change the time or text, or drop the reminder entirely.

You can have up to 25 active reminders at once per server. Reminders survive a bot restart — if the bot reboots right as one was due, it catches up and fires it on the next start rather than dropping it.

Tip. Relative reminders (/remind in) ignore timezones entirely — two hours is two hours. Only absolute reminders (/remind at) need your timezone set. If a member's reminder fires at a weird hour, the usual cause is they never ran /util set-timezone and are defaulting to US Eastern.

09

Looking-for-group posts

The looking-for-group board is how members rally a squad without spamming @everyone. Anyone posts an LFG card describing what they want to play; others join by clicking a button on the card. The card tracks who is in, counts down "need N more" until the party is full, and expires on its own so the channel does not fill up with stale posts.

  1. Post a card in the channel where people look for games:
    /lfg create game:"Overwatch competitive" slots:5 when:"8pm EST" notes:"Diamond+ preferred"
    Only game is required. slots (2–25) sets the target party size — the card shows "need N more" and flips to FULL when reached; omit it for an open-ended post. when and notes are free text, and expires takes a duration like 30m or 2h (default 2 hours, max 12).
  2. Members join by button. The card carries a join/leave button; clicking it adds or removes the member and updates the count live. Joiners run no commands — they just click.
  3. See what is open, or close a post:
    /lfg list
    /lfg close id:<id>
    /lfg list shows the guild's active cards with their short IDs; /lfg close ends your post early (it closes on its own at the expiry otherwise).

LFG is free and member-driven — any member with slash-command access can post one. A guild is capped at 25 active cards and each member at 3 of their own, so the board stays readable.

Tip. The bot needs Send Messages in the channel to post the card and keep its count updated. Cards expire on their own, so a forgotten post cleans itself up — but /lfg close is the polite way to take one down the moment your group fills.

10

Pick-up games: split and draft teams

Once a group has gathered and it is time to pick sides, the pick-up-game tools form teams so nobody has to be the bad guy doing it by hand. Two modes: a straight random split, or a captain draft. Both can take an explicit list of players or just pull everyone out of a voice channel.

  1. Randomly split into even teams:
    /pug split teams:2 channel:#Scrim VC
    /pug split teams:3 players:@A @B @C @D @E @F
    Choose how many teams (2–8, default 2). Pass players as @mentions, or channel to pull everyone currently in that voice channel. The bot shuffles and posts the rosters.
  2. Run a captain draft in snake order:
    /pug draft captains:@Captain1 @Captain2 channel:#Scrim VC
    Name the captains as @mentions (that sets the number of teams), or set teams and let the bot pick captains. Players come from players mentions or the named voice channel. The bot walks the draft in snake order (1-2-2-1…) so the first-pick advantage evens out.

Both are stateless, free, and game-agnostic — handy for any scrim, game night, or "let us just run customs." Pulling from a voice channel is the fast path: get everyone into the VC, then one command makes the teams.

Tip. Pulling players from a voice channel only sees who is in the channel at the moment you run the command. Get everyone in first, or pass the roster explicitly with player @mentions.

11

Scheduling game sessions with RSVP

Game sessions are how you plan a night ahead of time and see who is actually coming. A member schedules a session with a title and a start time; the bot posts a card with Going, Maybe, and Can't buttons, tracks the roster live, and pings everyone who said Going or Maybe shortly before kickoff so nobody forgets.

  1. Schedule a session:
    /session create title:"Overwatch game night" when:"2026-06-14 20:00" remind:30m
    title and when are required; when is YYYY-MM-DD HH:MM in your saved timezone (set it once with /util set-timezone). remind is how long before start the bot pings attendees (default 30 minutes, max 7 days); description and channel are optional.
  2. Members RSVP by button. The card carries Going / Maybe / Can't. Clicking sets your status and moves you out of the others; clicking the one you are already on toggles you back out. The roster updates live with no pings — the card just reflects who is in.
  3. The pre-start ping fires remind before kickoff and @-mentions everyone marked Going or Maybe. This is the one place the feature pings on purpose, so the nudge actually lands. RSVPs close once the session has started.
  4. See or cancel sessions:
    /session list
    /session cancel id:<id>
    /session list shows upcoming sessions sorted by start time; /session cancel calls one off (the organizer, or a mod with Manage Messages).

Anyone can schedule a session — it is free and member-driven, capped at 25 upcoming per guild. Sessions are one-off for now; recurring "every Friday" sessions are a planned follow-up.

Tip. The reminder is timezone-aware through your /util set-timezone setting. If a ping fires at the wrong hour, the usual cause is the organizer never set their timezone and defaulted to US Eastern.

12

Fun tools

The fun tools are exactly what they sound like. Light, instant, no setup required. They exist to give members easy ways to break a tie, settle a bet, or post a quick reaction. None of them touch the moderation or AI-access layers; any member who can use slash commands in your guild can use these.

  1. Flip a coin when you need a random binary decision:
    /fun coinflip
    The bot replies with heads or tails. The randomness is real (Node's crypto-grade PRNG), not a weighted pick. Both outcomes are equally likely.
  2. Pick from a list of options when "heads or tails" is not enough:
    /fun pick options:"option 1, option 2, option 3"
    The command takes a single options parameter containing a comma-separated list of choices. The bot splits on commas, picks one of the supplied options at random, and announces it. Useful for "what should we play tonight?" or "who buys the next round?". Add as many comma-separated entries as you like inside the one string.

None of these tools require permissions beyond the default member ability to use slash commands. None of them store any state. The bot does not remember what coin flips were heads or what options were picked. They are pure stateless utilities by design.

If your guild has channels where casual tools should not be runnable (an announcements channel, say, or a serious-discussion channel), restrict slash-command usage to specific channels using Discord's built-in Integrations settings under server settings. The bot does not need to enforce that itself; Discord's permission system handles it.

13

Custom lists and random picks

Custom lists are named, reusable pick-lists your server saves once and draws from whenever. Think map pools, game-mode rotations, a roster to draw a winner from, or anything else you would otherwise re-type into /fun pick every time. Mods curate the lists; anyone can pick from them.

  1. Create or replace a list (Manage Messages):
    /list set name:"maps" items:"Ilios, Lijiang, Oasis, Nepal, Busan"
    Items are comma-separated and de-duplicated. Edit a list in place with /list add name:maps items:"Antarctica" and /list remove name:maps items:"oasis" (matching is case-insensitive), or drop it entirely with /list delete name:maps.
  2. Pick from a list (anyone):
    /list pick name:maps
    /list pick name:maps count:3
    The bot draws count distinct items at random (1–25, default 1). Handy for "what map next?" or drawing names without re-typing the pool.
  3. Browse what exists:
    /list show name:maps
    /list all
    /list show prints one list's items; /list all lists every saved list with its item count. The name field autocompletes from existing lists everywhere it appears.

Lists are case-insensitive by name and shared across the guild, capped at 25 lists of up to 100 items each. Editing is gated to Manage Messages so a shared map pool cannot be wiped by just anyone; picking and viewing are open to all. For a one-off pick you do not want to save, /fun pick still takes a comma-separated list inline.

15

Meme tools

The meme tools are member-facing utilities for turning content into shareable formats. They are not moderation tools and they do not require any per-guild setup. Any member with slash-command access can run them. They exist because turning a 30-second video clip into a GIF, or applying a meme filter to an image, used to require leaving Discord and using a separate app. The bot does it inline.

  1. Extract a GIF from a video tweet when someone posts a Twitter/X URL with embedded video and you want to reuse the clip. The command lives in the /fun group, not /util:
    /fun gif url:https://x.com/SomeAccount/status/1234567890
    The bot fetches the video, transcodes the first few seconds (or the full clip if it is short enough) into a GIF, and posts the result as a Discord attachment. The conversion runs server-side via ffmpeg, so the member does not need to install anything.
  2. Deep-fry an image by attaching it to the command:
    /fun deep-fry
    Attach the source image in the same Discord upload widget you would use for any attachment. The bot processes the attachment (high-saturation, posterize, JPEG re-compress, sharpen) into the deep-fried meme aesthetic and posts the result in the channel. Original attachment is not preserved; the bot only posts the processed version.

Both tools enforce file-size limits set by your guild's Discord upload cap. Free guilds cap attachments at 10 MB; boosted guilds get higher caps depending on the boost level. If a video tweet is longer than the GIF conversion can fit inside your cap, the bot returns an error in the channel and does not post a partial result. The same applies to deep-fry: if the input image is too large to process and re-post inside the cap, the bot tells you instead of silently truncating.

Both tools also have an implicit timeout. Transcoding a video to GIF can take several seconds for longer clips, and Discord's slash-command interaction has a fixed deadline. The bot uses a "thinking" placeholder to extend the deadline, but if the conversion still does not finish in time, the bot returns a timeout error. Re-running on a shorter source clip usually resolves it.

If your guild does not want meme tools enabled in specific channels (an announcements channel, for example), deny the bot send-attachment permission in that channel. The slash commands will still show, but the result of the conversion will not post.

Continue your tour