diff --git a/default.config.json b/default.config.json index 8b271d5..9976be2 100644 --- a/default.config.json +++ b/default.config.json @@ -4,15 +4,16 @@ "alerts": "YOUR DISCORD USER ID", "torn": "TORN API KEY", "httpPort": 3000, + "taskWaitMinutes": 5, "channels": { "ocAlert": "000000000000000000" }, "upgradeColors": { - "core": "#FFFFFF", - "peace": "#FFFFFF", - "peaceDim": "#AAAAAA", - "war": "#FFFFFF", - "warDim": "#AAAAAA", - "background": "#0A2472" + "core": "#FFFFFF", + "peace": "#FFFFFF", + "peaceDim": "#AAAAAA", + "war": "#FFFFFF", + "warDim": "#AAAAAA", + "background": "#0A2472" } } \ No newline at end of file diff --git a/index.js b/index.js index cfe1c85..5051e9c 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -const cron = require('node-cron'); + const fs = require('fs'); const path = require('node:path'); const torn = require('./torn.js'); @@ -19,9 +19,9 @@ try { } catch { console.log("Core: No state file found, creating one.") state = { - "ocAlertLast": "2025-01-01T00:00:00.000Z", - "payoutAlertLast": "2025-01-01T00:00:00.000Z", - "itemAlertLast": "2025-01-01T00:00:00.000Z" + "ocAlertLast": "2025-01-01T00:00:00.000Z", + "payoutAlertLast": "2025-01-01T00:00:00.000Z", + "itemAlertLast": "2025-01-01T00:00:00.000Z" } fs.writeFileSync('./state.json', JSON.stringify(state)); stateWasCreated = true; @@ -32,18 +32,18 @@ try { const { Client, Collection, Events, GatewayIntentBits, EmbedBuilder, Partials, MessageFlags } = require('discord.js'); const client = new Client({ intents: [ - GatewayIntentBits.Guilds, + GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, - GatewayIntentBits.DirectMessages, + GatewayIntentBits.DirectMessages, GatewayIntentBits.MessageContent ], partials: [ Partials.Channel, Partials.Message - ] + ] }); client.once(Events.ClientReady, readyClient => { - console.log(`Discord: Connected as ${readyClient.user.tag}`); + console.log(`Discord: Connected as ${readyClient.user.tag}`); torn.readyCheck(config.torn); }); client.login(config.token); @@ -52,23 +52,50 @@ client.tasks = {}; fs.readdir('./tasks/', (err, files) => { if (err) return console.log(err); + const taskNames = []; files.forEach(file => { const taskFile = require(`./tasks/${file}`); const taskName = file.split('.')[0]; client.tasks[taskName] = taskFile; - if (taskFile.schedule) { - console.debug(`Tasks: Scheduling "${taskName}" for ${taskFile.schedule}`); - cron.schedule(taskFile.schedule, () => { taskFile(client, torn, config, state); }); - } else { - console.debug(`Tasks: Registered "${taskName}"`); - } + taskNames.push(taskName); + console.debug(`Tasks: Registered "${taskName}"`); }); + + // Round-robin scheduler + let currentTaskIndex = 0; + const runNextTask = () => { + if (taskNames.length === 0) return; + + const taskName = taskNames[currentTaskIndex]; + const taskFile = client.tasks[taskName]; + const now = new Date(); + const dateString = now.toLocaleTimeString('en-US', { hour12: false }) + ' ' + now.toLocaleDateString('en-US'); + + try { + console.debug(`Tasks: Executing "${taskName}" at ${dateString}`); + taskFile(client, torn, config, state); + } catch (error) { + console.error(`Tasks: Error executing "${taskName}" at ${dateString}:`, error); + } + + currentTaskIndex = (currentTaskIndex + 1) % taskNames.length; + + const waitMinutes = config.taskWaitMinutes || 5; + setTimeout(runNextTask, waitMinutes * 60 * 1000); + }; + + // Start the loop with an initial delay + if (taskNames.length > 0) { + const waitMinutes = config.taskWaitMinutes || 5; + console.log(`Tasks: Scheduler started. First task will run in ${waitMinutes} minutes.`); + setTimeout(runNextTask, waitMinutes * 60 * 1000); + } }); // discord command stuff also yoinked const foldersPath = path.join(__dirname, 'commands'); const commandFolders = fs.readdirSync(foldersPath); - for (const folder of commandFolders) { +for (const folder of commandFolders) { const commandsPath = path.join(foldersPath, folder); const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); for (const file of commandFiles) { @@ -94,8 +121,8 @@ client.on(Events.ClientReady, async () => { if (cmd && typeof cmd.execute === 'function') { console.debug('Startup: Generating upgrades image (missing or first run)'); const mockInteraction = { - deferReply: async () => {}, - editReply: async () => {} + deferReply: async () => { }, + editReply: async () => { } }; try { await cmd.execute(mockInteraction); @@ -109,7 +136,7 @@ client.on(Events.ClientReady, async () => { console.error('Startup: error while ensuring upgrades image', err); } }); - + client.on(Events.InteractionCreate, async interaction => { if (interaction.isButton()) { if (interaction.customId === 'delete_message') { diff --git a/package-lock.json b/package-lock.json index 74443db..58174cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,7 @@ "canvas": "^3.2.0", "discord.js": "^14.18.0", "express": "^5.2.1", - "fs": "^0.0.1-security", - "node-cron": "^3.0.3" + "fs": "^0.0.1-security" }, "devDependencies": { "@eslint/js": "^9.24.0", @@ -1835,18 +1834,6 @@ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "license": "MIT" }, - "node_modules/node-cron": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz", - "integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==", - "license": "ISC", - "dependencies": { - "uuid": "8.3.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -2547,15 +2534,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 2682a3f..cd75545 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,7 @@ "canvas": "^3.2.0", "discord.js": "^14.18.0", "express": "^5.2.1", - "fs": "^0.0.1-security", - "node-cron": "^3.0.3" + "fs": "^0.0.1-security" }, "devDependencies": { "@eslint/js": "^9.24.0", diff --git a/tasks/autoUpdateUpgrades.js b/tasks/autoUpdateUpgrades.js index cb31b61..a410844 100644 --- a/tasks/autoUpdateUpgrades.js +++ b/tasks/autoUpdateUpgrades.js @@ -1,5 +1,5 @@ module.exports = async (client, torn, config) => { - console.debug("Task: Executing autoUpdateUpgrades"); + const fs = require('fs'); const path = require('path'); const renderer = require('../utils/UpgradeRenderer.js'); diff --git a/tasks/noItemOC.js b/tasks/noItemOC.js index 246475e..4bbc24f 100644 --- a/tasks/noItemOC.js +++ b/tasks/noItemOC.js @@ -1,5 +1,5 @@ module.exports = async (client, torn, config) => { - console.debug("Task: Executing noItemOC"); + const fs = require('fs'); const channel = client.channels.resolve(config.channels.ocAlert); const now = new Date(); diff --git a/tasks/unavailableOC.js b/tasks/unavailableOC.js index 7d7142c..488851a 100644 --- a/tasks/unavailableOC.js +++ b/tasks/unavailableOC.js @@ -1,5 +1,5 @@ module.exports = async (client, torn, config) => { - console.debug("Task: Executing unavailableOC"); + const { EmbedBuilder } = require('discord.js'); const fs = require('fs'); const channel = client.channels.resolve(config.channels.ocAlert); @@ -41,6 +41,10 @@ module.exports = async (client, torn, config) => { let embed = new EmbedBuilder() .setTitle('Crime Availability Check') await torn.faction.crimes({ category: 'recruiting', offset: 0, sort: 'DESC' }).then(crimeList => { + if (!crimeList) { + console.error("unavailableOC: API returned no crimes."); + return; + } const data = { crimes: crimeList }; data.crimes.forEach(crime => { crimes.difficulty[crime.difficulty - 1].count++ diff --git a/tasks/unpaidOC.js b/tasks/unpaidOC.js index 571cffc..f604ed0 100644 --- a/tasks/unpaidOC.js +++ b/tasks/unpaidOC.js @@ -1,12 +1,17 @@ module.exports = async (client, torn, config) => { - console.debug("Task: Executing unpaidOC"); + const { EmbedBuilder } = require('discord.js'); const fs = require('fs'); const channel = client.channels.resolve(config.channels.ocAlert); const now = new Date(); const state = require('../state.json'); let embeds = []; - const data = { crimes: await torn.faction.crimes({ category: 'successful', from: now.getTime() / 1000 - 7 * 24 * 60 * 60, sort: 'DESC' }) }; + const crimesList = await torn.faction.crimes({ category: 'successful', from: now.getTime() / 1000 - 7 * 24 * 60 * 60, sort: 'DESC' }); + if (!crimesList) { + console.error("unpaidOC: API returned no crimes."); + return; + } + const data = { crimes: crimesList }; for (const crime of data.crimes) { if (!crime.rewards.payout) { console.debug(`unpaidOC: Found unpaid crime: ${crime.name}:${crime.id}`); diff --git a/torn.js b/torn.js index b80ac80..81dccae 100644 --- a/torn.js +++ b/torn.js @@ -97,7 +97,11 @@ async function getCached(collectionName, id, fetchFn, ttl) { async function fetchApi(path) { const glue = path.includes('?') ? '&' : '?'; const response = await fetch(`${path}${glue}key=${config.torn}`); - return response.json(); + const data = await response.json(); + if (data.error) { + console.error(`Torn API Error on ${path}:`, JSON.stringify(data.error)); + } + return data; } const api = { @@ -199,12 +203,12 @@ const api = { }, async crimes(options = {}) { let params = new URLSearchParams(); - let category = ''; + if (typeof options === 'string') { - category = options; + params.append('cat', options); } else { - if (options.category) category = options.category; + if (options.category) params.append('cat', options.category); if (options.from) params.append('from', options.from); if (options.to) params.append('to', options.to); if (options.limit) params.append('limit', options.limit); @@ -213,7 +217,7 @@ const api = { if (options.initiator) params.append('initiator', options.initiator); } - const endpoint = category ? `https://api.torn.com/v2/faction/crimes/${category}` : `https://api.torn.com/v2/faction/crimes`; + const endpoint = `https://api.torn.com/v2/faction/crimes`; const queryString = params.toString() ? `?${params.toString()}` : ''; const data = await fetchApi(`${endpoint}${queryString}`);