Skip to content

Commit

Permalink
feat: migrate birthday handler to scheduler job (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
benw202 authored May 23, 2024
1 parent cd3460f commit 78c2006
Show file tree
Hide file tree
Showing 18 changed files with 83 additions and 32 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ CLOUDFLARE_SECRET_ACCESS_KEY=TOKEN
HEARTBEAT_URL=https://uptime.betterstack.com/api/v1/heartbeat/TOKEN
# Set the interval in seconds for the heartbeat to be sent, 0 to disable
HEARTBEAT_INTERVAL=300

# Set the interval in seconds for the birthday handler to be run, 0 to disable
BIRTHDAY_INTERVAL=1800
1 change: 1 addition & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

Update <small>_ May 2024</small>

- feat: migrate birthday handler to scheduler job (23/05/2024)
- chore: load .env before config (23/05/2024)
- fix: lint workflow failing silently (18/05/2024)
- chore: fix eol issues on Windows (17/05/2024)
Expand Down
Binary file added assets/images/birthday/bubbles.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/birthday/carrey.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/birthday/cat.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/birthday/cat_2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/birthday/cat_3.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/birthday/dance.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/birthday/dog.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/birthday/dog_2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/birthday/dwight.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/birthday/elf.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/birthday/snoop.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/birthday/yoda.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 25 additions & 8 deletions src/events/ready.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
connect,
setupScheduler,
Logger,
processBirthdays,
imageBaseUrl,
getScheduler,
} from '../lib';
Expand Down Expand Up @@ -51,7 +50,7 @@ export default event(Events.ClientReady, async ({ log }, client) => {
}
}

// Connect to MongoDB
// Connect to MongoDB and set up scheduler
let dbConnected = false;
let dbError: Error | undefined;
let schedulerConnected = false;
Expand All @@ -76,12 +75,7 @@ export default event(Events.ClientReady, async ({ log }, client) => {
});
}

// Set birthday handler
const birthdayInterval = setInterval(processBirthdays, 1000 * 60 * 30, client);
client.on('disconnect', () => {
clearInterval(birthdayInterval);
});

