Files
erika/docs/integrations/leaderboard-bot.md
T
2026-08-06 22:46:47 +07:00

84 lines
2.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Leaderboard bot integration
The Discord bot owns all writes to `user_levels` and `voice_time`. The website only
subscribes to notifications and then reads the committed database state.
## Level formula contract
The website's personal profile calculates next-level progress from the bot's stored
level and cumulative XP. Both services must use this exact formula:
```text
required(0) = 500
required(level) = required(level - 1) + 100 + (level * 25)
total(0) = 0
total(level) = total(level - 1) + required(level - 1)
```
`required(level)` is the XP needed to advance from that level to the next. `total(level)`
is the cumulative XP needed to reach that level. Level 200 is the maximum; the XP
requirement caps at the level-199 value and the profile does not show next-level
progress at the cap.
Store cumulative XP as an integer value and update `xp` and `level` in the same bot
transaction. Changing the formula or maximum level requires a coordinated website
update because the website intentionally does not infer the formula from database
metadata.
## Redis channel
Publish leaderboard notifications to exactly:
```text
erika:sse:leaderboards
```
The bot and website use the same Redis deployment. Do not add Redis credentials to
source code or event payloads.
## Event envelopes
XP changes use this exact JSON envelope:
```json
{"event":"xp","data":{}}
```
Voice-time changes use the affected Discord channel ID:
```json
{"event":"vc","data":{"channelId":"123456789012345678"}}
```
No additional envelope or data fields are accepted. `channelId` is a Discord
snowflake represented as a decimal string.
## Publish ordering
1. Start and complete the bot’s database transaction.
2. Confirm that the transaction committed successfully.
3. Publish the corresponding Redis event or events.
Never publish before commit, from a rollback path, or from a `finally` block. The
notification tells an open website page to read the database again, so publishing
before commit can produce a stale refresh.
## Event cardinality
- Publish one XP event when one transaction changes XP data.
- Publish one VC event per affected channel when one transaction changes voice time.
- When one transaction changes both XP and voice time, publish one XP event and one VC
event for each affected channel.
- For a bulk transaction, deduplicate affected scopes and channel IDs. Publish per
affected scope/channel, never per user.
Example: a bulk transaction changes XP for 200 users and voice time in channels `111`
and `222`. After commit, publish three events: one XP event, one VC event for `111`,
and one VC event for `222`.
## Error handling
Publishing is best-effort. Log Redis failures with enough context to identify the
event scope, but do not roll back or report a successfully committed database
transaction as failed. Open pages recover on SSE reconnection or the next navigation.