import { getDb, WatchedItem } from './database'; import { checkStockStatus } from './scraper'; import { sendTelegramAlert } from './telegram'; const MIN_INTERVAL_SECONDS = 30; const timers = new Map(); async function runCheck(itemId: number): Promise { const db = getDb(); const item = db.prepare('SELECT * FROM watched_items WHERE id = ?').get(itemId) as WatchedItem | undefined; if (!item || !item.is_active) return; console.log(`[Scheduler] Checking item ${itemId} — ${item.name || item.url}`); try { const result = await checkStockStatus(item.url); const now = new Date().toISOString(); let alertSent = item.alert_sent; // Fire alert only on transition to in_stock when no alert has been sent yet if (result.status === 'in_stock' && !item.alert_sent) { const displayName = result.name || item.name || item.url; await sendTelegramAlert( `🟢 Back in Stock!\n\n` + `${displayName}\n\n` + `Open in Store →` ); alertSent = 1; } // Build update payload const updates: Record = { last_status: result.status, alert_sent: alertSent, check_count: item.check_count + 1, last_checked_at: now, }; // Populate name and thumbnail on first successful scrape if (result.name && !item.name) updates.name = result.name; if (result.thumbnail && !item.thumbnail_url) updates.thumbnail_url = result.thumbnail; const setClause = Object.keys(updates).map(k => `${k} = @${k}`).join(', '); db.prepare(`UPDATE watched_items SET ${setClause} WHERE id = @id`).run({ ...updates, id: itemId }); console.log(`[Scheduler] Item ${itemId} → ${result.status} (check #${item.check_count + 1})`); } catch (err) { console.error(`[Scheduler] Error checking item ${itemId}:`, err); // Still increment count so the UI shows activity db.prepare('UPDATE watched_items SET check_count = check_count + 1, last_checked_at = ? WHERE id = ?') .run(new Date().toISOString(), itemId); } } export function startItem(item: WatchedItem): void { stopItem(item.id); const intervalMs = Math.max(MIN_INTERVAL_SECONDS, item.check_interval) * 1000; const timer = setInterval(() => runCheck(item.id), intervalMs); timers.set(item.id, timer); console.log(`[Scheduler] Started item ${item.id} — interval ${intervalMs / 1000}s`); } export function stopItem(id: number): void { const timer = timers.get(id); if (timer) { clearInterval(timer); timers.delete(id); console.log(`[Scheduler] Stopped item ${id}`); } } export function initScheduler(): void { const db = getDb(); const activeItems = db.prepare('SELECT * FROM watched_items WHERE is_active = 1').all() as WatchedItem[]; for (const item of activeItems) { startItem(item); } console.log(`[Scheduler] Initialized — ${activeItems.length} active item(s)`); }