diff --git a/TODO.md b/TODO.md index b2f4b01..997f9dc 100644 --- a/TODO.md +++ b/TODO.md @@ -17,7 +17,7 @@ - [x] advanced moderation - [x] ban_all - [x] unban_all - - [x] /report to allow user to report (@admin is not implemented) + - [x] /report to allow user to report - [x] track ban, mute and kick done via telegram UI (not by command) - [ ] controlled moderation flow (see #42) - [x] audit log (implemented, need to audit every mod action) diff --git a/src/bot.ts b/src/bot.ts index 0527610..5371c9c 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -13,6 +13,7 @@ import { AutoModerationStack } from "./middlewares/auto-moderation-stack" import { BotMembershipHandler } from "./middlewares/bot-membership-handler" import { checkUsername } from "./middlewares/check-username" import { GroupSpecificActions } from "./middlewares/group-specific-actions" +import { MentionListener } from "./middlewares/mention-listener" import { messageLink } from "./middlewares/message-link" import { MessageUserStorage } from "./middlewares/message-user-storage" import { modules, sharedDataInit } from "./modules" @@ -79,6 +80,7 @@ bot.use(new BotMembershipHandler()) bot.use(new AutoModerationStack()) bot.use(new GroupSpecificActions()) bot.use(Moderation) +bot.use(new MentionListener()) bot.on("message", async (ctx, next) => { const { username, id } = ctx.message.from diff --git a/src/commands/report.ts b/src/commands/report.ts index 6c123dc..35c6af3 100644 --- a/src/commands/report.ts +++ b/src/commands/report.ts @@ -1,9 +1,25 @@ +import type { Context, Filter } from "grammy" +import type { Message } from "grammy/types" +import type { CommandScopedContext } from "@/lib/managed-commands" import { CommandsCollection } from "@/lib/managed-commands" import { logger } from "@/logger" import { modules } from "@/modules" import { fmt } from "@/utils/format" import type { Role } from "@/utils/types" +export const logReport = async (context: Filter | CommandScopedContext, repliedTo: Message) => { + const reportSent = await modules.get("tgLogger").report(repliedTo, context.from) + await context.reply( + reportSent + ? fmt(({ b, n }) => [b`✅ Message reported!`, n`Moderators have been notified.`], { sep: "\n" }) + : fmt(({ b, n }) => [b`⚠️ Report not sent`, n`Please try again in a moment.`], { sep: "\n" }), + { + disable_notification: false, + reply_parameters: { message_id: repliedTo.message_id }, + } + ) +} + export const report = new CommandsCollection().createCommand({ trigger: ["report", "admin"], description: "Report a message to admins", @@ -15,15 +31,6 @@ export const report = new CommandsCollection().createCommand({ return } - const reportSent = await modules.get("tgLogger").report(repliedTo, context.from) - await context.reply( - reportSent - ? fmt(({ b, n }) => [b`✅ Message reported!`, n`Moderators have been notified.`], { sep: "\n" }) - : fmt(({ b, n }) => [b`⚠️ Report not sent`, n`Please try again in a moment.`], { sep: "\n" }), - { - disable_notification: false, - reply_parameters: { message_id: repliedTo.message_id }, - } - ) + await logReport(context, repliedTo) }, }) diff --git a/src/middlewares/mention-listener.ts b/src/middlewares/mention-listener.ts new file mode 100644 index 0000000..6607e1c --- /dev/null +++ b/src/middlewares/mention-listener.ts @@ -0,0 +1,34 @@ +import { Composer, type Filter, type MiddlewareObj } from "grammy" +import { logReport } from "@/commands/report" +import { logger } from "@/logger" +import type { Context } from "@/utils/types" + +type MentionContext = Filter +export class MentionListener implements MiddlewareObj { + private composer = new Composer() + + constructor() { + this.composer + .on("message:entities:mention") + .fork() + .filter( + (ctx) => ctx.entities("mention").some((m) => m.text === "@admin"), + (ctx) => this.handleReport(ctx) + ) + } + + middleware() { + return this.composer.middleware() + } + + private async handleReport(ctx: MentionContext) { + await ctx.deleteMessage() + const repliedTo = ctx.message.reply_to_message + if (!repliedTo?.from) { + logger.error("report: no repliedTo or repliedTo.from field (the msg was sent in a channel)") + return + } + + await logReport(ctx, repliedTo) + } +}