Skip to content

Commit 66eeafc

Browse files
committed
docs: update readme with current project state
1 parent 1702c0a commit 66eeafc

1 file changed

Lines changed: 101 additions & 27 deletions

File tree

README.md

Lines changed: 101 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,102 @@
11
# Karma Bot
22

3-
A Discord bot that lets server members give each other karma points via mentions.
3+
A Discord bot for giving and tracking karma points across server members and teams.
44

55
## How It Works
66

7+
### Giving Karma
8+
79
Mention a user followed by `++` (two or more plus signs) to give them karma:
810

911
```
10-
@alice +++
12+
@alice +++ # gives Alice 3 karma
13+
@bob +++++ # gives Bob 5 karma
14+
```
15+
16+
### Removing Karma
17+
18+
Use `--` (two or more minus signs) or em dashes to remove karma:
19+
20+
```
21+
@alice --- # removes 3 karma
22+
@alice — # removes 2 karma (em dash counts as 2)
23+
@alice ——— # removes 6 karma
1124
```
1225

13-
This gives Alice 3 karma. The bot replies with their updated total.
26+
### Team Karma
27+
28+
Mention a team role to give karma to all members of that team:
29+
30+
```
31+
@Frontend +++ # gives +3 karma to every member with the Frontend role
32+
```
33+
34+
Team role karma allows self-karma (if you're on the team) and bypasses the global rate limit, but per-user limits still apply.
35+
36+
### Rate Limits
1437

15-
### Rules
38+
- **Global limit** — max total karma you can give/take in a rolling interval (`LIMIT_MAX_KARMA_SEND` / `LIMIT_MIN_KARMA_SEND`)
39+
- **Per-user limit** — max karma you can give/take to the same person per interval (`LIMIT_MAX_KARMA_USER_SEND` / `LIMIT_MIN_KARMA_USER_SEND`)
40+
- The bot tells you exactly why karma was capped (per-user limit, global budget, or rate limited with reset time)
1641

17-
- Only members with designated roles (configured via `KARMA_USER_ROLE_IDS`) can give and receive karma
18-
- You cannot give karma to yourself
19-
- Karma giving is rate-limited to `MAX_KARMA_SEND` points per `MAX_KARMA_SEND_INTERVAL`
20-
- Karma accumulates in time-based windows (semiannual by default), configurable via a `dateutil` rrule
42+
### Karma Windows
43+
44+
Karma accumulates in configurable time-based windows (semiannual by default). When a window ends, the leaderboard is automatically posted and a new window begins. Historical windows are preserved.
45+
46+
## Commands
47+
48+
### Karma
49+
50+
| Command | Description |
51+
|---|---|
52+
| `/leaderboard` | View the karma leaderboard (with period selector dropdown) |
53+
| `/team-leaderboard` | View the team karma leaderboard |
54+
| `/ping` | Check bot latency |
55+
56+
### Configuration (requires Manage Server)
57+
58+
| Command | Description |
59+
|---|---|
60+
| `/config set-leaderboard-channel #channel` | Set where leaderboards are posted on window rollover |
61+
| `/config add-karma-role @role` | Add a role that can give and receive karma |
62+
| `/config remove-karma-role @role` | Remove a karma role |
63+
| `/config view-karma-roles` | List all karma roles |
64+
| `/config add-team-role @role` | Add a role as a team for the team leaderboard |
65+
| `/config remove-team-role @role` | Remove a team role |
66+
| `/config view-team-roles` | List all team roles |
67+
68+
When no karma roles are configured, everyone can use the bot.
2169

2270
## Project Structure
2371

2472
```
2573
src/
26-
bot.py # Entry point
27-
core/config.py # Constants and karma window rule
74+
bot.py # Entry point, error handler, persistent views
75+
core/
76+
config.py # Rate limits, karma window rrule
77+
checks.py # @has_karma_role() decorator + member check
78+
emojis.py # Custom Discord emoji constants
79+
functions/karma.py # Leaderboard embed builders
80+
message_utils/paginator.py # Persistent paginated embeds
2881
backend/
29-
db.py # Database singleton (async SQLModel + asyncpg)
30-
models.py # User, KarmaWindow, KarmaTransaction
82+
db.py # Database singleton (async SQLModel + asyncpg)
83+
cache.py # Stale-while-revalidate cache manager
84+
models.py # User, KarmaWindow, KarmaTransaction, GuildConfig, UserTeamRole
3185
tables/
32-
base.py # BaseDB[T] — generic CRUD
33-
db_user.py # UserDB — user operations + increment_karma
34-
db_karma_window.py # KarmaWindowDB — window queries
35-
db_karma_transaction.py # KarmaTransactionDB — transaction log + rate limit
86+
base.py # BaseDB[T] — generic CRUD
87+
db_user.py # UserDB — increment_karma
88+
db_karma_window.py # KarmaWindowDB — leaderboard + window queries
89+
db_karma_transaction.py # KarmaTransactionDB — rate limit queries
90+
db_guild_config.py # GuildConfigDB — per-guild settings (cached)
91+
db_user_team_role.py # UserTeamRoleDB — team role cache + aggregation
3692
cogs/
3793
commands/
38-
general.py # /ping, /hello
39-
karma.py # Karma listener with role + rate limit checks
40-
tests/ # pytest suite (in-memory SQLite, no Docker needed)
94+
general.py # /ping
95+
karma.py # Karma listener, /leaderboard, /team-leaderboard
96+
config.py # /config command group
97+
workers/
98+
karma_history.py # Auto-post leaderboard on window rollover
99+
tests/ # 61 tests (in-memory SQLite, no Docker needed)
41100
```
42101

43102
## Setup
@@ -50,12 +109,11 @@ tests/ # pytest suite (in-memory SQLite, no Docker nee
50109
### Development
51110

52111
```bash
53-
# Clone and enter the project
54112
cp .env.example .env
55113
# Edit .env with your DISCORD_TOKEN
56114

57115
# Start the bot + Postgres
58-
docker compose up
116+
docker compose up --build
59117

60118
# Or run locally (with Postgres already running)
61119
pip install -r requirements.txt
@@ -69,6 +127,12 @@ python -m src.bot
69127
| `DISCORD_TOKEN` | Your Discord bot token |
70128
| `DATABASE_URL` | PostgreSQL connection string (dev override sets this automatically) |
71129

130+
### Discord Developer Portal
131+
132+
Enable these Privileged Gateway Intents for your bot:
133+
- **Message Content Intent** — needed to read the `++`/`--` karma pattern
134+
- **Server Members Intent** — needed for `guild.get_member()` in leaderboards
135+
72136
## Testing
73137

74138
Tests use in-memory SQLite — no database setup required.
@@ -80,18 +144,21 @@ python -m pytest tests/ -v
80144

81145
## Configuration
82146

83-
Edit `src/core/config.py` to adjust:
147+
Edit `src/core/config.py` to adjust rate limits:
84148

85149
| Setting | Default | Description |
86150
|---|---|---|
87-
| `MAX_KARMA_SEND` | 5 | Max karma a user can give per interval |
88-
| `MAX_KARMA_SEND_INTERVAL` | 6 hours | Rolling window for the rate limit |
89-
| `KARMA_USER_ROLE_IDS` | MAC Team role | Discord role IDs allowed to use karma |
151+
| `LIMIT_MAX_KARMA_SEND` | 100 | Max total karma a user can give per interval |
152+
| `LIMIT_MIN_KARMA_SEND` | -100 | Max total karma a user can remove per interval |
153+
| `LIMIT_KARMA_SEND_INTERVAL` | 6 hours | Rolling window for the global rate limit |
154+
| `LIMIT_MAX_KARMA_USER_SEND` | 5 | Max karma a user can give to one person per interval |
155+
| `LIMIT_MIN_KARMA_USER_SEND` | -5 | Max karma a user can remove from one person per interval |
156+
| `LIMIT_KARMA_USER_SEND_INTERVAL` | 6 hours | Rolling window for the per-user rate limit |
90157
| `KARMA_WINDOW_RULE` | Semiannual (Jan/Jul) | `dateutil.rrule` defining karma reset periods |
91158

92159
### Changing the Karma Window Period
93160

94-
The `KARMA_WINDOW_RULE` in `config.py` accepts any `dateutil.rrule`:
161+
The `KARMA_WINDOW_RULE` accepts any `dateutil.rrule`:
95162

96163
```python
97164
from dateutil.rrule import MONTHLY, WEEKLY, MO, rrule
@@ -106,4 +173,11 @@ KARMA_WINDOW_RULE = rrule(WEEKLY, interval=2, byweekday=MO, dtstart=...)
106173
KARMA_WINDOW_RULE = rrule(MONTHLY, dtstart=...)
107174
```
108175

109-
When a new period starts, the next karma increment automatically creates a new window row. Historical windows are preserved.
176+
## Architecture
177+
178+
- **SQLModel** — typed models that double as Pydantic + SQLAlchemy
179+
- **asyncpg** — async PostgreSQL driver
180+
- **BaseDB[T]** — generic CRUD base class; per-table classes add domain methods
181+
- **cached()** — stale-while-revalidate wrapper for any table (used on `guild_config`)
182+
- **Singleton pattern**`from src.backend.tables import user, karma_window` and use directly
183+
- **Persistent paginator** — embed buttons survive bot restarts via `PersistentPaginatorView`

0 commit comments

Comments
 (0)