Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
27 changes: 17 additions & 10 deletions src/commands/report.ts
Original file line number Diff line number Diff line change
@@ -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<Context, "message"> | 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<Role>().createCommand({
trigger: ["report", "admin"],
description: "Report a message to admins",
Expand All @@ -15,15 +31,6 @@ export const report = new CommandsCollection<Role>().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)
},
})
34 changes: 34 additions & 0 deletions src/middlewares/mention-listener.ts
Original file line number Diff line number Diff line change
@@ -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<C extends Context> = Filter<C, "message:entities:mention">
export class MentionListener<C extends Context> implements MiddlewareObj<C> {
private composer = new Composer<C>()

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<C>) {
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)
}
}
Loading