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
5 changes: 4 additions & 1 deletion src/bot/commands/checkin/handlers/checkin-status.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ChatInputCommandInteraction, Client, GuildMember, TextChannel } from 'discord.js'
import { registerCommand } from '@commands/registry'
import { FLAMEWARDEN_ROLE } from '@config/discord'
import { getBot, sendReply } from '@utils/discord'
import { getBot, getMember, sendReply } from '@utils/discord'
import { DiscordBaseError } from '@utils/discord/error'
import { log } from '@utils/logger'
import { SlashCommandBuilder } from 'discord.js'
Expand Down Expand Up @@ -33,10 +33,13 @@ registerCommand({
const userDiscordId: string = interaction.user.id
const user = await CheckinStatus.getUser(client.prisma, userDiscordId)

const checkin = user?.checkins?.[0]
const reviewer = checkin?.reviewed_by ? await getMember(interaction.guild, checkin.reviewed_by) : null
const { content, embed } = await CheckinStatus.getEmbedStatusContent(
interaction.guild,
user?.discord_id ?? member.id,
user?.checkins?.[0],
reviewer,
)

await sendReply(interaction, content, false, { embeds: [embed], allowedMentions: { roles: [FLAMEWARDEN_ROLE] } })
Expand Down
20 changes: 10 additions & 10 deletions src/bot/commands/checkin/messages/checkin-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ ${checkin.public_id}
🔎 **Status**: Menunggu peninjauan <@&${FLAMEWARDEN_ROLE}>
> *"Percikan telah Tuan/Nona <@${userDiscordId}> titipkan. Mohon menanti sesaat, <@&${FLAMEWARDEN_ROLE}> tengah menakar apakah [nyala tersebut](${checkin.link}) layak menjadi bagian dari perjalanan Tuan/Nona."*
`,
ApprovedCheckin: (userDiscordId: string, flamewarden: GuildMember, checkin: Checkin) => `
ApprovedCheckin: (userDiscordId: string, reviewer: GuildMember, checkin: Checkin) => `
🆔 **Check-In ID**:
\`\`\`bash
${checkin.public_id}
Expand All @@ -58,11 +58,11 @@ ${checkin.public_id}
🔥 **Current Streak**: ${checkin.checkin_streak!.streak} day(s)
🔎 **Status**: Disetujui; api Tuan/Nona kian terang
🗓 **Approved At**: ${getParsedNow(getNow(checkin.updated_at!))}
👀 **Approved By**: <@${flamewarden.id}>
✍🏻 **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'}
👀 **Approved By**: <@${reviewer.id}>
✍🏻 **${reviewer.displayName}'(s) Comment**: ${checkin.comment ?? '-'}
> *"[Nyala hari ini](${checkin.link}) diterima. Teruslah menenun aksara disiplin, satu hari demi satu hari."*
`,
RejectedCheckin: (userDiscordId: string, flamewarden: GuildMember, checkin: Checkin) => `
RejectedCheckin: (userDiscordId: string, reviewer: GuildMember, checkin: Checkin) => `
🆔 **Check-In ID**:
\`\`\`bash
${checkin.public_id}
Expand All @@ -72,11 +72,11 @@ ${checkin.public_id}
🔥 **Current Streak**: ${checkin.checkin_streak!.streak} day(s)
🔎 **Status**: Ditolak; percikan tak cukup kuat
🗓 **Reviewed At**: ${getParsedNow(getNow(checkin.updated_at!))}
👀 **Reviewed By**: <@${flamewarden.id}>
✍🏻 **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'}
👀 **Reviewed By**: <@${reviewer.id}>
✍🏻 **${reviewer.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: (guildName: string, userDiscordId: string, checkin: Checkin, flamewarden?: GuildMember) => `
LastCheckin: (guildName: string, userDiscordId: string, checkin: Checkin, reviewer?: GuildMember | null) => `
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 ${guildName} dan dapat ditinjau kembali.
Expand All @@ -92,10 +92,10 @@ ${checkin.public_id}
🔥 **Last Streak**: ${checkin.checkin_streak!.streak} day(s)
💥 **Broken Streak**: ${checkin.checkin_streak!.streak_broken_at ? '✅' : '❌'}
🔎 **Status**: ${checkin.status}
${flamewarden?.displayName
${reviewer?.displayName
? `🗓 **Reviewed At**: ${getParsedNow(getNow(checkin.updated_at!))}
👀 **Reviewed By**: ${flamewarden.displayName} (@${flamewarden.user.username})
✍🏻 **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'}`
👀 **Reviewed By**: ${reviewer.displayName} (@${reviewer.user.username})
✍🏻 **${reviewer.displayName}'(s) Comment**: ${checkin.comment ?? '-'}`
: ''}
> *"[Percikan ini](${checkin.link}) pernah kamu titipkan pada api, namun belum sempat ditakar oleh penjaga nyala."*
`,
Expand Down
125 changes: 69 additions & 56 deletions src/bot/commands/checkin/validators/checkin-status.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { PrismaClient } from '@generatedDB/client'
import type { CheckinStatusType, Checkin as CheckinType } from '@type/checkin'
import type { CheckinStatusEmbedContent, Checkin as CheckinType, ResolvedCheckinState } from '@type/checkin'
import type { User } from '@type/user'
import type { EmbedBuilder, Guild, Interaction, ThreadAutoArchiveDuration } from 'discord.js'
import type { Guild, GuildMember, Interaction, ThreadAutoArchiveDuration } from 'discord.js'
import { CHECKIN_CHANNEL, FLAMEWARDEN_ROLE } from '@config/discord'
import { Checkin } from '@events/interaction-create/checkin/validators'
import { createEmbed, decodeSnowflakes } from '@utils/component'
import { isDateYesterday } from '@utils/date'
import { DiscordAssert, getMember } from '@utils/discord'
import { DiscordAssert } from '@utils/discord'
import { DUMMY } from '@utils/placeholder'
import { messageLink, PermissionsBitField } from 'discord.js'
import { CheckinStatusError } from '../handlers/checkin-status'
Expand Down Expand Up @@ -37,73 +37,86 @@ export class CheckinStatus extends CheckinStatusMessage {
return { prefix, guildId, checkinLink }
}

static async getEmbedStatusContent(guild: Guild, userDiscordId: string, checkin?: CheckinType) {
let content = ''
let embed: EmbedBuilder
static resolveCheckinState(checkin?: CheckinType): ResolvedCheckinState {
if (!checkin)
return { type: 'NO_CHECKIN' }

const checkinStreak = checkin?.checkin_streak
const hasCheckedInToday = Checkin.hasCheckinToday(checkinStreak, checkin)
const hasToday = Checkin.hasCheckinToday(checkin.checkin_streak, checkin)
if (hasToday) {
switch (checkin.status) {
case 'WAITING': return { type: 'WAITING' }
case 'APPROVED': return { type: 'APPROVED' }
default: return { type: 'REJECTED' }
}
}

if (checkin && hasCheckedInToday) {
const flamewarden = await getMember(guild, checkin.reviewed_by!)
if (checkin.status === 'APPROVED' && isDateYesterday(checkin.created_at))
return { type: 'NO_CHECKIN' }

return { type: 'LAST_CHECKIN' }
}

switch (checkin.status as CheckinStatusType) {
case 'WAITING': {
content = `<@&${FLAMEWARDEN_ROLE}>`
embed = createEmbed(
`🧭 Check-In #${checkin.public_id}`,
CheckinStatus.MSG.WaitingCheckin(userDiscordId, checkin),
static async getEmbedStatusContent(guild: Guild, userDiscordId: string, checkin?: CheckinType, reviewer?: GuildMember | null): Promise<CheckinStatusEmbedContent> {
const state = this.resolveCheckinState(checkin)
const footer = { text: DUMMY.FOOTER(guild.name) }

switch (state.type) {
case 'WAITING':
return {
content: `<@&${FLAMEWARDEN_ROLE}>`,
embed: createEmbed(
`🧭 Check-In #${checkin!.public_id}`,
this.MSG.WaitingCheckin(userDiscordId, checkin!),
DUMMY.COLOR,
{ text: DUMMY.FOOTER(guild.name) },
)
break
footer,
),
}

case 'APPROVED': {
embed = createEmbed(
`🔥 Check-In #${checkin.public_id}`,
CheckinStatus.MSG.ApprovedCheckin(userDiscordId, flamewarden, checkin),
case 'APPROVED':
return {
embed: createEmbed(
`🔥 Check-In #${checkin!.public_id}`,
this.MSG.ApprovedCheckin(userDiscordId, reviewer!, checkin!),
DUMMY.COLOR,
{ text: DUMMY.FOOTER(guild.name) },
)
break
footer,
),
}

default: {
embed = createEmbed(
`❌ Check-In #${checkin.public_id}`,
CheckinStatus.MSG.RejectedCheckin(userDiscordId, flamewarden, checkin),
case 'REJECTED':
return {
embed: createEmbed(
`❌ Check-In #${checkin!.public_id}`,
this.MSG.RejectedCheckin(userDiscordId, reviewer!, checkin!),
DUMMY.COLOR,
{ text: DUMMY.FOOTER(guild.name) },
)
break
footer,
),
}
}

return { content, embed }
}

const shouldShowNoCheckin = !checkin || (checkin.status === 'APPROVED' && isDateYesterday(checkin.created_at))
if (shouldShowNoCheckin) {
embed = createEmbed(
`🧐 Check-In`,
CheckinStatus.MSG.NoCheckin(userDiscordId, checkinStreak),
DUMMY.COLOR,
{ text: DUMMY.FOOTER(guild.name) },
)
case 'LAST_CHECKIN':
return {
embed: createEmbed(
`🕯️ Check-In #${checkin!.public_id}`,
this.MSG.LastCheckin(
guild.name,
userDiscordId,
checkin!,
reviewer,
),
DUMMY.COLOR,
footer,
),
}

return { content, embed }
case 'NO_CHECKIN':
return {
embed: createEmbed(
`🧐 Check-In`,
this.MSG.NoCheckin(userDiscordId, checkin?.checkin_streak),
DUMMY.COLOR,
footer,
),
}
}

const flamewarden = checkin.reviewed_by ? await getMember(guild, checkin.reviewed_by) : undefined
embed = createEmbed(
`🕯️ Check-In #${checkin.public_id}`,
CheckinStatus.MSG.LastCheckin(guild.name, userDiscordId, checkin, flamewarden),
DUMMY.COLOR,
{ text: DUMMY.FOOTER(guild.name) },
)

return { content, embed }
}

static async getUser(prisma: PrismaClient, userDiscordId: string): Promise<User> {
Expand Down
68 changes: 45 additions & 23 deletions src/bot/events/client-ready/jobs/validators/reset-grinder-roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,22 +69,18 @@ export class ResetGrinderRoles extends ResetGrinderRolesMessage {
}
}

static async validateWaitingCheckin(guild: Guild, auditFlameChannel: TextChannel, member: GuildMember, user: User, checkin: CheckinType): Promise<PublicThreadChannel | undefined> {
static async notifyWaitingCheckin(guild: Guild, auditFlameChannel: TextChannel, member: GuildMember, user: User, checkin: CheckinType): Promise<PublicThreadChannel | undefined> {
if (checkin && checkin.status as CheckinStatusType === 'WAITING') {
const { content, embed } = await CheckinStatus.getEmbedStatusContent(
guild,
user.discord_id,
checkin,
)
const { content, embed } = await CheckinStatus.getEmbedStatusContent(guild, user.discord_id, checkin)
const message = await sendAsBot(null, auditFlameChannel, { embeds: [embed], allowedMentions: { roles: [FLAMEWARDEN_ROLE] }, content }) as Message
await message.react(CheckinStatus.CLARIFICATION_EMOJI)

const thread = await message.startThread({
name: CheckinStatus.MSG.ThreadName(checkin.public_id),
reason: CheckinStatus.MSG.ThreadReason(member.user.tag),
autoArchiveDuration: CheckinStatus.THREAD_ARCHIVE_DURATION,
})

await thread.send({ content: CheckinStatus.MSG.ThreadContent(user.discord_id, checkin) })
await message.react(CheckinStatus.CLARIFICATION_EMOJI)

return thread
}
Expand All @@ -96,33 +92,59 @@ export class ResetGrinderRoles extends ResetGrinderRolesMessage {

for (const user of users) {
const checkinStreak = user.checkin_streaks?.[0]
const lastCheckin = checkinStreak?.checkins?.[0]

if (!checkinStreak)
continue

const lastCheckin = checkinStreak.checkins?.[0]
if (this.hasValidCheckin(lastCheckin))
continue

const member = members.get(user.discord_id) as GuildMember
await this.removeGrinderRoles(member)
await this.breakCheckinStreakAt(prisma, checkinStreak, lastCheckin!)
const thread = await this.validateWaitingCheckin(guild, auditFlameChannel, member, user, lastCheckin!)

const payloads: InteractionReplyOptions = {
content: ResetGrinderRoles.MSG.GoodBye(guild.name, member),
allowedMentions: { users: [member.id], roles: [] },
}
if (thread)
payloads.components = [this.generateButton(guild.id, thread)]
if (!member)
continue

await sendAsBot(
null,
await this.processUser(
prisma,
guild,
member,
user,
checkinStreak,
lastCheckin!,
grindAshesChannel,
payloads,
auditFlameChannel,
)
}
}

log.info(this.MSG.RemoveGrinderRoleFrom(member))
static async processUser(
prisma: PrismaClient,
guild: Guild,
member: GuildMember,
user: User,
checkinStreak: CheckinStreak,
lastCheckin: CheckinType,
grindAshesChannel: TextChannel,
auditFlameChannel: TextChannel,
) {
await this.removeGrinderRoles(member)
await this.breakCheckinStreakAt(prisma, checkinStreak, lastCheckin!)
const thread = await this.notifyWaitingCheckin(guild, auditFlameChannel, member, user, lastCheckin!)

const payloads: InteractionReplyOptions = {
content: ResetGrinderRoles.MSG.GoodBye(guild.name, member),
allowedMentions: { users: [member.id], roles: [] },
}
if (thread)
payloads.components = [this.generateButton(guild.id, thread)]

await sendAsBot(
null,
grindAshesChannel,
payloads,
)

log.info(this.MSG.RemoveGrinderRoleFrom(member))
}

static async getUsersWithLatestStreak(prisma: PrismaClient): Promise<User[]> {
Expand Down
12 changes: 6 additions & 6 deletions src/bot/events/interaction-create/checkin/messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,29 +47,29 @@ ${checkin.public_id}

> 🔎 Sedang menunggu peninjauan Flamewarden; mohon Tuan/Nona bersabar`,

CheckinApproved: (flamewarden: GuildMember, checkin: Checkin) => `
CheckinApproved: (reviewer: GuildMember, checkin: Checkin) => `
[Nyala api](${checkin.link}) Tuan/Nona berkobar lebih terang pada hari ini.
🆔 **Check-In ID**:
\`\`\`bash
${checkin.public_id}
\`\`\`
🔥 **Current Streak**: ${checkin.checkin_streak!.streak}
🗓 **Approved At**: ${getParsedNow(getNow(checkin.updated_at!))}
👀 **Approved By**: <@${flamewarden.id}>
✍🏻 **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'}
👀 **Approved By**: <@${reviewer.id}>
✍🏻 **${reviewer.displayName}'(s) Comment**: ${checkin.comment ?? '-'}

> 🔥 Konsistensi ialah bahan bakar nyala api; teruskan langkah Tuan/Nona`,

CheckinRejected: (flamewarden: GuildMember, checkin: Checkin) => `
CheckinRejected: (reviewer: GuildMember, checkin: Checkin) => `
[Check-in ini](${checkin.link}) tidak memenuhi syarat dan dengan demikian telah ditolak.
🆔 **Check-In ID**:
\`\`\`bash
${checkin.public_id}
\`\`\`
🔥 **Current Streak**: ${checkin.checkin_streak!.streak}
🗓 **Reviewed At**: ${getParsedNow(getNow(checkin.updated_at!))}
👀 **Reviewed By**: <@${flamewarden.id}>
✍🏻 **${flamewarden.displayName}'(s) Comment**: ${checkin.comment ?? '-'}
👀 **Reviewed By**: <@${reviewer.id}>
✍🏻 **${reviewer.displayName}'(s) Comment**: ${checkin.comment ?? '-'}

> 🧯 Nyala api Tuan/Nona meredup, namun belum padam; silakan mencuba kembali`,
}
Expand Down
Loading