From d1626e36c6f2c9d57a3a49f82b71cb88cd448989 Mon Sep 17 00:00:00 2001 From: alfianchii Date: Tue, 30 Dec 2025 19:45:41 +0700 Subject: [PATCH 1/3] feat: opening --- .../jobs/handlers/notify-waiting-checkin.ts | 44 ++++++++++++++++ .../jobs/messages/notify-waiting-checkin.ts | 32 ++++++++++++ .../jobs/validators/notify-waiting-checkin.ts | 50 +++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 src/bot/events/client-ready/jobs/handlers/notify-waiting-checkin.ts create mode 100644 src/bot/events/client-ready/jobs/messages/notify-waiting-checkin.ts create mode 100644 src/bot/events/client-ready/jobs/validators/notify-waiting-checkin.ts diff --git a/src/bot/events/client-ready/jobs/handlers/notify-waiting-checkin.ts b/src/bot/events/client-ready/jobs/handlers/notify-waiting-checkin.ts new file mode 100644 index 0000000..2d8abfb --- /dev/null +++ b/src/bot/events/client-ready/jobs/handlers/notify-waiting-checkin.ts @@ -0,0 +1,44 @@ +import type { Client, TextChannel } from 'discord.js' +import process from 'node:process' +import { WARDEN_DUTY_CHANNEL } from '@config/discord' +import { registerClientReadyHandler } from '@events/client-ready/registry' +import { EVENT_PATH } from '@events/index' +import { getChannel } from '@utils/discord' +import { DiscordBaseError } from '@utils/discord/error' +import { getModuleName } from '@utils/io' +import { log } from '@utils/logger' +import cron from 'node-cron' +import { NotifyWaitingCheckin } from '../validators/notify-waiting-checkin' + +export class NotifyWaitingCheckinError extends DiscordBaseError { + constructor(message: string, options?: { cause?: unknown }) { + super('NotifyWaitingCheckinError', message, options) + } +} + +const moduleName = getModuleName(EVENT_PATH, __filename) + +registerClientReadyHandler({ + desc: 'Notifies Flamewardens if there are users with check-in status still waiting for review at 22:00 (WIB).', + errorTag: () => `${moduleName}: ${NotifyWaitingCheckin.ERR.UnexpectedNotifyWaitingCheckin}`, + async exec(client: Client) { + try { + cron.schedule('0 22 * * *', async () => { + log.check(NotifyWaitingCheckin.MSG.JobRunning) + + const guild = await client.guilds.fetch(process.env.GUILD_ID!) + const wardenDutyChannel = await getChannel(guild, WARDEN_DUTY_CHANNEL) as TextChannel + NotifyWaitingCheckin.assertChannel(wardenDutyChannel) + const checkins = await NotifyWaitingCheckin.getTodayWaitingCheckins(client.prisma) + + await NotifyWaitingCheckin.sendOpening(guild.name, wardenDutyChannel) + + log.success(NotifyWaitingCheckin.MSG.JobSuccess) + }) + } + catch (err) { + if (!(err instanceof DiscordBaseError)) + throw err + } + }, +}) diff --git a/src/bot/events/client-ready/jobs/messages/notify-waiting-checkin.ts b/src/bot/events/client-ready/jobs/messages/notify-waiting-checkin.ts new file mode 100644 index 0000000..2020e91 --- /dev/null +++ b/src/bot/events/client-ready/jobs/messages/notify-waiting-checkin.ts @@ -0,0 +1,32 @@ +import type { Checkin } from '@type/checkin' +import { FLAMEWARDEN_ROLE, GRINDER_ROLE } from '@config/discord' +import { getParsedNow } from '@utils/date' +import { DiscordAssert } from '@utils/discord' + +export class NotifyWaitingCheckinMessage extends DiscordAssert { + static override readonly ERR = { + ...DiscordAssert.ERR, + UnexpectedNotifyWaitingCheckin: '❌ Something went wrong while notifying waiting check-in', + } + + static override readonly MSG = { + ...DiscordAssert.MSG, + JobRunning: '[JOB] Running notify waiting checkin...', + JobSuccess: '[JOB] Notify waiting checkin finished successfully', + Opening: (guildName: string) => ` +Wahai para <@&${FLAMEWARDEN_ROLE}>, +tatkala malam kian mendekat dan waktu hampir beralih hari, ${guildName} mencatat bahwa masih terdapat percikan api yang belum ditakar. +**📜 Laporan Status Api** +Beberapa *check-in* para <@&${GRINDER_ROLE}> masih berada dalam keadaan *WAITING* dan belum memperoleh keputusan hingga saat ini. +**⏳ Waktu Genting** +Apabila nyala tersebut tidak ditinjau sebelum 23:59 WIB, +maka rangkaian api para <@&${GRINDER_ROLE}> terkait berisiko gugur pada pergantian hari. +**⚔️ Tugas Penjagaan** +Demi menjaga keadilan perjalanan dan kesinambungan disiplin, +dimohon para <@&${FLAMEWARDEN_ROLE}> berkenan: +Ⅰ. Meninjau *check-in* yang masih tertunda, +Ⅱ. Menetapkan keputusan dengan bijaksana, +Ⅲ. Atau memberi arahan seperlunya sebelum waktu berganti. + `, + } +} diff --git a/src/bot/events/client-ready/jobs/validators/notify-waiting-checkin.ts b/src/bot/events/client-ready/jobs/validators/notify-waiting-checkin.ts new file mode 100644 index 0000000..1d842ce --- /dev/null +++ b/src/bot/events/client-ready/jobs/validators/notify-waiting-checkin.ts @@ -0,0 +1,50 @@ +import type { PrismaClient } from '@generatedDB/client' +import type { Checkin as CheckinType } from '@type/checkin' +import type { TextChannel } from 'discord.js' +import { FLAMEWARDEN_ROLE, GRINDER_ROLE } from '@config/discord' +import { createEmbed } from '@utils/component' +import { DiscordAssert, sendAsBot } from '@utils/discord' +import { DUMMY } from '@utils/placeholder' +import { NotifyWaitingCheckinMessage } from '../messages/notify-waiting-checkin' + +export class NotifyWaitingCheckin extends NotifyWaitingCheckinMessage { + static override BASE_PERMS = [ + ...DiscordAssert.BASE_PERMS, + ] + + static async sendOpening(guildName: string, wardenDutyChannel: TextChannel) { + const openingEmbed = createEmbed( + `🔥 Maklumat Penjagaan Nyala`, + this.MSG.Opening(guildName), + DUMMY.COLOR, + null, + null, + null, + null, + false, + ) + await sendAsBot(null, wardenDutyChannel, { + content: `<@&${FLAMEWARDEN_ROLE}>`, + embeds: [openingEmbed], + allowedMentions: { roles: [FLAMEWARDEN_ROLE, GRINDER_ROLE] }, + }) + } + + static async getTodayWaitingCheckins(prisma: PrismaClient): Promise { + const waitingCheckins = await prisma.checkin.findMany({ + where: { + status: 'WAITING', + reviewed_by: null, + created_at: { + gte: new Date(new Date().setHours(0, 0, 0, 0)), + }, + }, + include: { + user: true, + }, + orderBy: { created_at: 'asc' }, + }) as CheckinType[] + + return waitingCheckins + } +} From caf856e669097ba95c62978423303c7578ef5000 Mon Sep 17 00:00:00 2001 From: alfianchii Date: Tue, 30 Dec 2025 19:46:11 +0700 Subject: [PATCH 2/3] feat: list --- .../jobs/handlers/notify-waiting-checkin.ts | 1 + .../jobs/messages/notify-waiting-checkin.ts | 3 +++ .../jobs/validators/notify-waiting-checkin.ts | 20 +++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/src/bot/events/client-ready/jobs/handlers/notify-waiting-checkin.ts b/src/bot/events/client-ready/jobs/handlers/notify-waiting-checkin.ts index 2d8abfb..f13c5b5 100644 --- a/src/bot/events/client-ready/jobs/handlers/notify-waiting-checkin.ts +++ b/src/bot/events/client-ready/jobs/handlers/notify-waiting-checkin.ts @@ -32,6 +32,7 @@ registerClientReadyHandler({ const checkins = await NotifyWaitingCheckin.getTodayWaitingCheckins(client.prisma) await NotifyWaitingCheckin.sendOpening(guild.name, wardenDutyChannel) + await NotifyWaitingCheckin.sendList(checkins, wardenDutyChannel) log.success(NotifyWaitingCheckin.MSG.JobSuccess) }) diff --git a/src/bot/events/client-ready/jobs/messages/notify-waiting-checkin.ts b/src/bot/events/client-ready/jobs/messages/notify-waiting-checkin.ts index 2020e91..26243a5 100644 --- a/src/bot/events/client-ready/jobs/messages/notify-waiting-checkin.ts +++ b/src/bot/events/client-ready/jobs/messages/notify-waiting-checkin.ts @@ -28,5 +28,8 @@ dimohon para <@&${FLAMEWARDEN_ROLE}> berkenan: Ⅱ. Menetapkan keputusan dengan bijaksana, Ⅲ. Atau memberi arahan seperlunya sebelum waktu berganti. `, + List: (checkin: Checkin) => ` +- 🔥 <@${checkin.user!.discord_id}> pada [${getParsedNow(checkin.created_at)}](${checkin.link}) + `, } } diff --git a/src/bot/events/client-ready/jobs/validators/notify-waiting-checkin.ts b/src/bot/events/client-ready/jobs/validators/notify-waiting-checkin.ts index 1d842ce..a3a3340 100644 --- a/src/bot/events/client-ready/jobs/validators/notify-waiting-checkin.ts +++ b/src/bot/events/client-ready/jobs/validators/notify-waiting-checkin.ts @@ -30,6 +30,26 @@ export class NotifyWaitingCheckin extends NotifyWaitingCheckinMessage { }) } + static async sendList(checkins: CheckinType[], wardenDutyChannel: TextChannel) { + const list: string[] = [] + for (const checkin of checkins) { + list.push(this.MSG.List(checkin)) + } + const listEmbed = createEmbed( + `⏳ Daftar Waiting Check-In`, + list.join('\n'), + DUMMY.COLOR, + null, + null, + null, + null, + false, + ) + await sendAsBot(null, wardenDutyChannel, { + embeds: [listEmbed], + }) + } + static async getTodayWaitingCheckins(prisma: PrismaClient): Promise { const waitingCheckins = await prisma.checkin.findMany({ where: { From ae47a2bab3d54f2e49e54dfa147336d6298f4edf Mon Sep 17 00:00:00 2001 From: alfianchii Date: Tue, 30 Dec 2025 19:46:29 +0700 Subject: [PATCH 3/3] feat: closing --- .../jobs/handlers/notify-waiting-checkin.ts | 1 + .../jobs/messages/notify-waiting-checkin.ts | 8 ++++++++ .../jobs/validators/notify-waiting-checkin.ts | 13 +++++++++++++ 3 files changed, 22 insertions(+) diff --git a/src/bot/events/client-ready/jobs/handlers/notify-waiting-checkin.ts b/src/bot/events/client-ready/jobs/handlers/notify-waiting-checkin.ts index f13c5b5..98c4642 100644 --- a/src/bot/events/client-ready/jobs/handlers/notify-waiting-checkin.ts +++ b/src/bot/events/client-ready/jobs/handlers/notify-waiting-checkin.ts @@ -33,6 +33,7 @@ registerClientReadyHandler({ await NotifyWaitingCheckin.sendOpening(guild.name, wardenDutyChannel) await NotifyWaitingCheckin.sendList(checkins, wardenDutyChannel) + await NotifyWaitingCheckin.sendClosing(guild.name, wardenDutyChannel) log.success(NotifyWaitingCheckin.MSG.JobSuccess) }) diff --git a/src/bot/events/client-ready/jobs/messages/notify-waiting-checkin.ts b/src/bot/events/client-ready/jobs/messages/notify-waiting-checkin.ts index 26243a5..19caa9a 100644 --- a/src/bot/events/client-ready/jobs/messages/notify-waiting-checkin.ts +++ b/src/bot/events/client-ready/jobs/messages/notify-waiting-checkin.ts @@ -31,5 +31,13 @@ dimohon para <@&${FLAMEWARDEN_ROLE}> berkenan: List: (checkin: Checkin) => ` - 🔥 <@${checkin.user!.discord_id}> pada [${getParsedNow(checkin.created_at)}](${checkin.link}) `, + Closing: ` +Apabila hingga pergantian hari *check-in* di atas belum ditinjau, maka rangkaian nyala para <@&${GRINDER_ROLE}> terkait berisiko terputus oleh hukum waktu. + +Kami mohon kebijaksanaan dan perhatian para <@&${FLAMEWARDEN_ROLE}>, +agar setiap api dinilai dengan adil sebelum malam berganti. + +> *"Api bukan sekadar menyala; ia dijaga agar tak padam oleh kelalaian."* + `, } } diff --git a/src/bot/events/client-ready/jobs/validators/notify-waiting-checkin.ts b/src/bot/events/client-ready/jobs/validators/notify-waiting-checkin.ts index a3a3340..3229252 100644 --- a/src/bot/events/client-ready/jobs/validators/notify-waiting-checkin.ts +++ b/src/bot/events/client-ready/jobs/validators/notify-waiting-checkin.ts @@ -50,6 +50,19 @@ export class NotifyWaitingCheckin extends NotifyWaitingCheckinMessage { }) } + static async sendClosing(guildName: string, wardenDutyChannel: TextChannel) { + const closingEmbed = createEmbed( + '🛡️ Amanat Penjagaan', + this.MSG.Closing, + DUMMY.COLOR, + { text: DUMMY.FOOTER(guildName) }, + ) + await sendAsBot(null, wardenDutyChannel, { + embeds: [closingEmbed], + allowedMentions: { roles: [FLAMEWARDEN_ROLE, GRINDER_ROLE] }, + }) + } + static async getTodayWaitingCheckins(prisma: PrismaClient): Promise { const waitingCheckins = await prisma.checkin.findMany({ where: {