feat: shoutbox replies
This commit is contained in:
parent
cafaf94e83
commit
421203ca3a
8 changed files with 144 additions and 3 deletions
1
drizzle/0002_neat_firedrake.sql
Normal file
1
drizzle/0002_neat_firedrake.sql
Normal file
|
@ -0,0 +1 @@
|
|||
ALTER TABLE `shouts` ADD `reply` text;
|
101
drizzle/meta/0002_snapshot.json
Normal file
101
drizzle/meta/0002_snapshot.json
Normal 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": {}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,13 @@
|
|||
"when": 1722973085867,
|
||||
"tag": "0001_sharp_luckman",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "6",
|
||||
"when": 1722975470474,
|
||||
"tag": "0002_neat_firedrake",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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 }
|
||||
|
|
|
@ -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', {
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -69,3 +69,7 @@
|
|||
.paginationLink {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.reply {
|
||||
margin-top: 6px;
|
||||
}
|
|
@ -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>
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue