feat: shoutbox replies

This commit is contained in:
alina 🌸 2024-08-06 23:23:12 +03:00
parent cafaf94e83
commit 421203ca3a
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
8 changed files with 144 additions and 3 deletions

View file

@ -0,0 +1 @@
ALTER TABLE `shouts` ADD `reply` text;

View file

@ -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": {}
}
}

View file

@ -15,6 +15,13 @@
"when": 1722973085867,
"tag": "0001_sharp_luckman",
"breakpoints": true
},
{
"idx": 2,
"version": "6",
"when": 1722975470474,
"tag": "0002_neat_firedrake",
"breakpoints": true
}
]
}

View file

@ -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}<br><br>✅ Approved!`,
text: html`${msg.textWithEntities}<br><br>✅ Approved! ID: <code>${serial}</code>`,
}))
})
@ -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 }

View file

@ -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', {

View file

@ -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({

View file

@ -69,3 +69,7 @@
.paginationLink {
color: var(--text-secondary);
}
.reply {
margin-top: 6px;
}

View file

@ -73,6 +73,12 @@ function ShoutboxInner(props: {
</div>
<div class={css.text}>
{props.text}
{props.reply && (
<div class={css.reply}>
<b>reply: </b>
{props.reply}
</div>
)}
</div>
</div>
)