-- SQL Script to initialize the Supabase PostgreSQL database -- Copy and paste this into the Supabase SQL Editor (SQL Editor -> New Query) -- 1. STREAMS TABLE CREATE TABLE IF NOT EXISTS streams ( id SERIAL PRIMARY KEY, start_time TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, end_time TIMESTAMP WITH TIME ZONE, title TEXT, twitch_stream_id TEXT UNIQUE, category TEXT, backfill_status TEXT DEFAULT 'pending', twitch_vod_id TEXT ); -- 2. MESSAGES TABLE CREATE TABLE IF NOT EXISTS messages ( id TEXT PRIMARY KEY, -- Twitch Message ID (UUID-like from tags) stream_id INTEGER REFERENCES streams(id) ON DELETE SET NULL, username TEXT NOT NULL, display_name TEXT, message TEXT NOT NULL, timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, is_streamer BOOLEAN DEFAULT FALSE, is_mod BOOLEAN DEFAULT FALSE, is_sub BOOLEAN DEFAULT FALSE ); -- Create indexes for fast querying and statistics aggregation CREATE INDEX IF NOT EXISTS idx_messages_stream_id ON messages(stream_id); CREATE INDEX IF NOT EXISTS idx_messages_username ON messages(username); CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON messages(timestamp); -- 2.5 STREAM VIEWERS TABLE CREATE TABLE IF NOT EXISTS stream_viewers ( stream_id INTEGER REFERENCES streams(id) ON DELETE CASCADE, username TEXT NOT NULL, display_name TEXT, has_chatted BOOLEAN DEFAULT FALSE, is_mod BOOLEAN DEFAULT FALSE, is_sub BOOLEAN DEFAULT FALSE, first_seen TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (stream_id, username) ); CREATE INDEX IF NOT EXISTS idx_stream_viewers_stream_id ON stream_viewers(stream_id); -- 3. VOICE WORDS TABLE (transcribed spoken words from streamer) CREATE TABLE IF NOT EXISTS voice_words ( id SERIAL PRIMARY KEY, stream_id INTEGER REFERENCES streams(id) ON DELETE CASCADE, word TEXT NOT NULL, timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_voice_words_stream_id ON voice_words(stream_id); CREATE INDEX IF NOT EXISTS idx_voice_words_word ON voice_words(word); -- 4. MOD ACTIONS TABLE (from EventSub: bans, timeouts, message deletions) CREATE TABLE IF NOT EXISTS mod_actions ( id SERIAL PRIMARY KEY, stream_id INTEGER REFERENCES streams(id) ON DELETE SET NULL, action_type TEXT NOT NULL, -- 'ban', 'timeout', 'unban', 'delete' moderator TEXT NOT NULL, -- Moderator's username target_user TEXT, -- Banned/timed out user's username duration INTEGER, -- Timeout duration in seconds (NULL for bans/deletions) reason TEXT, -- Reason provided message_text TEXT, -- Text of deleted message (for 'delete' action) timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, reaction_time REAL ); CREATE INDEX IF NOT EXISTS idx_mod_actions_stream_id ON mod_actions(stream_id); CREATE INDEX IF NOT EXISTS idx_mod_actions_timestamp ON mod_actions(timestamp); -- 5. SETTINGS TABLE (for persistent oauth tokens, etc.) CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- A trigger to automatically update updated_at in settings table CREATE OR REPLACE FUNCTION update_modified_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = now(); RETURN NEW; END; $$ language 'plpgsql'; CREATE OR REPLACE TRIGGER update_settings_modtime BEFORE UPDATE ON settings FOR EACH ROW EXECUTE FUNCTION update_modified_column(); -- ========================================================================= -- HELPER VIEWS FOR STATISTICS -- ========================================================================= -- View for chatter statistics per stream CREATE OR REPLACE VIEW stream_chatter_stats AS SELECT stream_id, username, max(display_name) as display_name, bool_or(is_mod) as is_mod, bool_or(is_sub) as is_sub, count(*) as message_count FROM messages GROUP BY stream_id, username; -- View for chatter statistics overall (all-time) CREATE OR REPLACE VIEW global_chatter_stats AS SELECT username, max(display_name) as display_name, bool_or(is_mod) as is_mod, bool_or(is_sub) as is_sub, count(*) as message_count FROM messages GROUP BY username; -- View for spoken word statistics per stream CREATE OR REPLACE VIEW stream_voice_word_stats AS SELECT stream_id, word, count(*) as word_count FROM voice_words GROUP BY stream_id, word; -- View for spoken word statistics overall (all-time) CREATE OR REPLACE VIEW global_voice_word_stats AS SELECT word, count(*) as word_count FROM voice_words GROUP BY word;