From 8c0869fcd876bfc0f572ddd871efe071b62bba86 Mon Sep 17 00:00:00 2001 From: TheShadowEevee Date: Mon, 6 Oct 2025 11:40:08 -0400 Subject: [PATCH] Add Attendance Thread Creation and Reminders --- src/crons/attendance-creation.ts | 67 ++++++++++++++++++++ src/crons/attendance-reminder-friday.ts | 75 +++++++++++++++++++++++ src/crons/attendance-reminder-saturday.ts | 75 +++++++++++++++++++++++ src/crons/index.ts | 11 +++- src/utils/consts.ts | 10 +-- 5 files changed, 233 insertions(+), 5 deletions(-) create mode 100644 src/crons/attendance-creation.ts create mode 100644 src/crons/attendance-reminder-friday.ts create mode 100644 src/crons/attendance-reminder-saturday.ts diff --git a/src/crons/attendance-creation.ts b/src/crons/attendance-creation.ts new file mode 100644 index 0000000..84b54d0 --- /dev/null +++ b/src/crons/attendance-creation.ts @@ -0,0 +1,67 @@ +import { type Client, ThreadAutoArchiveDuration } from "discord.js"; +import { schedule } from "node-cron"; + +import { + HACK_NIGHT_ATTENDANCE_CHANNEL_ID, + HACK_NIGHT_ATTENDANCE_ROLE_ID, +} from "../utils/consts"; + +export default async function startTask(client: Client) { + const task = schedule("0 20 * * 5", handler(client)); + return task.start(); +} + +function handler(client: Client) { + return async () => { + const channel = client.channels.cache.get( + HACK_NIGHT_ATTENDANCE_CHANNEL_ID, + ); + + if (!channel) { + console.error("Could not find channel"); + return; + } + + if (!channel.isSendable()) { + console.error("Cannot send messages channel"); + return; + } + + const message = await channel.send({ + content: `<@&${HACK_NIGHT_ATTENDANCE_ROLE_ID}>: Put attendance for the night here! Please rename the thread to tonight's version number.`, + }); + + if (!message) { + console.error("Could not create Hack Night attendance thread"); + return; + } + + await message.pin(); + + const dateObj = new Date(); + const date = `${`${1 + dateObj.getMonth()}`.padStart(2, "0")}/${`${dateObj.getDate()}`.padStart(2, "0")}`; + + await message.startThread({ + name: `Hack Night Attendance - ${date}`, + autoArchiveDuration: ThreadAutoArchiveDuration.OneDay, + }); + + const pinnedMessage = await channel.messages.fetch({ limit: 1 }); + + if (!pinnedMessage) { + console.error("Could not fetch last message"); + return; + } + + const systemMessage = pinnedMessage.first(); + + if (!systemMessage) { + console.error("Could not find last message"); + return; + } + + await systemMessage.delete(); + + console.log("Created Hack Night attendance thread"); + }; +} diff --git a/src/crons/attendance-reminder-friday.ts b/src/crons/attendance-reminder-friday.ts new file mode 100644 index 0000000..3ac354c --- /dev/null +++ b/src/crons/attendance-reminder-friday.ts @@ -0,0 +1,75 @@ +import { ChannelType, type Client } from "discord.js"; +import { schedule } from "node-cron"; + +import { + HACK_NIGHT_ATTENDANCE_CHANNEL_ID, + HACK_NIGHT_ATTENDANCE_ROLE_ID, +} from "../utils/consts"; + +// Every 15 Minutes starting at 9pm +export default async function startTask(client: Client) { + const task = schedule("*/15 21-23 * * 5", handler(client)); + return task.start(); +} + +function handler(client: Client) { + return async () => { + const channel = client.channels.cache.get( + HACK_NIGHT_ATTENDANCE_CHANNEL_ID, + ); + + if (!channel) { + console.error("Could not find channel"); + return; + } + + if (!channel.isSendable()) { + console.error("Cannot send messages to channel"); + return; + } + + if (channel.type !== ChannelType.GuildText) { + console.error("Cannot create threads in channel"); + return; + } + + const threads = await channel.threads.fetchActive(); + + if (!threads) { + console.error("Could not fetch active threads"); + return; + } + + const hackNightAttendanceThread = threads.threads + .filter((t) => { + return t.name.startsWith(""); + }) + .sorted((a, b) => { + if (!a.createdTimestamp || !b.createdTimestamp) return 0; + return b.createdTimestamp - a.createdTimestamp; + }) + .first(); + + if (!hackNightAttendanceThread) { + console.error("Could not find latest thread"); + return; + } + + const lastMessage = channel.lastMessage; + + if (lastMessage) { + const timeSent = lastMessage.createdAt; + + // If most recent message was sent more than 1.5 hours ago + if ( + new Date().getTime() - timeSent.getTime() > + 1.5 * 60 * 60 * 1000 + ) { + } + } + + hackNightAttendanceThread.send({ + content: `<@&${HACK_NIGHT_ATTENDANCE_ROLE_ID}>: Last attendance taken (or attendance reminder) was more than an hour and a half ago!`, + }); + }; +} diff --git a/src/crons/attendance-reminder-saturday.ts b/src/crons/attendance-reminder-saturday.ts new file mode 100644 index 0000000..ea851ce --- /dev/null +++ b/src/crons/attendance-reminder-saturday.ts @@ -0,0 +1,75 @@ +import { ChannelType, type Client } from "discord.js"; +import { schedule } from "node-cron"; + +import { + HACK_NIGHT_ATTENDANCE_CHANNEL_ID, + HACK_NIGHT_ATTENDANCE_ROLE_ID, +} from "../utils/consts"; + +// Every 15 Minutes until 2am +export default async function startTask(client: Client) { + const task = schedule("*/15 0-2 * * 6", handler(client)); + return task.start(); +} + +function handler(client: Client) { + return async () => { + const channel = client.channels.cache.get( + HACK_NIGHT_ATTENDANCE_CHANNEL_ID, + ); + + if (!channel) { + console.error("Could not find channel"); + return; + } + + if (!channel.isSendable()) { + console.error("Cannot send messages to channel"); + return; + } + + if (channel.type !== ChannelType.GuildText) { + console.error("Cannot create threads in channel"); + return; + } + + const threads = await channel.threads.fetchActive(); + + if (!threads) { + console.error("Could not fetch active threads"); + return; + } + + const hackNightAttendanceThread = threads.threads + .filter((t) => { + return t.name.startsWith(""); + }) + .sorted((a, b) => { + if (!a.createdTimestamp || !b.createdTimestamp) return 0; + return b.createdTimestamp - a.createdTimestamp; + }) + .first(); + + if (!hackNightAttendanceThread) { + console.error("Could not find latest thread"); + return; + } + + const lastMessage = channel.lastMessage; + + if (lastMessage) { + const timeSent = lastMessage.createdAt; + + // If most recent message was sent more than 1.5 hours ago + if ( + new Date().getTime() - timeSent.getTime() > + 1.5 * 60 * 60 * 1000 + ) { + } + } + + hackNightAttendanceThread.send({ + content: `<@&${HACK_NIGHT_ATTENDANCE_ROLE_ID}>: Last attendance taken was more than an hour and a half ago!`, + }); + }; +} diff --git a/src/crons/index.ts b/src/crons/index.ts index 2ffebcc..572fd52 100644 --- a/src/crons/index.ts +++ b/src/crons/index.ts @@ -1,4 +1,13 @@ import hackNightPhotosCleanup from "./hack-night-photo-cleanup"; import hackNightPhotos from "./hack-night-photos"; +import hackNightAttendanceInit from "./attendance-creation"; +import hackNightAttendanceReminderFriday from "./attendance-reminder-friday"; +import hackNightAttendanceReminderSaturday from "./attendance-reminder-saturday"; -export const tasks = [hackNightPhotos, hackNightPhotosCleanup]; +export const tasks = [ + hackNightPhotos, + hackNightPhotosCleanup, + hackNightAttendanceInit, + hackNightAttendanceReminderFriday, + hackNightAttendanceReminderSaturday, +]; diff --git a/src/utils/consts.ts b/src/utils/consts.ts index 4de2d8c..819f161 100644 --- a/src/utils/consts.ts +++ b/src/utils/consts.ts @@ -1,14 +1,16 @@ export const LOUNGE_CHANNEL_ID = "809628073896443904"; export const HACK_NIGHT_CHANNEL_ID = "1020777328172859412"; export const ADMINS = ["636701123620634653"]; +export const HACK_NIGHT_ATTENDANCE_CHANNEL_ID = "1287861645628149760"; +export const HACK_NIGHT_ATTENDANCE_ROLE_ID = "1183270835469963354"; export const ORGANIZER_ROLE_ID = "1012751663322382438"; export const BISHOP_ROLE_ID = "1199891815780847647"; export const HACK_NIGHT_PHOTOGRAPHY_AWARD_ROLE_ID = "1340775295233560606"; export const EVERGREEN_CREATE_ISSUE_STRING = "evergreen it"; export const INTERNAL_CATEGORIES = [ - "809620177347411998", - "1290013838955249734", - "1082077318329143336", - "938975633885782037", + "809620177347411998", + "1290013838955249734", + "1082077318329143336", + "938975633885782037", ]; export const WACKY_ROLE_ID = "1419119560627458129";