aboutsummaryrefslogtreecommitdiff
path: root/src/tasks/feature
diff options
context:
space:
mode:
Diffstat (limited to 'src/tasks/feature')
-rw-r--r--src/tasks/feature/handleReminders.ts39
-rw-r--r--src/tasks/feature/memberCount.ts28
-rw-r--r--src/tasks/feature/removeExpiredPunishements.ts84
-rw-r--r--src/tasks/feature/updateStats.ts26
4 files changed, 177 insertions, 0 deletions
diff --git a/src/tasks/feature/handleReminders.ts b/src/tasks/feature/handleReminders.ts
new file mode 100644
index 0000000..7863c9a
--- /dev/null
+++ b/src/tasks/feature/handleReminders.ts
@@ -0,0 +1,39 @@
+import { BushTask, dateDelta, format, Reminder, Time } from '#lib';
+const { Op } = (await import('sequelize')).default;
+
+export default class HandlerRemindersTask extends BushTask {
+ public constructor() {
+ super('handlerReminders', {
+ delay: 30 * Time.Second,
+ runOnStart: true
+ });
+ }
+
+ public async exec() {
+ const expiredEntries = await Reminder.findAll({
+ where: {
+ expires: {
+ [Op.lt]: new Date(Date.now() + 30 * Time.Second) // Find all rows with an expiry date before 30 seconds from now
+ },
+ notified: false
+ }
+ });
+
+ void this.client.logger.verbose(
+ `handlerReminders`,
+ `Queried reminders, found <<${expiredEntries.length}>> expired reminders.`
+ );
+
+ for (const entry of expiredEntries) {
+ setTimeout(() => {
+ void this.client.users
+ .send(
+ entry.user,
+ `The reminder you set ${dateDelta(entry.created)} ago has expired: ${format.bold(entry.content)}\n${entry.messageUrl}`
+ )
+ .catch(() => false);
+ void entry.update({ notified: true });
+ }, entry.expires.getTime() - new Date().getTime());
+ }
+ }
+}
diff --git a/src/tasks/feature/memberCount.ts b/src/tasks/feature/memberCount.ts
new file mode 100644
index 0000000..ea422fa
--- /dev/null
+++ b/src/tasks/feature/memberCount.ts
@@ -0,0 +1,28 @@
+import { BushTask, MemberCount, Time } from '#lib';
+import assert from 'assert';
+
+export default class MemberCountTask extends BushTask {
+ public constructor() {
+ super('memberCount', {
+ delay: Time.Minute,
+ runOnStart: true
+ });
+ }
+
+ public override async exec() {
+ if (!this.client.config.isProduction) return;
+
+ const res = await Promise.allSettled(
+ this.client.guilds.cache
+ .filter((g) => g.memberCount >= 100)
+ .map((g) => MemberCount.create({ guildId: g.id, memberCount: g.memberCount }))
+ );
+
+ res
+ .filter((r) => r.status === 'rejected')
+ .forEach((r) => {
+ assert(r.status === 'rejected');
+ void this.client.console.error('memberCount', r.status);
+ });
+ }
+}
diff --git a/src/tasks/feature/removeExpiredPunishements.ts b/src/tasks/feature/removeExpiredPunishements.ts
new file mode 100644
index 0000000..0b20a27
--- /dev/null
+++ b/src/tasks/feature/removeExpiredPunishements.ts
@@ -0,0 +1,84 @@
+import { ActivePunishment, ActivePunishmentType, BushTask, Time } from '#lib';
+import assert from 'assert';
+const { Op } = (await import('sequelize')).default;
+
+export default class RemoveExpiredPunishmentsTask extends BushTask {
+ public constructor() {
+ super('removeExpiredPunishments', {
+ delay: 15 * Time.Second,
+ runOnStart: true
+ });
+ }
+
+ public async exec() {
+ const expiredEntries = await ActivePunishment.findAll({
+ where: {
+ expires: {
+ [Op.lt]: new Date(Date.now() + 15 * Time.Second) // Find all rows with an expiry date before 15 seconds from now
+ }
+ }
+ });
+
+ void this.client.logger.verbose(
+ `removeExpiredPunishments`,
+ `Queried punishments, found <<${expiredEntries.length}>> expired punishments.`
+ );
+
+ for (const entry of expiredEntries) {
+ const guild = this.client.guilds.cache.get(entry.guild);
+ if (!guild) continue;
+
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
+ setTimeout(async () => {
+ const member = guild.members.cache.get(entry.user);
+ const user = await this.client.utils.resolveNonCachedUser(entry.user);
+ assert(guild);
+
+ switch (entry.type) {
+ case ActivePunishmentType.BAN: {
+ assert(user);
+ const result = await guild.bushUnban({ user: user, reason: 'Punishment expired' });
+ if (['success', 'user not banned', 'cannot resolve user'].includes(result)) await entry.destroy();
+ else throw new Error(result);
+ void this.client.logger.verbose(`removeExpiredPunishments`, `Unbanned ${entry.user}.`);
+ break;
+ }
+ case ActivePunishmentType.BLOCK: {
+ if (!member) {
+ await entry.destroy(); // channel overrides are removed when the member leaves the guild
+ return;
+ }
+ const result = await member.bushUnblock({ reason: 'Punishment expired', channel: entry.extraInfo });
+ if (['success', 'user not blocked'].includes(result)) await entry.destroy();
+ else throw new Error(result);
+ void this.client.logger.verbose(`removeExpiredPunishments`, `Unblocked ${entry.user}.`);
+ break;
+ }
+ case ActivePunishmentType.MUTE: {
+ if (!member) return;
+ const result = await member.bushUnmute({ reason: 'Punishment expired' });
+ if (['success', 'failed to dm'].includes(result)) await entry.destroy();
+ else throw new Error(result);
+ void this.client.logger.verbose(`removeExpiredPunishments`, `Unmuted ${entry.user}.`);
+ break;
+ }
+ case ActivePunishmentType.ROLE: {
+ if (!member) return;
+ const role = guild?.roles?.cache?.get(entry.extraInfo);
+ if (!role) throw new Error(`Cannot unmute ${member.user.tag} because I cannot find the mute role.`);
+ const result = await member.bushRemoveRole({
+ reason: 'Punishment expired',
+ role: role,
+ addToModlog: true
+ });
+
+ if (['success', 'failed to dm'].includes(result)) await entry.destroy();
+ else throw new Error(result);
+ void this.client.logger.verbose(`removeExpiredPunishments`, `Removed a punishment role from ${entry.user}.`);
+ break;
+ }
+ }
+ }, entry.expires!.getTime() - new Date().getTime());
+ }
+ }
+}
diff --git a/src/tasks/feature/updateStats.ts b/src/tasks/feature/updateStats.ts
new file mode 100644
index 0000000..0d0e661
--- /dev/null
+++ b/src/tasks/feature/updateStats.ts
@@ -0,0 +1,26 @@
+import { BushTask, Stat, Time } from '#lib';
+import { Client } from 'discord.js';
+
+export default class UpdateStatsTask extends BushTask {
+ public constructor() {
+ super('updateStats', {
+ delay: 10 * Time.Minute,
+ runOnStart: true
+ });
+ }
+
+ public async exec() {
+ const row =
+ (await Stat.findByPk(this.client.config.environment)) ??
+ (await Stat.create({ environment: this.client.config.environment }));
+ row.commandsUsed = this.client.stats.commandsUsed;
+ row.slashCommandsUsed = this.client.stats.slashCommandsUsed;
+ await row.save();
+ }
+
+ public static async init(client: Client): Promise<{ commandsUsed: bigint; slashCommandsUsed: bigint }> {
+ const temp =
+ (await Stat.findByPk(client.config.environment)) ?? (await Stat.create({ environment: client.config.environment }));
+ return { commandsUsed: temp.commandsUsed, slashCommandsUsed: temp.slashCommandsUsed };
+ }
+}