replace scheduler with round robin every x minutes

This commit is contained in:
2026-01-11 12:19:43 -05:00
parent b222d4a5d7
commit d00d5bb313
9 changed files with 77 additions and 59 deletions

View File

@@ -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"
}
}

View File

@@ -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') {

24
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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');

View File

@@ -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();

View File

@@ -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++

View File

@@ -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}`);

14
torn.js
View File

@@ -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}`);