-- Daemon Boyfriend Database Schema -- Run with: psql -U postgres -d daemon_boyfriend -f schema.sql -- Users table CREATE TABLE IF NOT EXISTS users ( id BIGSERIAL PRIMARY KEY, discord_id BIGINT NOT NULL UNIQUE, discord_username VARCHAR(255) NOT NULL, discord_display_name VARCHAR(255), custom_name VARCHAR(255), first_seen_at TIMESTAMPTZ DEFAULT NOW(), last_seen_at TIMESTAMPTZ DEFAULT NOW(), is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS ix_users_discord_id ON users(discord_id); CREATE INDEX IF NOT EXISTS ix_users_last_seen_at ON users(last_seen_at); -- User preferences table CREATE TABLE IF NOT EXISTS user_preferences ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, preference_key VARCHAR(100) NOT NULL, preference_value TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(user_id, preference_key) ); CREATE INDEX IF NOT EXISTS ix_user_preferences_user_id ON user_preferences(user_id); -- User facts table CREATE TABLE IF NOT EXISTS user_facts ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, fact_type VARCHAR(50) NOT NULL, fact_content TEXT NOT NULL, confidence FLOAT DEFAULT 1.0, source VARCHAR(50) DEFAULT 'conversation', is_active BOOLEAN NOT NULL DEFAULT TRUE, learned_at TIMESTAMPTZ DEFAULT NOW(), last_referenced_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS ix_user_facts_user_id ON user_facts(user_id); CREATE INDEX IF NOT EXISTS ix_user_facts_fact_type ON user_facts(fact_type); CREATE INDEX IF NOT EXISTS ix_user_facts_is_active ON user_facts(is_active); -- Guilds table CREATE TABLE IF NOT EXISTS guilds ( id BIGSERIAL PRIMARY KEY, discord_id BIGINT NOT NULL UNIQUE, name VARCHAR(255) NOT NULL, joined_at TIMESTAMPTZ DEFAULT NOW(), is_active BOOLEAN NOT NULL DEFAULT TRUE, settings JSONB DEFAULT '{}', created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS ix_guilds_discord_id ON guilds(discord_id); -- Guild members table CREATE TABLE IF NOT EXISTS guild_members ( id BIGSERIAL PRIMARY KEY, guild_id BIGINT NOT NULL REFERENCES guilds(id) ON DELETE CASCADE, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, guild_nickname VARCHAR(255), roles TEXT[], joined_guild_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(guild_id, user_id) ); CREATE INDEX IF NOT EXISTS ix_guild_members_guild_id ON guild_members(guild_id); CREATE INDEX IF NOT EXISTS ix_guild_members_user_id ON guild_members(user_id); -- Conversations table CREATE TABLE IF NOT EXISTS conversations ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, guild_id BIGINT, channel_id BIGINT, started_at TIMESTAMPTZ DEFAULT NOW(), last_message_at TIMESTAMPTZ DEFAULT NOW(), message_count INTEGER DEFAULT 0, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS ix_conversations_user_id ON conversations(user_id); CREATE INDEX IF NOT EXISTS ix_conversations_channel_id ON conversations(channel_id); CREATE INDEX IF NOT EXISTS ix_conversations_last_message_at ON conversations(last_message_at); CREATE INDEX IF NOT EXISTS ix_conversations_is_active ON conversations(is_active); -- Messages table CREATE TABLE IF NOT EXISTS messages ( id BIGSERIAL PRIMARY KEY, conversation_id BIGINT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, discord_message_id BIGINT, role VARCHAR(20) NOT NULL, content TEXT NOT NULL, has_images BOOLEAN NOT NULL DEFAULT FALSE, image_urls TEXT[], token_count INTEGER, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS ix_messages_conversation_id ON messages(conversation_id); CREATE INDEX IF NOT EXISTS ix_messages_user_id ON messages(user_id); CREATE INDEX IF NOT EXISTS ix_messages_created_at ON messages(created_at);