diff --git a/db/migrations/20250810031908_init/migration.sql b/db/migrations/20250810031908_init/migration.sql index f01eaeb..588e9bc 100644 --- a/db/migrations/20250810031908_init/migration.sql +++ b/db/migrations/20250810031908_init/migration.sql @@ -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, diff --git a/db/schema.prisma b/db/schema.prisma index b63aff1..ef20bc4 100644 --- a/db/schema.prisma +++ b/db/schema.prisma @@ -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? diff --git a/src/bot/commands/checkin/handlers/checkin-audit.ts b/src/bot/commands/checkin/handlers/checkin-audit.ts index 17a0f3c..d3ff30a 100644 --- a/src/bot/commands/checkin/handlers/checkin-audit.ts +++ b/src/bot/commands/checkin/handlers/checkin-audit.ts @@ -46,6 +46,7 @@ registerCommand({ CHECKIN_AUDIT_ID, encodeSnowflake(interaction.guildId), checkinId, + checkin.created_at.getTime().toString(), ]) const modal = createCheckinReviewModal(modalCustomId, checkin, false) diff --git a/src/bot/events/client-ready/jobs/validators/reset-grinder-roles.ts b/src/bot/events/client-ready/jobs/validators/reset-grinder-roles.ts index 75e1017..def0b3d 100644 --- a/src/bot/events/client-ready/jobs/validators/reset-grinder-roles.ts +++ b/src/bot/events/client-ready/jobs/validators/reset-grinder-roles.ts @@ -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) } } diff --git a/src/bot/events/interaction-create/checkin/handlers/approve-button.ts b/src/bot/events/interaction-create/checkin/handlers/approve-button.ts index 7664cec..728ea5a 100644 --- a/src/bot/events/interaction-create/checkin/handlers/approve-button.ts +++ b/src/bot/events/interaction-create/checkin/handlers/approve-button.ts @@ -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) @@ -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', ) } diff --git a/src/bot/events/interaction-create/checkin/handlers/audit-modal.ts b/src/bot/events/interaction-create/checkin/handlers/audit-modal.ts index 842fc86..b1437ab 100644 --- a/src/bot/events/interaction-create/checkin/handlers/audit-modal.ts +++ b/src/bot/events/interaction-create/checkin/handlers/audit-modal.ts @@ -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' @@ -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) @@ -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) diff --git a/src/bot/events/interaction-create/checkin/handlers/custom-button-modal.ts b/src/bot/events/interaction-create/checkin/handlers/custom-button-modal.ts index 0965780..b239bf8 100644 --- a/src/bot/events/interaction-create/checkin/handlers/custom-button-modal.ts +++ b/src/bot/events/interaction-create/checkin/handlers/custom-button-modal.ts @@ -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, ) diff --git a/src/bot/events/interaction-create/checkin/handlers/custom-button.ts b/src/bot/events/interaction-create/checkin/handlers/custom-button.ts index 7fa3753..62cdfba 100644 --- a/src/bot/events/interaction-create/checkin/handlers/custom-button.ts +++ b/src/bot/events/interaction-create/checkin/handlers/custom-button.ts @@ -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) diff --git a/src/bot/events/interaction-create/checkin/handlers/modal.ts b/src/bot/events/interaction-create/checkin/handlers/modal.ts index aae08ed..ff52429 100644 --- a/src/bot/events/interaction-create/checkin/handlers/modal.ts +++ b/src/bot/events/interaction-create/checkin/handlers/modal.ts @@ -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, diff --git a/src/bot/events/interaction-create/checkin/handlers/reject-button.ts b/src/bot/events/interaction-create/checkin/handlers/reject-button.ts index ae80cea..a5db077 100644 --- a/src/bot/events/interaction-create/checkin/handlers/reject-button.ts +++ b/src/bot/events/interaction-create/checkin/handlers/reject-button.ts @@ -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) @@ -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', ) } diff --git a/src/bot/events/interaction-create/checkin/messages/audit.ts b/src/bot/events/interaction-create/checkin/messages/audit.ts index a9b7a7e..eecca18 100644 --- a/src/bot/events/interaction-create/checkin/messages/audit.ts +++ b/src/bot/events/interaction-create/checkin/messages/audit.ts @@ -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}>.`, + } } diff --git a/src/bot/events/interaction-create/checkin/validators/audit.ts b/src/bot/events/interaction-create/checkin/validators/audit.ts index 6881cdd..713916a 100644 --- a/src/bot/events/interaction-create/checkin/validators/audit.ts +++ b/src/bot/events/interaction-create/checkin/validators/audit.ts @@ -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 = [ @@ -18,7 +18,7 @@ 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) @@ -26,8 +26,14 @@ export class CheckinAudit extends CheckinAuditMessage { 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) { diff --git a/src/bot/events/interaction-create/checkin/validators/index.ts b/src/bot/events/interaction-create/checkin/validators/index.ts index 68eca66..25d85b1 100644 --- a/src/bot/events/interaction-create/checkin/validators/index.ts +++ b/src/bot/events/interaction-create/checkin/validators/index.ts @@ -4,7 +4,7 @@ import type { Attachment as AttachmentType } from '@type/attachment' import type { CheckinAllowedEmojiType, CheckinColumn, CheckinStatusType, Checkin as CheckinType } from '@type/checkin' import type { CheckinStreak } from '@type/checkin-streak' import type { User } from '@type/user' -import type { Attachment, EmbedBuilder, Guild, GuildMember, Interaction, Message } from 'discord.js' +import type { Attachment, Client, EmbedBuilder, Guild, GuildMember, Interaction, Message, TextChannel } from 'discord.js' import crypto from 'node:crypto' import { CheckinError } from '@commands/checkin/handlers/checkin' import { AURA_FARMING_CHANNEL, CHECKIN_CHANNEL, GRINDER_ROLE } from '@config/discord' @@ -54,8 +54,7 @@ export class Checkin extends CheckinMessage { } static getModalReviewId(interaction: Interaction, customId: string) { - const [prefix, guildId, checkinId, messageId] = decodeSnowflakes(customId) - const checkinIdNum = Number(checkinId) + const [prefix, guildId, checkinId, checkinTs] = decodeSnowflakes(customId) if (!guildId) throw new CheckinCustomButtonModalError(this.ERR.GuildMissing) @@ -63,15 +62,22 @@ export class Checkin extends CheckinMessage { throw new CheckinCustomButtonModalError(this.ERR.NotGuild) if (!checkinId) throw new CheckinCustomButtonModalError(this.ERR.CheckinIdMissing) - if (!messageId) - throw new CheckinCustomButtonModalError(this.ERR.MessageIdMissing) + if (!checkinTs) + throw new CheckinCustomButtonModalError(this.ERR.CheckinDateMissing) + + const checkinIdNum = Number(checkinId) + if (Number.isNaN(checkinIdNum)) + throw new CheckinError(this.ERR.CheckinIdInvalid) + + const checkinCreatedAt = new Date(Number(checkinTs)) + if (!checkinCreatedAt) + throw new CheckinCustomButtonModalError(this.ERR.CheckinDateInvalid) - return { prefix, guildId, checkinId: checkinIdNum, messageId } + return { prefix, guildId, checkinId: checkinIdNum, checkinCreatedAt } } static getButtonId(interaction: Interaction, customId: string) { - const [prefix, guildId, checkinId] = decodeSnowflakes(customId) - const checkinIdNum = Number(checkinId) + const [prefix, guildId, checkinId, checkinTs] = decodeSnowflakes(customId) if (!guildId) throw new CheckinError(this.ERR.GuildMissing) @@ -79,10 +85,18 @@ export class Checkin extends CheckinMessage { throw new CheckinError(this.ERR.NotGuild) if (!checkinId) throw new CheckinError(this.ERR.CheckinIdMissing) + if (!checkinTs) + throw new CheckinError(this.ERR.CheckinDateMissing) + + const checkinIdNum = Number(checkinId) if (Number.isNaN(checkinIdNum)) throw new CheckinError(this.ERR.CheckinIdInvalid) - return { prefix, guildId, checkinId: checkinIdNum } + const checkinCreatedAt = new Date(Number(checkinTs)) + if (!checkinCreatedAt) + throw new CheckinError(this.ERR.CheckinDateInvalid) + + return { prefix, guildId, checkinId: checkinIdNum, checkinCreatedAt } } static generatePublicId(): string { @@ -100,26 +114,26 @@ export class Checkin extends CheckinMessage { } } - static generateButtons(guildId: string, checkinId: string): ActionRowBuilder { - const detailButtonId = getCustomId([CHECKIN_DETAIL_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(checkinId)]) + static generateButtons(guildId: string, checkinId: string, checkinCreatedAt: Date): ActionRowBuilder { + const detailButtonId = getCustomId([CHECKIN_DETAIL_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(checkinId), checkinCreatedAt.getTime().toString()]) const detailButton = new ButtonBuilder() .setCustomId(detailButtonId) .setLabel('🔍 Detail') .setStyle(ButtonStyle.Primary) - const approveButtonId = getCustomId([CHECKIN_APPROVE_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(checkinId)]) + const approveButtonId = getCustomId([CHECKIN_APPROVE_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(checkinId), checkinCreatedAt.getTime().toString()]) const approveButton = new ButtonBuilder() .setCustomId(approveButtonId) .setLabel('🔥 Approve') .setStyle(ButtonStyle.Success) - const rejectButtonId = getCustomId([CHECKIN_REJECT_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(checkinId)]) + const rejectButtonId = getCustomId([CHECKIN_REJECT_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(checkinId), checkinCreatedAt.getTime().toString()]) const rejectButton = new ButtonBuilder() .setCustomId(rejectButtonId) .setLabel('🙅 Reject') .setStyle(ButtonStyle.Danger) - const customButtonId = getCustomId([CHECKIN_CUSTOM_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(checkinId)]) + const customButtonId = getCustomId([CHECKIN_CUSTOM_BUTTON_ID, encodeSnowflake(guildId), encodeSnowflake(checkinId), checkinCreatedAt.getTime().toString()]) const customButton = new ButtonBuilder() .setCustomId(customButtonId) .setLabel('⚙️ Review') @@ -140,11 +154,11 @@ export class Checkin extends CheckinMessage { if (!newRole) return - const alreadyHasRole = member.roles.cache.has(newRole.id) + const hasGrindRole = this.isMemberHasRole(member, newRole.id) const channel = await getChannel(guild, AURA_FARMING_CHANNEL) this.assertChannel(channel) - if (!alreadyHasRole) { + if (!hasGrindRole) { await attachNewGrindRole(member, newRole) await sendAsBot(null, channel, { content: `**Congratulations, <@${member.id}>** ${this.MSG.ReachNewGrindRole(newRole)}`, @@ -171,7 +185,9 @@ export class Checkin extends CheckinMessage { throw new CheckinModalError(this.ERR.AlreadyCheckinToday(latestCheckin!.link!)) } - static assertSubmittedCheckinToday(checkin: CheckinType) { + static async assertSubmittedCheckinToday(prisma: PrismaClient, opt: CheckinColumn) { + const checkin = await this.getWaitingCheckin(prisma, opt.key, opt.value) + const isCheckinToday = this.hasCheckinToday(checkin.checkin_streak, checkin) if (!isCheckinToday) throw new SubmittedCheckinError(this.ERR.SubmittedCheckinNotToday(checkin.link!)) @@ -401,25 +417,37 @@ export class Checkin extends CheckinMessage { } static async validateCheckin( - prisma: PrismaClient, + client: Client, guild: Guild, flamewarden: GuildMember, opt: CheckinColumn, - message: Message, + checkinCreatedAt: Date, checkinStatus: CheckinStatusType, comment?: string | null, - ) { - const checkin = await this.getWaitingCheckin(prisma, opt.key, opt.value) - this.assertSubmittedCheckinToday(checkin) - const updatedCheckin = await this.updateCheckinStatus(prisma, flamewarden, checkin, checkinStatus, comment) as CheckinType + isAudit: boolean = false, + ): Promise { + if (!isAudit) + await this.assertSubmittedCheckinToday(client.prisma, opt) + const updatedCheckin = await this.updateCheckinStatus(client.prisma, flamewarden, opt, checkinCreatedAt, checkinStatus, comment, isAudit) as CheckinType - await this.validateCheckinHandleToUser(guild, flamewarden, checkin.user!.discord_id, updatedCheckin) + const checkinChannel = await client.channels.fetch(CHECKIN_CHANNEL) as TextChannel + const { messageId } = this.getMessageFromLink(updatedCheckin.link!) + const message = await checkinChannel.messages.fetch(messageId) + + await this.validateCheckinHandleToUser(guild, flamewarden, updatedCheckin.user!.discord_id, updatedCheckin) await message.react(this.REVERSED_EMOJI_STATUS[checkinStatus]) + + return updatedCheckin } static async validateCheckinHandleToUser(guild: Guild, flamewarden: GuildMember, userDiscordId: string, updatedCheckin: CheckinType) { const member = await guild.members.fetch(userDiscordId) this.assertMember(member) + + const hasGrinderRole = this.isMemberHasRole(member, GRINDER_ROLE) + if (!hasGrinderRole) + await member.roles.add(GRINDER_ROLE) + const newGrindRole = this.getNewGrindRole(guild, updatedCheckin.checkin_streak!.streak) await this.setMemberNewGrindRole(guild, member, newGrindRole) await this.sendCheckinStatusToMember(flamewarden, member, updatedCheckin) @@ -434,18 +462,21 @@ export class Checkin extends CheckinMessage { }) } - static async updateCheckinStatus( + static async updateCheckinStatus( prisma: PrismaClient, member: GuildMember, - checkin: CheckinType, + opt: CheckinColumn, + checkinCreatedAt: Date, checkinStatus: CheckinStatusType, comment: string | null = null, - isLateCheckin: boolean = false, + isAudit: boolean = false, ): Promise { - const updatedDate = isLateCheckin ? checkin.created_at : new Date() + const updatedDate = isAudit ? checkinCreatedAt : new Date() const updatedCheckin = await prisma.checkin.update({ - where: { id: checkin.id }, + where: { + [opt.key!]: opt.value!, + } as Prisma.CheckinWhereUniqueInput, data: { status: checkinStatus, reviewed_by: member.id, @@ -458,12 +489,11 @@ export class Checkin extends CheckinMessage { }, last_date: updatedDate, updated_at: updatedDate, + streak_broken_at: null, }, }, }, - include: { - checkin_streak: true, - }, + include: { user: true, checkin_streak: true }, }) return updatedCheckin diff --git a/src/bot/events/interaction-create/embed/handlers/role-grant-create-button.ts b/src/bot/events/interaction-create/embed/handlers/role-grant-create-button.ts index 9c4b35f..bed00cd 100644 --- a/src/bot/events/interaction-create/embed/handlers/role-grant-create-button.ts +++ b/src/bot/events/interaction-create/embed/handlers/role-grant-create-button.ts @@ -41,7 +41,7 @@ registerInteractionHandler({ await member.roles.add(role) await sendReply(interaction, ` - ${RoleGrantCreate.roleGranted(role.id)}`) + ${RoleGrantCreate.MSG.RoleGranted(role.id)}`) } catch (err: any) { if (err instanceof DiscordBaseError) diff --git a/src/bot/events/message-reaction-add/checkin/handlers/submitted.ts b/src/bot/events/message-reaction-add/checkin/handlers/submitted.ts index fdd185d..6215cac 100644 --- a/src/bot/events/message-reaction-add/checkin/handlers/submitted.ts +++ b/src/bot/events/message-reaction-add/checkin/handlers/submitted.ts @@ -36,11 +36,11 @@ registerReactionHandler({ await Checkin.assertAllowedChannel(guild, message.channel.id, CHECKIN_CHANNEL) await Checkin.validateCheckin( - client.prisma, + client, guild, flamewarden, { key: 'link', value: message.url }, - message, + message.createdAt, Checkin.EMOJI_STATUS[emoji], ) } diff --git a/src/types/checkin.d.ts b/src/types/checkin.d.ts index ee00121..521ef8d 100644 --- a/src/types/checkin.d.ts +++ b/src/types/checkin.d.ts @@ -26,5 +26,5 @@ export interface Checkin { export interface CheckinColumn { key: T - value: Prisma.CheckinWhereInput[T] + value: Prisma.CheckinWhereInput[T] | Prisma.CheckinWhereUniqueInput[K] } diff --git a/src/utils/discord/assert.ts b/src/utils/discord/assert.ts index 936d4b3..d434dc9 100644 --- a/src/utils/discord/assert.ts +++ b/src/utils/discord/assert.ts @@ -77,7 +77,7 @@ export class DiscordAssert extends DiscordMessage { static assertMemberAlreadyHasRole(member: GuildMember, roleId: string) { if (this.isMemberHasRole(member, roleId)) - throw new DiscordAssertError(this.roleRevoked(roleId)) + throw new DiscordAssertError(this.MSG.RoleRevoked(roleId)) } static assertMemberHasRole(member: GuildMember, roleId: string) { diff --git a/src/utils/discord/message.ts b/src/utils/discord/message.ts index 720b1b2..ac35589 100644 --- a/src/utils/discord/message.ts +++ b/src/utils/discord/message.ts @@ -25,6 +25,8 @@ export class DiscordMessage { PlainMessage: '❌ There is nothing to do with this plain message', CheckinIdMissing: '❌ Check-in ID is missing or invalid', CheckinIdInvalid: '❌ The provided check-in ID is invalid', + CheckinDateMissing: '❌ Check-in date is missing or invalid', + CheckinDateInvalid: '❌ The check-in date is invalid', UnexpectedModal: '❌ Something went wrong while handling the modal component', UnexpectedButton: '❌ Something went wrong while handling the button component', @@ -35,13 +37,11 @@ export class DiscordMessage { ReachNewGrindRole(role: GrindRole) { return `🎉 You have reached a new grind role: <@&${(role.id)}>~` }, - } - - static roleGranted(roleId: string): string { - return `✅ Granted <@&${(roleId)}> to you` - } - - static roleRevoked(roleId: string): string { - return `❌ You already have the <@&${(roleId)}> role` + RoleGranted(roleId: string): string { + return `✅ Granted <@&${(roleId)}> to you` + }, + RoleRevoked(roleId: string): string { + return `❌ You already have the <@&${(roleId)}> role` + }, } }