From 421203ca3ad6809c03ad9bbce970359651786ecc Mon Sep 17 00:00:00 2001 From: alina sireneva Date: Tue, 6 Aug 2024 23:23:12 +0300 Subject: [PATCH] feat: shoutbox replies --- drizzle/0002_neat_firedrake.sql | 1 + drizzle/meta/0002_snapshot.json | 101 ++++++++++++++++++ drizzle/meta/_journal.json | 7 ++ src/backend/bot/shoutbox.ts | 17 ++- src/backend/models/shoutbox.ts | 1 + src/backend/service/shoutbox.ts | 10 ++ .../PageMain/Shoutbox/Shoutbox.module.css | 4 + .../pages/PageMain/Shoutbox/Shoutbox.tsx | 6 ++ 8 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 drizzle/0002_neat_firedrake.sql create mode 100644 drizzle/meta/0002_snapshot.json diff --git a/drizzle/0002_neat_firedrake.sql b/drizzle/0002_neat_firedrake.sql new file mode 100644 index 0000000..6b901bc --- /dev/null +++ b/drizzle/0002_neat_firedrake.sql @@ -0,0 +1 @@ +ALTER TABLE `shouts` ADD `reply` text; \ No newline at end of file diff --git a/drizzle/meta/0002_snapshot.json b/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..7f15471 --- /dev/null +++ b/drizzle/meta/0002_snapshot.json @@ -0,0 +1,101 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "7b42bd29-38af-477e-a68a-7e24f641a26a", + "prevId": "98471e1c-ba87-4757-bd0d-89bc45907686", + "tables": { + "shouts": { + "name": "shouts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "serial": { + "name": "serial", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "from_ip": { + "name": "from_ip", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pending": { + "name": "pending", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(CURRENT_TIMESTAMP)" + }, + "reply": { + "name": "reply", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "shouts_bans": { + "name": "shouts_bans", + "columns": { + "ip": { + "name": "ip", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 1a73db9..770d6e2 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -15,6 +15,13 @@ "when": 1722973085867, "tag": "0001_sharp_luckman", "breakpoints": true + }, + { + "idx": 2, + "version": "6", + "when": 1722975470474, + "tag": "0002_neat_firedrake", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/backend/bot/shoutbox.ts b/src/backend/bot/shoutbox.ts index 80a97b6..31ee2d3 100644 --- a/src/backend/bot/shoutbox.ts +++ b/src/backend/bot/shoutbox.ts @@ -3,7 +3,7 @@ import { html } from '@mtcute/node' import parseDuration from 'parse-duration' import { env } from '../env' -import { approveShout, banShouts, declineShout, deleteBySerial, unbanShouts } from '../service/shoutbox' +import { answerBySerial, approveShout, banShouts, declineShout, deleteBySerial, unbanShouts } from '../service/shoutbox' export const ShoutboxAction = new CallbackDataBuilder('shoutbox', 'id', 'action') @@ -12,9 +12,9 @@ const dp = Dispatcher.child() dp.onCallbackQuery(ShoutboxAction.filter({ action: 'approve' }), async (ctx) => { if (ctx.chat.id !== env.TG_CHAT_ID) return - approveShout(ctx.match.id) + const serial = approveShout(ctx.match.id) await ctx.editMessageWith(msg => ({ - text: html`${msg.textWithEntities}

✅ Approved!`, + text: html`${msg.textWithEntities}

✅ Approved! ID: ${serial}`, })) }) @@ -61,4 +61,15 @@ dp.onNewMessage(filters.and(filters.chatId(env.TG_CHAT_ID), filters.command('sho await ctx.answerText('done') }) +dp.onNewMessage(filters.and(filters.chatId(env.TG_CHAT_ID), filters.command('shoutbox_reply')), async (ctx) => { + const serial = Number(ctx.command[1]) + if (Number.isNaN(serial)) { + await ctx.answerText('invalid serial') + return + } + + answerBySerial(serial, ctx.command[2]) + await ctx.answerText('done') +}) + export { dp as shoutboxDp } diff --git a/src/backend/models/shoutbox.ts b/src/backend/models/shoutbox.ts index ee093dd..ad26944 100644 --- a/src/backend/models/shoutbox.ts +++ b/src/backend/models/shoutbox.ts @@ -10,6 +10,7 @@ export const shouts = sqliteTable('shouts', { pending: integer('pending', { mode: 'boolean' }).notNull().default(true), text: text('text'), createdAt: text('created_at').notNull().default(sql`(CURRENT_TIMESTAMP)`), + reply: text('reply'), }) export const shoutsBans = sqliteTable('shouts_bans', { diff --git a/src/backend/service/shoutbox.ts b/src/backend/service/shoutbox.ts index 757fc1b..6b530e9 100644 --- a/src/backend/service/shoutbox.ts +++ b/src/backend/service/shoutbox.ts @@ -26,6 +26,7 @@ const fetchList = db.select({ text: shouts.text, pending: shouts.pending, serial: shouts.serial, + reply: shouts.reply, }).from(shouts) .where(filter) .limit(SHOUTS_PER_PAGE) @@ -58,6 +59,8 @@ export function approveShout(id: string) { .set({ pending: false, serial: nextSerial }) .where(eq(shouts.id, id)) .run() + + return nextSerial } export function declineShout(id: string) { @@ -80,6 +83,13 @@ export function deleteBySerial(serial: number) { .run({ serial }) } +export function answerBySerial(serial: number, reply: string) { + db.update(shouts) + .set({ reply }) + .where(eq(shouts.serial, serial)) + .execute() +} + export function banShouts(ip: string, expires: number) { db.insert(shoutsBans) .values({ diff --git a/src/components/pages/PageMain/Shoutbox/Shoutbox.module.css b/src/components/pages/PageMain/Shoutbox/Shoutbox.module.css index 6ea4f3c..f4a96b2 100644 --- a/src/components/pages/PageMain/Shoutbox/Shoutbox.module.css +++ b/src/components/pages/PageMain/Shoutbox/Shoutbox.module.css @@ -69,3 +69,7 @@ .paginationLink { color: var(--text-secondary); } + +.reply { + margin-top: 6px; +} \ No newline at end of file diff --git a/src/components/pages/PageMain/Shoutbox/Shoutbox.tsx b/src/components/pages/PageMain/Shoutbox/Shoutbox.tsx index 3a11cbe..57930d6 100644 --- a/src/components/pages/PageMain/Shoutbox/Shoutbox.tsx +++ b/src/components/pages/PageMain/Shoutbox/Shoutbox.tsx @@ -73,6 +73,12 @@ function ShoutboxInner(props: {
{props.text} + {props.reply && ( +
+ reply: + {props.reply} +
+ )}
)