Discord Voice State Snapshot
A Discord server-moderation bot that keeps an accurate, queryable ledger of who was in which voice channel and when, including across the bot's own restarts and outages, which Discord's event model makes hard to reconstruct. Built with Python and PostgreSQL.
- Python
- discord.py
- PostgreSQL
- SQLAlchemy
- Alembic
- Docker Compose
Overview
A Discord server-moderation bot that records every voice-channel join and leave into PostgreSQL as an append-only ledger. It answers questions about the history through slash commands: who’s in voice now, a channel’s log over any window, one member’s sessions, and the exact intervals a given set of members shared a channel.
Usage
View data as table
| Month | Voice events / month | Servers with ≥1 event / month |
|---|---|---|
| Jan ’25 | 12,213 | 10 |
| Feb ’25 | 13,453 | 13 |
| Mar ’25 | 12,979 | 15 |
| Apr ’25 | 22,530 | 29 |
| May ’25 | 24,027 | 32 |
| Jun ’25 | 30,099 | 39 |
| Jul ’25 | 34,591 | 47 |
| Aug ’25 | 32,134 | 52 |
| Sep ’25 | 57,693 | 70 |
| Oct ’25 | 65,130 | 63 |
| Nov ’25 | 86,386 | 87 |
| Dec ’25 | 119,189 | 107 |
| Jan ’26 | 115,880 | 145 |
| Feb ’26 | 122,739 | 138 |
| Mar ’26 | 138,187 | 192 |
| Apr ’26 | 293,970 | 193 |
| May ’26 | 200,811 | 207 |
| Jun ’26 | 191,516 | 238 |
1,616,622 events across 435 servers as of early July 2026. It was shared with a handful of friends at the start and spread to the rest on its own, without any advertising.
How it works
How to use this
- Move people in and out of voice with the name buttons. Each move appends one immutable row to the ledger on the right. Nothing is ever overwritten.
- Run the bot's real commands in the console below the timeline. Every answer is computed from the ledger, the same way the running bot's SQL does it.
- Press play to let the clock run, or leave it paused and place events by hand.
- Take the bot offline, move people around, then bring it back: reality drifts from the ledger during the gap, and reconciliation heals it with
UNKNOWN_rows dated to the last heartbeat, never to the restart. - Load an example to start from a ready-made scene, then query or extend it.