A Discord verification bot built with Python and discord.py that:
- Keeps a persistent verification panel in channel A.
- Shows two buttons: Yes and No.
- Sends an ephemeral message for No.
- Opens a modal for Yes with check-in questions.
- Creates a review thread in channel B after modal submission.
- Lets moderators choose Kudos or Poopers from a dropdown.
- Sends approved (Kudos) records to channel C for logs.
- Logs runtime events to
logs/bot.log.
- Python 3.10+ recommended
discord.py- Discord API (through
discord.py) pytestfor lightweight tests
bot.py- Main bot implementation, event handling, modal, and logging setuprequirements.txt- Python dependencies.env.example- Environment variable template for bot tokentests/test_bot.py- Basic automated testsCHANGELOG.md- Ordered record of project changeslogs/bot.log- Runtime log file (created automatically at startup)
- Bot starts and connects to Discord.
- In
on_ready, bot ensures channel A contains a verification panel embed with a persistentView. - User clicks No:
- Bot sends an ephemeral response visible only to that user.
- User clicks Yes:
- Bot opens
HowAreYouModal.
- Bot opens
- User submits modal:
- Bot stores the submission in memory.
- Bot creates a thread in channel B named with the user's ID.
- Bot posts review UI (dropdown with
Kudos/Poopers) in that thread.
- Moderator selects dropdown option:
- If
Kudos, bot forwards submission details to channel C. - Bot replies ephemerally to the moderator with decision status.
- If
GroundsmanBot(commands.Bot)- Subclass of
commands.Bot - Handles lifecycle events (
setup_hook,on_ready) - Registers persistent UI with
add_view(...) - Maintains in-memory submissions map for review decisions
- Subclass of
VerificationPanelView(discord.ui.View)- Persistent UI attached to the channel A verification message
- Contains
YesandNobuttons - Uses custom component IDs so callbacks survive restarts
HowAreYouModal(discord.ui.Modal)- Modal form shown when user clicks Yes
- Contains two text fields:
- Required mood input
- Optional details input
- Handles submission in
on_submitand creates review thread in channel B
ReviewView(discord.ui.View)+ReviewSelect(discord.ui.Select)- Mod-only moderation UI in channel B thread
- Dropdown decisions:
KudosorPoopers Kudosforwards a full embed log to channel C
configure_logging()- Creates
logs/directory andlogs/bot.log - Writes timestamped logs to file and console
- Creates
required_env_int(...)- Validates required channel ID environment variables
discord.ui.View- Container for interactive UI components (buttons/selects)
- Supports persistent components with
timeout=None
discord.ui.Button- Clickable action component
- Used for Yes/No choices in channel A
discord.ui.Modal+discord.ui.TextInput- Pop-up form for collecting structured user input
discord.ui.Select- Dropdown component for moderator decision workflow
interaction.response.send_message(..., ephemeral=True)- Sends per-user temporary responses only visible to clicker
TextChannel.create_thread(...)- Creates per-submission mod review thread in channel B
Bot.add_view(...)- Registers persistent component handlers on startup
The bot logs:
- Startup and login events
- Verification panel creation/update status
- Modal submission metadata
- Moderator decisions
Log output goes to:
- Console (stdout)
logs/bot.log
- Go to the Discord Developer Portal.
- Create a new application.
- Under Bot, create a bot user.
- Copy the bot token
- No privileged message intent is required for this button-first workflow.
- In OAuth2 URL generator:
- Scope:
bot - Bot permissions (minimum):
Send Messages,Read Message History,View Channels,Create Public Threads,Send Messages in Threads
- Scope:
- Open generated URL and add bot to your test server.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtCreate .env from template:
cp .env.example .envSet token:
DISCORD_BOT_TOKEN=your_real_token_hereYou can export it manually:
export DISCORD_BOT_TOKEN="your_real_token_here"Set channel IDs:
export CHANNEL_A_ID="channel_id_for_verification_panel"
export CHANNEL_B_ID="channel_id_for_mod_review_threads"
export CHANNEL_C_ID="channel_id_for_approved_logs"python bot.pyExpected startup output:
- "Logged in as ..."
logs/bot.logcreated automatically
Run unit tests:
pytest -qManual test in Discord:
- Start bot and confirm panel message appears in channel A.
- Click No and verify an ephemeral message appears only for you.
- Click Yes and verify modal opens with both questions.
- Submit modal and verify:
- Ephemeral confirmation to user
- Thread is created in channel B with user ID in thread name
- Dropdown (
Kudos/Poopers) appears in thread
- As moderator, choose
Kudosand verify a log embed is posted to channel C.
All project changes should be appended to CHANGELOG.md in date order.