-- ============================================================= -- V016: Add platform identity columns and platform_messages table -- ============================================================= -- Scope: Prepare schema for multi-platform support (Discord, etc). -- Legacy telegram_* columns are retained for backward compatibility. -- ============================================================= -- -- Players: platform-agnostic identity ALTER TABLE players ADD COLUMN platform VARCHAR(50), ADD COLUMN external_user_id VARCHAR(255), ADD COLUMN external_username VARCHAR(255); CREATE UNIQUE INDEX ix_players_platform_external_user_id ON players (platform, external_user_id) WHERE platform IS NOT NULL AND external_user_id IS NOT NULL; -- -- Game groups: platform-agnostic identity ALTER TABLE game_groups ADD COLUMN platform VARCHAR(50), ADD COLUMN external_group_id VARCHAR(255), ADD COLUMN external_channel_id VARCHAR(255); CREATE UNIQUE INDEX ix_game_groups_platform_external_group_id ON game_groups (platform, external_group_id) WHERE platform IS NOT NULL AND external_group_id IS NOT NULL; -- -- Backfill existing Telegram data UPDATE players SET platform = 'Telegram', external_user_id = telegram_id::TEXT, external_username = telegram_username WHERE platform IS NULL; UPDATE game_groups SET platform = 'Telegram', external_group_id = telegram_chat_id::TEXT WHERE platform IS NULL; -- -- Platform messages: store per-platform message references CREATE TABLE platform_messages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), platform VARCHAR(50) NOT NULL, group_id UUID REFERENCES game_groups(id) ON DELETE CASCADE, batch_id UUID, session_id UUID REFERENCES sessions(id) ON DELETE CASCADE, external_channel_id VARCHAR(255), external_thread_id VARCHAR(255), external_message_id VARCHAR(255) NOT NULL, purpose VARCHAR(50) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX ix_platform_messages_group_id ON platform_messages(group_id); CREATE INDEX ix_platform_messages_batch_id ON platform_messages(batch_id); CREATE INDEX ix_platform_messages_session_id ON platform_messages(session_id); CREATE INDEX ix_platform_messages_platform_message ON platform_messages (platform, external_message_id); -- -- Recreate attendance stats function for new columns (prod back-compat) CREATE OR REPLACE FUNCTION get_group_attendance_stats(p_group_id UUID) RETURNS TABLE ( player_id UUID, display_name VARCHAR, telegram_username VARCHAR, total_sessions BIGINT, confirmed_count BIGINT, declined_count BIGINT, no_response_count BIGINT, waitlisted_count BIGINT, cancellation_affected_count BIGINT, attendance_rate NUMERIC ) AS $$ BEGIN RETURN QUERY WITH player_sessions AS ( SELECT sp.player_id, s.id AS session_id, sp.rsvp_status, sp.registration_status, s.status AS session_status, s.scheduled_at FROM session_participants sp JOIN sessions s ON s.id = sp.session_id WHERE s.group_id = p_group_id ), player_totals AS ( SELECT ps.player_id, COUNT(*) FILTER (WHERE ps.session_status <> 'Cancelled') AS total_sessions, COUNT(*) FILTER (WHERE ps.rsvp_status = 'Confirmed' AND ps.session_status <> 'Cancelled') AS confirmed_count, COUNT(*) FILTER (WHERE ps.rsvp_status = 'Declined' AND ps.session_status <> 'Cancelled') AS declined_count, COUNT(*) FILTER (WHERE ps.rsvp_status = 'Pending' AND ps.scheduled_at < NOW() AND ps.session_status <> 'Cancelled') AS no_response_count, COUNT(*) FILTER (WHERE ps.registration_status = 'Waitlisted' AND ps.session_status <> 'Cancelled') AS waitlisted_count, COUNT(*) FILTER (WHERE ps.session_status = 'Cancelled') AS cancellation_affected_count FROM player_sessions ps GROUP BY ps.player_id ) SELECT pt.player_id, p.display_name, COALESCE(p.external_username, p.telegram_username) AS telegram_username, pt.total_sessions, pt.confirmed_count, pt.declined_count, pt.no_response_count, pt.waitlisted_count, pt.cancellation_affected_count, ROUND( 100.0 * pt.confirmed_count / NULLIF(pt.total_sessions, 0), 1 ) AS attendance_rate FROM player_totals pt JOIN players p ON p.id = pt.player_id ORDER BY pt.confirmed_count DESC, pt.total_sessions DESC; END; $$ LANGUAGE plpgsql STABLE;