From cb9b6e0f32b229b04c73a3fb2dee994a89429471 Mon Sep 17 00:00:00 2001 From: Izan Gil <66965250+SrIzan10@users.noreply.github.com> Date: Sun, 5 Oct 2025 16:53:29 +0200 Subject: [PATCH] feat: anonymous replies --- bun.lock | 4 +- package.json | 2 +- .../20251005144059_reply_user/migration.sql | 16 ++++++ prisma/schema.prisma | 13 +++++ src/commands/confessionReply.ts | 51 +++++++++++++++---- 5 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 prisma/migrations/20251005144059_reply_user/migration.sql diff --git a/bun.lock b/bun.lock index ccf49e2..f339ffe 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,7 @@ "name": "ts-example", "dependencies": { "@prisma/client": "^6.16.2", - "@sern/handler": "^4.0.0", + "@sern/handler": "^4.2.6", "@sern/publisher": "^1.1.1", "discord.js": "latest", "dotenv": "^16.3.1", @@ -110,7 +110,7 @@ "@sern/cli": ["@sern/cli@1.4.0", "", { "dependencies": { "@esbuild-kit/cjs-loader": "^2.4.2", "@esbuild-kit/esm-loader": "^2.5.5", "colorette": "2.0.20", "commander": "11.0.0", "dotenv": "^16.3.1", "esbuild": "^0.19.1", "execa": "7.2.0", "find-up": "6.3.0", "glob": "^10.3.3", "ora": "6.3.1", "prompts": "2.4.2", "undici": "5.23.0" }, "bin": { "sern": "dist/index.js" } }, "sha512-IePGYYJvIVwNtnukblYxE2X7hiFivLa/p4UVaMi0XLpZ+Fa3BAdsSiBKRzLtBYWl8J2Se9StLSFwBYQW7ik+/Q=="], - "@sern/handler": ["@sern/handler@4.2.5", "", { "dependencies": { "@sern/ioc": "^1.1.2", "callsites": "^3.1.0", "cron": "^3.1.7", "deepmerge": "^4.3.1" } }, "sha512-ASvJMJf8agvn7J7jZVu7ujTEb8xycA34PG+sXu28HqxGXqvCea4LsRbX3MrAxw3FE+bPQRLc3mYreKc50lmChg=="], + "@sern/handler": ["@sern/handler@4.2.6", "", { "dependencies": { "@sern/ioc": "^1.1.2", "callsites": "^3.1.0", "cron": "^3.1.7", "deepmerge": "^4.3.1" } }, "sha512-LuRs56kVsZKNZ8KRSTdFDrjIv12u62rmHqfwHocjOEF/1LMA2Xd5qDjntaaHOLRTN4il1ZiY3mXRJUHVpMDHYA=="], "@sern/ioc": ["@sern/ioc@1.1.2", "", {}, "sha512-n84w7n5hB1dl8N6dfSbeYIo0QYORMS1bpG/P7J7GoMNTu8c28EYVZ8uGs3Md9GB09UseOKn3mfv1QBDtRsbb1g=="], diff --git a/package.json b/package.json index 8fc0adb..a22aff9 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ ], "dependencies": { "@prisma/client": "^6.16.2", - "@sern/handler": "^4.0.0", + "@sern/handler": "^4.2.6", "@sern/publisher": "^1.1.1", "discord.js": "latest", "dotenv": "^16.3.1" diff --git a/prisma/migrations/20251005144059_reply_user/migration.sql b/prisma/migrations/20251005144059_reply_user/migration.sql new file mode 100644 index 0000000..c54c79d --- /dev/null +++ b/prisma/migrations/20251005144059_reply_user/migration.sql @@ -0,0 +1,16 @@ +-- CreateTable +CREATE TABLE "ReplyUser" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "guildId" TEXT NOT NULL, + "userId" INTEGER NOT NULL, + "postId" INTEGER NOT NULL, + "userHash" TEXT NOT NULL, + CONSTRAINT "ReplyUser_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "ReplyUser_userId_key" ON "ReplyUser"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "ReplyUser_guildId_userHash_key" ON "ReplyUser"("guildId", "userHash"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 406bfac..98e2db8 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -23,6 +23,7 @@ model Post { publicMsgId String? votes Vote[] + replyUsers ReplyUser[] } model Vote { @@ -43,4 +44,16 @@ model GuildConfig { guildId String @unique verifChannelId String publicChannelId String +} + +model ReplyUser { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + guildId String + userId Int @unique + postId Int + post Post @relation(fields: [postId], references: [id]) + userHash String + + @@unique([guildId, userHash]) } \ No newline at end of file diff --git a/src/commands/confessionReply.ts b/src/commands/confessionReply.ts index 6439b5d..de876bb 100644 --- a/src/commands/confessionReply.ts +++ b/src/commands/confessionReply.ts @@ -1,5 +1,5 @@ import { commandModule, CommandType } from '@sern/handler'; -import { MessageFlags } from 'discord.js'; +import { bold, MessageFlags } from 'discord.js'; export default commandModule({ type: CommandType.Modal, @@ -7,6 +7,14 @@ export default commandModule({ execute: async (ctx, sdt) => { const db = sdt.deps.prisma; const text = ctx.fields.getTextInputValue('confessionReplyText'); + + if (!ctx.channel?.isSendable()) { + return ctx.reply({ + content: "i can't seem to send anything :heavysob:", + flags: MessageFlags.Ephemeral, + }); + } + const hashedUser = new Bun.CryptoHasher('sha256') .update(`${process.env.SALT}-${ctx.user.id}`) .digest('hex'); @@ -18,23 +26,48 @@ export default commandModule({ }, }); - if (dbPost?.posterHash !== hashedUser) { + if (!dbPost) { return ctx.reply({ - content: "you don't seem to be op here...", + content: "there's no post?", flags: MessageFlags.Ephemeral, }); } - if (!ctx.channel?.isSendable()) { - return ctx.reply({ - content: "i can't seem to send anything :heavysob:", - flags: MessageFlags.Ephemeral, + const isOp = dbPost.posterHash === hashedUser; + let userReplyId: number | null = null; + + if (!isOp) { + const replyUserExists = await db.replyUser.findFirst({ + where: { + userHash: hashedUser, + guildId: ctx.guildId!, + postId: dbPost.id, + }, }); + + if (replyUserExists) { + userReplyId = replyUserExists.id; + } else { + const maxUserId = await db.replyUser.aggregate({ + where: { guildId: ctx.guildId!, postId: dbPost.id }, + _max: { userId: true }, + }); + const nextUserId = (maxUserId._max.userId || 0) + 1; + const newReplyUser = await db.replyUser.create({ + data: { + guildId: ctx.guildId!, + postId: dbPost.id, + userHash: hashedUser, + userId: nextUserId, + }, + }); + userReplyId = newReplyUser.id; + } } await ctx.channel.send({ - content: text, - allowedMentions: { parse: [] } + content: `${bold(isOp ? 'OP' : `Anon #${userReplyId}`)}: ${text}`, + allowedMentions: { parse: [] }, }); return ctx.deferUpdate(); },