Skip to main content
Use the first part of this page to play. Use the second part to run the bot on your own machine. Also see roles, items, commands, and server settings.

Join a game

Create a lobby with !create or /create. The host message has Join & Play, Spectate instead, and Allow DMs from this bot.
1

Allow direct messages

Press Allow DMs from this bot if the join DM does not arrive. That button is the failsafe. Night actions and your role are still sent as DMs.Allow DMs from this bot is a user install: integration_type=1 and scope=applications.commands. The bot does not receive or store that OAuth grant.
2

Share the lobby code

Codes look like MM-7F2A. Other people join with !join MM-7F2A or /join. Use !list or /list to see open codes.
3

Stay in one lobby

A person can only be in one lobby, and only once. Use !leave before you join another.
Discord administrators add -overwriteAdminWarning when joining, for example !join MM-7F2A -overwriteAdminWarning. The host can force-start with !forceStart (!ownerstart or !fs). Slash: /force-start. Starting a specific code with !startGame or /start-game is limited to the lobby host or someone with admin.game.startGame. The full list is on the commands page.

Where messages go

Public status stays in the server’s setup channel, or in the channel where the lobby was created. The bot does not create a channel, category, role, or voice channel per game. Each status message is tagged with the lobby code and shows the phase, day, player count, weather, and moon. Private roles, night menus, shop use, and whispers are DMs. Joining also sends a DM. If Discord blocks that DM, the status channel mentions the player and shows Allow DMs from this bot.

Day

1

Open the day

The status channel announces the new day. Private night results stay in DMs.
2

Report overnight deaths

If someone died overnight, the status channel says so without revealing secret roles.
3

Pay gold

Gold per day starts at 1, increases by 1 every 3 days, and increases again when 5 or fewer players remain. Everyone receives that day’s gold.
4

Vote

Vote with !vote or /vote. The player with the most votes is executed. Executing the murderer wins the game for the town.
5

Move to night

The status channel shows the coming weather and moon. Night prompts go out by DM. The status channel only says that night has started.

Night

Shop with !shop and !buy, or /shop and /buy, in your DM. Item costs are on the items page. Role abilities use a menu in the DM. Typing the option number still works. When the murderer or werewolf attacks someone, the doctor gets Heal them and Let them die.

Leveling

!level and /level show XP for this server. A finished game grants 5 × day count XP, plus 20 for a murderer, werewolf, or fool win, or 10 for a villager win. The next level costs (level + 1) × 5 × level XP.

Add the bot to a server

Guild install. Replace the application id if you self-host: https://discord.com/oauth2/authorize?client_id=1452886075621249024&permissions=137439332416&integration_type=0&scope=applications.commands+bot client_id is the Discord application id. The scope includes applications.commands. Enable the Message Content and Server Members intents. The invite grants Add Reactions, Send Messages, Embed Links, Attach Files, Read Message History, Use External Emojis, and Use External Sounds. It does not ask for Manage Channels, Manage Roles, Manage Messages, or Move Members. Support server: discord.gg/kriti.

Run it yourself

Requirements

  • Docker, or Python 3.11+
  • A Discord application with the Message Content and Server Members intents enabled
  • A bot token
Keep the token in .env on the machine that runs the bot. When you report an issue, leave the token out. A deletion request should include the server id and user id, still without a token. Details are on bot privacy.

Quick start

1

Clone the repository

The public source is Krithiv-7/Murder-Mystery-Bot-main. More projects are on justkrithiv.
2

Add the token

Put your token in .env:
3

Start the bot

Stop the stack, restart it, or update after a pull:
Other useful commands:

Data on disk

Persistent files live in ./data (the SQLite database and heartbeat). The database uses WAL mode and an in-memory cache so many servers can read settings without waiting on disk. Backups are written to ./backups. Logs are written to ./logs. Those directories are mounted into the container, so docker compose down does not delete them. Write a backup:
That writes backups/backup-YYYY-MM-DD.sqlite. To restore, stop the bot, replace data/bot.sqlite with that file, and start the bot again. Import an older data.json without deleting it:
The first SQLite startup also imports data.json automatically when the database has no guilds yet.

Configuration

Environment defaults live in .env. Each server can override the prefix and the game timers from !settings or the settings panel. See server settings. MongoDB is optional. Install it with pip install pymongo dnspython and set MMB_STORAGE=mongo. The default Docker setup does not run MongoDB.

Local development

make run uses the local Python environment. make docker starts Compose. Tests do not connect to Discord.

If something fails

  • ERROR: DISCORD_TOKEN is missing. Add the token to .env and run docker compose up -d again.
  • The bot is online but ignores ! commands. Enable the Message Content intent, and check that an admin has not disabled the command in the settings panel.
  • The invite scope includes applications.commands. Enable the Message Content and Server Members intents before you invite the bot.
  • A player never receives role DMs. Joining sends a DM. If Discord blocks it, the status channel mentions that player and shows Allow DMs from this bot.
  • docker compose ps shows the container as unhealthy. Wait for the start period, then check docker compose logs -f bot. The health check only looks at a heartbeat file written by the running bot.
  • Discord administrators join with -overwriteAdminWarning, for example !join MM-7F2A -overwriteAdminWarning.
  • !startGame <code> and /start-game are for the lobby host or someone with admin.game.startGame. The host can force-start the active lobby with !forceStart (!ownerstart or !fs). A person can only be in one lobby. Use !leave before joining another.