// Set heartbeat handler
if (schedulerConnected && process.env.HEARTBEAT_URL && process.env.HEARTBEAT_INTERVAL) {
const scheduler = getScheduler();
if (scheduler) {
Expand All @@ -103,6 +97,29 @@ export default event(Events.ClientReady, async ({ log }, client) => {
}
}

// Set birthday handler
if (schedulerConnected && process.env.BIRTHDAY_INTERVAL) {
const scheduler = getScheduler();
if (scheduler) {
const birthdayJobList = await scheduler.jobs({ name: 'postBirthdays' });
if (birthdayJobList.length === 0) {
scheduler.every(`${process.env.BIRTHDAY_INTERVAL} seconds`, 'postBirthdays', { interval: process.env.BIRTHDAY_INTERVAL });
Logger.info(`Birthday job scheduled with interval ${process.env.BIRTHDAY_INTERVAL}`);
} else {
const birthdayJob = birthdayJobList[0];
const { interval } = birthdayJob.attrs.data as { interval: string };
if (interval !== process.env.BIRTHDAY_INTERVAL) {
await scheduler.cancel({ name: 'postBirthdays' });
scheduler.every(`${process.env.BIRTHDAY_INTERVAL} seconds`, 'postBirthdays', { interval: process.env.BIRTHDAY_INTERVAL });
Logger.info(`Birthday job rescheduled with new interval ${process.env.BIRTHDAY_INTERVAL}`);
} else {
Logger.info('Birthday job already scheduled');
}
}
}
}

// Send bot status message to bot-dev channel
const botDevChannel = client.channels.resolve(constantsConfig.channels.MOD_LOGS) as TextChannel;
if (botDevChannel) {
const currentDate = new Date();
Expand Down
2 changes: 1 addition & 1 deletion src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export * from './slashCommand';
export * from './replies';
export * from './contextMenuCommand';
export * from './durationInEnglish';
export * from './birthdayFunction';
export * from './genericPagination';

//Schemas
Expand All @@ -20,3 +19,4 @@ export * from './schemas/birthdaySchema';
//Scheduler Jobs
export * from './schedulerJobs/autoDisableSlowMode';
export * from './schedulerJobs/sendHeartbeat';
export * from './schedulerJobs/postBirthdays';
3 changes: 2 additions & 1 deletion src/lib/scheduler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Agenda } from '@hokify/agenda';
import { Logger, autoDisableSlowMode, sendHeartbeat } from './index';
import { Logger, autoDisableSlowMode, sendHeartbeat, postBirthdays } from './index';

let scheduler: Agenda;

Expand All @@ -17,6 +17,7 @@ export async function setupScheduler(name: string, url: string, callback = Logge
await scheduler.start();
scheduler.define('autoDisableSlowMode', autoDisableSlowMode);
scheduler.define('sendHeartbeat', sendHeartbeat);
scheduler.define('postBirthdays', postBirthdays);
Logger.info('Scheduler set up');
} catch (err) {
callback(err);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,37 @@
import { Client, Colors, Guild, TextChannel } from 'discord.js';
import { constantsConfig, Birthday, getConn, Logger, makeEmbed } from './index';
import { Job } from '@hokify/agenda';
import { Colors, Guild, TextChannel, ThreadChannel } from 'discord.js';
import { Logger, getScheduler, constantsConfig, getConn, Birthday, makeEmbed, imageBaseUrl } from '../index';
import { client } from '../../client';

const gifs: string[] = [
'https://c.tenor.com/rngI-iARtUsAAAAC/happy-birthday.gif',
'https://c.tenor.com/VMC8fNKdQrcAAAAd/happy-birthday-bon-anniversaire.gif',
'https://c.tenor.com/ZVG_H1ebQ88AAAAC/hbd-happy.gif',
'https://c.tenor.com/xRSLl2b1NtkAAAAd/happy-birthday-wish.gif',
'https://c.tenor.com/2v8fJf67VTkAAAAC/holiday-classics-elf.gif',
'https://c.tenor.com/WcaloX5M08oAAAAC/kingsqueedgybot-meme.gif',
'https://c.tenor.com/UwRRdD3mCQ0AAAAC/love-sis.gif',
'https://c.tenor.com/5_VwBuyzBaAAAAAd/scream-happy-birthday.gif',
'https://c.tenor.com/PZckaksfSQIAAAAC/lets-party.gif',
'https://c.tenor.com/7fg9ogkiEmgAAAAC/happy-birthday-celebrating.gif',
'https://c.tenor.com/dfL34nBDOrcAAAAC/happy-birthday.gif',
'https://c.tenor.com/BiEt0CS2YLUAAAAd/happy-birthday-birthday.gif',
`${imageBaseUrl}/birthday/bubbles.gif`,
`${imageBaseUrl}/birthday/carrey.gif`,
`${imageBaseUrl}/birthday/cat.gif`,
`${imageBaseUrl}/birthday/cat_2.gif`,
`${imageBaseUrl}/birthday/cat_3.gif`,
`${imageBaseUrl}/birthday/dance.gif`,
`${imageBaseUrl}/birthday/dog.gif`,
`${imageBaseUrl}/birthday/dog_2.gif`,
`${imageBaseUrl}/birthday/dwight.gif`,
`${imageBaseUrl}/birthday/elf.gif`,
`${imageBaseUrl}/birthday/snoop.gif`,
`${imageBaseUrl}/birthday/yoda.gif`,
];

export async function processBirthdays(client: Client) {
export async function postBirthdays(job: Job) {
const scheduler = getScheduler();
if (!scheduler) {
Logger.error('Failed to get scheduler instance');
return;
}

// eslint-disable-next-line no-underscore-dangle
const matchingJobs = await scheduler.jobs({ _id: job.attrs._id });
if (matchingJobs.length !== 1) {
Logger.debug('Job has been deleted already, skipping execution.');
return;
}

const guild = client.guilds.resolve(constantsConfig.guildId) as Guild | null;
if (!guild) {
Logger.error('BirthdayHandler - Guild not found.');
Expand All @@ -32,7 +47,7 @@ export async function processBirthdays(client: Client) {
// Get all threads (archived included)

await channel.threads.fetch({ archived: {} });
const thread = channel.threads.cache.find((t) => t.id === constantsConfig.threads.BIRTHDAY_THREAD);
const thread = channel.threads.cache.find((t) => t.id === constantsConfig.threads.BIRTHDAY_THREAD) as ThreadChannel | null;
if (!thread) {
Logger.error('Birthday handler - Thread not found');
return;
Expand Down Expand Up @@ -68,22 +83,29 @@ export async function processBirthdays(client: Client) {
// Send birthday messages

for (const birthday of birthdays) {
// eslint-disable-next-line no-await-in-loop
const user = await guild.members.fetch(birthday.userID!);
// If the user is not found, we can't mention them
let user;
try {
// eslint-disable-next-line no-await-in-loop
user = await guild.members.fetch(birthday.userID!);
} catch (error) {
Logger.error('BirthdayHandler - Failed to fetch user', error);
}

if (!user) {
continue;
}

const gif = gifs[Math.floor(Math.random() * gifs.length)];

// Happy birthday!
const birthdayEmbed = makeEmbed({
title: 'Happy Birthday!',
description: `${user.displayName}'s birthday is today!`,
color: Colors.Green,
image: { url: gifs[Math.floor(Math.random() * gifs.length)] },
image: { url: gif },
});

//Update birthday to next year
// Update birthday to next year
const nextBirthdayDatetime = new Date(Date.UTC(currentDate.getUTCFullYear() + 1, birthday.month! - 1, birthday.day!));
nextBirthdayDatetime.setUTCHours(10 - birthday.timezone!);
birthday.utcDatetime = nextBirthdayDatetime;
Expand All @@ -94,6 +116,13 @@ export async function processBirthdays(client: Client) {
}

// Send the birthday message
thread.send({ content: user.toString(), embeds: [birthdayEmbed] });
try {
thread.send({
content: user.toString(),
embeds: [birthdayEmbed],
});
} catch (error) {
Logger.error('BirthdayHandler - Failed to send birthday message', error);
}
}
}

0 comments on commit 78c2006

Please sign in to comment.