CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- ============================================================= -- Players: all known Telegram users who interact with the bot -- ============================================================= CREATE TABLE players ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), telegram_id BIGINT NOT NULL UNIQUE, display_name VARCHAR(255) NOT NULL, telegram_username VARCHAR(255), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- ============================================================= -- Game groups: Telegram group chats where games are organized -- ============================================================= CREATE TABLE game_groups ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), telegram_chat_id BIGINT NOT NULL UNIQUE, name VARCHAR(255) NOT NULL, gm_telegram_id BIGINT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- ============================================================= -- Group membership: which players belong to which groups -- ============================================================= CREATE TABLE game_group_members ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), group_id UUID NOT NULL REFERENCES game_groups(id) ON DELETE CASCADE, player_id UUID NOT NULL REFERENCES players(id) ON DELETE CASCADE, is_gm BOOLEAN NOT NULL DEFAULT false, joined_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (group_id, player_id) ); -- ============================================================= -- Sessions: individual game sessions with scheduling -- ============================================================= CREATE TABLE sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), group_id UUID NOT NULL REFERENCES game_groups(id) ON DELETE CASCADE, title VARCHAR(500) NOT NULL, join_link TEXT NOT NULL, scheduled_at TIMESTAMPTZ NOT NULL, status VARCHAR(50) NOT NULL DEFAULT 'Planned' CHECK (status IN ('Planned','ConfirmationSent','Confirmed','Cancelled')), confirmation_message_id INTEGER, link_message_id INTEGER, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- Partial index: only scan sessions that still need processing CREATE INDEX ix_sessions_pending ON sessions (scheduled_at) WHERE status IN ('Planned', 'ConfirmationSent', 'Confirmed'); -- ============================================================= -- Session participants: per-session RSVP tracking -- ============================================================= CREATE TABLE session_participants ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE, player_id UUID NOT NULL REFERENCES players(id) ON DELETE CASCADE, is_gm BOOLEAN NOT NULL DEFAULT false, rsvp_status VARCHAR(50) NOT NULL DEFAULT 'Pending' CHECK (rsvp_status IN ('Pending','Confirmed','Declined')), responded_at TIMESTAMPTZ, UNIQUE (session_id, player_id) );