Skip to content

Auto Voice Channels

Jason Tucker edited this page May 8, 2026 · 5 revisions

Auto Voice Channels

The hub system

One or more hub voice channels act as entry points. When a user joins a hub:

  1. The hub channel is renamed in place to the user's new channel name — the user stays in it, no move needed
  2. A replacement hub is created immediately in the same category
  3. An attached text channel is created below the voice channel — private, only visible to members in the voice channel
  4. A compact control panel is posted (silently) in the text channel with interactive buttons
  5. When the channel becomes empty, both voice + text channels are deleted after 30 seconds

Channel naming

Channel names track Discord rich presence (requires the Presence Intent enabled in Dev Portal) for every member in the voice channel, not just the owner. Any member's Playing X activity can drive the rename.

Scenario Channel name
1 of 1 member playing Overwatch Overwatch
3 of 4 members playing Overwatch (the most-played game wins, count prefixed when more than one matches) (3) Overwatch
Manually renamed via the panel, or Tryhard / Chill template Whatever was set — also stored as the channel's fallback_name
Nobody is playing anything Reverts to the channel's fallback_name (the manual name, the initial random tech name on creation, or the last template choice)
Pre-existing channel with no fallback_name (legacy row) Stays at its current name until the next manual rename or template pick

The auto-rename is implemented in services/voice/autoNaming.ts and is shared between presenceUpdate (the live driver), the Auto / Counter template buttons, and the boot-time reconciler retry. presenceUpdate keys off the changed user's voice channel rather than the channel's owner — so a non-owner's game change can flip the channel name.

Control panel

The panel is posted automatically in the private text channel as soon as the channel is created, with SuppressNotifications set so nobody is pinged. It stays the first/top message in the channel and is re-rendered on every voice-state change so the member list and timestamps stay current.

Layout (Components V2):

🔊 host @owner · created <t:N:R>
👥 In channel
• @member-a joined <t:N:R> · 🎮 Overwatch
• @member-b joined <t:N:R>
[Rename] [Hosts] [Templates]
[Locked / Unlocked] [Visible / Hidden] [Claim] [Delete]

A silent 📋 Open Panel sticky message lives at the bottom of the text channel so the panel is always one click away — the sticky is a single non-CV2 button (no header / warning text). Use /voice from any channel to get an ephemeral copy too.

Status-flip toggles

Toggle buttons show the current state, not the pending action — same convention as the profile birthday-pings / year toggles and the /games view/pings buttons.

Button Current state ‑ green Current state ‑ red
Lock Unlocked (anyone can join) Locked (@everyone Connect denied)
Hide Visible Hidden

Click to flip. The button label updates immediately because the panel re-renders on every state change.

Buttons

Button Who can use What it does
✏️ Rename Owner, hosts, sudo Modal to set a custom name (also updates fallback_name)
👑 Hosts Owner, hosts, sudo One panel listing every member with their current rank emoji (👑 host · 🛡️ sudo · 👤 member). Click toggles host status.
📋 Templates Owner, hosts, sudo Auto / Counter ([x/y]) / Comp 5-stack / Tryhard / Chill — sets name + user limit in one click. Tryhard / Chill also overwrite fallback_name.
🔒/🔓 Locked / Unlocked Owner, hosts, sudo Toggles @everyone Connect
🙈/👁️ Hidden / Visible Owner, hosts, sudo Toggles channel visibility
👤 Claim Anyone in channel Claim ownership when owner has left
🗑️ Delete Owner, hosts, sudo Deletes both channels right away

Text channel permissions

Who Access
@everyone Denied (hidden)
Bot Full access (send, manage)
Channel owner View + send + read history
Hosts View + send + read history
Members in voice channel View + send + read history (added on join, removed on leave)
Sudo roles View + send + manage messages

Ownership transfer

When the owner leaves the voice channel and others remain, ownership automatically transfers to the longest-present remaining member. The control panel updates to reflect the new owner. The original owner can reclaim with the 👤 Claim button if they rejoin.

DB state tracked

auto_channels holds the per-channel config (guild ID, voice channel ID, text channel ID, owner, hosts, allowed users/roles, lock state, hidden state, user limit, auto_name_enabled, name_template, fallback_name, control panel message ID, cleanup timestamp, source hub ID).

auto_channel_members(voice_channel_id, user_id, joined_at) backs the panel's "In channel" list. Written from voiceStateUpdate on join (upsert) and leave (delete). On boot, the reconciler:

  1. Backfills currently-occupying members at now() (so old times pre-restart are lost but new joins are tracked accurately).
  2. Re-runs the auto-rename for any tracked auto channel where the owner is currently in the channel and playing a game (and auto_name_enabled is on) — closes the gap where presence updates between bot restarts were lost.

Configuration

Set in .env (or override at runtime via /sudo → Settings → Voice / Hub Channels):

  • AUTO_VOICE_CATEGORY_ID — the Discord category that contains both hubs and auto channels
  • HUB_CHANNEL_IDS — legacy comma-separated voice channel IDs that act as hubs (one-time seed; runtime list lives in the hub_channels table)
  • VOICE_CLEANUP_DELAY_MS — how long to wait before deleting empty channels (default: 30000)

Clone this wiki locally