From 27281489808745b068719cba30d2029a8950f733 Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 18:31:10 +0700 Subject: [PATCH 01/19] chore: remove grinder role assertion --- src/bot/commands/checkin/handlers/checkin-status.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bot/commands/checkin/handlers/checkin-status.ts b/src/bot/commands/checkin/handlers/checkin-status.ts index 909a5c2..c027a3b 100644 --- a/src/bot/commands/checkin/handlers/checkin-status.ts +++ b/src/bot/commands/checkin/handlers/checkin-status.ts @@ -1,6 +1,6 @@ import type { ChatInputCommandInteraction, Client, GuildMember } from 'discord.js' import { registerCommand } from '@commands/registry' -import { AUDIT_FLAME_CHANNEL, FLAMEWARDEN_ROLE, GRINDER_ROLE } from '@config/discord' +import { AUDIT_FLAME_CHANNEL, FLAMEWARDEN_ROLE } from '@config/discord' import { sendReply } from '@utils/discord' import { DiscordBaseError } from '@utils/discord/error' import { log } from '@utils/logger' @@ -31,7 +31,6 @@ registerCommand({ const user = await CheckinStatus.getUser(client.prisma, userDiscordId) CheckinStatus.assertMember(member) - CheckinStatus.assertMemberHasRole(member, GRINDER_ROLE) const { content, embed } = await CheckinStatus.getEmbedStatusContent( interaction.guild, From 97b859d804eea588c69b1a2cc4c0958730ed449c Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 18:32:02 +0700 Subject: [PATCH 02/19] chore: remove date when no checkin --- src/bot/commands/checkin/messages/checkin-status.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bot/commands/checkin/messages/checkin-status.ts b/src/bot/commands/checkin/messages/checkin-status.ts index 790245d..0f77090 100644 --- a/src/bot/commands/checkin/messages/checkin-status.ts +++ b/src/bot/commands/checkin/messages/checkin-status.ts @@ -16,7 +16,6 @@ export class CheckinStatusMessage extends DiscordAssert { NoCheckin: (userDiscordId: string, checkinStreak: CheckinStreak | undefined) => ` Wahai Tuan/Nona <@${userDiscordId}>, Nyala api Tuan/Nona belum dinyalakan hari ini. -🗓 **Date**: ${getParsedNow()} 🔥 **Current Streak**: ${checkinStreak?.streak ?? 0} day(s) 🔎 **Status**: Belum melakukan *check-in* > *"Percikan hari ini belum ditorehkan. Lakukan check-in sebelum 23:59 WIB, agar api Tuan/Nona tak meredup."* From b5e74960166ef2063f0b4825c08eeae148ef8c76 Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 18:51:06 +0700 Subject: [PATCH 03/19] fix: rejected checkin status message --- src/bot/commands/checkin/messages/checkin-status.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/commands/checkin/messages/checkin-status.ts b/src/bot/commands/checkin/messages/checkin-status.ts index 0f77090..594395f 100644 --- a/src/bot/commands/checkin/messages/checkin-status.ts +++ b/src/bot/commands/checkin/messages/checkin-status.ts @@ -54,7 +54,7 @@ ${checkin.public_id} 🌟 **Grinder**: <@${userDiscordId}> 📁 **Attachment:** ${checkin.attachments?.length ? '✅' : '❌'} 🔥 **Current Streak**: ${checkin.checkin_streak!.streak} day(s) -🔎 **Status**: Disetujui; api Tuan/Nona kian terang +🔎 **Status**: Ditolak; percikan tak cukup kuat 🗓 **Reviewed At**: ${getParsedNow(getNow(checkin.updated_at!))} 👀 **Reviewed By**: ${flamewarden.displayName} (@${flamewarden.user.username}) ✍🏻 **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'} From ed574a15d58de6bcaafaae5eeee911118f2702df Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 18:54:42 +0700 Subject: [PATCH 04/19] feat: last checkin status message --- .../checkin/messages/checkin-status.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/bot/commands/checkin/messages/checkin-status.ts b/src/bot/commands/checkin/messages/checkin-status.ts index 594395f..3e997b7 100644 --- a/src/bot/commands/checkin/messages/checkin-status.ts +++ b/src/bot/commands/checkin/messages/checkin-status.ts @@ -60,5 +60,25 @@ ${checkin.public_id} ✍🏻 **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'} > *"[Api Tuan/Nona](${checkin.link}) <@${userDiscordId}> meredup hari ini, namun belum padam sepenuhnya. Perbaiki, dan nyalakan kembali percikan yang benar."* `, + LastCheckin: (userDiscordId: string, flamewarden: GuildMember, checkin: Checkin) => ` +Wahai Tuan/Nona <@${userDiscordId}>, +Tercatat bahwa rangkaian nyala api Tuan/Nona telah terputus pada pergantian hari sebelumnya. +Namun demikian, percikan terakhir masih tersimpan dalam arsip Aksaria dan dapat ditinjau kembali. + +Berikut adalah check-in terakhir yang pernah Tuan/Nona torehkan: +🆔 **Check-In ID**: +\`\`\`bash +${checkin.public_id} +\`\`\` +🌟 **Grinder**: <@${userDiscordId}> +📁 **Attachment:** ${checkin.attachments?.length ? '✅' : '❌'} +🗓 **Submitted At**: ${getParsedNow(getNow(checkin.created_at))} +🔥 **Last Streak**: ${checkin.checkin_streak!.streak} day(s) +🔎 **Status**: ${checkin.status} +🗓 **Reviewed At**: ${getParsedNow(getNow(checkin.updated_at!))} +👀 **Reviewed By**: ${flamewarden.displayName} (@${flamewarden.user.username}) +✍🏻 **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'} +> *"[Percikan ini](${checkin.link}) pernah kau titipkan pada api, namun belum sempat ditakar oleh penjaga nyala."* + `, } } From 520ab11623012fd99c3a9229051f0fa87770cdf5 Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 19:29:35 +0700 Subject: [PATCH 05/19] fix: typography when there is no flamewarden on last checkin --- src/bot/commands/checkin/messages/checkin-status.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/bot/commands/checkin/messages/checkin-status.ts b/src/bot/commands/checkin/messages/checkin-status.ts index 3e997b7..e7d9ac0 100644 --- a/src/bot/commands/checkin/messages/checkin-status.ts +++ b/src/bot/commands/checkin/messages/checkin-status.ts @@ -65,7 +65,7 @@ Wahai Tuan/Nona <@${userDiscordId}>, Tercatat bahwa rangkaian nyala api Tuan/Nona telah terputus pada pergantian hari sebelumnya. Namun demikian, percikan terakhir masih tersimpan dalam arsip Aksaria dan dapat ditinjau kembali. -Berikut adalah check-in terakhir yang pernah Tuan/Nona torehkan: +Berikut adalah *check-in* terakhir yang pernah Tuan/Nona torehkan: 🆔 **Check-In ID**: \`\`\`bash ${checkin.public_id} @@ -75,9 +75,12 @@ ${checkin.public_id} 🗓 **Submitted At**: ${getParsedNow(getNow(checkin.created_at))} 🔥 **Last Streak**: ${checkin.checkin_streak!.streak} day(s) 🔎 **Status**: ${checkin.status} +${flamewarden?.displayName + ? ` 🗓 **Reviewed At**: ${getParsedNow(getNow(checkin.updated_at!))} 👀 **Reviewed By**: ${flamewarden.displayName} (@${flamewarden.user.username}) -✍🏻 **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'} +✍🏻 **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'}` + : ''} > *"[Percikan ini](${checkin.link}) pernah kau titipkan pada api, namun belum sempat ditakar oleh penjaga nyala."* `, } From e7bce478f9eff6bae650c8cbc8862cef64c933aa Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 19:29:58 +0700 Subject: [PATCH 06/19] feat: last checkin status message --- .../checkin/messages/checkin-status.ts | 2 +- .../checkin/validators/checkin-status.ts | 32 +++++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/bot/commands/checkin/messages/checkin-status.ts b/src/bot/commands/checkin/messages/checkin-status.ts index e7d9ac0..c626f02 100644 --- a/src/bot/commands/checkin/messages/checkin-status.ts +++ b/src/bot/commands/checkin/messages/checkin-status.ts @@ -60,7 +60,7 @@ ${checkin.public_id} ✍🏻 **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'} > *"[Api Tuan/Nona](${checkin.link}) <@${userDiscordId}> meredup hari ini, namun belum padam sepenuhnya. Perbaiki, dan nyalakan kembali percikan yang benar."* `, - LastCheckin: (userDiscordId: string, flamewarden: GuildMember, checkin: Checkin) => ` + LastCheckin: (userDiscordId: string, checkin: Checkin, flamewarden?: GuildMember) => ` Wahai Tuan/Nona <@${userDiscordId}>, Tercatat bahwa rangkaian nyala api Tuan/Nona telah terputus pada pergantian hari sebelumnya. Namun demikian, percikan terakhir masih tersimpan dalam arsip Aksaria dan dapat ditinjau kembali. diff --git a/src/bot/commands/checkin/validators/checkin-status.ts b/src/bot/commands/checkin/validators/checkin-status.ts index f77da52..4e791aa 100644 --- a/src/bot/commands/checkin/validators/checkin-status.ts +++ b/src/bot/commands/checkin/validators/checkin-status.ts @@ -16,17 +16,18 @@ export class CheckinStatus extends CheckinStatusMessage { PermissionsBitField.Flags.UseApplicationCommands, ] - static async getEmbedStatusContent(guild: Guild, userDiscordId: string, checkin: CheckinType | undefined) { + static async getEmbedStatusContent(guild: Guild, userDiscordId: string, checkin?: CheckinType) { let content = '' let embed: EmbedBuilder - const checkinStreak = checkin?.checkin_streak + const checkinStreak = checkin?.checkin_streak const hasCheckedInToday = Checkin.hasCheckinToday(checkinStreak, checkin) - if (hasCheckedInToday && checkin) { + + if (checkin && hasCheckedInToday) { const flamewarden = await guild.members.fetch(checkin.reviewed_by!) switch (checkin.status) { - case 'WAITING': + case 'WAITING': { content = `<@&${FLAMEWARDEN_ROLE}>` embed = createEmbed( `🧭 Check-In #${checkin.public_id}`, @@ -35,8 +36,9 @@ export class CheckinStatus extends CheckinStatusMessage { { text: DUMMY.FOOTER }, ) break + } - case 'APPROVED': + case 'APPROVED': { embed = createEmbed( `🔥 Check-In #${checkin.public_id}`, CheckinStatus.MSG.ApprovedCheckin(userDiscordId, flamewarden, checkin), @@ -44,8 +46,9 @@ export class CheckinStatus extends CheckinStatusMessage { { text: DUMMY.FOOTER }, ) break + } - default: + default: { embed = createEmbed( `❌ Check-In #${checkin.public_id}`, CheckinStatus.MSG.RejectedCheckin(userDiscordId, flamewarden, checkin), @@ -53,17 +56,32 @@ export class CheckinStatus extends CheckinStatusMessage { { text: DUMMY.FOOTER }, ) break + } } + + return { content, embed } } - else { + + const shouldShowNoCheckin = !checkin || (checkin.status === 'APPROVED' && !hasCheckedInToday) + if (shouldShowNoCheckin) { embed = createEmbed( `🧐 Check-In`, CheckinStatus.MSG.NoCheckin(userDiscordId, checkinStreak), DUMMY.COLOR, { text: DUMMY.FOOTER }, ) + + return { content, embed } } + const flamewarden = await guild.members.fetch(checkin!.reviewed_by!) + embed = createEmbed( + `🕯️ Check-In #${checkin!.public_id}`, + CheckinStatus.MSG.LastCheckin(userDiscordId, checkin!, flamewarden), + DUMMY.COLOR, + { text: DUMMY.FOOTER }, + ) + return { content, embed } } From f74b71d46cfac05d87a4fee98fd2a81a8e04839e Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 19:44:29 +0700 Subject: [PATCH 07/19] chore: typography --- .../client-ready/jobs/messages/reset-grinder-roles.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bot/events/client-ready/jobs/messages/reset-grinder-roles.ts b/src/bot/events/client-ready/jobs/messages/reset-grinder-roles.ts index 4ec6821..21ec8b4 100644 --- a/src/bot/events/client-ready/jobs/messages/reset-grinder-roles.ts +++ b/src/bot/events/client-ready/jobs/messages/reset-grinder-roles.ts @@ -25,10 +25,10 @@ Namun jangan berduka, jalan ini selalu terbuka bagi mereka yang bersedia memulai `, GoodByeNotes: ` > Apabila *check-in* Tuan/Nona masih berada dalam status menunggu peninjauan (*waiting*) dan belum memperoleh keputusan hingga mendekati pergantian hari, maka dengan ini disampaikan ketentuan berikut: -> 1. Jangan terlebih dahulu memasuki ⁠<#${IGNITE_PATH_CHANNEL}>, demi menjaga ketertiban alur peninjauan. -> 2. Silakan menjalankan perintah **\`/checkin-status\`** pada <#${AUDIT_FLAME_CHANNEL}> untuk menampilkan status *check-in* terakhir Tuan/Nona. -> 3. Setelah pesan status tersebut muncul, berikan reaksi "❓" pada pesan tersebut. -> 4. Dari reaksi tersebut, sebuah *thread* akan tercipta secara otomatis sebagai ruang klarifikasi dan komunikasi dengan <@&${FLAMEWARDEN_ROLE}>. +> Ⅰ. Jangan terlebih dahulu memasuki ⁠<#${IGNITE_PATH_CHANNEL}>, demi menjaga ketertiban alur peninjauan. +> Ⅱ. Silakan menjalankan perintah **\`/checkin-status\`** pada <#${AUDIT_FLAME_CHANNEL}> untuk menampilkan status *check-in* terakhir Tuan/Nona. +> Ⅲ. Setelah pesan status tersebut muncul, berikan reaksi "❓" pada pesan tersebut. +> Ⅳ. Dari reaksi tersebut, sebuah *thread* akan tercipta secara otomatis sebagai ruang klarifikasi dan komunikasi dengan <@&${FLAMEWARDEN_ROLE}>. > ⏳ Batas waktu penantian atas status *WAITING* adalah maksimal 1×24 jam sejak *check-in* diajukan. `, } From 961d81c76a0a068ef9a7abb555aca76610294b26 Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 19:44:49 +0700 Subject: [PATCH 08/19] feat: last checkin status message note button --- .../checkin/handlers/checkin-status.ts | 14 +++++++++++--- .../checkin/validators/checkin-status.ts | 18 +++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/bot/commands/checkin/handlers/checkin-status.ts b/src/bot/commands/checkin/handlers/checkin-status.ts index c027a3b..64493a3 100644 --- a/src/bot/commands/checkin/handlers/checkin-status.ts +++ b/src/bot/commands/checkin/handlers/checkin-status.ts @@ -1,6 +1,8 @@ -import type { ChatInputCommandInteraction, Client, GuildMember } from 'discord.js' +import type { ChatInputCommandInteraction, Client, GuildMember, InteractionReplyOptions } from 'discord.js' +import { COMMAND_PATH } from '@commands/index' import { registerCommand } from '@commands/registry' import { AUDIT_FLAME_CHANNEL, FLAMEWARDEN_ROLE } from '@config/discord' +import { generateCustomId } from '@utils/component' import { sendReply } from '@utils/discord' import { DiscordBaseError } from '@utils/discord/error' import { log } from '@utils/logger' @@ -13,6 +15,8 @@ export class CheckinStatusError extends DiscordBaseError { } } +export const LAST_CHECKIN_STATUS_NOTE_BUTTON_ID = `${generateCustomId(COMMAND_PATH, __filename)}` + registerCommand({ data: new SlashCommandBuilder() .setName('checkin-status') @@ -32,13 +36,17 @@ registerCommand({ CheckinStatus.assertMember(member) - const { content, embed } = await CheckinStatus.getEmbedStatusContent( + const { content, embed, button } = await CheckinStatus.getEmbedStatusContent( interaction.guild, user?.discord_id ?? member.id, user?.checkins?.[0], ) - await sendReply(interaction, content, false, { embeds: [embed], allowedMentions: { roles: [FLAMEWARDEN_ROLE] } }) + const payloads = { embeds: [embed], allowedMentions: { roles: [FLAMEWARDEN_ROLE] } } as InteractionReplyOptions + if (button) + payloads.components = [button] + + await sendReply(interaction, content, false, payloads) } catch (err: any) { if (err instanceof DiscordBaseError) diff --git a/src/bot/commands/checkin/validators/checkin-status.ts b/src/bot/commands/checkin/validators/checkin-status.ts index 4e791aa..add4400 100644 --- a/src/bot/commands/checkin/validators/checkin-status.ts +++ b/src/bot/commands/checkin/validators/checkin-status.ts @@ -4,10 +4,11 @@ import type { User } from '@type/user' import type { EmbedBuilder, Guild } from 'discord.js' import { FLAMEWARDEN_ROLE } from '@config/discord' import { Checkin } from '@events/interaction-create/checkin/validators' -import { createEmbed } from '@utils/component' +import { createEmbed, encodeSnowflake, getCustomId } from '@utils/component' import { DiscordAssert } from '@utils/discord' import { DUMMY } from '@utils/placeholder' -import { PermissionsBitField } from 'discord.js' +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, PermissionsBitField } from 'discord.js' +import { LAST_CHECKIN_STATUS_NOTE_BUTTON_ID } from '../handlers/checkin-status' import { CheckinStatusMessage } from '../messages/checkin-status' export class CheckinStatus extends CheckinStatusMessage { @@ -75,6 +76,7 @@ export class CheckinStatus extends CheckinStatusMessage { } const flamewarden = await guild.members.fetch(checkin!.reviewed_by!) + const button = this.generateButton(guild.id) embed = createEmbed( `🕯️ Check-In #${checkin!.public_id}`, CheckinStatus.MSG.LastCheckin(userDiscordId, checkin!, flamewarden), @@ -82,7 +84,17 @@ export class CheckinStatus extends CheckinStatusMessage { { text: DUMMY.FOOTER }, ) - return { content, embed } + return { content, embed, button } + } + + static generateButton(guildId: string): ActionRowBuilder { + const noteButtonId = getCustomId([LAST_CHECKIN_STATUS_NOTE_BUTTON_ID, encodeSnowflake(guildId)]) + const noteButton = new ButtonBuilder() + .setCustomId(noteButtonId) + .setLabel('❓ Ajukan Klarifikasi') + .setStyle(ButtonStyle.Primary) + + return new ActionRowBuilder().addComponents(noteButton) } static async getUser(prisma: PrismaClient, userDiscordId: string): Promise { From ecdd55488cd9bf59168e219c6ab4696517fc78ff Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 19:45:03 +0700 Subject: [PATCH 09/19] feat: last checkin note --- src/bot/commands/checkin/messages/checkin-status.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/bot/commands/checkin/messages/checkin-status.ts b/src/bot/commands/checkin/messages/checkin-status.ts index c626f02..7873225 100644 --- a/src/bot/commands/checkin/messages/checkin-status.ts +++ b/src/bot/commands/checkin/messages/checkin-status.ts @@ -1,7 +1,7 @@ import type { Checkin } from '@type/checkin' import type { CheckinStreak } from '@type/checkin-streak' import type { GuildMember } from 'discord.js' -import { FLAMEWARDEN_ROLE } from '@config/discord' +import { FLAMEWARDEN_ROLE, IGNITE_PATH_CHANNEL } from '@config/discord' import { getNow, getParsedNow } from '@utils/date' import { DiscordAssert } from '@utils/discord' @@ -83,5 +83,16 @@ ${flamewarden?.displayName : ''} > *"[Percikan ini](${checkin.link}) pernah kau titipkan pada api, namun belum sempat ditakar oleh penjaga nyala."* `, + LastCheckinNote: () => ` +Apabila Tuan/Nona meyakini bahwa *check-in* di atas belum sempat ditinjau oleh <@&${FLAMEWARDEN_ROLE}>, +maka Aksaria membuka ruang klarifikasi dengan tata cara sebagai berikut: +Ⅰ. Berikan reaksi ❓ pada pesan status *check-in* ini. +Ⅱ. Sebuah *thread* khusus akan tercipta secara otomatis. +Ⅲ. Gunakan *thread* tersebut untuk berkomunikasi dan mengajukan peninjauan kepada <@&${FLAMEWARDEN_ROLE}>. + +⚠️ Ketentuan Penting: +Selama proses klarifikasi berlangsung, Tuan/Nona tidak diperkenankan terlebih dahulu memasuki <#${IGNITE_PATH_CHANNEL}>, demi menjaga ketertiban alur peninjauan. +Waktu klarifikasi dibuka maksimal 1x24 jam sejak *check-in* diajukan. + `, } } From f2676ebcd2f5abab6b1e972d295459a2016b62b4 Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 20:02:03 +0700 Subject: [PATCH 10/19] feat: last checkin note button handler --- .../checkin/handlers/checkin-status.ts | 4 -- .../checkin/messages/checkin-status.ts | 4 +- .../checkin/validators/checkin-status.ts | 34 ++++++++++---- .../handlers/status-last-checkin-button.ts | 45 +++++++++++++++++++ 4 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 src/bot/events/interaction-create/checkin/handlers/status-last-checkin-button.ts diff --git a/src/bot/commands/checkin/handlers/checkin-status.ts b/src/bot/commands/checkin/handlers/checkin-status.ts index 64493a3..3f7f394 100644 --- a/src/bot/commands/checkin/handlers/checkin-status.ts +++ b/src/bot/commands/checkin/handlers/checkin-status.ts @@ -1,8 +1,6 @@ import type { ChatInputCommandInteraction, Client, GuildMember, InteractionReplyOptions } from 'discord.js' -import { COMMAND_PATH } from '@commands/index' import { registerCommand } from '@commands/registry' import { AUDIT_FLAME_CHANNEL, FLAMEWARDEN_ROLE } from '@config/discord' -import { generateCustomId } from '@utils/component' import { sendReply } from '@utils/discord' import { DiscordBaseError } from '@utils/discord/error' import { log } from '@utils/logger' @@ -15,8 +13,6 @@ export class CheckinStatusError extends DiscordBaseError { } } -export const LAST_CHECKIN_STATUS_NOTE_BUTTON_ID = `${generateCustomId(COMMAND_PATH, __filename)}` - registerCommand({ data: new SlashCommandBuilder() .setName('checkin-status') diff --git a/src/bot/commands/checkin/messages/checkin-status.ts b/src/bot/commands/checkin/messages/checkin-status.ts index 7873225..6882ca7 100644 --- a/src/bot/commands/checkin/messages/checkin-status.ts +++ b/src/bot/commands/checkin/messages/checkin-status.ts @@ -83,8 +83,8 @@ ${flamewarden?.displayName : ''} > *"[Percikan ini](${checkin.link}) pernah kau titipkan pada api, namun belum sempat ditakar oleh penjaga nyala."* `, - LastCheckinNote: () => ` -Apabila Tuan/Nona meyakini bahwa *check-in* di atas belum sempat ditinjau oleh <@&${FLAMEWARDEN_ROLE}>, + LastCheckinNote: (checkinLink: string) => ` +Apabila Tuan/Nona meyakini bahwa [*check-in*](${checkinLink}) belum sempat ditinjau oleh <@&${FLAMEWARDEN_ROLE}>, maka Aksaria membuka ruang klarifikasi dengan tata cara sebagai berikut: Ⅰ. Berikan reaksi ❓ pada pesan status *check-in* ini. Ⅱ. Sebuah *thread* khusus akan tercipta secara otomatis. diff --git a/src/bot/commands/checkin/validators/checkin-status.ts b/src/bot/commands/checkin/validators/checkin-status.ts index add4400..0e7c4f4 100644 --- a/src/bot/commands/checkin/validators/checkin-status.ts +++ b/src/bot/commands/checkin/validators/checkin-status.ts @@ -1,14 +1,15 @@ import type { PrismaClient } from '@generatedDB/client' import type { Checkin as CheckinType } from '@type/checkin' import type { User } from '@type/user' -import type { EmbedBuilder, Guild } from 'discord.js' -import { FLAMEWARDEN_ROLE } from '@config/discord' +import type { EmbedBuilder, Guild, Interaction } from 'discord.js' +import { CHECKIN_CHANNEL, FLAMEWARDEN_ROLE } from '@config/discord' +import { LAST_CHECKIN_STATUS_NOTE_BUTTON_ID } from '@events/interaction-create/checkin/handlers/status-last-checkin-button' import { Checkin } from '@events/interaction-create/checkin/validators' -import { createEmbed, encodeSnowflake, getCustomId } from '@utils/component' +import { createEmbed, decodeSnowflakes, encodeSnowflake, getCustomId } from '@utils/component' import { DiscordAssert } from '@utils/discord' import { DUMMY } from '@utils/placeholder' -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, PermissionsBitField } from 'discord.js' -import { LAST_CHECKIN_STATUS_NOTE_BUTTON_ID } from '../handlers/checkin-status' +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, messageLink, PermissionsBitField } from 'discord.js' +import { CheckinStatusError } from '../handlers/checkin-status' import { CheckinStatusMessage } from '../messages/checkin-status' export class CheckinStatus extends CheckinStatusMessage { @@ -17,6 +18,21 @@ export class CheckinStatus extends CheckinStatusMessage { PermissionsBitField.Flags.UseApplicationCommands, ] + static getButtonId(interaction: Interaction, customId: string) { + const [prefix, guildId, checkinMessageId] = decodeSnowflakes(customId) + + if (!guildId) + throw new CheckinStatusError(this.ERR.GuildMissing) + if (interaction.guildId !== guildId) + throw new CheckinStatusError(this.ERR.NotGuild) + if (!checkinMessageId) + throw new CheckinStatusError(this.ERR.CheckinIdMissing) + + const checkinLink = messageLink(CHECKIN_CHANNEL, checkinMessageId, interaction.guildId) + + return { prefix, guildId, checkinLink } + } + static async getEmbedStatusContent(guild: Guild, userDiscordId: string, checkin?: CheckinType) { let content = '' let embed: EmbedBuilder @@ -76,7 +92,7 @@ export class CheckinStatus extends CheckinStatusMessage { } const flamewarden = await guild.members.fetch(checkin!.reviewed_by!) - const button = this.generateButton(guild.id) + const button = this.generateButton(guild.id, checkin.link!) embed = createEmbed( `🕯️ Check-In #${checkin!.public_id}`, CheckinStatus.MSG.LastCheckin(userDiscordId, checkin!, flamewarden), @@ -87,8 +103,10 @@ export class CheckinStatus extends CheckinStatusMessage { return { content, embed, button } } - static generateButton(guildId: string): ActionRowBuilder { - const noteButtonId = getCustomId([LAST_CHECKIN_STATUS_NOTE_BUTTON_ID, encodeSnowflake(guildId)]) + static generateButton(guildId: string, checkinLink: string): ActionRowBuilder { + const { messageId } = this.getMessageFromLink(checkinLink) + + const noteButtonId = getCustomId([LAST_CHECKIN_STATUS_NOTE_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(messageId)]) const noteButton = new ButtonBuilder() .setCustomId(noteButtonId) .setLabel('❓ Ajukan Klarifikasi') diff --git a/src/bot/events/interaction-create/checkin/handlers/status-last-checkin-button.ts b/src/bot/events/interaction-create/checkin/handlers/status-last-checkin-button.ts new file mode 100644 index 0000000..f2264eb --- /dev/null +++ b/src/bot/events/interaction-create/checkin/handlers/status-last-checkin-button.ts @@ -0,0 +1,45 @@ +import type { TextChannel } from 'discord.js' +import { CheckinStatus } from '@commands/checkin/validators/checkin-status' +import { EVENT_PATH } from '@events/index' +import { registerInteractionHandler } from '@events/interaction-create/registry' +import { generateCustomId } from '@utils/component' +import { sendReply } from '@utils/discord' +import { DiscordBaseError } from '@utils/discord/error' +import { getModuleName } from '@utils/io' +import { Checkin } from '../validators' + +export class StatusLastCheckinButtonError extends DiscordBaseError { + constructor(message: string, options?: { cause?: unknown }) { + super('StatusLastCheckinButtonError', message, options) + } +} + +const moduleName = getModuleName(EVENT_PATH, __filename) +export const LAST_CHECKIN_STATUS_NOTE_BUTTON_ID = `${generateCustomId(EVENT_PATH, __filename)}` + +registerInteractionHandler({ + desc: 'Opens a note about how to request clarification for the last check-in if the streak was broken and did not reviewed.', + id: LAST_CHECKIN_STATUS_NOTE_BUTTON_ID, + errorTag: () => `${moduleName}: ${Checkin.ERR.UnexpectedButton}`, + async exec(_, interaction) { + if (!interaction.isButton()) + return + + try { + if (!interaction.inCachedGuild()) + throw new StatusLastCheckinButtonError(Checkin.ERR.NotGuild) + + const { checkinLink } = CheckinStatus.getButtonId(interaction, interaction.customId) + + const channel = interaction.channel as TextChannel + Checkin.assertMissPerms(interaction.client.user, channel) + + await sendReply(interaction, CheckinStatus.MSG.LastCheckinNote(checkinLink)) + } + catch (err: any) { + if (err instanceof DiscordBaseError) + await sendReply(interaction, err.message) + else throw err + } + }, +}) From 448805001a24f8abcaff0a6374ccef30b980c446 Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 20:09:39 +0700 Subject: [PATCH 11/19] feat: status message link --- src/bot/commands/checkin/messages/checkin-status.ts | 4 ++-- .../checkin/handlers/status-last-checkin-button.ts | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bot/commands/checkin/messages/checkin-status.ts b/src/bot/commands/checkin/messages/checkin-status.ts index 6882ca7..b7707b6 100644 --- a/src/bot/commands/checkin/messages/checkin-status.ts +++ b/src/bot/commands/checkin/messages/checkin-status.ts @@ -83,10 +83,10 @@ ${flamewarden?.displayName : ''} > *"[Percikan ini](${checkin.link}) pernah kau titipkan pada api, namun belum sempat ditakar oleh penjaga nyala."* `, - LastCheckinNote: (checkinLink: string) => ` + LastCheckinNote: (checkinLink: string, statusLink: string) => ` Apabila Tuan/Nona meyakini bahwa [*check-in*](${checkinLink}) belum sempat ditinjau oleh <@&${FLAMEWARDEN_ROLE}>, maka Aksaria membuka ruang klarifikasi dengan tata cara sebagai berikut: -Ⅰ. Berikan reaksi ❓ pada pesan status *check-in* ini. +Ⅰ. Berikan reaksi ❓ pada pesan [*status check-in*](${statusLink}) ini. Ⅱ. Sebuah *thread* khusus akan tercipta secara otomatis. Ⅲ. Gunakan *thread* tersebut untuk berkomunikasi dan mengajukan peninjauan kepada <@&${FLAMEWARDEN_ROLE}>. diff --git a/src/bot/events/interaction-create/checkin/handlers/status-last-checkin-button.ts b/src/bot/events/interaction-create/checkin/handlers/status-last-checkin-button.ts index f2264eb..1bbc30a 100644 --- a/src/bot/events/interaction-create/checkin/handlers/status-last-checkin-button.ts +++ b/src/bot/events/interaction-create/checkin/handlers/status-last-checkin-button.ts @@ -6,6 +6,7 @@ import { generateCustomId } from '@utils/component' import { sendReply } from '@utils/discord' import { DiscordBaseError } from '@utils/discord/error' import { getModuleName } from '@utils/io' +import { messageLink } from 'discord.js' import { Checkin } from '../validators' export class StatusLastCheckinButtonError extends DiscordBaseError { @@ -34,7 +35,9 @@ registerInteractionHandler({ const channel = interaction.channel as TextChannel Checkin.assertMissPerms(interaction.client.user, channel) - await sendReply(interaction, CheckinStatus.MSG.LastCheckinNote(checkinLink)) + const statusMessageLink = messageLink(interaction.channelId, interaction.message.id, interaction.guildId) + + await sendReply(interaction, CheckinStatus.MSG.LastCheckinNote(checkinLink, statusMessageLink)) } catch (err: any) { if (err instanceof DiscordBaseError) From 1c35a7757e223f877ec33fc8b9dce4ec728e8cad Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 20:10:15 +0700 Subject: [PATCH 12/19] chore: chore --- src/bot/commands/checkin/handlers/checkin-status.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bot/commands/checkin/handlers/checkin-status.ts b/src/bot/commands/checkin/handlers/checkin-status.ts index 3f7f394..42a7c06 100644 --- a/src/bot/commands/checkin/handlers/checkin-status.ts +++ b/src/bot/commands/checkin/handlers/checkin-status.ts @@ -25,13 +25,12 @@ registerCommand({ const channel = await CheckinStatus.assertAllowedChannel(interaction.guild, interaction.channelId, AUDIT_FLAME_CHANNEL) CheckinStatus.assertMissPerms(interaction.client.user, channel) + const member = interaction.member as GuildMember + CheckinStatus.assertMember(member) const userDiscordId: string = interaction.user.id - const member = interaction.member as GuildMember const user = await CheckinStatus.getUser(client.prisma, userDiscordId) - CheckinStatus.assertMember(member) - const { content, embed, button } = await CheckinStatus.getEmbedStatusContent( interaction.guild, user?.discord_id ?? member.id, From d1e0e6aa8ab72682759ea7e977d23abceb18056e Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 20:12:23 +0700 Subject: [PATCH 13/19] chore: typography --- src/bot/commands/checkin/validators/checkin-status.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/commands/checkin/validators/checkin-status.ts b/src/bot/commands/checkin/validators/checkin-status.ts index 0e7c4f4..c94754f 100644 --- a/src/bot/commands/checkin/validators/checkin-status.ts +++ b/src/bot/commands/checkin/validators/checkin-status.ts @@ -109,7 +109,7 @@ export class CheckinStatus extends CheckinStatusMessage { const noteButtonId = getCustomId([LAST_CHECKIN_STATUS_NOTE_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(messageId)]) const noteButton = new ButtonBuilder() .setCustomId(noteButtonId) - .setLabel('❓ Ajukan Klarifikasi') + .setLabel('📜 Maklumat Klarifikasi') .setStyle(ButtonStyle.Primary) return new ActionRowBuilder().addComponents(noteButton) From 504dc3f5401e646729b00e7bb158857cca487cf2 Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 20:17:39 +0700 Subject: [PATCH 14/19] feat: clarification button for future feature --- .../checkin/handlers/checkin-status.ts | 10 +++++++--- .../checkin/validators/checkin-status.ts | 20 ++++++++++++------- ....ts => status-last-checkin-note-button.ts} | 4 ++-- 3 files changed, 22 insertions(+), 12 deletions(-) rename src/bot/events/interaction-create/checkin/handlers/{status-last-checkin-button.ts => status-last-checkin-note-button.ts} (94%) diff --git a/src/bot/commands/checkin/handlers/checkin-status.ts b/src/bot/commands/checkin/handlers/checkin-status.ts index 42a7c06..a119d10 100644 --- a/src/bot/commands/checkin/handlers/checkin-status.ts +++ b/src/bot/commands/checkin/handlers/checkin-status.ts @@ -1,6 +1,8 @@ import type { ChatInputCommandInteraction, Client, GuildMember, InteractionReplyOptions } from 'discord.js' +import { COMMAND_PATH } from '@commands/index' import { registerCommand } from '@commands/registry' import { AUDIT_FLAME_CHANNEL, FLAMEWARDEN_ROLE } from '@config/discord' +import { generateCustomId } from '@utils/component' import { sendReply } from '@utils/discord' import { DiscordBaseError } from '@utils/discord/error' import { log } from '@utils/logger' @@ -13,6 +15,8 @@ export class CheckinStatusError extends DiscordBaseError { } } +export const STATUS_LAST_CHECKIN_CLARIFICATION_BUTTON_ID = `${generateCustomId(COMMAND_PATH, __filename)}` + registerCommand({ data: new SlashCommandBuilder() .setName('checkin-status') @@ -31,15 +35,15 @@ registerCommand({ const userDiscordId: string = interaction.user.id const user = await CheckinStatus.getUser(client.prisma, userDiscordId) - const { content, embed, button } = await CheckinStatus.getEmbedStatusContent( + const { content, embed, buttons } = await CheckinStatus.getEmbedStatusContent( interaction.guild, user?.discord_id ?? member.id, user?.checkins?.[0], ) const payloads = { embeds: [embed], allowedMentions: { roles: [FLAMEWARDEN_ROLE] } } as InteractionReplyOptions - if (button) - payloads.components = [button] + if (buttons) + payloads.components = [buttons] await sendReply(interaction, content, false, payloads) } diff --git a/src/bot/commands/checkin/validators/checkin-status.ts b/src/bot/commands/checkin/validators/checkin-status.ts index c94754f..3cd790a 100644 --- a/src/bot/commands/checkin/validators/checkin-status.ts +++ b/src/bot/commands/checkin/validators/checkin-status.ts @@ -3,13 +3,13 @@ import type { Checkin as CheckinType } from '@type/checkin' import type { User } from '@type/user' import type { EmbedBuilder, Guild, Interaction } from 'discord.js' import { CHECKIN_CHANNEL, FLAMEWARDEN_ROLE } from '@config/discord' -import { LAST_CHECKIN_STATUS_NOTE_BUTTON_ID } from '@events/interaction-create/checkin/handlers/status-last-checkin-button' +import { STATUS_LAST_CHECKIN_NOTE_BUTTON_ID } from '@events/interaction-create/checkin/handlers/status-last-checkin-note-button' import { Checkin } from '@events/interaction-create/checkin/validators' import { createEmbed, decodeSnowflakes, encodeSnowflake, getCustomId } from '@utils/component' import { DiscordAssert } from '@utils/discord' import { DUMMY } from '@utils/placeholder' import { ActionRowBuilder, ButtonBuilder, ButtonStyle, messageLink, PermissionsBitField } from 'discord.js' -import { CheckinStatusError } from '../handlers/checkin-status' +import { CheckinStatusError, STATUS_LAST_CHECKIN_CLARIFICATION_BUTTON_ID } from '../handlers/checkin-status' import { CheckinStatusMessage } from '../messages/checkin-status' export class CheckinStatus extends CheckinStatusMessage { @@ -92,7 +92,7 @@ export class CheckinStatus extends CheckinStatusMessage { } const flamewarden = await guild.members.fetch(checkin!.reviewed_by!) - const button = this.generateButton(guild.id, checkin.link!) + const buttons = this.generateButtons(guild.id, checkin.link!) embed = createEmbed( `🕯️ Check-In #${checkin!.public_id}`, CheckinStatus.MSG.LastCheckin(userDiscordId, checkin!, flamewarden), @@ -100,19 +100,25 @@ export class CheckinStatus extends CheckinStatusMessage { { text: DUMMY.FOOTER }, ) - return { content, embed, button } + return { content, embed, buttons } } - static generateButton(guildId: string, checkinLink: string): ActionRowBuilder { + static generateButtons(guildId: string, checkinLink: string): ActionRowBuilder { const { messageId } = this.getMessageFromLink(checkinLink) - const noteButtonId = getCustomId([LAST_CHECKIN_STATUS_NOTE_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(messageId)]) + const noteButtonId = getCustomId([STATUS_LAST_CHECKIN_NOTE_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(messageId)]) const noteButton = new ButtonBuilder() .setCustomId(noteButtonId) .setLabel('📜 Maklumat Klarifikasi') .setStyle(ButtonStyle.Primary) - return new ActionRowBuilder().addComponents(noteButton) + const clarificationButtonId = getCustomId([STATUS_LAST_CHECKIN_CLARIFICATION_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(messageId)]) + const clarificationButton = new ButtonBuilder() + .setCustomId(clarificationButtonId) + .setLabel('❓ Ajukan Klarifikasi') + .setStyle(ButtonStyle.Success) + + return new ActionRowBuilder().addComponents(noteButton, clarificationButton) } static async getUser(prisma: PrismaClient, userDiscordId: string): Promise { diff --git a/src/bot/events/interaction-create/checkin/handlers/status-last-checkin-button.ts b/src/bot/events/interaction-create/checkin/handlers/status-last-checkin-note-button.ts similarity index 94% rename from src/bot/events/interaction-create/checkin/handlers/status-last-checkin-button.ts rename to src/bot/events/interaction-create/checkin/handlers/status-last-checkin-note-button.ts index 1bbc30a..bcdb5f6 100644 --- a/src/bot/events/interaction-create/checkin/handlers/status-last-checkin-button.ts +++ b/src/bot/events/interaction-create/checkin/handlers/status-last-checkin-note-button.ts @@ -16,11 +16,11 @@ export class StatusLastCheckinButtonError extends DiscordBaseError { } const moduleName = getModuleName(EVENT_PATH, __filename) -export const LAST_CHECKIN_STATUS_NOTE_BUTTON_ID = `${generateCustomId(EVENT_PATH, __filename)}` +export const STATUS_LAST_CHECKIN_NOTE_BUTTON_ID = `${generateCustomId(EVENT_PATH, __filename)}` registerInteractionHandler({ desc: 'Opens a note about how to request clarification for the last check-in if the streak was broken and did not reviewed.', - id: LAST_CHECKIN_STATUS_NOTE_BUTTON_ID, + id: STATUS_LAST_CHECKIN_NOTE_BUTTON_ID, errorTag: () => `${moduleName}: ${Checkin.ERR.UnexpectedButton}`, async exec(_, interaction) { if (!interaction.isButton()) From 5ee747208b6eb1f9b1dccf38c9d08167ecd8483e Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 20:52:42 +0700 Subject: [PATCH 15/19] fix: buttons only on waiting checkin --- .../checkin/validators/checkin-status.ts | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/bot/commands/checkin/validators/checkin-status.ts b/src/bot/commands/checkin/validators/checkin-status.ts index 3cd790a..d055e98 100644 --- a/src/bot/commands/checkin/validators/checkin-status.ts +++ b/src/bot/commands/checkin/validators/checkin-status.ts @@ -92,7 +92,7 @@ export class CheckinStatus extends CheckinStatusMessage { } const flamewarden = await guild.members.fetch(checkin!.reviewed_by!) - const buttons = this.generateButtons(guild.id, checkin.link!) + const buttons = this.generateButtons(guild.id, checkin) embed = createEmbed( `🕯️ Check-In #${checkin!.public_id}`, CheckinStatus.MSG.LastCheckin(userDiscordId, checkin!, flamewarden), @@ -103,22 +103,24 @@ export class CheckinStatus extends CheckinStatusMessage { return { content, embed, buttons } } - static generateButtons(guildId: string, checkinLink: string): ActionRowBuilder { - const { messageId } = this.getMessageFromLink(checkinLink) + static generateButtons(guildId: string, checkin: CheckinType): ActionRowBuilder | undefined { + if (checkin.status === 'WAITING') { + const { messageId } = this.getMessageFromLink(checkin.link!) - const noteButtonId = getCustomId([STATUS_LAST_CHECKIN_NOTE_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(messageId)]) - const noteButton = new ButtonBuilder() - .setCustomId(noteButtonId) - .setLabel('📜 Maklumat Klarifikasi') - .setStyle(ButtonStyle.Primary) + const noteButtonId = getCustomId([STATUS_LAST_CHECKIN_NOTE_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(messageId)]) + const noteButton = new ButtonBuilder() + .setCustomId(noteButtonId) + .setLabel('📜 Maklumat Klarifikasi') + .setStyle(ButtonStyle.Primary) - const clarificationButtonId = getCustomId([STATUS_LAST_CHECKIN_CLARIFICATION_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(messageId)]) - const clarificationButton = new ButtonBuilder() - .setCustomId(clarificationButtonId) - .setLabel('❓ Ajukan Klarifikasi') - .setStyle(ButtonStyle.Success) + const clarificationButtonId = getCustomId([STATUS_LAST_CHECKIN_CLARIFICATION_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(messageId)]) + const clarificationButton = new ButtonBuilder() + .setCustomId(clarificationButtonId) + .setLabel('❓ Ajukan Klarifikasi') + .setStyle(ButtonStyle.Success) - return new ActionRowBuilder().addComponents(noteButton, clarificationButton) + return new ActionRowBuilder().addComponents(noteButton, clarificationButton) + } } static async getUser(prisma: PrismaClient, userDiscordId: string): Promise { From daccb31ca7bebfaf570dd4c1349c431edd36abcb Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 20:57:20 +0700 Subject: [PATCH 16/19] fix: show no checkin only when checkin is yesterday --- src/bot/commands/checkin/validators/checkin-status.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bot/commands/checkin/validators/checkin-status.ts b/src/bot/commands/checkin/validators/checkin-status.ts index d055e98..34f898a 100644 --- a/src/bot/commands/checkin/validators/checkin-status.ts +++ b/src/bot/commands/checkin/validators/checkin-status.ts @@ -6,6 +6,7 @@ import { CHECKIN_CHANNEL, FLAMEWARDEN_ROLE } from '@config/discord' import { STATUS_LAST_CHECKIN_NOTE_BUTTON_ID } from '@events/interaction-create/checkin/handlers/status-last-checkin-note-button' import { Checkin } from '@events/interaction-create/checkin/validators' import { createEmbed, decodeSnowflakes, encodeSnowflake, getCustomId } from '@utils/component' +import { isDateYesterday } from '@utils/date' import { DiscordAssert } from '@utils/discord' import { DUMMY } from '@utils/placeholder' import { ActionRowBuilder, ButtonBuilder, ButtonStyle, messageLink, PermissionsBitField } from 'discord.js' @@ -79,7 +80,7 @@ export class CheckinStatus extends CheckinStatusMessage { return { content, embed } } - const shouldShowNoCheckin = !checkin || (checkin.status === 'APPROVED' && !hasCheckedInToday) + const shouldShowNoCheckin = !checkin || (checkin.status === 'APPROVED' && isDateYesterday(checkin.created_at)) if (shouldShowNoCheckin) { embed = createEmbed( `🧐 Check-In`, From 1f7d7dcd61b1f116bce8ae7e4f4d5d5205ed5f39 Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 20:57:58 +0700 Subject: [PATCH 17/19] chore: remove unused `!` --- src/bot/commands/checkin/validators/checkin-status.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bot/commands/checkin/validators/checkin-status.ts b/src/bot/commands/checkin/validators/checkin-status.ts index 34f898a..9404028 100644 --- a/src/bot/commands/checkin/validators/checkin-status.ts +++ b/src/bot/commands/checkin/validators/checkin-status.ts @@ -92,11 +92,11 @@ export class CheckinStatus extends CheckinStatusMessage { return { content, embed } } - const flamewarden = await guild.members.fetch(checkin!.reviewed_by!) + const flamewarden = await guild.members.fetch(checkin.reviewed_by!) const buttons = this.generateButtons(guild.id, checkin) embed = createEmbed( - `🕯️ Check-In #${checkin!.public_id}`, - CheckinStatus.MSG.LastCheckin(userDiscordId, checkin!, flamewarden), + `🕯️ Check-In #${checkin.public_id}`, + CheckinStatus.MSG.LastCheckin(userDiscordId, checkin, flamewarden), DUMMY.COLOR, { text: DUMMY.FOOTER }, ) From c88600055a00d633a62be7245b52adb6cfe8f4b4 Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 21:02:29 +0700 Subject: [PATCH 18/19] feat: broken streak prop --- src/bot/commands/checkin/messages/checkin-status.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bot/commands/checkin/messages/checkin-status.ts b/src/bot/commands/checkin/messages/checkin-status.ts index b7707b6..9e25186 100644 --- a/src/bot/commands/checkin/messages/checkin-status.ts +++ b/src/bot/commands/checkin/messages/checkin-status.ts @@ -74,6 +74,7 @@ ${checkin.public_id} 📁 **Attachment:** ${checkin.attachments?.length ? '✅' : '❌'} 🗓 **Submitted At**: ${getParsedNow(getNow(checkin.created_at))} 🔥 **Last Streak**: ${checkin.checkin_streak!.streak} day(s) +💥 **Broken Streak**: ${checkin.checkin_streak!.streak_broken_at ? '✅' : '❌'} 🔎 **Status**: ${checkin.status} ${flamewarden?.displayName ? ` From 33a2a36285af1b5c823b788d6c44582f6cf339c3 Mon Sep 17 00:00:00 2001 From: alfianchii Date: Thu, 25 Dec 2025 21:02:41 +0700 Subject: [PATCH 19/19] fix: new line --- src/bot/commands/checkin/messages/checkin-status.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bot/commands/checkin/messages/checkin-status.ts b/src/bot/commands/checkin/messages/checkin-status.ts index 9e25186..cb44bdd 100644 --- a/src/bot/commands/checkin/messages/checkin-status.ts +++ b/src/bot/commands/checkin/messages/checkin-status.ts @@ -77,8 +77,7 @@ ${checkin.public_id} 💥 **Broken Streak**: ${checkin.checkin_streak!.streak_broken_at ? '✅' : '❌'} 🔎 **Status**: ${checkin.status} ${flamewarden?.displayName - ? ` -🗓 **Reviewed At**: ${getParsedNow(getNow(checkin.updated_at!))} + ? `🗓 **Reviewed At**: ${getParsedNow(getNow(checkin.updated_at!))} 👀 **Reviewed By**: ${flamewarden.displayName} (@${flamewarden.user.username}) ✍🏻 **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'}` : ''}