diff --git a/db/migrations/20250810031908_init/migration.sql b/db/migrations/20250810031908_init/migration.sql index e1c14bd..f01eaeb 100644 --- a/db/migrations/20250810031908_init/migration.sql +++ b/db/migrations/20250810031908_init/migration.sql @@ -13,6 +13,7 @@ CREATE TABLE "public"."CheckinStreak" ( "first_date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "last_date" TIMESTAMP(3), "streak" INTEGER NOT NULL DEFAULT 0, + "streak_broken_at" TIMESTAMP(3) DEFAULT NULL, "updated_at" TIMESTAMP(3), CONSTRAINT "CheckinStreak_pkey" PRIMARY KEY ("id") diff --git a/db/schema.prisma b/db/schema.prisma index d51a486..b63aff1 100644 --- a/db/schema.prisma +++ b/db/schema.prisma @@ -23,6 +23,7 @@ model CheckinStreak { first_date DateTime @default(now()) last_date DateTime? streak Int @default(0) + streak_broken_at DateTime? updated_at DateTime? user User @relation(fields: [user_id], references: [id]) diff --git a/docker-compose.yml b/docker-compose.yml index a433552..09711c4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,7 @@ services: condition: service_healthy volumes: - ./src:/usr/src/app/src - - ./db:/usr/src/app/db + - ./db/schema.prisma:/usr/src/app/db/schema.prisma - ./prisma.config.ts:/usr/src/app/prisma.config.ts - ./package.json:/usr/src/app/package.json - ./tsconfig.json:/usr/src/app/tsconfig.json diff --git a/src/bot/events/client-ready/jobs/handlers/reset-grinder-roles.ts b/src/bot/events/client-ready/jobs/handlers/reset-grinder-roles.ts index 5d7e831..e8a6940 100644 --- a/src/bot/events/client-ready/jobs/handlers/reset-grinder-roles.ts +++ b/src/bot/events/client-ready/jobs/handlers/reset-grinder-roles.ts @@ -27,9 +27,9 @@ export default { const guild = await client.guilds.fetch(process.env.GUILD_ID!) const channel = await getChannel(guild, GRIND_ASHES_CHANNEL) ResetGrinderRoles.assertChannel(channel) - const users = await ResetGrinderRoles.getUsersWithLatestCheckin(client.prisma) + const users = await ResetGrinderRoles.getUsersWithLatestStreak(client.prisma) - await ResetGrinderRoles.validateUsers(guild, channel, users) + await ResetGrinderRoles.validateUsers(client.prisma, guild, channel, users) log.success(ResetGrinderRoles.MSG.JobSuccess) }) 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 b462bf2..287406c 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 @@ -1,4 +1,6 @@ import type { PrismaClient } from '@generatedDB/client' +import type { CheckinStreak } from '@type/checkin-streak' +import type { User } from '@type/user' import type { Guild, GuildMember, TextChannel } from 'discord.js' import { getGrindRoles, GRINDER_ROLE } from '@config/discord' import { isDateToday, isDateYesterday } from '@utils/date' @@ -6,14 +8,6 @@ import { sendAsBot } from '@utils/discord' import { log } from '@utils/logger' import { ResetGrinderRolesMessage } from '../messages/reset-grinder-roles' -interface UserWithLatestCheckin { - discord_id: string - checkins: { - status: string - created_at: Date - }[] -} - export class ResetGrinderRoles extends ResetGrinderRolesMessage { static hasValidCheckin(checkin?: { created_at: Date, status: string }): boolean { if (!checkin) @@ -39,14 +33,19 @@ export class ResetGrinderRoles extends ResetGrinderRolesMessage { } } - static async validateUsers(guild: Guild, channel: TextChannel, users: UserWithLatestCheckin[]) { + static async validateUsers(prisma: PrismaClient, guild: Guild, channel: TextChannel, users: User[]) { for (const user of users) { const lastCheckin = user.checkins?.[0] if (this.hasValidCheckin(lastCheckin)) continue + const checkinStreak = user.checkin_streaks?.[0] + if (!checkinStreak) + continue + const member = await guild.members.fetch(user.discord_id) await this.removeGrinderRoles(member) + await this.breakCheckinStreakAt(prisma, checkinStreak) await sendAsBot( null, @@ -58,7 +57,7 @@ export class ResetGrinderRoles extends ResetGrinderRolesMessage { } } - static async getUsersWithLatestCheckin(prisma: PrismaClient): Promise { + static async getUsersWithLatestStreak(prisma: PrismaClient): Promise { const users = await prisma.user.findMany({ select: { discord_id: true, @@ -70,9 +69,32 @@ export class ResetGrinderRoles extends ResetGrinderRolesMessage { orderBy: { created_at: 'desc' }, take: 1, }, + checkin_streaks: { + orderBy: { first_date: 'desc' }, + take: 1, + where: { + streak_broken_at: null, + }, + include: { + checkins: { + orderBy: { created_at: 'desc' }, + take: 1, + }, + }, + }, }, - }) as UserWithLatestCheckin[] + }) as User[] return users } + + static async breakCheckinStreakAt(prisma: PrismaClient, checkinStreak: CheckinStreak) { + await prisma.checkinStreak.update({ + where: { id: checkinStreak.id }, + data: { + streak_broken_at: new Date(), + updated_at: new Date(), + }, + }) + } } diff --git a/src/types/checkin-streak.d.ts b/src/types/checkin-streak.d.ts index 301006b..bb06cd4 100644 --- a/src/types/checkin-streak.d.ts +++ b/src/types/checkin-streak.d.ts @@ -7,6 +7,7 @@ export interface CheckinStreak { first_date: Date last_date?: Date | null streak: number + streak_broken_at?: Date | null updated_at?: Date | null user?: User