A comprehensive Discord bot for managing competitions and more in Realm of the Mad God. You can track loot, complete quests, set points for items, and maintain a variety of leaderboards.
Having issues or confused? Check out our FAQ for common questions!
An admin guide can be found here, at the Administrator Guide!
- Create & Manage PPEs: Create PPEs with automatic penalty calculations.
- Season Management: Track whole season loot, beyond individual characters. Auto-updated with character loot.
- Item Sets: Complete item sets automatically to earn bonus points and receive congratulations messages. Track completed sets per PPE.
- Account Quests: Get item, shiny, and skin quests with automated completion tracking via
/myquests, including board views for active and completed quest sets. - Team Functionality: You can be added to a team and be assigned a team-specific role with points automatically counted together.
- Loot Tracking: Track and graphically view regular, shiny, and divine items and skins automatically or manually.
- Sniffer Integration: Automatically connect in-game characters to your bot account via sniffer for automated logging.
- Bonus System: Add character bonuses such as fame and maxed stats.
- Point Management: Automated and adjustable point calculations with duplicate handling.
- Personal Dashboard: Use
/myinfoas a central menu for all player actions.
- Player Management: One central dashboard for managing any player via
/manageplayer. - Quest Management: View and reset quest progress for any contest player, configure quest mode (Global vs Team Shared), and manage global quest pools via
/managequests. - Set Completion Configuration: Configure bonus points for completed item sets (separate for ST and UT) via
/manageseason->Manage Point Settings. - Team Management: Use
/manageteamsto create, update, and delete teams; assign members through/manageplayer - Global Season Management: Manage the overall season and the nitty gritty details of how the bot works with
/manageseason. Note: this board is still a work in-progress.
Note: These are instructions for self-hosting the bot. The bot typically costs less than a dollar a month to run, and you can try self-hosting it with Railway for free. If you have a large community, you may eventually need to pay for their Hobby plan ($5/month). Make sure to set up monetary limits on your hosting account if you decide to upgrade!
- A GitHub Account
- A Discord Account
- Fork the Repository
- Click on the Fork button at the top right of this repository's webpage.
- Click Create Fork.
- This builds a personal copy of the project under your own GitHub account.
- Update
main.py
- In your newly created fork, click on the
main.pyfile. - Click the pencil icon (labeled
Edit this file) in the top right corner of the file view. - Locate the following lines of code:
# In main.py, update these IDs to your discord server's ID:
SERVER1_ID = 000000000000000000 # Replace with your server ID
SERVER2_ID = 00000000000000000000 # Add more servers as needed
SERVER3_ID = 00000000000000000000 # Add more servers as needed- Update the value of
SERVER1_IDto match your Discord server's ID. (To find this, enable Developer Mode in your Discord advanced settings, then right-click your server icon and select "Copy Server ID".) - If you intend to only have the bot in one Discord server, you can delete the lines for
SERVER2_IDandSERVER3_ID. - If you delete them, be sure to update the very next line of code to look exactly like this:
guilds = [discord.Object(id=SERVER1_ID)]- Click on Commit Changes at the top right, and then click Commit Changes again without changing any settings.
Tip: Whenever you want to update your bot to the latest version in the future, just go to your GitHub repository and click the Sync button!
Before we can host the bot, we need to create it on Discord and get its "password" (the token).
- Create the Application
- Head over to the Discord Developer Portal.
- Log in, click New Application in the top right, give your bot a name, and click Create.
- Get Your Bot Token
- On the left-hand menu, click on the Bot tab.
- Under the bot's username, click the Reset Token button, then click Yes, do it!.
- Click Copy to save your token. Keep this secret and save it somewhere safe! You will need it for the next section. (If you lose it, you'll have to come back and reset it again).
- Optional but recommended: While on this page, scroll down to Privileged Gateway Intents and toggle on Message Content Intent, Server Members Intent, and Presence Intent, then save your changes.
- Generate Your Invite Link
- On the left-hand menu, click on General Information (or OAuth2).
- Find your Application ID (also called Client ID) and click Copy.
- Take the link below and replace
YOUR_CLIENT_ID_HEREwith the number you just copied:
https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID_HERE&permissions=8&integration_type=0&scope=bot+applications.commands
- Save this customized link! You will use it to invite the bot to your server after we finish hosting it in the next step.
For those unfamiliar with programming, Railway offers a simple way to host this bot 24/7 in the cloud with minimal setup. This is perfect if you don't want to run the bot on your personal computer.
Why Railway?
- Affordable: Costs approximately $1β$2 per month (often less).
- Reliable: 24/7 uptime for your bot.
- Simple: No command-line experience needed.
- Easy Deployment: Connect your GitHub account and deploy in minutes.
- Create a Railway Account
- Go to railway.app and sign up using your GitHub account.
- Create a New Project
- Click Create New Project.
- Select Deploy from GitHub repo.
- Authorize Railway to access your GitHub account.
- Select your forked
rotmgppebotrepository.
- Configure Environment Variables
- In your Railway dashboard, click on your project, go to the Variables tab.
- Add a new variable: Name it
DISCORD_TOKENand paste the bot token you saved earlier in the value box. - Add a new variable: Name it
REALMSHARK_INGEST_HOSTand set the value to0.0.0.0. - (Optional) If you want the Sniffer integration to be allowed, add a third variable: Name it
REALMSHARK_INGEST_ENABLEDand set the value totrue.
- Configure Settings
- Navigate to the Settings tab. Keep all settings default except for the following:
- Networking (For Sniffer Integration): If you enabled Sniffer, scroll to the
Networkingsection. Type8080into the Port box and click Generate Domain. This creates your public web link (Endpoint) that you will give people later. - Build: Scroll to the
Buildsection. Set theBuildertoDockerfileand set theDockerfile Pathto/Dockerfile. - Click the purple Deploy button.
- Create the Data Volume
- This is critical: The bot stores all player records in a
/datadirectory. If you skip this, player data will delete itself every time the bot restarts! - In the Railway project view, right-click on empty space (or click the 'New' button) and select Volume.
- Attach it to your
rotmgppebotservice. - Go to your service settings, find the Volume section, and set the Mount Path to
/data. - You will likely need to click Deploy again to apply these changes.
- Deploy
- Railway will automatically deploy the bot. You can watch the build logs in the dashboard.
- Once deployment completes successfully, your bot will start running!
- Add the Bot to Your Server
- Paste the Invite Link you created in Step 2 into your web browser. (Note: You must have "Manage Server" permissions or be the owner of the Discord server to do this).
- Select your server and authorize the bot.
- Check your Discord server to make sure the bot is online.
- Type
/ppehelpin a channel to confirm it's responding!
Now that the bot is in your server, you need to set up its basic functions:
- Create roles: Type
/setuprolesin your server to automatically create the required roles. - Assign permissions: Give users access to the bot by using the
/addplayercommand to register contest members. You can use/manageplayerto give specific people access to management menus.
Please note that this step assumes you've followed the regular Sniffer setup and can successfully use regular Sniffer. If you haven't, you can find the RealmShark sniffer here.
Note: The link to the custom sniffer extension for the bot can be found here. The original sniffer file will not work with it, so you will need to download the one attached below. You can also generate the file yourself.
- Verify Your Endpoint: Check if your bot's sniffer setup is working by opening a web browser and going to
https://<your-railway-domain>/realmshark/health.
- Make sure to replace
<your-railway-domain>with the exact Networking Domain URL you generated in Railway Step 4! * If it works, the webpage should display:{"ok": true, "service": "realmshark-ingest"}.
- Enable in Discord: Go to your Discord server, type
/managesniffer, and selectEnable Sniffer. - Set the Endpoint: Select
Set Endpoint/Edit Endpointand paste your ingest URL:https://<your-railway-domain>/realmshark/ingest. (Again, remember to replace the placeholder with your actual Railway domain). - Download Required Files: Download the Sniffer File and the Loot CSV file here. Keep both of these files in the exact same folder on your computer.
- The latest version of Sniffer can also be found in the RealmShark releases here: https://github.com/LastEternity/RealmShark/releases. The Google Drive link above is still useful because it includes both required files, which the releases page does not.
- You can review and generate your own version of this extended Sniffer File by going to this link: https://github.com/LastEternity/RealmShark/tree/tomato_integration.
- Note: If you rename
rotmg_loot_drops_updated.csvto something else, you will have to update the filename field in the Bridge Review.
- Use /mysniffer to see instructions on how to link your sniffer to the bot. Once you save the settings, you should be pinged by the bot!
- Customize Loot (Optional): You can edit your own version of
rotmg_loot_drops_updated.csvby editing the file in your GitHub repository, saving it, and downloading it. This file determines the base point values of items and tells Sniffer which items to send to the bot. If an item drops but isn't being logged by the bot, it's likely missing from this CSV file!
If you don't want to set up the Sniffer, you can easily enable text-based image recognition instead.
Note: For the bot to recognize an item in a screenshot, the user must be actively hovering their mouse over the item in-game.
- Type
/manageseasonin your Discord server and click onPicture Suggestions. - Enable it and select
Add Channels. You can select any channel IDs where you want the bot to automatically scan images. - Test it out! Post an in-game picture of an item in the designated channel, and the bot will reply with an option for you to accept the suggested item.
| Command | Description |
|---|---|
/newppe |
Create a new PPE with class and penalty setup |
/myinfo |
Open the reusable My Info menu (season loot, quests, character management, bonus editing) |
/setactiveppe |
Switch between your PPE characters |
/addloot |
Add items to your active PPE |
/removeloot |
Remove items from your active PPE |
/addbonus |
Add a bonus to your active PPE |
/removebonus |
Remove a bonus from your active PPE |
/addseasonloot |
Add a unique item to your season collection |
/removeseasonloot |
Remove a unique item from your season collection |
/myquests |
Open the same reusable quest menu available from My Info -> Show Quests |
| Command | Description |
|---|---|
/addplayer |
Register a player for the contest |
/addadmin |
Grant PPE Admin role to a member (server owner only) |
/listplayers |
View all contest participants |
/manageplayer |
Open the admin menu to manage a player's PPE, season loot, quests, team state, and roles (owner-only buttons for Make/Remove Admin) |
/addlootfor |
Add items to any player's specific PPE |
/removelootfrom |
Remove items from any player's PPE |
/addbonusfor |
Add bonuses to any player's PPE |
/removebonusfrom |
Remove bonuses from any player's PPE |
/addpointsfor |
Manually add points to a specific PPE |
/refreshpointsfor |
Recalculate points for a specific PPE |
/refreshallpoints |
Fix all PPE point totals server-wide |
/managequests |
View or update per-server quest targets (regular/shiny/skin), manage quest mode (Global/Team Shared), edit global quest pools, and run Reset All Quests from the menu |
/manageseason |
Open season admin controls: step-by-step Reset Season, contests, picture suggestions, and point settings |
/forcereset |
Emergency full data wipe for this guild (server owner only); use when you need a complete restart |
/addseasonlootfor |
Add a unique item to another player's season collection |
/removeseasonlootfrom |
Remove a unique item from another player's season collection |
Legacy standalone commands for /myloot, /myppes, and /showseasonloot were retired in favor of /myinfo.
The /myquests command and the My Info -> Show Quests button now use the same shared menu view implementation.
When Team Shared Quests are disabled, each member's personal completed quests are rebuilt from team-completed quests they personally own in seasonal loot.
Quest reset actions are now menu-integrated:
- Use
/myquests->Reset Questsfor self resets. - Use
/manageplayer->Show Quests->Reset Questsfor admin resets. - Use
/managequests->Reset All Questsfor server-wide quest resets.
Season reset and recovery flow:
- Use
/manageseason->Reset Seasonfor the guided multi-step reset flow (each action has its own confirmation). - Use
/manageseason->Reset Season->Reset Sniffer Informationto selectively clear sniffer data. - Use
/manageseason->Manage Point Settings->Edit Duplicate Item Pointsto set duplicate scoring reduction (set Point Reduction to0to disable duplicate points). - Use
/forceresetonly as an emergency full wipe when you want to delete all stored bot data for the guild and restart setup.
| Command | Description |
|---|---|
/manageteams |
Open admin team management menu (create team, add/remove members, set leader, rename, delete, and view team leaderboard) |
/myteam |
View your team members and their rankings (optional: specify team name) |
Player team assignment/removal is now handled through /manageplayer in the Team actions panel. Standalone /removeppeadminrole was retired; use /manageplayer for admin-role toggles.
| Command | Description |
|---|---|
/leaderboard |
Open interactive leaderboard menu (PPE, Quest, Character by class, Season Loot, Team), all with paged embeds |
/ppehelp |
Display all available commands |
/listadmins |
View all PPE admins |
/listroles |
Show server role information |
| Command | Description |
|---|---|
/mysniffer |
Player sniffer dashboard: setup steps, generate token, unlink token, and open character configure panel |
/managesniffer |
Admin sniffer dashboard: enable/disable, manage player sniffer view, manage/revoke tokens, change output channel, and reset all sniffer settings |
When Tomato sends loot with character_id, routing is policy-driven:
- If
character_idis mapped to one of your PPEs, the bot logs through/addlootbehavior on that mapped PPE. - If
character_idis mapped as seasonal, the bot logs through/addseasonlootbehavior. - If
character_idis unseen/unmapped, the bot logs through/addseasonloot, pings the player, and stores a pending loot log (item + rarity + flags) for review.
Use /mysniffer and click Configure Characters to manage all character mappings and pending loot through an interactive panel. Start in Show All (all known characters) or Show Pending (only pending unmapped characters), then use the panel buttons to map characters to PPEs (which automatically applies pending loot), set characters as seasonal, clear pending logs, and navigate between entries.
The panel is intuitive with Prev / Next buttons to cycle through characters, and all destructive actions require explicit confirmation.
The panel/config view shows detected in-game character name/class when available, and mapping is class-validated: a character cannot be mapped to a PPE of a different class.
Pending unmapped character events are stored in per-player files so main guild config files stay compact.
- Never commit your
.envfile with the bot token to GitHub - The
/datavolume is where all player records, loot tracking, and season data are storedβnever delete it - If you need to update the code, simply push changes to your GitHub repo and Railway will redeploy automatically
- Check Railway's documentation for additional help
PlayerData
βββ is_member: bool
βββ active_ppe: int
βββ team_name: str (optional) # Name of team player is on
βββ season_item_history: Dict[str, List[int]] # season item variant key -> sorted timestamps
βββ quests: QuestData
β βββ current_items: List[str]
β βββ current_shinies: List[str]
β βββ current_skins: List[str]
β βββ completed_items: List[str]
β βββ completed_shinies: List[str]
β βββ completed_skins: List[str]
βββ ppes: List[PPEData]
βββ id: int
βββ name: ROTMGClass
βββ points: float
βββ completed_sets: List[str] # Names of completed item sets
βββ loot: List[Loot]
β βββ item_name: str
β βββ quantity: int
β βββ divine: bool
β βββ shiny: bool
βββ bonuses: List[Bonus]
βββ name: str
βββ points: float
βββ repeatable: bool
βββ quantity: int
Seasonal ownership, rarity spread, and time-based summaries are derived from `season_item_history` at read time.
When loot rarity is missing or `None`, it is treated as `common`.
TeamData
βββ name: str # Team name
βββ leader_id: int # Discord user ID of leader
βββ members: List[int] # Discord user IDs of all members
rotmg_loot_drops_updated.csv: Item point valuesbonuses.csv: Available achievement bonuses{guild_id}_{user_id}_loot_records.json: Player data storage (one file per player){guild_id}_teams.json: Team data storage (per guild){guild_id}_config.json: Per-server settings storage (quest target counts and future config){guild_id}_{user_id}_realmshark_pending.json: Pending unmapped character logs for each player
- PPE Player: Can create PPEs and manage own data
- PPE Admin: Full administrative access to all features
- Items have base point values from
rotmg_loot_drops_updated.csv. - Divine items: 2x multiplier
- Shiny items: Special point values from separate entries
- First item: Full point value
- Additional copies: Base value multiplied by the configurable Point Reduction setting (default
0.5) - Disable duplicates: Set Point Reduction to
0in/manageseason->Manage Point Settings->Edit Duplicate Item Points - Removal: Correctly calculates which points to subtract
- Pet Level: -0.25 points per level
- Exalts: -0.5 points per exalt
- Loot Boost: -2 points per 1% boost
- In-Combat Reduction: -10 points per 0.2 seconds
- What Are Sets: Item sets are collections of 4 special items (Weapon, Ability, Armor, Ring). When you collect all 4 items from a set, you complete the set.
- Auto-Detection: The bot automatically detects when a set is completed as soon as you log the final item via
/addloot, sniffer, or/submitloot. - Congratulations Message: When a set is completed, a public congratulation message is posted to the channel.
- Point Rewards: Each completed set grants configurable bonus points (default: 0). Admins can set different points for ST (Standard) and UT (Unique) sets via
/manageseason->Manage Point Settings->Manage Set Completion Points. - One-Time Per PPE: You only get the bonus once per set per PPE. Completing the same set on a different PPE grants the bonus again.
- Configuration: Admins can manage set bonus points through the interactive menu, with separate sections for ST and UT sets. Use the form to set points per set (e.g.,
Golden Archer Set=50) or reset all to 0.
Teams enable collaborative PPE competition where multiple players combine their efforts for group rankings.
- Team Creation: Admins create empty teams from
/manageteamsand assign leader/member later - Member Management: Admins can add/remove players through
/manageplayer - One Team Per Player: Players cannot be on multiple teams simultaneously
- Team Leaderboard: Teams ranked by combined points (using each member's best PPE)
- Team Viewer:
/myteamshows all team members ranked by their best PPE points - Automatic Roles: Discord roles created automatically for each team
- Team Renaming: Admins can rename teams from
/manageteams - Season Reset: All teams are deleted when season resets
- Robust Removal: Team removal works even for players no longer in contest records when handled from
/manageplayer
- Each team member's highest-scoring PPE is counted toward the team total
- Team points = Sum of all members' best PPE points
- Members added/removed update totals automatically
- Team leader can be viewed on the team leaderboard
/manageteams: Admin-only menu includes a Team Leaderboard button with paged rankings/myteam: View your team members and their individual rankings (or specify a team name to view any team)
/manageplayer: Removes players from teams and/or contest data from a single admin panel, and exposes owner-only PPE Admin add/remove actions
- Admin Only: Create teams, delete teams, set leaders, rename teams, force remove players from teams
- All Players: View team leaderboard, view own team with
/myteam
Most non-regular PPEs are represented as a normalized signature like:
pet:yes|tiered:yes|minimum:legendary|shiny:no|enforce_shiny_rarity:no|duo:no
Fields:
pet:yesmeans pets allowed,nomeans no-pet rules.tiered:yesmeans tiered items allowed,nomeans UT-only style rules.minimum: minimum rarity floor (common,uncommon,rare,legendary,divine, plus internalall_shinies_allowed).shiny:yesmeans shiny-only mode.enforce_shiny_rarity: whether the minimum rarity also applies to shiny items.duo:yesfor duo mode,nofor solo.
The bot may show:
- A full label (for example
Legendary or Shiny PPE). - A compact label (for example
All_SH|LPE,DPE,Duo NPE) used in summaries andType Multiplier (...)text.
Compact labels are generated from options, not only from static preset names, so custom combos can still produce meaningful shorthand.
All_SH|... means: minimum rarity applies to non-shiny items, while shiny items are still all allowed.
This prefix appears only when all of the following are true:
shiny:nominimumislegendaryordivineenforce_shiny_rarity:no
Examples:
minimum:legendary,enforce_shiny_rarity:no->All_SH|LPEminimum:legendary,enforce_shiny_rarity:yes->LPEminimum:divine,enforce_shiny_rarity:no->All_SH|DPEminimum:divine,enforce_shiny_rarity:yes->DPE
For minimum:rare (or lower), enforce behavior does not change label style. Internally, low minimum rarities normalize to enforce_shiny_rarity:yes.
Duo mode is a modifier on top of a base PPE ruleset.
How duo labels are shown:
- The bot first computes the non-duo compact label.
- Then it prefixes with
Duo.
Examples:
- Base
PPE+ duo ->Duo PPE - Base
NPE+ duo ->Duo NPE - Base
All_SH|LPE+ duo ->Duo All_SH|LPE
Multiplier behavior mirrors this model: base rules are resolved first, then duo multiplier is applied on top.
rotmgppebot/
βββ main.py # Bot entry point
βββ dataclass.py # Data structures
βββ requirements.txt # Dependencies
βββ menus/ # Shared menu/view architecture
β βββ myinfo/ # My Info menu package (home/season/character/common)
β βββ myquests/ # My Quests menu package (view/common)
β βββ menu_utils/ # Reusable owner/confirm menu components
βββ slash_commands/ # Command implementations
βββ utils/ # Utility modules
β βββ player_records.py # Data persistence
β βββ calc_points.py # Point calculations
β βββ autocomplete.py # Dynamic suggestions
β βββ embed_builders.py # Discord embeds
β βββ role_checks.py # Permission validation
βββ data/ # CSV and JSON files
- Slash commands stay thin and delegate to reusable menu modules in
menus/. - The same
MyQuestsViewis used by both/myquestsand/myinfo-> Show Quests. - My Info and My Quests are split into focused submodules so each button path maps to a dedicated menu/view class instead of one bloated file.
- Create command file in
slash_commands/ - Import in
main.py - Register with Discord using decorators
- Add to help system in
ppehelp_cmd.py
Use the migration system in player_records.py to safely update data structures without losing existing player data.
- Fork the repository
- Create a feature branch
- Make your changes with proper error handling
- Test thoroughly with edge cases
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
- Realm of the Mad God community for inspiration and feedback
- Discord.py developers for the excellent library
- Contributors who help improve the bot
- Issues: Report bugs on GitHub Issues
- Discord: Join our community server for help and updates
- Documentation: Check the
/ppehelpcommand for in-bot guidance
Made with β€οΈ for the RotMG PPE community