Most Discord bots handle leveling by simply counting chat messages. I wanted Dansday to track actual community engagement, which means handling complex states like voice activity, camera usage, and screen sharing. Building a system that tracks all of this simultaneously without falling over or losing data requires a solid architecture.
Experience points in Dansday do not just come from typing. I built four distinct tracking mechanisms.
- Chat Messages
Users earn experience for sending messages, throttled by a configurable cooldown to prevent basic spamming. - Active Voice
Members who sit in voice channels unmuted and undeafened earn points over time. The system ticks internally every few seconds but only awards experience when a specific interval passes. - AFK Voice
I do not rely on standard idle channels. If a user mutes or deafens themselves, they drop into an AFK earning rate, which administrators can adjust independently. - Video and Streaming
Sharing a camera or broadcasting a screen to the server triggers separate experience rates, rewarding users who actively present or interact visually.
To manage all this data, every member gets a dedicated row in my database to store their permanent server member levels. I never store just the level. I store the absolute raw experience earned. The level itself is computed entirely from that raw number. I built two progression modes for server administrators. The linear mode applies a flat multiplier, while the exponential mode uses a math curve where each new level requires progressively more experience. Because the raw experience is the single source of truth, whenever someone levels up, the bot instantly recalculates the leaderboard ranks across the entire server by sorting that experience descending.
When a user finally crosses a threshold, the level updates in the database, the server rank is recalculated, and the system fires off notifications. Depending on how they earned the level, the bot drops an announcement in a designated progress channel with specific context regarding chat or voice. If they hit the top three spots on the server, I added a special rank up notification. They also get a direct message with a link to the public leaderboard, assuming they have their messages open.
Speaking of the leaderboard, I wanted these statistics accessible outside the chat client. I built an opt in public web interface featuring a live streaming API. Using Server Sent Events, the backend pushes live leaderboard updates to the web page. The interface lets anyone sort the top hundred members by chat messages, total voice time, or streaming minutes. To protect server resources, I implemented a twenty second local cache that only pushes state when it detects a genuine change.
The hardest part of building stateful bots is crash recovery. When a bot goes down, active voice sessions usually corrupt. I designed Dansday to survive restarts with minimal data loss. On startup, a recovery routine scans every voice channel in every connected server. If it finds a member still sitting in voice, it resumes their session right where it left off by checking their last rewarded timestamp and catching up on any missed minutes. If the database thinks someone is in voice but they left while the bot was offline, those orphaned state flags are automatically scrubbed.
Five seconds after the bot becomes ready, it runs a massive integrity check. It compares every stored level against the raw experience, corrects any mathematical mismatches, and locks in the server ranks. I made an intentional tradeoff with message cooldowns, keeping them purely in memory. They vanish on restart. This means users get a brief grace period to earn chat experience immediately after a reboot, but it saves the database from rapid, unnecessary disk writes.
On the front end, everything is driven by interactive buttons. Users click a leveling button to open their personal profile, checking their progress bar and exact voice minutes. Administrators get a full control panel to tweak base experience, multipliers, cooldowns, and role eligibility. Building it this way separated the complex state tracking from the user interface, resulting in a system that feels completely seamless to the end user.