feat: anonymous replies

This commit is contained in:
2025-10-05 16:53:29 +02:00
parent bc7058c84c
commit cb9b6e0f32
5 changed files with 74 additions and 12 deletions

View File

@@ -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=="],

View File

@@ -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"

View File

@@ -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");

View File

@@ -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])
}

View File

@@ -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();
},