A Telegram group "Undercover" party game bot built with Python, aiogram 3, and Redis. Supports point wagering, blank-slate mode, speak timeout adjudication, automatic voting resolution, and fully panel-based group interaction.
- Panel-based interaction: create room, join, start, view status, and disband — all via buttons
- Speaking rotation: sequential speaker advancement with automatic round summary recording
- Timeout adjudication: speaking timeout = instant elimination; voting timeout = auto self-vote
- Auto-progression: automatically advances to voting when no one left to speak; auto-advances to next round or settlement after voting
- Blank-slate mechanic: the blank player has no word; if voted out, can guess the word via DM within a time limit to share the winner's reward
- Multi-undercover support: automatically assigns additional undercovers and a blank as player count scales
- Point wagering: bets auto-deducted on game start; winning faction splits the pot
- Shared points: can connect to an external Redis to share a point ledger with other bots
- Maintenance capabilities: super-admin force-stop, maintenance mode, and compensation
- Players first DM the bot with
/startto ensure they can receive their secret word when the game starts. - In the group, send
/uc_helpfor help, or/uc_newto create a room. - Other players join the room. Once the minimum count is met, the host can manually start or wait for auto-start.
- The bot privately sends each player their role:
- Civilians receive the civilian word
- Undercovers receive a similar but different word
- The blank receives no word
- During the speaking phase, players describe their word in turn without saying it directly.
- After all have spoken, voting begins. The voted-out player is eliminated and the game continues or settles based on remaining roles.
- If the blank is voted out, they can DM the bot to guess the word within a time limit for a chance at the reward.
/uc_help— view help/uc_new— create a room/uc_join— join the current room/uc_leave— leave the room (double-confirm during an active game)/uc_start— host starts the game/uc_say <description>— submit your description/uc_status— view current room status/uc_bal— check your point balance/uc_end— host disbands the room/uc_force_stop— super-admin force-stops the game/uc_maintain— super-admin enables maintenance mode/uc_compensate [notes]— super-admin issues maintenance compensation
Copy the template:
cp .env.example .envKey environment variables:
BOT_TOKEN: Telegram Bot TokenRUN_MODE:pollingorwebhookWEBHOOK_BASE_URL: publicly accessible URL in webhook modeREDIS_HOST/REDIS_PORT/REDIS_DB: primary Redis configurationPOINTS_REDIS_HOSTetc.: shared points Redis (leave empty if unused)ALLOWED_CHAT_ID/ALLOWED_THREAD_ID: optionally restrict the bot to a specific group or topicSUPER_ADMIN_IDS: super-admin user IDs, comma-separatedMIN_PLAYERS/MAX_PLAYERS: player count range for starting a gameBET_PER_PLAYER: wager per gameSPEAK_TIMEOUT_SECONDS: speaking timeoutSPEAK_REMIND_BEFORE_SECONDS: reminder threshold before timeoutAUTO_START_IDLE_SECONDS: lobby auto-start wait timeVOTING_TIMEOUT_SECONDS: voting timeoutWORD_PAIRS: word bank, formatword1|word2;word3|word4
With Docker Compose:
docker compose up -d --buildIf the code directory is volume-mounted into the container, restarting the bot container is usually sufficient after editing .py files:
docker compose restart undercover_botmain.py: entry point, command registration, polling or webhook startuphandlers.py: commands, button callbacks, flow orchestration, maintenance logicgame.py: core game state machine and Redis data operationsbalance.py: point read/writeconfig.py: environment variables and runtime parameterscore.py: bot, dispatcher, and Redis initializationdocker-compose.yml: local deployment orchestration
- Do not commit real
.envfiles, databases, Redis persistence directories, or local debug data to the repository - When sharing publicly, keep only
.env.example - If using webhook mode, configure your own domain, reverse proxy, and TLS certificate