# Database The bot uses PostgreSQL through `database/sql` with the pgx driver. All SQL lives in repos under `internal/db/repos`. ## Who owns the schema **`schema.sql`** in this repository is the schema. It describes every table the bot reads and writes, and it is enough to run the bot. It is safe to run more than once (`CREATE TABLE IF NOT EXISTS`). The bot never runs migrations itself. The hosted Velox also has a proprietary dashboard API that keeps its own migrations for the same tables. That is the maintainer's concern: if your change adds or alters a table, update `schema.sql` and say so in your pull request, and the maintainer ports it. You never need the API to develop or run the bot. Two things are not in `schema.sql`, because only a dashboard needs them: the `NOTIFY` triggers on the three `*_commands` tables (see below), and a migration history. If you write a tool that inserts command rows, add the trigger yourself: ```sql CREATE OR REPLACE FUNCTION notify_music_command() RETURNS TRIGGER AS $$ BEGIN PERFORM pg_notify('music_commands', NEW.id::text); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER music_commands_notify AFTER INSERT ON music_commands FOR EACH ROW EXECUTE FUNCTION notify_music_command(); ``` The same works for `ticket_commands` and `security_commands`. Without the trigger the bot still picks the rows up, but only the next time it starts or is notified for another reason. ## Tables | Area | Tables | Notes | | --- | --- | --- | | Leveling | `levels`, `levelsettings`, `levelrewards`, `levelup` | XP and level per member, settings, role rewards, the level-up channel | | Welcome | `welcome`, `defaultrole` | Welcome channel, templates, GIF, default role | | Logging | `logsettings` | Log channel and on/off | | Twitch | `twitch`, `twitch_config` | Streamers and the notification channel | | Fun | `rps` | Rock-paper-scissors stats | | Meeting rooms | `meeting_lobbies`, `meeting_temp_channels`, `meeting_settings` | Lobbies (10 per server, enforced by a trigger), the temporary rooms and who created them. `meeting_settings.lobby_channel_id` is the old single lobby column and is no longer read | | Projects | `projects`, `project_members` | | | Scheduling | `schedules`, `schedule_messages`, `user_settings` | Sessions, the DM messages to update, and each member's timezone | | Music | `music_settings`, `music_now_playing`, `music_queue`, `music_commands` | State a dashboard can show, plus requests to the bot | | Tickets | `ticket_settings`, `ticket_categories`, `tickets`, `ticket_commands` | | | Security | `security_settings`, `security_trusted`, `security_quarantines`, `security_lockdowns`, `security_raids`, `security_raid_caught`, `security_snapshots`, `security_events`, `security_commands` | Settings, who is trusted, quarantines (with the roles to give back), locked channels (with the permission to restore), raids in progress, snapshots, an event history, and dashboard requests | | Beta | `beta_settings`, `beta_guilds` | `beta_settings` is one row: the beta server and the Beta Tester role. `beta_guilds` lists servers whose owner opted in to beta features. Both are written by the dashboard and read by the bot. `beta_registered` is the bot's own note of where it registered beta commands | Discord IDs are `BIGINT` (snowflakes fit in 64 bits). In Go they are `int64`; Discord's own API gives them to you as strings, so convert at the edge. ## Dashboard to bot: settings and requests There are two kinds of traffic from a dashboard to the bot, and the difference matters. **Settings.** A dashboard writes a row, and the bot reads it when it next needs it. Nothing wakes the bot up. Hot paths cache settings for a short time. The bot's beta settings are polled every 30 seconds. **Requests that need the bot to act.** `music_commands`, `ticket_commands` and `security_commands` work as a small queue: 1. A dashboard inserts a row (`command_type`, a JSON `payload`, `requested_by`, `processed_at` empty). 2. A trigger in the database runs `pg_notify('