feat: added cron manager

This commit is contained in:
Slincnik 2025-01-10 21:42:05 +03:00
parent e8606e4977
commit ea9b52cc4a
No known key found for this signature in database
7 changed files with 250 additions and 130 deletions

View file

@ -1,4 +1,7 @@
import logger from "../helpers/logger.js"; import logger from "../helpers/logger.js";
import { resolve } from "node:path";
import loadCronTasks from "../utils/loadCronTasks.js";
import { CronManager } from "../services/cron/index.js";
export const data = { export const data = {
name: "ready", name: "ready",
@ -11,4 +14,11 @@ export const data = {
*/ */
export async function run(client) { export async function run(client) {
logger.ready(client.user.tag + " is online!"); logger.ready(client.user.tag + " is online!");
const taskPath = resolve(client.configService.get("paths.tasks"));
const cronTasks = await loadCronTasks(taskPath);
const cronManager = new CronManager(cronTasks);
await cronManager.init();
} }

View file

@ -1,71 +0,0 @@
import { CronJob } from "cron";
import useClient from "../utils/use-client.js";
import UserModel from "../models/UserModel.js";
import { createEmbed } from "../utils/create-embed.js";
import logger from "./logger.js";
import { getNoun } from "./functions.js";
const checkBirthdays = async () => {
const client = useClient();
const guilds = client.guilds.cache.values();
const users = await client.adapter.find(UserModel, { birthdate: { $gt: 1 } });
const currentData = new Date();
const currentYear = currentData.getFullYear();
const currentMonth = currentData.getMonth();
const currentDate = currentData.getDate();
for (const guild of guilds) {
try {
const data = await client.getGuildData(guild.id);
const channel = data.plugins.birthdays ? await client.channels.fetch(data.plugins.birthdays) : null;
if (!channel) return;
const userIDs = users.filter(u => guild.members.cache.has(u.id)).map(u => u.id);
await Promise.all(
userIDs.map(async userID => {
const user = users.find(u => u.id === userID);
const userData = new Date(user.birthdate).getFullYear() <= 1970 ? new Date(user.birthdate * 1000) : new Date(user.birthdate);
const userYear = userData.getFullYear();
const userMonth = userData.getMonth();
const userDate = userData.getDate();
const age = currentYear - userYear;
if (userDate === currentDate && userMonth === currentMonth) {
const embed = createEmbed({
author: client.user.username,
fields: [
{
name: client.translate("economy/birthdate:HAPPY_BIRTHDAY", {
lng: data.language,
}),
value: client.translate("economy/birthdate:HAPPY_BIRTHDAY_MESSAGE", {
lng: data.language,
user: user.id,
age: `**${age}** ${getNoun(age, [
client.translate("misc:NOUNS:AGE:1", null, data.language),
client.translate("misc:NOUNS:AGE:2", null, data.language),
client.translate("misc:NOUNS:AGE:5", null, data.language),
])}`,
}),
},
],
});
await channel.send({ embeds: [embed] }).then(m => m.react(" "));
}
}),
);
} catch (error) {
logger.error(error);
}
}
};
export async function init() {
new CronJob("0 5 * * *", checkBirthdays(), null, true, "Europe/Moscow");
}

View file

@ -1,59 +0,0 @@
import UserModel from "../models/UserModel";
import useClient from "../utils/use-client";
const checkReminds = async () => {
const client = useClient();
client.adapter.find(UserModel, { reminds: { $gt: [] } }).then(users => {
for (const user of users) {
if (!client.users.cache.has(user.id)) client.users.fetch(user.id);
client.cacheReminds.set(user.id, user);
}
});
client.cacheReminds.forEach(async user => {
const cachedUser = client.users.cache.get(user.id);
if (!cachedUser) return;
const reminds = user.reminds,
mustSent = reminds.filter(r => r.sendAt < Math.floor(Date.now() / 1000));
if (!mustSent.length) return;
mustSent.forEach(r => {
const embed = client.embed({
author: client.translate("general/remindme:EMBED_TITLE"),
fields: [
{
name: client.translate("general/remindme:EMBED_CREATED"),
value: `<t:${r.createdAt}:f>`,
inline: true,
},
{
name: client.translate("general/remindme:EMBED_TIME"),
value: `<t:${r.sendAt}:f>`,
inline: true,
},
{
name: client.translate("common:MESSAGE"),
value: r.message,
},
],
});
cachedUser.send({ embeds: [embed] }).then(() => {
client.adapter.updateOne(UserModel, { id: user.id }, { $pull: { reminds: { _id: r._id } } });
});
});
if (!user.reminds.length) client.cacheReminds.delete(user.id);
});
};
export const init = async () => {
setInterval(async () => {
await checkReminds();
}, 1000);
};

View file

@ -0,0 +1,70 @@
import useClient from "../../utils/use-client.js";
import UserModel from "../../models/UserModel.js";
import { createEmbed } from "../../utils/create-embed.js";
import logger from "../logger.js";
import { getNoun } from "../functions.js";
export const data = {
name: "birthdays",
task: async () => {
const client = useClient();
const guilds = client.guilds.cache.values();
const users = await client.adapter.find(UserModel, { birthdate: { $gt: 1 } });
const currentData = new Date();
const currentYear = currentData.getFullYear();
const currentMonth = currentData.getMonth();
const currentDate = currentData.getDate();
for (const guild of guilds) {
try {
const data = await client.getGuildData(guild.id);
const channel = data.plugins.birthdays ? await client.channels.fetch(data.plugins.birthdays) : null;
if (!channel) return;
const userIDs = users.filter(u => guild.members.cache.has(u.id)).map(u => u.id);
await Promise.all(
userIDs.map(async userID => {
const user = users.find(u => u.id === userID);
const userData = new Date(user.birthdate).getFullYear() <= 1970 ? new Date(user.birthdate * 1000) : new Date(user.birthdate);
const userYear = userData.getFullYear();
const userMonth = userData.getMonth();
const userDate = userData.getDate();
const age = currentYear - userYear;
if (userDate === currentDate && userMonth === currentMonth) {
const embed = createEmbed({
author: client.user.username,
fields: [
{
name: client.translate("economy/birthdate:HAPPY_BIRTHDAY", {
lng: data.language,
}),
value: client.translate("economy/birthdate:HAPPY_BIRTHDAY_MESSAGE", {
lng: data.language,
user: user.id,
age: `**${age}** ${getNoun(age, [
client.translate("misc:NOUNS:AGE:1", null, data.language),
client.translate("misc:NOUNS:AGE:2", null, data.language),
client.translate("misc:NOUNS:AGE:5", null, data.language),
])}`,
}),
},
],
});
await channel.send({ embeds: [embed] }).then(m => m.react(" "));
}
}),
);
} catch (error) {
logger.error(error);
}
}
},
schedule: "0 5 * * *",
};

View file

@ -0,0 +1,68 @@
import UserModel from "../../models/UserModel.js";
import useClient from "../../utils/use-client.js";
import { createEmbed } from "../../utils/index.js";
export const data = {
name: "checkReminds",
task: async () => {
const client = useClient();
client.adapter.find(UserModel, { reminds: { $gt: [] } }).then(users => {
for (const user of users) {
if (!client.users.cache.has(user.id)) client.users.fetch(user.id);
client.cacheReminds.set(user.id, user);
}
});
client.cacheReminds.forEach(async user => {
const cachedUser = client.users.cache.get(user.id);
if (!cachedUser) return;
const reminds = user.reminds,
mustSent = reminds.filter(r => r.sendAt < Math.floor(Date.now() / 1000));
if (!mustSent.length) return;
mustSent.forEach(r => {
const embed = createEmbed({
author: {
name: client.translate("general/remindme:EMBED_TITLE"),
},
fields: [
{
name: client.translate("general/remindme:EMBED_CREATED"),
value: `<t:${r.createdAt}:f>`,
inline: true,
},
{
name: client.translate("general/remindme:EMBED_TIME"),
value: `<t:${r.sendAt}:f>`,
inline: true,
},
{
name: client.translate("common:MESSAGE"),
value: r.message,
},
],
});
cachedUser
.send({
embeds: [embed],
})
.then(() => {
client.adapter.updateOne(UserModel, { id: user.id }, { $pull: { reminds: { _id: r._id } } });
});
});
user.reminds = user.reminds.filter(r => r.sendAt >= Math.floor(Date.now() / 1000));
await user.save();
if (!user.reminds.length) client.cacheReminds.delete(user.id);
});
},
schedule: "* * * * * *",
};

View file

@ -0,0 +1,61 @@
import { CronJob } from "cron";
import logger from "../../helpers/logger.js";
export class CronManager {
constructor(tasks) {
this.tasks = tasks;
this.jobs = new Map();
}
async init() {
if (!this.tasks.length) {
logger.warn("No cron tasks to schedule.");
return;
}
for (const task of this.tasks) {
const taskJob = this.jobs.get(task.name);
if (taskJob?.isRunning) {
logger.warn(`Cron task "${task.name}" is already running.`);
continue;
}
const job = new CronJob(
task.schedule,
async () => {
if (this.jobs.get(task.name)?.isError) {
return;
}
try {
await task.task();
} catch (error) {
logger.error(`Error executing cron task "${task.name}":`, error);
this.jobs.get(task.name).isError = true;
}
},
null,
false,
"Europe/Moscow",
);
job.start();
this.jobs.set(task.name, {
job,
isRunning: true,
isError: false,
});
}
logger.log(`Cron tasks scheduled: ${this.jobs.size}`);
}
stopAll() {
if (!this.jobs.length) return;
// eslint-disable-next-line no-unused-vars
for (const [_, jobInfo] of this.jobs.entries()) {
jobInfo.job.stop();
}
logger.log("All cron jobs stopped.");
}
}

View file

@ -0,0 +1,41 @@
import logger from "../helpers/logger.js";
import { getFilePaths } from "./get-path.js";
import { toFileURL } from "./resolve-file.js";
const loadCronTasks = async taskPath => {
try {
const filePaths = (await getFilePaths(taskPath, true)).filter(file => file.endsWith(".js"));
const tasks = [];
for (const filePath of filePaths) {
const { data } = await import(toFileURL(filePath));
if (!data) continue;
if (!data.name) {
logger.warn("No name found in task:", filePath);
continue;
}
if (!data.schedule) {
logger.warn("No schedule found in task:", filePath);
continue;
}
if (typeof data.task !== "function") {
logger.warn("Task is not a function:", filePath);
continue;
}
tasks.push(data);
}
return tasks;
} catch (error) {
logger.error("Error loading cron tasks:", error);
return [];
}
};
export default loadCronTasks;