diff --git a/.env.example b/.env.example index bb272bc96..4c1575268 100644 --- a/.env.example +++ b/.env.example @@ -18,7 +18,8 @@ DISCORD_BOT_TOKEN="discord-bot-token" DISCORD_CLIENT_ID="discord-client-id" DISCORD_CLIENT_SECRET="discord-client-secret" DISCORD_WEATHER_API_KEY="discord-weather-api-key" -DISCORD_WEBHOOK_ANIMAL="" +DISCORD_WEBHOOK_ANIMAL="discord-webhook-animal" +DISCORD_WEBHOOK_BIRTHDAY="discord-webhook-birthday" DISCORD_WEBHOOK_LEETCODE="discord-webhook-leetcode" DISCORD_WEBHOOK_REMINDERS="discord-webhook-reminders" DISCORD_WEBHOOK_REMINDERS_HACK="discord-webhook-reminders-hack" diff --git a/apps/cron/src/crons/birthday.ts b/apps/cron/src/crons/birthday.ts new file mode 100644 index 000000000..4912e247a --- /dev/null +++ b/apps/cron/src/crons/birthday.ts @@ -0,0 +1,54 @@ +import { WebhookClient } from "discord.js"; +import { and, eq, exists, or, sql } from "drizzle-orm"; + +import { db } from "@forge/db/client"; +import { Permissions, User } from "@forge/db/schemas/auth"; +import { DuesPayment, Member } from "@forge/db/schemas/knight-hacks"; +import { logger } from "@forge/utils"; + +import { env } from "../env"; +import { CronBuilder } from "../structs/CronBuilder"; + +const BIRTHDAY_WEBHOOK = new WebhookClient({ + url: env.DISCORD_WEBHOOK_BIRTHDAY, +}); + +export const birthday = new CronBuilder({ + name: "birthday", + color: 7, +}).addCron( + "0 12 * * *", // every day at 12 (noon!) + async () => { + const today = new Date(); + const month = today.getMonth() + 1; + const day = today.getDate(); + + const members = await db + .select({ + firstName: Member.firstName, + lastName: Member.lastName, + guildProfileVisible: Member.guildProfileVisible, + discordId: User.discordUserId, + }) + .from(Member) + .leftJoin(User, eq(User.id, Member.userId)) + .where( + and( + exists( + db + .select() + .from(Permissions) + .where(eq(Permissions.userId, Member.userId)), + ), + eq(Member.guildProfileVisible, true), + eq(sql`EXTRACT(MONTH FROM ${Member.dob})`, month), + eq(sql`EXTRACT(DAY FROM ${Member.dob})`, day), + ), + ); + + for (const u of members) { + logger.log(`${u.firstName} ${u.lastName}'s birthday today!`); + await BIRTHDAY_WEBHOOK.send(`Happy Birthday, <@${u.discordId}>`); + } + }, +); diff --git a/apps/cron/src/env.ts b/apps/cron/src/env.ts index d8864e6d3..ea1ac1dfb 100644 --- a/apps/cron/src/env.ts +++ b/apps/cron/src/env.ts @@ -6,6 +6,7 @@ export const env = createEnv({ DISCORD_BOT_TOKEN: z.string(), DISCORD_WEBHOOK_ANIMAL: z.string(), DISCORD_WEBHOOK_LEETCODE: z.string(), + DISCORD_WEBHOOK_BIRTHDAY: z.string(), DISCORD_WEBHOOK_REMINDERS: z.string(), DISCORD_WEBHOOK_REMINDERS_PRE: z.string(), DISCORD_WEBHOOK_REMINDERS_HACK: z.string(), @@ -13,6 +14,7 @@ export const env = createEnv({ runtimeEnvStrict: { DISCORD_BOT_TOKEN: process.env.DISCORD_BOT_TOKEN, DISCORD_WEBHOOK_ANIMAL: process.env.DISCORD_WEBHOOK_ANIMAL, + DISCORD_WEBHOOK_BIRTHDAY: process.env.DISCORD_WEBHOOK_BIRTHDAY, DISCORD_WEBHOOK_LEETCODE: process.env.DISCORD_WEBHOOK_LEETCODE, DISCORD_WEBHOOK_REMINDERS: process.env.DISCORD_WEBHOOK_REMINDERS, DISCORD_WEBHOOK_REMINDERS_PRE: process.env.DISCORD_WEBHOOK_REMINDERS_PRE, diff --git a/apps/cron/src/index.ts b/apps/cron/src/index.ts index 90a8c9275..6d255d90f 100644 --- a/apps/cron/src/index.ts +++ b/apps/cron/src/index.ts @@ -1,6 +1,7 @@ import { alumniAssign } from "./crons/alumni-assign"; import { capybara, cat, duck, goat } from "./crons/animals"; import { backupFilteredDb } from "./crons/backup-filtered-db"; +import { birthday } from "./crons/birthday"; import { leetcode } from "./crons/leetcode"; import { preReminders, reminders } from "./crons/reminder"; import { roleSync } from "./crons/role-sync"; @@ -22,4 +23,6 @@ reminders.schedule(); // Silencing for now, needs to be manually re-enabled for hacks @WHOEVER_IS_DEV_LEAD_RN // hackReminders.schedule(); +birthday.schedule(); + roleSync.schedule();