Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion db/migrations/20250810031908_init/migration.sql
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ CREATE TABLE "public"."Checkin" (
"user_id" INTEGER NOT NULL,
"checkin_streak_id" INTEGER NOT NULL,
"description" TEXT NOT NULL,
"link" TEXT,
"link" TEXT UNIQUE,
"status" TEXT NOT NULL DEFAULT 'WAITING',
"reviewed_by" TEXT,
"comment" TEXT,
Expand Down
2 changes: 1 addition & 1 deletion db/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ model Checkin {
user_id Int
checkin_streak_id Int
description String
link String?
link String? @unique
status String
reviewed_by String?
comment String?
Expand Down
1 change: 1 addition & 0 deletions src/bot/commands/checkin/handlers/checkin-audit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ registerCommand({
CHECKIN_AUDIT_ID,
encodeSnowflake(interaction.guildId),
checkinId,
checkin.created_at.getTime().toString(),
])
const modal = createCheckinReviewModal(modalCustomId, checkin, false)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class ResetGrinderRoles extends ResetGrinderRolesMessage {

const grindRoles = getGrindRoles()
for (const grindRole of grindRoles) {
if (member.roles.cache.has(grindRole.id)) {
if (this.isMemberHasRole(member, grindRole.id)) {
await member.roles.remove(grindRole.id)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ registerInteractionHandler({
if (!interaction.inCachedGuild())
throw new CheckinApproveButtonError(Checkin.ERR.NotGuild)

const { checkinId } = Checkin.getButtonId(interaction, interaction.customId)
const { checkinId, checkinCreatedAt } = Checkin.getButtonId(interaction, interaction.customId)

const channel = interaction.channel as TextChannel
Checkin.assertMissPerms(interaction.client.user, channel)
Expand All @@ -40,11 +40,11 @@ registerInteractionHandler({
Checkin.assertMemberHasRole(flamewarden, FLAMEWARDEN_ROLE)

await Checkin.validateCheckin(
client.prisma,
client,
interaction.guild,
flamewarden,
{ key: 'id', value: checkinId },
interaction.message,
checkinCreatedAt,
'APPROVED',
)
}
Expand Down
22 changes: 13 additions & 9 deletions src/bot/events/interaction-create/checkin/handlers/audit-modal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { CheckinStatusType } from '@type/checkin'
import type { TextChannel } from 'discord.js'
import { CHECKIN_CHANNEL, FLAMEWARDEN_ROLE } from '@config/discord'
import { FLAMEWARDEN_ROLE } from '@config/discord'
import { EVENT_PATH } from '@events/index'
import { registerInteractionHandler } from '@events/interaction-create/registry'
import { generateCustomId } from '@utils/component'
Expand Down Expand Up @@ -33,10 +33,9 @@ registerInteractionHandler({
if (!interaction.inCachedGuild())
throw new CheckinAuditModalError(CheckinAudit.ERR.NotGuild)

const { checkinId } = CheckinAudit.getModalReviewId(interaction, interaction.customId)
const { checkinId, checkinCreatedAt } = CheckinAudit.getModalReviewId(interaction, interaction.customId)

const channel = interaction.channel as TextChannel
const checkinChannel = await interaction.client.channels.fetch(CHECKIN_CHANNEL) as TextChannel
CheckinAudit.assertMissPerms(interaction.client.user, channel)
const flamewarden = await interaction.guild.members.fetch(interaction.member.id)
CheckinAudit.assertMember(flamewarden)
Expand All @@ -45,13 +44,18 @@ registerInteractionHandler({
const status: CheckinStatusType = 'APPROVED'
const comment = interaction.fields.getTextInputValue('comment')

const checkin = await Checkin.getWaitingCheckin(client.prisma, 'public_id', checkinId)
const updatedCheckin = await Checkin.updateCheckinStatus(client.prisma, flamewarden, checkin, status, comment, true)
await Checkin.validateCheckinHandleToUser(interaction.guild, flamewarden, checkin.user!.discord_id, updatedCheckin)
const updatedCheckin = await Checkin.validateCheckin(
client,
interaction.guild,
flamewarden,
{ key: 'public_id', value: checkinId },
checkinCreatedAt,
status,
comment,
true,
)

const { messageId } = CheckinAudit.getMessageFromLink(checkin.link!)
const message = await checkinChannel.messages.fetch(messageId)
await Checkin.validateCheckinHandleSubmittedMsg(message, updatedCheckin, status)
await sendReply(interaction, CheckinAudit.MSG.AuditSuccess(updatedCheckin.link!, updatedCheckin.user!.discord_id))
}
catch (err: any) {
if (err instanceof DiscordBaseError)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,23 @@ registerInteractionHandler({
if (!interaction.inCachedGuild())
throw new CheckinCustomButtonModalError(Checkin.ERR.NotGuild)

const { checkinId, messageId } = Checkin.getModalReviewId(interaction, interaction.customId)
const { checkinId, checkinCreatedAt } = Checkin.getModalReviewId(interaction, interaction.customId)

const channel = interaction.channel as TextChannel
Checkin.assertMissPerms(interaction.client.user, channel)
const flamewarden = await interaction.guild.members.fetch(interaction.member.id)
Checkin.assertMember(flamewarden)
Checkin.assertMemberHasRole(flamewarden, FLAMEWARDEN_ROLE)
const message = await channel.messages.fetch(messageId)

const status = interaction.fields.getStringSelectValues('status')[0] as CheckinStatusType
const comment = interaction.fields.getTextInputValue('comment')

await Checkin.validateCheckin(
client.prisma,
client,
interaction.guild,
flamewarden,
{ key: 'id', value: checkinId },
message,
checkinCreatedAt,
status,
comment,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ registerInteractionHandler({
Checkin.assertMember(flamewarden)
Checkin.assertMemberHasRole(flamewarden, FLAMEWARDEN_ROLE)

const { checkinId } = Checkin.getButtonId(interaction, interaction.customId)
const { checkinId, checkinCreatedAt } = Checkin.getButtonId(interaction, interaction.customId)
const checkin = await Checkin.getWaitingCheckin(client.prisma, 'id', checkinId)
const modalCustomId = getCustomId([
CHECKIN_CUSTOM_BUTTON_MODAL_ID,
encodeSnowflake(interaction.guildId),
encodeSnowflake(checkinId.toString()),
encodeSnowflake(interaction.message.id),
checkinCreatedAt.getTime().toString(),
])
const modal = createCheckinReviewModal(modalCustomId, checkin)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ registerInteractionHandler({
Checkin.assertCheckinToday(user)

const { checkin } = await Checkin.validateCheckinStreak(client.prisma, user.id, user.checkin_streaks?.[0], todo)
const buttons = Checkin.generateButtons(interaction.guildId, checkin.id.toString())
const buttons = Checkin.generateButtons(interaction.guildId, checkin.id.toString(), checkin.created_at)

const msg = await sendReply(
interaction,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ registerInteractionHandler({
if (!interaction.inCachedGuild())
throw new CheckinRejectButtonError(Checkin.ERR.NotGuild)

const { checkinId } = Checkin.getButtonId(interaction, interaction.customId)
const { checkinId, checkinCreatedAt } = Checkin.getButtonId(interaction, interaction.customId)

const channel = interaction.channel as TextChannel
Checkin.assertMissPerms(interaction.client.user, channel)
Expand All @@ -40,11 +40,11 @@ registerInteractionHandler({
Checkin.assertMemberHasRole(flamewarden, FLAMEWARDEN_ROLE)

await Checkin.validateCheckin(
client.prisma,
client,
interaction.guild,
flamewarden,
{ key: 'id', value: checkinId },
interaction.message,
checkinCreatedAt,
'REJECTED',
)
}
Expand Down
5 changes: 5 additions & 0 deletions src/bot/events/interaction-create/checkin/messages/audit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ ${waitingCheckinList}
`,
UnexpectedCheckinAudit: '❌ Something went wrong during the check-in audit',
}

static override readonly MSG = {
...DiscordAssert.MSG,
AuditSuccess: (msgLink: string, userDiscordId: string) => `✅ Successfully [audited check-in](${msgLink}) for <@${userDiscordId}>.`,
}
}
12 changes: 9 additions & 3 deletions src/bot/events/interaction-create/checkin/validators/audit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { decodeSnowflakes } from '@utils/component'
import { isDateToday } from '@utils/date'
import { DiscordAssert } from '@utils/discord'
import { PermissionsBitField } from 'discord.js'
import { Checkin } from '.'
import { CheckinAuditModalError } from '../handlers/audit-modal'
import { CheckinAuditMessage } from '../messages/audit'
import { Checkin } from '.'

export class CheckinAudit extends CheckinAuditMessage {
static override BASE_PERMS = [
Expand All @@ -18,16 +18,22 @@ export class CheckinAudit extends CheckinAuditMessage {
]

static getModalReviewId(interaction: Interaction, customId: string) {
const [prefix, guildId, checkinId] = decodeSnowflakes(customId)
const [prefix, guildId, checkinId, checkinTs] = decodeSnowflakes(customId)

if (!guildId)
throw new CheckinAuditModalError(this.ERR.GuildMissing)
if (interaction.guildId !== guildId)
throw new CheckinAuditModalError(this.ERR.NotGuild)
if (!checkinId)
throw new CheckinAuditModalError(this.ERR.CheckinIdMissing)
if (!checkinTs)
throw new CheckinAuditModalError(this.ERR.CheckinDateMissing)

const checkinCreatedAt = new Date(Number(checkinTs))
if (!checkinCreatedAt)
throw new CheckinAuditModalError(this.ERR.CheckinDateInvalid)

return { prefix, guildId, checkinId }
return { prefix, guildId, checkinId, checkinCreatedAt }
}

static assertCheckinNotToday(checkin: CheckinType) {
Expand Down
Loading