feat: replace browser login with imported Reddit sessions
This commit is contained in:
1 parent
2b0545afcf
commit
c483c7b8be
76 files changed
+2048
-2828
No files matched your search
+83
-28
@@ -4,7 +4,9 @@ import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
export function createDatabase(config: Config): Database.Database {
|
||||
const dbPath = path.resolve(process.cwd(), config.database.path);
|
||||
const dbPath = config.database.path === ':memory:'
|
||||
? ':memory:'
|
||||
: path.resolve(process.cwd(), config.database.path);
|
||||
const dbDir = path.dirname(dbPath);
|
||||
|
||||
// Ensure directory exists
|
||||
@@ -22,8 +24,8 @@ export function createDatabase(config: Config): Database.Database {
|
||||
}
|
||||
|
||||
export function runMigrations(db: Database.Database): void {
|
||||
// Create tables if they don't exist
|
||||
db.exec(`
|
||||
const migrations: ReadonlyArray<() => void> = [
|
||||
() => db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS processed_posts (
|
||||
id TEXT PRIMARY KEY,
|
||||
subreddit TEXT NOT NULL,
|
||||
@@ -83,36 +85,89 @@ export function runMigrations(db: Database.Database): void {
|
||||
ON processed_posts(message_sent);
|
||||
CREATE INDEX IF NOT EXISTS idx_activity_log_timestamp
|
||||
ON activity_log(timestamp);
|
||||
`);
|
||||
`),
|
||||
|
||||
// Add new columns to existing tables (safe to run multiple times)
|
||||
const addColumnIfNotExists = (table: string, column: string, definition: string) => {
|
||||
() => {
|
||||
addColumnIfNotExists(db, 'sent_messages', 'got_response', 'INTEGER DEFAULT 0');
|
||||
addColumnIfNotExists(db, 'sent_messages', 'response_checked_at', 'INTEGER');
|
||||
addColumnIfNotExists(db, 'sent_messages', 'strategy_id', 'TEXT');
|
||||
addColumnIfNotExists(db, 'sent_messages', 'chat_room_id', 'TEXT');
|
||||
db.exec(`
|
||||
CREATE INDEX IF NOT EXISTS idx_sent_messages_response ON sent_messages(got_response);
|
||||
CREATE INDEX IF NOT EXISTS idx_sent_messages_recipient ON sent_messages(recipient);
|
||||
CREATE INDEX IF NOT EXISTS idx_sent_messages_chat_room ON sent_messages(chat_room_id);
|
||||
`);
|
||||
makeSentMessagesPostIdNullable(db);
|
||||
},
|
||||
() => {
|
||||
addColumnIfNotExists(db, 'successful_messages', 'subreddit', 'TEXT');
|
||||
makeSuccessfulMessageIdNullable(db);
|
||||
},
|
||||
() => db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS delivery_outbox (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
post_id TEXT NOT NULL UNIQUE,
|
||||
recipient TEXT NOT NULL,
|
||||
subject TEXT NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
strategy_id TEXT,
|
||||
idempotency_key TEXT NOT NULL UNIQUE,
|
||||
status TEXT NOT NULL CHECK(status IN ('discovered', 'prepared', 'sending', 'sent', 'failed')),
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
lease_owner TEXT,
|
||||
lease_expires_at INTEGER,
|
||||
attempt_count INTEGER NOT NULL DEFAULT 0,
|
||||
last_error TEXT,
|
||||
sent_message_id INTEGER,
|
||||
FOREIGN KEY (post_id) REFERENCES processed_posts(id),
|
||||
FOREIGN KEY (sent_message_id) REFERENCES sent_messages(id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_delivery_outbox_claim
|
||||
ON delivery_outbox(status, lease_expires_at, created_at);
|
||||
`),
|
||||
() => db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS conversation_states (
|
||||
conversation_id TEXT PRIMARY KEY,
|
||||
state_json TEXT NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
`),
|
||||
() => db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS conversation_outcomes (
|
||||
sent_message_id INTEGER PRIMARY KEY,
|
||||
outcome TEXT NOT NULL CHECK(outcome IN ('engaged', 'declined', 'boundary', 'upsell', 'unknown')),
|
||||
response_snippet TEXT,
|
||||
recorded_at INTEGER NOT NULL,
|
||||
FOREIGN KEY (sent_message_id) REFERENCES sent_messages(id)
|
||||
);
|
||||
`),
|
||||
];
|
||||
|
||||
let currentVersion = db.pragma('user_version', { simple: true }) as number;
|
||||
if (currentVersion > migrations.length) {
|
||||
throw new Error(`Database schema version ${currentVersion} is newer than this application supports`);
|
||||
}
|
||||
|
||||
for (let index = currentVersion; index < migrations.length; index += 1) {
|
||||
const version = index + 1;
|
||||
db.transaction(() => {
|
||||
migrations[index]();
|
||||
db.pragma(`user_version = ${version}`);
|
||||
})();
|
||||
currentVersion = version;
|
||||
}
|
||||
}
|
||||
|
||||
function addColumnIfNotExists(db: Database.Database, table: string, column: string, definition: string): void {
|
||||
const columns = db.prepare(`PRAGMA table_info(${table})`).all() as { name: string }[];
|
||||
const exists = columns.some((c) => c.name === column);
|
||||
if (!exists) {
|
||||
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
addColumnIfNotExists('sent_messages', 'got_response', 'INTEGER DEFAULT 0');
|
||||
addColumnIfNotExists('sent_messages', 'response_checked_at', 'INTEGER');
|
||||
|
||||
// Create index for response checking
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_sent_messages_response ON sent_messages(got_response)`);
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_sent_messages_recipient ON sent_messages(recipient)`);
|
||||
|
||||
// Add strategy_id column for A/B testing
|
||||
addColumnIfNotExists('sent_messages', 'strategy_id', 'TEXT');
|
||||
|
||||
// Add subreddit column to successful_messages for smarter example selection
|
||||
addColumnIfNotExists('successful_messages', 'subreddit', 'TEXT');
|
||||
|
||||
// Track chats sent via chat.reddit.com so dashboard stats include them.
|
||||
addColumnIfNotExists('sent_messages', 'chat_room_id', 'TEXT');
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_sent_messages_chat_room ON sent_messages(chat_room_id)`);
|
||||
|
||||
// Migration: Make post_id nullable in sent_messages (chat-originated rows
|
||||
// have no associated processed_post). SQLite can't ALTER COLUMN, so recreate.
|
||||
function makeSentMessagesPostIdNullable(db: Database.Database): void {
|
||||
const sentMsgColumns = db.prepare(`PRAGMA table_info(sent_messages)`).all() as { name: string; notnull: number }[];
|
||||
const postIdCol = sentMsgColumns.find((c) => c.name === 'post_id');
|
||||
if (postIdCol && postIdCol.notnull === 1) {
|
||||
@@ -143,9 +198,9 @@ export function runMigrations(db: Database.Database): void {
|
||||
CREATE INDEX IF NOT EXISTS idx_sent_messages_chat_room ON sent_messages(chat_room_id);
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
// Migration: Make sent_message_id nullable in successful_messages
|
||||
// SQLite doesn't support ALTER COLUMN, so we need to recreate the table
|
||||
function makeSuccessfulMessageIdNullable(db: Database.Database): void {
|
||||
const successfulMsgColumns = db.prepare(`PRAGMA table_info(successful_messages)`).all() as { name: string; notnull: number }[];
|
||||
const sentMsgIdCol = successfulMsgColumns.find((c) => c.name === 'sent_message_id');
|
||||
|
||||
|
||||
Reference in new issue
Block a user