const REMINDER_EMOTE = '🔔'; const REMINDER_LEAD_SECONDS = 1 * 60 * 60; module.exports = { timeout: 60000, immediate: true, name: 'VRC.TL Event Reminders', async tick(client, timer) { try { const db = await client.localDB; const nowUnix = Math.floor(Date.now() / 1000); const dueReminders = db.prepare(` SELECT id, message_id, channel_id, event_name, event_start FROM vrctl_event_reminders WHERE reminder_sent = 0 AND event_start - ? <= ? `).all(REMINDER_LEAD_SECONDS, nowUnix); for (const reminder of dueReminders) { const markSent = db.prepare(`UPDATE vrctl_event_reminders SET reminder_sent = 1 WHERE id = ?`); if (reminder.event_start <= nowUnix) { // Event already started (e.g. bot was down) - don't send a stale reminder. markSent.run(reminder.id); continue; } try { const channel = await client.channels.fetch(reminder.channel_id); const message = await channel.messages.fetch(reminder.message_id); const reaction = message.reactions.cache.get(REMINDER_EMOTE); const users = reaction ? await reaction.users.fetch() : new Map(); const mentions = [...users.values()] .filter(user => !user.bot) .map(user => `<@${user.id}>`); if (mentions.length > 0) { await channel.send({ content: `⏰ **${reminder.event_name}** starts ! ${mentions.join(' ')}`, reply: { messageReference: reminder.message_id }, allowedMentions: { parse: [], users: mentions.map(m => m.slice(2, -1)) } }); } markSent.run(reminder.id); } catch (error) { console.error(`[TIMER] Failed to send vrc.tl reminder for message ${reminder.message_id}:`, error); } } } catch (error) { console.error('[TIMER] VRC.TL Event Reminders error:', error); } }, };