Initial commit: GM-Relay Telegram Bot

This commit is contained in:
2026-04-13 13:52:49 +03:00
commit 9db4bee2f6
51 changed files with 3407 additions and 0 deletions
@@ -0,0 +1,70 @@
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)
);
@@ -0,0 +1,3 @@
-- Add batch_id to sessions to support multiple sessions per message
ALTER TABLE sessions ADD COLUMN batch_id UUID NOT NULL DEFAULT gen_random_uuid();
CREATE INDEX ix_sessions_batch ON sessions (batch_id);
@@ -0,0 +1,2 @@
-- Add thread_id to sessions to store the forum topic ID so we can delete it later
ALTER TABLE sessions ADD COLUMN thread_id INTEGER;
@@ -0,0 +1,27 @@
-- Reschedule proposals: tracks GM-initiated time change requests
-- Status flow: AwaitingTime → Voting → Approved/Rejected
CREATE TABLE reschedule_proposals (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
proposed_at TIMESTAMPTZ, -- new proposed time (NULL while AwaitingTime)
proposed_by BIGINT NOT NULL, -- GM's telegram_id
status VARCHAR(50) NOT NULL DEFAULT 'AwaitingTime'
CHECK (status IN ('AwaitingTime', 'Voting', 'Approved', 'Rejected')),
vote_message_id INTEGER, -- message ID of the voting message in chat
vote_chat_id BIGINT, -- chat where voting takes place
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Only one active proposal per session at a time
CREATE UNIQUE INDEX ix_reschedule_active ON reschedule_proposals (session_id)
WHERE status IN ('AwaitingTime', 'Voting');
CREATE TABLE reschedule_votes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
proposal_id UUID NOT NULL REFERENCES reschedule_proposals(id) ON DELETE CASCADE,
player_id UUID NOT NULL REFERENCES players(id) ON DELETE CASCADE,
vote VARCHAR(10) NOT NULL CHECK (vote IN ('yes', 'no')),
voted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (proposal_id, player_id)
);
@@ -0,0 +1,2 @@
-- Store the message ID of the batch schedule message so we can edit it later
ALTER TABLE sessions ADD COLUMN batch_message_id INTEGER;