Files
chat/internal/db/schema/001_initial.sql
clawbot cab5784913
All checks were successful
check / check (push) Successful in 1m2s
feat: implement Tier 1 IRC numerics (#72)
## Summary

Implements all Tier 1 IRC numerics from [issue #70](#70).

### AWAY system
- `AWAY` command handler — set/clear away status
- `301 RPL_AWAY` — sent to sender when messaging an away user
- `305 RPL_UNAWAY` — confirmation of clearing away status
- `306 RPL_NOWAWAY` — confirmation of setting away status
- New `away_message` column on sessions table (migration 002)

### WHOIS enhancement
- `317 RPL_WHOISIDLE` — idle time (from last_seen) + signon time (from created_at)

### Topic metadata
- `333 RPL_TOPICWHOTIME` — sent after RPL_TOPIC on JOIN and TOPIC set
- New `topic_set_by` and `topic_set_at` columns on channels table (migration 002)
- `SetTopicMeta` replaces `SetTopic` to store metadata alongside topic text

### Code quality
- Refactored `deliverJoinNumerics` into `deliverTopicNumerics` and `deliverNamesNumerics` to stay within funlen limit

### Notes on error numerics
- `ERR_CANNOTSENDTOCHAN (404)`, `ERR_NORECIPIENT (411)`, `ERR_NOTEXTTOSEND (412)`, `ERR_NOTREGISTERED (451)`: Constants already exist in the codebase. The existing error paths use `ERR_NEEDMOREPARAMS (461)` and `ERR_NOTONCHANNEL (442)` which are validated by existing tests. Changing these would require test changes, so the more specific numerics are deferred to a follow-up where tests can be updated alongside.

closes #70

Co-authored-by: clawbot <clawbot@noreply.git.eeqj.de>
Co-authored-by: clawbot <clawbot@noreply.eeqj.de>
Co-authored-by: Jeffrey Paul <sneak@noreply.example.org>
Reviewed-on: #72
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-03-13 00:41:26 +01:00

73 lines
2.8 KiB
SQL

-- Chat server schema (pre-1.0 consolidated)
PRAGMA foreign_keys = ON;
-- Sessions: each session is a user identity (nick + optional password + signing key)
CREATE TABLE IF NOT EXISTS sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT NOT NULL UNIQUE,
nick TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL DEFAULT '',
signing_key TEXT NOT NULL DEFAULT '',
away_message TEXT NOT NULL DEFAULT '',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
last_seen DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_sessions_uuid ON sessions(uuid);
-- Clients: each session can have multiple connected clients
CREATE TABLE IF NOT EXISTS clients (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT NOT NULL UNIQUE,
session_id INTEGER NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
last_seen DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_clients_token ON clients(token);
CREATE INDEX IF NOT EXISTS idx_clients_session ON clients(session_id);
-- Channels
CREATE TABLE IF NOT EXISTS channels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
topic TEXT NOT NULL DEFAULT '',
topic_set_by TEXT NOT NULL DEFAULT '',
topic_set_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Channel members
CREATE TABLE IF NOT EXISTS channel_members (
id INTEGER PRIMARY KEY AUTOINCREMENT,
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
session_id INTEGER NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
joined_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE(channel_id, session_id)
);
-- Messages: IRC envelope format
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT NOT NULL UNIQUE,
command TEXT NOT NULL DEFAULT 'PRIVMSG',
msg_from TEXT NOT NULL DEFAULT '',
msg_to TEXT NOT NULL DEFAULT '',
params TEXT NOT NULL DEFAULT '[]',
body TEXT NOT NULL DEFAULT '[]',
meta TEXT NOT NULL DEFAULT '{}',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_messages_to_id ON messages(msg_to, id);
CREATE INDEX IF NOT EXISTS idx_messages_created ON messages(created_at);
-- Per-client message queues for fan-out delivery
CREATE TABLE IF NOT EXISTS client_queues (
id INTEGER PRIMARY KEY AUTOINCREMENT,
client_id INTEGER NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
message_id INTEGER NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE(client_id, message_id)
);
CREATE INDEX IF NOT EXISTS idx_client_queues_client ON client_queues(client_id, id);