|
5 | 5 |
|
6 | 6 | from telegram import Update |
7 | 7 | from telegram.constants import ChatMemberStatus |
8 | | -from telegram.ext import CommandHandler, ContextTypes |
| 8 | +from telegram.ext import CommandHandler, ContextTypes, MessageHandler, filters |
9 | 9 |
|
10 | 10 | from ..services.moderation import ModerationService |
11 | 11 |
|
@@ -35,6 +35,12 @@ def create_moderation_handlers(moderation_service: ModerationService) -> list: |
35 | 35 | CommandHandler("unmute", _handle_unmute), |
36 | 36 | CommandHandler("report", _handle_report), |
37 | 37 | CommandHandler("forcegroupregistration", _handle_force_group_registration), |
| 38 | + MessageHandler( |
| 39 | + (filters.TEXT | filters.CAPTION) |
| 40 | + & filters.ChatType.GROUPS |
| 41 | + & filters.Regex(r"(?i)@admin"), |
| 42 | + _handle_admin_mention, |
| 43 | + ), |
38 | 44 | ] |
39 | 45 |
|
40 | 46 |
|
@@ -360,6 +366,89 @@ async def _handle_report(update: Update, context: ContextTypes.DEFAULT_TYPE) -> |
360 | 366 | ) |
361 | 367 |
|
362 | 368 |
|
| 369 | +async def _handle_admin_mention( |
| 370 | + update: Update, context: ContextTypes.DEFAULT_TYPE |
| 371 | +) -> None: |
| 372 | + """Handle @admin mention: notify admins of intervention request (no reply needed).""" |
| 373 | + message = update.message |
| 374 | + if message is None or message.from_user is None: |
| 375 | + return |
| 376 | + |
| 377 | + chat = update.effective_chat |
| 378 | + if chat is None or chat.type == "private": |
| 379 | + return |
| 380 | + |
| 381 | + text = message.text or message.caption or "" |
| 382 | + reason = _extract_reason_after_admin(text) |
| 383 | + |
| 384 | + await _notify_admins_of_admin_request( |
| 385 | + context=context, |
| 386 | + chat=chat, |
| 387 | + reporter=message.from_user, |
| 388 | + message_id=message.message_id, |
| 389 | + reason=reason, |
| 390 | + ) |
| 391 | + |
| 392 | + await message.reply_text( |
| 393 | + "Richiesta inviata. Gli amministratori interverranno." |
| 394 | + ) |
| 395 | + logger.info( |
| 396 | + "Admin request: %s in chat %s", |
| 397 | + message.from_user.id, |
| 398 | + chat.id, |
| 399 | + ) |
| 400 | + |
| 401 | + |
| 402 | +def _extract_reason_after_admin(text: str) -> str | None: |
| 403 | + """Extract optional reason from text after @admin.""" |
| 404 | + match = re.search(r"@admin\s*(.+)?", text, re.IGNORECASE | re.DOTALL) |
| 405 | + if match and match.group(1): |
| 406 | + return match.group(1).strip() or None |
| 407 | + return None |
| 408 | + |
| 409 | + |
| 410 | +async def _notify_admins_of_admin_request( |
| 411 | + context: ContextTypes.DEFAULT_TYPE, |
| 412 | + chat, |
| 413 | + reporter, |
| 414 | + message_id: int, |
| 415 | + reason: str | None, |
| 416 | +) -> None: |
| 417 | + """Send admin intervention request notification to all chat admins via private message.""" |
| 418 | + try: |
| 419 | + admins = await context.bot.get_chat_administrators(chat.id) |
| 420 | + except Exception as e: |
| 421 | + logger.warning("Failed to get admins for @admin notification: %s", e) |
| 422 | + return |
| 423 | + |
| 424 | + chat_title = chat.title or "Chat" |
| 425 | + reporter_name = _get_user_display_name(reporter) |
| 426 | + message_link = _build_message_link(chat, message_id) |
| 427 | + |
| 428 | + report_text = f"<b>{chat_title}:</b>\n" |
| 429 | + report_text += f'Richiesta intervento da: <a href="tg://user?id={reporter.id}">{reporter_name}</a> ({reporter.id})\n' |
| 430 | + if message_link: |
| 431 | + report_text += f'Link: <a href="{message_link}">qui</a>\n' |
| 432 | + if reason: |
| 433 | + report_text += f"Messaggio: {reason}" |
| 434 | + |
| 435 | + for admin in admins: |
| 436 | + if admin.user.is_bot: |
| 437 | + continue |
| 438 | + try: |
| 439 | + await context.bot.send_message( |
| 440 | + chat_id=admin.user.id, |
| 441 | + text=report_text, |
| 442 | + parse_mode="HTML", |
| 443 | + ) |
| 444 | + except Exception as e: |
| 445 | + logger.debug( |
| 446 | + "Could not send @admin request to admin %s: %s", |
| 447 | + admin.user.id, |
| 448 | + e, |
| 449 | + ) |
| 450 | + |
| 451 | + |
363 | 452 | def _get_user_display_name(user) -> str: |
364 | 453 | """Get display name for a user (full name or username).""" |
365 | 454 | if user.first_name: |
|
0 commit comments