Pedro-Bot is a Discord bot with two core features:
- Matchmaking (organizing game lobbies with threads, join/leave buttons, etc.).
- Leveling (assigning XP for messages and granting roles when users level up).
Below is a detailed breakdown of the repository’s structure, how each component works, and how developers can extend it further.
- Project Structure
- Matchmaking Feature
- Leveling Feature
- User Profiles
- Server Statistics
- Backup and Restore
- Admin Audit Logs
- Environment Variables
- How to Run via Docker Compose
- Health Check
- Adding New Features
- Reusing Functions and Avoiding Duplicates
- CI/CD and Releases
Pedro-Bot/ ├── buttons/ │ ├── join.js │ ├── leave.js ├── commands/ │ ├── admin/ │ │ ├── reactionRoles.command.js │ │ ├── schedule.command.js │ │ └── settings.command.js │ ├── levels/ │ │ └── level.command.js │ ├── matchmaking/ │ │ └── matchmaking.command.js │ └── manageChannels.command.js ├── config/ │ └── constants.js ├── events/ │ ├── interactionCreate.js │ ├── messageCreate.js │ ├── guildMemberAdd.js │ ├── guildMemberRemove.js │ ├── commandExecution.js │ ├── ready.js │ └── [other-events].js ├── models/ │ ├── Lobby.js │ ├── ReactionRole.js │ ├── Schedule.js │ ├── Settings.js │ └── UserXP.js ├── services/ │ ├── lobbyService.js │ ├── scheduleService.js │ ├── settingsService.js │ └── userService.js ├── utils/ │ ├── ButtonManager.js │ ├── database.js │ ├── errorHandler.js │ ├── levelUtils.js │ ├── matchmakingHelpers.js │ ├── scheduler.js │ ├── roleManager.js │ └── threadManager.js ├── index.js
index.js: Sets up the Discord client, registers commands, and listens for both interactions (interactionCreate) and standard messages (messageCreate) for awarding XP.utils/database.js: Loads environment variables for MongoDB, then runsmongoose.connect(...). Exported once for the entire app.utils/cache.js: Connects to Redis for caching frequently accessed data.models/*.js: Each file defines a Mongoose schema for storing data:Lobby.js: For matchmaking lobbies.UserXP.js: For leveling / XP.ReactionRole.js: For reaction roles.
commands/matchmaking/: Self-contained logic for the matchmaking system.commands/levels/: Self-contained logic for leveling. Contains:levelsManager.js: Awarding XP and checking level-ups.levelUtils.js: XP/level threshold formulas.level.js: The/levelslash command.
-
Slash Command:
/matchmaking(incommands/matchmaking.js):- Prompts the user for time, game code, optional template, slot count, and description.
- Creates a new message in the configured matchmaking channel (default
#matchmaking) with an embed and two buttons (JOIN, LEAVE). - Creates a new thread under that message for discussion.
- Stores the lobby data in MongoDB (
Lobbymodel). - Schedules a start time with
scheduler.scheduleLobbyStartto mark the lobby as started and update the embed. - Also schedules automatic cleanup six hours after the start time, archiving the lobby into
LobbyHistory. - The role mentioned in the embed is configured with
/settings set-matchmaking-roleand stored in MongoDB.
-
Join/Leave Logic:
- Inside
index.jsinteractionCreateevent, we handleisButton(). - If the button ID is “join” or “leave,” it updates the corresponding lobby’s data (stored in Mongo), and edits the embed to reflect the updated users.
- Inside
-
Embed Updates:
helpers.jswithin matchmaking builds or rebuilds the embed (buildLobbyEmbed), adding a small footer(MATAC) The Mature Tactical Circkle.updateLobbyEmbedre-edits the original message.- Full lobbies prevent additional joins based on the configured slot count.
- History & Recurring Lobbies:
- Finished lobbies are archived automatically and can be counted with
historyService.getLobbyStats(). - Use
/scheduleto create recurring lobbies by scheduling the/matchmakingcommand.
- Finished lobbies are archived automatically and can be counted with
By keeping all “matchmaking” references in commands/matchmaking/ and using the MATCHMAKING_CHANNEL environment variable for the channel name, we avoid scattering that code throughout the project.
| HEALTH_PORT | Port for the health endpoint (default 3000)
- Mongo Model:
UserXP.jsstores each user’s_id(Discord ID), theirxptotal, currentlevel, etc. - XP Awarding:
- In
index.js, themessageCreateevent grants XP. Each activity can use a multiplier defined inconfig/constants.js. - The logic of awarding XP is in
commands/levels/levelsManager.js→incrementXP(). - We store and fetch the user doc from Mongo, apply any XP decay, add XP, check for a new level, and if so, assign a role and post a “level up” message.
- In
- Level/XP Formula:
- Role Assignment:
- Roles for each level are stored in MongoDB using the
/settings set-rolecommand. - When a user levels up, the bot checks if a role is configured for that level and assigns it.
- Roles for each level are stored in MongoDB using the
/levelCommand (optional, inlevel.js):- A user can check their current XP, level, and how much XP remains until the next level.
/leaderboardCommand:- Shows the top users with pagination (
/leaderboard page:2).
- Shows the top users with pagination (
/challengeCommand:- Lets users claim a daily or weekly XP bonus.
- XP Decay:
- After seven days of inactivity, XP slowly decays when the user returns.
This system is minimal, but it can be expanded easily with leaderboards, cooldowns, or advanced spam checks.
Use /profile to see your level, XP, and rank. The command reads from the same UserXP collection used by the leveling system.
/serverstats shows total members, how many users have XP records, the number of active lobbies, and how many scheduled commands are defined.
Admins can run /backup create to save the database to backups/. Use /backup restore file:<path> to restore from a saved file.
Every admin command writes a record to MongoDB. Query the AuditLog collection to review changes.
| Variable | Description |
|---|---|
DISCORD_TOKEN |
Discord bot token |
CLIENT_ID |
Your bot’s application client ID |
GUILD_ID |
The server (guild) ID where you want to register commands |
MONGO_URI |
MongoDB connection string (e.g., mongodb://mongodb:27017/pedro-bot) |
REDIS_URI |
Redis connection string (e.g., redis://redis:6379) |
MATCHMAKING_CHANNEL |
Name of the channel used for matchmaking lobbies (default matchmaking) |
HEALTH_PORT |
Port for the health endpoint (default 3000) |
Note: Role mappings and the matchmaking mention role are configured with the /settings command and stored in MongoDB. The matchmaking channel is defined with the MATCHMAKING_CHANNEL environment variable.
Usage:
The application exits if any of the required variables are missing at startup.
Docker secrets can be used to supply sensitive values like DISCORD_TOKEN.
- In Docker Compose, set these as environment variables under
pedro-bot. - See
docker-compose.ymlfor an example.
- Clone this repo.
- Configure your
.envor environment variables (token, guild ID, Mongo URI, etc.). - Ensure your
docker-compose.ymlbuilds from the current directory:services: pedro-bot: build: context: . secrets: - discord_token environment: - CLIENT_ID=... - GUILD_ID=... - MONGO_URI=mongodb://mongodb:27017/pedro-bot - REDIS_URI=redis://redis:6379 depends_on: - mongodb - redis mongodb: image: mongo:7.0 volumes: - mongo_data:/data/db redis: image: redis:7-alpine
volumes: mongo_data:
-
The repository includes a
.dockerignoreso your build context stays small. -
Run
docker compose build&&docker compose up -d(ordocker compose up --build -d). -
Your bot will connect to Discord, connect to Mongo, register slash commands, and start listening for messages.
The bot exposes GET /health on the port defined by HEALTH_PORT (defaults to 3000). A response of OK indicates the service is up.
Adding New Features
Create a new folder/file under commands//.js for your logic.
Define Mongoose models in models/.js if you need database storage.
Register a slash command by creating a file under commands/.js that exports a SlashCommandBuilder.
The index.js automatically picks up .js files in /commands, registering them with Discord.
If your feature requires listening to raw events (like messageCreate), add it in index.js or a dedicated event file, just be mindful of the single “bot instance” approach to avoid collisions.
Reusing Functions and Avoiding Duplicates
This codebase follows a few guidelines to keep things clean:
-
No Hardcoding: References to specific channels (like #matchmaking) and roles are done in the command or environment variables, not scattered across utility files.
-
Helpers for Generic Logic:
ButtonManager.js provides a generic way to create buttons (no mention of matchmaking or level).
database.js is a single point for connecting to Mongo.
- Command-Specific Folders:
commands/matchmaking/ has all the matchmaking logic in one place.
commands/levels/ has the leveling logic in one place.
This helps reduce duplication of logic across multiple commands.
- Models:
Each domain (lobbies, user XP) has its own model file in models/.
All of them share the same mongoose instance from utils/database.js.
These practices ensure that if you rename or remove a channel, or if you want to update an XP formula, you only do it in one place.
CI/CD and Releases
GitHub Actions build and publish the Docker image whenever application code or build files change on main or in a pull request. When a release is published, the same workflow runs and pushes an image tagged with that version. Documentation or workflow tweaks won't trigger a new image. Each pull request gets its own pr-N tag so you can test before merging. release-please opens a pull request with the next version. After that PR merges, GitHub creates the release and the Docker workflow uploads images tagged latest, the short commit SHA, and the release number (for example 1.0.1). The workflow reads the tag straight from the GitHub release so the container tag always matches. Release-please stores the current version in .release-please-manifest.json.
Release tags simply use X.Y.Z. Downstream jobs, including the Docker workflow, use that tag directly when naming images.
The repository also runs a PR Validation workflow on every branch and pull request. It installs dependencies, checks formatting with ESLint and Prettier, audits dependencies, runs the unit tests, and ensures the Docker image builds and can start.
Questions or Feedback?
If you run into issues or want to propose enhancements (like a spam filter for XP, or more advanced matchmaking scheduling), feel free to open a pull request or file an issue on the repository!
Enjoy using Pedro-Bot, and happy gaming!