From 16b67e9e4874d43556bb35ebfb01a27b950864e4 Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 10 Mar 2026 13:46:45 +0100 Subject: [PATCH 1/2] feat: add in-chat logs for mod-actions and report --- src/commands/ban.ts | 24 +++++++++++++--------- src/commands/del.ts | 9 +++++---- src/commands/kick.ts | 8 +++++--- src/commands/mute.ts | 24 +++++++++++++--------- src/commands/report.ts | 18 ++++++++++++++++- src/modules/moderation/index.ts | 1 - src/modules/tg-logger/index.ts | 35 +++++++++++++++++++++++++++++++++ src/utils/format.ts | 9 +++++++-- 8 files changed, 99 insertions(+), 29 deletions(-) diff --git a/src/commands/ban.ts b/src/commands/ban.ts index d51fe40..db4831e 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -27,9 +27,11 @@ _commandsBase } const res = await Moderation.ban(repliedTo.from, context.chat, context.from, null, [repliedTo], args.reason) - const msg = await context.reply(res.isErr() ? res.error.fmtError : "OK") - await wait(5000) - await msg.delete() + if (res.isErr()) { + const msg = await context.reply(res.error.fmtError) + await wait(5000) + await msg.delete() + } }, }) .createCommand({ @@ -65,9 +67,11 @@ _commandsBase [repliedTo], args.reason ) - const msg = await context.reply(res.isErr() ? res.error.fmtError : "OK") - await wait(5000) - await msg.delete() + if (res.isErr()) { + const msg = await context.reply(res.error.fmtError) + await wait(5000) + await msg.delete() + } }, }) .createCommand({ @@ -102,8 +106,10 @@ _commandsBase } const res = await Moderation.unban(user, context.chat, context.from) - const msg = await context.reply(res.isErr() ? res.error.fmtError : "OK") - await wait(5000) - await msg.delete() + if (res.isErr()) { + const msg = await context.reply(res.error.fmtError) + await wait(5000) + await msg.delete() + } }, }) diff --git a/src/commands/del.ts b/src/commands/del.ts index fb73bda..ccc1515 100644 --- a/src/commands/del.ts +++ b/src/commands/del.ts @@ -24,9 +24,10 @@ _commandsBase.createCommand({ }) const res = await Moderation.deleteMessages([repliedTo], context.from, "Command /del") - // TODO: better error and ok response - const msg = await context.reply(res.isErr() ? "Cannot delete the message" : "OK") - await wait(5000) - await msg.delete() + if (res.isErr()) { + const msg = await context.reply("Cannot delete the message") + await wait(5000) + await msg.delete() + } }, }) diff --git a/src/commands/kick.ts b/src/commands/kick.ts index 66b3689..2a98759 100644 --- a/src/commands/kick.ts +++ b/src/commands/kick.ts @@ -22,8 +22,10 @@ _commandsBase.createCommand({ } const res = await Moderation.kick(repliedTo.from, context.chat, context.from, [repliedTo], args.reason) - const msg = await context.reply(res.isErr() ? res.error.fmtError : "OK") - await wait(5000) - await msg.delete() + if (res.isErr()) { + const msg = await context.reply(res.error.fmtError) + await wait(5000) + await msg.delete() + } }, }) diff --git a/src/commands/mute.ts b/src/commands/mute.ts index 09db062..db8cf16 100644 --- a/src/commands/mute.ts +++ b/src/commands/mute.ts @@ -42,9 +42,11 @@ _commandsBase [repliedTo], args.reason ) - const msg = await context.reply(res.isErr() ? res.error.fmtError : "OK") - await wait(5000) - await msg.delete() + if (res.isErr()) { + const msg = await context.reply(res.error.fmtError) + await wait(5000) + await msg.delete() + } }, }) .createCommand({ @@ -65,9 +67,11 @@ _commandsBase } const res = await Moderation.mute(repliedTo.from, context.chat, context.from, null, [repliedTo], args.reason) - const msg = await context.reply(res.isErr() ? res.error.fmtError : "OK") - await wait(5000) - await msg.delete() + if (res.isErr()) { + const msg = await context.reply(res.error.fmtError) + await wait(5000) + await msg.delete() + } }, }) .createCommand({ @@ -101,8 +105,10 @@ _commandsBase } const res = await Moderation.unmute(user, context.chat, context.from) - const msg = await context.reply(res.isErr() ? res.error.fmtError : "OK") - await wait(5000) - await msg.delete() + if (res.isErr()) { + const msg = await context.reply(res.error.fmtError) + await wait(5000) + await msg.delete() + } }, }) diff --git a/src/commands/report.ts b/src/commands/report.ts index 7df8a9d..e29c71d 100644 --- a/src/commands/report.ts +++ b/src/commands/report.ts @@ -1,5 +1,6 @@ import { logger } from "@/logger" import { modules } from "@/modules" +import { fmt, fmtUser } from "@/utils/format" import { _commandsBase } from "./_base" _commandsBase.createCommand({ @@ -14,6 +15,21 @@ _commandsBase.createCommand({ return } - await modules.get("tgLogger").report(repliedTo, context.from) + const reportSent = await modules.get("tgLogger").report(repliedTo, context.from) + await context.reply( + reportSent + ? fmt( + ({ b, n }) => [ + b`✅ Message reported!`, + n`Thanks ${fmtUser(context.from, false)}, 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 }, + } + ) }, }) diff --git a/src/modules/moderation/index.ts b/src/modules/moderation/index.ts index a2819bd..00c347c 100644 --- a/src/modules/moderation/index.ts +++ b/src/modules/moderation/index.ts @@ -45,7 +45,6 @@ const MAP_ACTIONS: Record< MUTE_ALL: "mute_all", } -// TODO: missing in-channel user feedback (eg. has been muted by ...) class ModerationClass implements MiddlewareObj { private composer = new Composer() private static instance: ModerationClass | null = null diff --git a/src/modules/tg-logger/index.ts b/src/modules/tg-logger/index.ts index 1d72c3e..ae5a4f1 100644 --- a/src/modules/tg-logger/index.ts +++ b/src/modules/tg-logger/index.ts @@ -278,6 +278,7 @@ export class TgLogger extends Module { ? new InlineKeyboard().url("See Deleted Message", props.preDeleteRes.link) : undefined await this.log(isAutoModeration ? this.topics.autoModeration : this.topics.adminActions, mainMsg, { reply_markup }) + if (!isAutoModeration) await this.logModActionInChat(props) return mainMsg } @@ -498,4 +499,38 @@ export class TgLogger extends Module { await this.log(this.topics.exceptions, msg) return msg } + + private async logModActionInChat(p: ModerationAction): Promise { + if ( + p.action !== "BAN" && + p.action !== "KICK" && + p.action !== "MUTE" && + p.action !== "UNBAN" && + p.action !== "UNMUTE" + ) + return + + const msg = fmt( + ({ b, n, skip }) => [ + skip`${MOD_ACTION_TITLE(p)}`, + n`${b`Target:`} ${fmtUser(p.target, false)}`, + n`${b`Moderator:`} ${fmtUser(p.from, false)}`, + "duration" in p && p.duration ? n`${b`Duration:`} ${p.duration.raw} (until ${p.duration.dateStr})` : undefined, + "reason" in p && p.reason ? n`${b`Reason:`} ${p.reason}` : undefined, + ], + { sep: "\n" } + ) + + await this.shared.api + .sendMessage(p.chat.id, msg, { + disable_notification: false, + link_preview_options: { is_disabled: true }, + }) + .catch((error: unknown) => { + logger.warn( + { error, action: p.action }, + "[Moderation:logActionInChat] Failed to post moderation action in chat" + ) + }) + } } diff --git a/src/utils/format.ts b/src/utils/format.ts index fd0ce17..dc4fe01 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -138,9 +138,14 @@ export function fmt(cb: (formatters: Formatters) => string | (string | undefined ) } -export function fmtUser(user: Partial> & { id: number }): string { +export function fmtUser( + user: Partial> & { id: number }, + showId: boolean = true +): string { const fullname = user.last_name ? `${user.first_name} ${user.last_name}` : user.first_name - return formatters.n`${formatters.link(fullname ?? "[no-name]", `tg://user?id=${user.id}`)} [${formatters.code`${user.id}`}]` + return showId + ? formatters.n`${formatters.link(fullname ?? "[no-name]", `tg://user?id=${user.id}`)} [${formatters.code`${user.id}`}]` + : formatters.n`${formatters.link(fullname ?? "[no-name]", `tg://user?id=${user.id}`)}` } export function fmtChat(chat: Chat, inviteLink?: string): string { From 044b4cde23ad9f508640d7cfa1fd01769793b6d3 Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Sun, 15 Mar 2026 14:05:16 +0100 Subject: [PATCH 2/2] fix: remove reporter mention in feedback --- src/commands/report.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/commands/report.ts b/src/commands/report.ts index e29c71d..9ebb0cb 100644 --- a/src/commands/report.ts +++ b/src/commands/report.ts @@ -1,6 +1,6 @@ import { logger } from "@/logger" import { modules } from "@/modules" -import { fmt, fmtUser } from "@/utils/format" +import { fmt } from "@/utils/format" import { _commandsBase } from "./_base" _commandsBase.createCommand({ @@ -18,13 +18,7 @@ _commandsBase.createCommand({ const reportSent = await modules.get("tgLogger").report(repliedTo, context.from) await context.reply( reportSent - ? fmt( - ({ b, n }) => [ - b`✅ Message reported!`, - n`Thanks ${fmtUser(context.from, false)}, moderators have been notified.`, - ], - { sep: "\n" } - ) + ? 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,