import Database from 'better-sqlite3' import { join, dirname } from 'path' import { fileURLToPath } from 'url' import { mkdirSync } from 'fs' const __dirname = dirname(fileURLToPath(import.meta.url)) const dataDir = join(__dirname, '..', '..', 'data') mkdirSync(dataDir, { recursive: true }) const sqlite = new Database(join(dataDir, 'botfights.db')) sqlite.pragma('journal_mode = WAL') sqlite.pragma('foreign_keys = ON') sqlite.exec(` CREATE TABLE IF NOT EXISTS bots ( id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, webhook_url TEXT NOT NULL, avatar_seed TEXT NOT NULL, archetype TEXT NOT NULL DEFAULT 'standard', secret_hash TEXT NOT NULL, public_key TEXT, profile_pic_url TEXT, elo_rating REAL NOT NULL DEFAULT 1200, wins INTEGER NOT NULL DEFAULT 0, losses INTEGER NOT NULL DEFAULT 0, win_streak INTEGER NOT NULL DEFAULT 0, best_streak INTEGER NOT NULL DEFAULT 0, tier INTEGER NOT NULL DEFAULT 0, is_active INTEGER NOT NULL DEFAULT 1, last_fight_at TEXT, consecutive_errors INTEGER NOT NULL DEFAULT 0, last_error_at TEXT, customization TEXT, sats_won INTEGER NOT NULL DEFAULT 0, sats_wagered INTEGER NOT NULL DEFAULT 0, has_wallet INTEGER NOT NULL DEFAULT 0, zaps_received INTEGER NOT NULL DEFAULT 0, bot_type TEXT NOT NULL DEFAULT 'regular', created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS fights ( id TEXT PRIMARY KEY, bot_a_id TEXT NOT NULL REFERENCES bots(id), bot_b_id TEXT NOT NULL REFERENCES bots(id), arena TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'scheduled', winner_id TEXT REFERENCES bots(id), bot_a_hp INTEGER NOT NULL DEFAULT 200, bot_b_hp INTEGER NOT NULL DEFAULT 200, total_rounds INTEGER NOT NULL DEFAULT 0, scheduled_at TEXT, started_at TEXT, ended_at TEXT, mode TEXT NOT NULL DEFAULT 'free', pot_sats INTEGER NOT NULL DEFAULT 0, payout_status TEXT, current_season TEXT, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS rounds ( id TEXT PRIMARY KEY, fight_id TEXT NOT NULL REFERENCES fights(id), round_number INTEGER NOT NULL, challenge_type TEXT NOT NULL, challenge_data TEXT NOT NULL, bot_a_response TEXT, bot_a_time_ms INTEGER, bot_a_score REAL, bot_b_response TEXT, bot_b_time_ms INTEGER, bot_b_score REAL, winner_id TEXT REFERENCES bots(id), narration TEXT, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS payments ( id TEXT PRIMARY KEY, fight_id TEXT REFERENCES fights(id), bot_id TEXT NOT NULL REFERENCES bots(id), direction TEXT NOT NULL, amount_sats INTEGER NOT NULL, method TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', invoice TEXT, preimage TEXT, cashu_token TEXT, error_reason TEXT, created_at TEXT NOT NULL, confirmed_at TEXT, refunded_at TEXT ); CREATE TABLE IF NOT EXISTS wallet_connections ( id TEXT PRIMARY KEY, bot_id TEXT NOT NULL UNIQUE REFERENCES bots(id), method TEXT NOT NULL, connection_data TEXT NOT NULL, created_at TEXT NOT NULL, last_used_at TEXT ); CREATE TABLE IF NOT EXISTS bets ( id TEXT PRIMARY KEY, fight_id TEXT NOT NULL REFERENCES fights(id), bettor_pubkey TEXT NOT NULL, bot_id TEXT NOT NULL REFERENCES bots(id), amount_sats INTEGER NOT NULL, odds_at_placement REAL NOT NULL, cashu_token TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', payout_sats INTEGER, payout_token TEXT, created_at TEXT NOT NULL, settled_at TEXT ); CREATE TABLE IF NOT EXISTS tournaments ( id TEXT PRIMARY KEY, name TEXT NOT NULL, format TEXT NOT NULL, size INTEGER NOT NULL, entry_sats INTEGER NOT NULL DEFAULT 0, prize_sats INTEGER NOT NULL DEFAULT 0, status TEXT NOT NULL DEFAULT 'open', current_round INTEGER NOT NULL DEFAULT 0, season_id TEXT, created_at TEXT NOT NULL, started_at TEXT, finished_at TEXT ); CREATE TABLE IF NOT EXISTS tournament_entries ( id TEXT PRIMARY KEY, tournament_id TEXT NOT NULL REFERENCES tournaments(id), bot_id TEXT NOT NULL REFERENCES bots(id), seed INTEGER NOT NULL DEFAULT 0, eliminated INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS analytics ( date TEXT NOT NULL, metric TEXT NOT NULL, value INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS tournament_matches ( id TEXT PRIMARY KEY, tournament_id TEXT NOT NULL REFERENCES tournaments(id), round INTEGER NOT NULL, match_index INTEGER NOT NULL, bot_a_id TEXT REFERENCES bots(id), bot_b_id TEXT REFERENCES bots(id), fight_id TEXT REFERENCES fights(id), winner_id TEXT REFERENCES bots(id), status TEXT NOT NULL DEFAULT 'pending' ); `) // Migrations for existing databases const migrations = [ `ALTER TABLE bots ADD COLUMN archetype TEXT NOT NULL DEFAULT 'standard'`, `ALTER TABLE bots ADD COLUMN profile_pic_url TEXT`, `ALTER TABLE bots ADD COLUMN last_fight_at TEXT`, `ALTER TABLE bots ADD COLUMN consecutive_errors INTEGER NOT NULL DEFAULT 0`, `ALTER TABLE bots ADD COLUMN last_error_at TEXT`, `ALTER TABLE bots ADD COLUMN customization TEXT`, `ALTER TABLE bots ADD COLUMN sats_won INTEGER NOT NULL DEFAULT 0`, `ALTER TABLE bots ADD COLUMN sats_wagered INTEGER NOT NULL DEFAULT 0`, `ALTER TABLE bots ADD COLUMN has_wallet INTEGER NOT NULL DEFAULT 0`, `ALTER TABLE bots ADD COLUMN zaps_received INTEGER NOT NULL DEFAULT 0`, `ALTER TABLE bots ADD COLUMN bot_type TEXT NOT NULL DEFAULT 'regular'`, `ALTER TABLE fights ADD COLUMN mode TEXT NOT NULL DEFAULT 'free'`, `ALTER TABLE fights ADD COLUMN pot_sats INTEGER NOT NULL DEFAULT 0`, `ALTER TABLE fights ADD COLUMN payout_status TEXT`, `ALTER TABLE fights ADD COLUMN current_season TEXT`, ] for (const sql of migrations) { try { sqlite.exec(sql) } catch { /* column already exists */ } } // eslint-disable-next-line no-console -- migration script runs before logger init console.log('[botfights] database migrated') sqlite.close()