Fix scheduled events not executing due to property name mismatch

The scheduler was correctly scheduling events but they weren't executing because the code was checking for camelCase property names (channelId, pingRoleId) while the database returns snake_case names (channel_id, ping_role_id). This caused channel and role validation to be skipped, resulting in silent failures.

Also added sqlite3 CLI to Docker container for debugging database issues.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Chris Ham
2025-08-17 09:53:38 -07:00
parent 8823eac094
commit 5d72159cb2
2 changed files with 48 additions and 32 deletions

View File

@@ -1,6 +1,8 @@
# Use Node 20 LTS with full Debian for better compatibility # Use Node 20 LTS with full Debian for better compatibility
FROM node:20 FROM node:20
RUN apt update && apt install -y sqlite3
WORKDIR /app WORKDIR /app
# Copy package files (npm will work better for native modules in Docker) # Copy package files (npm will work better for native modules in Docker)

View File

@@ -1,4 +1,4 @@
const schedule = require('node-schedule'); const schedule = require("node-schedule");
class SchedulerService { class SchedulerService {
constructor() { constructor() {
@@ -7,14 +7,14 @@ class SchedulerService {
/** /**
* Initialize scheduled events for all guilds * Initialize scheduled events for all guilds
* @param {Client} client * @param {Client} client
* @param {ConfigManager} configManager * @param {ConfigManager} configManager
*/ */
async initialize(client, configManager) { async initialize(client, configManager) {
console.log('Initializing scheduled events...'); console.log("Initializing scheduled events...");
const guildConfigs = configManager.getAllGuildConfigs(); const guildConfigs = configManager.getAllGuildConfigs();
for (const guildConfig of guildConfigs) { for (const guildConfig of guildConfigs) {
try { try {
const guild = await client.guilds.fetch(guildConfig.id); const guild = await client.guilds.fetch(guildConfig.id);
@@ -27,45 +27,53 @@ class SchedulerService {
const databaseService = configManager.databaseService; const databaseService = configManager.databaseService;
if (!databaseService) continue; if (!databaseService) continue;
const scheduledEvents = databaseService.getScheduledEvents(guildConfig.id); const scheduledEvents = databaseService.getScheduledEvents(
guildConfig.id
);
if (!scheduledEvents || scheduledEvents.length === 0) { if (!scheduledEvents || scheduledEvents.length === 0) {
continue; continue;
} }
for (const event of scheduledEvents) { for (const event of scheduledEvents) {
await this.scheduleEvent(guild, event, guildConfig); await this.scheduleEvent(guild, event);
} }
} catch (error) { } catch (error) {
console.error(`Error setting up scheduled events for guild ${guildConfig.id}:`, error); console.error(
`Error setting up scheduled events for guild ${guildConfig.id}:`,
error
);
} }
} }
} }
/** /**
* Schedule a single event * Schedule a single event
* @param {Guild} guild * @param {Guild} guild
* @param {Object} event * @param {Object} event
* @param {Object} guildConfig
*/ */
async scheduleEvent(guild, event, guildConfig) { async scheduleEvent(guild, event) {
try { try {
// Validate channel // Validate channel
let channel = null; let channel = null;
if (event.channelId) { if (event.channel_id) {
channel = await guild.channels.fetch(event.channelId); channel = await guild.channels.fetch(event.channel_id);
if (!channel) { if (!channel) {
console.error(`Invalid channel ${event.channelId} for event ${event.id} in guild ${guild.name}`); console.error(
`Invalid channel ${event.channel_id} for event ${event.id} in guild ${guild.name}`
);
return; return;
} }
} }
// Validate role // Validate role
let pingRole = null; let pingRole = null;
if (event.pingRoleId) { if (event.ping_role_id) {
pingRole = await guild.roles.fetch(event.pingRoleId); pingRole = await guild.roles.fetch(event.ping_role_id);
if (!pingRole) { if (!pingRole) {
console.warn(`Invalid role ${event.pingRoleId} for event ${event.id} in guild ${guild.name}`); console.warn(
`Invalid role ${event.ping_role_id} for event ${event.id} in guild ${guild.name}`
);
} }
} }
@@ -80,10 +88,16 @@ class SchedulerService {
// Store job reference // Store job reference
const jobKey = `${guild.id}-${event.id}`; const jobKey = `${guild.id}-${event.id}`;
this.jobs.set(jobKey, job); this.jobs.set(jobKey, job);
console.log(`Event ${event.id} scheduled. Next invocation: ${job.nextInvocation()}`); console.log(
`Event ${
event.id
} scheduled. Next invocation: ${job.nextInvocation()}`
);
} else { } else {
console.error(`Failed to schedule event ${event.id} - invalid cron expression: ${event.schedule}`); console.error(
`Failed to schedule event ${event.id} with schedule: ${event.schedule}`
);
} }
} catch (error) { } catch (error) {
console.error(`Error scheduling event ${event.id}:`, error); console.error(`Error scheduling event ${event.id}:`, error);
@@ -92,9 +106,9 @@ class SchedulerService {
/** /**
* Execute a scheduled event * Execute a scheduled event
* @param {TextChannel} channel * @param {TextChannel} channel
* @param {Object} event * @param {Object} event
* @param {Role} pingRole * @param {Role} pingRole
*/ */
async executeEvent(channel, event, pingRole) { async executeEvent(channel, event, pingRole) {
try { try {
@@ -112,7 +126,7 @@ class SchedulerService {
// Send the message // Send the message
if (content.length > 0 && channel) { if (content.length > 0 && channel) {
await channel.send(content.join(' ')); await channel.send(content.join(" "));
console.log(`Executed scheduled event ${event.id}`); console.log(`Executed scheduled event ${event.id}`);
} }
} catch (error) { } catch (error) {
@@ -122,13 +136,13 @@ class SchedulerService {
/** /**
* Cancel a scheduled job * Cancel a scheduled job
* @param {string} guildId * @param {string} guildId
* @param {string} eventId * @param {string} eventId
*/ */
cancelJob(guildId, eventId) { cancelJob(guildId, eventId) {
const jobKey = `${guildId}-${eventId}`; const jobKey = `${guildId}-${eventId}`;
const job = this.jobs.get(jobKey); const job = this.jobs.get(jobKey);
if (job) { if (job) {
job.cancel(); job.cancel();
this.jobs.delete(jobKey); this.jobs.delete(jobKey);
@@ -138,7 +152,7 @@ class SchedulerService {
/** /**
* Cancel all jobs for a guild * Cancel all jobs for a guild
* @param {string} guildId * @param {string} guildId
*/ */
cancelGuildJobs(guildId) { cancelGuildJobs(guildId) {
for (const [key, job] of this.jobs) { for (const [key, job] of this.jobs) {
@@ -158,8 +172,8 @@ class SchedulerService {
job.cancel(); job.cancel();
} }
this.jobs.clear(); this.jobs.clear();
console.log('Cancelled all scheduled events'); console.log("Cancelled all scheduled events");
} }
} }
module.exports = new SchedulerService(); module.exports = new SchedulerService();