Skip to content

Commit e32d485

Browse files
committed
feat: Add report command with cancel functionality
- Implemented a /report command to allow users to report messages to admins. - Reports are sent privately to admins without tagging them in the chat. - Introduced a 5-minute cancellation window, allowing users to cancel their reports before they are sent to admins. - Added a cancel command to cancel any pending report. - Pending reports are stored in a static map with the user ID as the key and the timeout ID as the value. - Added proper error handling for invalid report attempts (e.g., when no message is being replied to). - Notifications are sent to the user after their report is received.
1 parent 17dfa6b commit e32d485

1 file changed

Lines changed: 80 additions & 0 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { Context } from 'grammy';
2+
import { BotReply } from '../../../utils/chat/BotReply';
3+
4+
export class ReportCommand {
5+
// Store pending reports with user IDs as keys
6+
private static pendingReports = new Map<number, number>();
7+
8+
/**
9+
* Sends a report to the group admins without publicly tagging them.
10+
* If the report is canceled before notification, the message will be deleted.
11+
*
12+
* @param ctx - The context object containing information about the incoming update.
13+
*/
14+
static async report(ctx: Context) {
15+
const reply = new BotReply(ctx);
16+
17+
// Ensure the report command is a reply to another message
18+
const reportedMessage = ctx.message?.reply_to_message;
19+
if (!reportedMessage || !reportedMessage.from) {
20+
await ctx.reply('Please use the /report command by replying to the message you want to report.');
21+
return;
22+
}
23+
24+
// Get reported user details
25+
const reportedUser = reportedMessage.from;
26+
const reportedUserName = reportedUser.first_name;
27+
const reportedUserId = reportedUser.id;
28+
const chatId = Math.abs(ctx.chat!.id + 1000000000000);
29+
const messageLink = `https://t.me/c/${chatId}/${reportedMessage.message_id}`;
30+
// Collect secret mentions of all admins
31+
const admins = await ctx.api.getChatAdministrators(ctx.chat!.id);
32+
const adminMentions = admins
33+
.filter((admin) => !admin.user.is_bot)
34+
.map((admin) => `<a href="tg://user?id=${admin.user.id}"> </a>`)
35+
.join('');
36+
37+
// Format the report message for admins
38+
const reportMessage = `
39+
<b>🚨 Report Notification</b>
40+
- Reported User: ${reportedUserName} (ID: ${reportedUserId})
41+
- Reported by: @${ctx.from?.username || ctx.from?.first_name} (ID: ${ctx.from?.id})
42+
- Message Link: <a href="${messageLink}">Click here to view the message</a>
43+
`.trim();
44+
const finalMessage = `${reportMessage}`;
45+
// Notify the reporter
46+
await reply.replyToMessage(`${reportedUserName}${adminMentions}(${reportedUserId}) was reported to administrators.`);
47+
48+
// Send the report to admins only after a delay, allowing time for cancellation
49+
const delayForCancel = 300000; // 5 minutes (300,000 milliseconds) delay for cancellation
50+
const reportTimeout = setTimeout(async () => {
51+
for (const admin of admins) {
52+
if (admin.user.is_bot) continue;
53+
await ctx.api.sendMessage(admin.user.id, finalMessage, { parse_mode: 'HTML' });
54+
}
55+
// Remove the report from pendingReports after sending
56+
ReportCommand.pendingReports.delete(ctx.from!.id);
57+
}, delayForCancel);
58+
59+
// Store the report with user ID and the setTimeout ID for canceling
60+
ReportCommand.pendingReports.set(ctx.from!.id, Number(reportTimeout));
61+
}
62+
63+
/**
64+
* Cancels the pending report.
65+
* If the report is canceled, it will not be sent to the admins.
66+
*
67+
* @param ctx - The context object containing information about the incoming update.
68+
*/
69+
static async cancel(ctx: Context) {
70+
const reply = new BotReply(ctx);
71+
const reportTimeout = ReportCommand.pendingReports.get(ctx.from!.id);
72+
if (reportTimeout) {
73+
clearTimeout(reportTimeout);
74+
ReportCommand.pendingReports.delete(ctx.from!.id);
75+
await reply.textReply('Your report has been canceled and will not be sent to the admins.');
76+
} else {
77+
await reply.textReply('There is no report to cancel.');
78+
}
79+
}
80+
}

0 commit comments

Comments
 (0)