Files
new_lottery_bot/src/handlers/message_management.py
Trevor1985 f3f8d0f737
All checks were successful
continuous-integration/drone/push Build is passing
Fix text dialog routing and club card registration recovery
2026-09-13 21:39:40 +09:00

181 lines
8.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Хэндлеры для управления сообщениями администратором
"""
from src.utils.errors import public_error
from src.core.access import is_admin
import logging
from aiogram import Router, F, Bot
from aiogram.types import Message, CallbackQuery
from aiogram.filters import StateFilter
from src.filters.case_insensitive import CaseInsensitiveCommand as Command
from src.filters.case_insensitive import CaseInsensitiveCommand
from ..core.config import ADMIN_IDS
from ..core.database import async_session_maker
from ..core.chat_services import ChatMessageService
from ..core.services import UserService
logger = logging.getLogger(__name__)
message_admin_router = Router(name="message_admin")
@message_admin_router.message(CaseInsensitiveCommand("delete"))
async def delete_replied_message(message: Message):
"""
Удаление сообщения по команде /delete (регистронезависимо)
Работает только если команда является ответом на сообщение бота
"""
if not await is_admin(message.from_user.id):
await message.answer("❌ Недостаточно прав")
return
if not message.reply_to_message:
await message.answer("⚠️ Ответьте на сообщение бота командой /delete чтобы удалить его")
return
if message.reply_to_message.from_user.id != message.bot.id:
await message.answer("⚠️ Можно удалять только сообщения бота")
return
try:
# Удаляем сообщение бота
await message.reply_to_message.delete()
# Удаляем команду
await message.delete()
logger.info(f"Администратор {message.from_user.id} удалил сообщение {message.reply_to_message.message_id}")
except Exception as e:
logger.error(f"Ошибка при удалении сообщения: {e}")
await message.answer(f"Не удалось удалить сообщение: {public_error(e)}")
@message_admin_router.callback_query(F.data == "delete_message")
async def delete_message_callback(callback: CallbackQuery):
"""
Удаление сообщения по нажатию кнопки
"""
if not await is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
try:
await callback.message.delete()
await callback.answer("✅ Сообщение удалено")
logger.info(f"Администратор {callback.from_user.id} удалил сообщение через кнопку")
except Exception as e:
logger.error(f"Ошибка при удалении сообщения через кнопку: {e}")
await callback.answer(f"❌ Ошибка: {public_error(e)}", show_alert=True)
# Функция-фильтр для проверки триггерных слов
async def is_delete_trigger(message: Message) -> bool:
"""Проверяет, является ли сообщение триггером для удаления"""
if not message.text:
return False
text_lower = message.text.lower().strip()
triggers = ["удалить", "delete", "del", "🗑️", "🗑", ""]
return text_lower in triggers and await is_admin(message.from_user.id)
@message_admin_router.message(F.reply_to_message, is_delete_trigger, StateFilter(None, "ChatStates:in_chat", "P2PChatStates:chatting"))
async def quick_delete_replied_message(message: Message):
"""
Быстрое удаление сообщения по reply с триггерными словами или emoji
Работает для админов при ответе на любое сообщение
Триггеры:
- "удалить", "delete", "del"
- 🗑️ (мусорная корзина)
- ❌ (крестик)
Удаляет сообщение у всех получателей broadcast рассылки
"""
if not await is_admin(message.from_user.id):
return # Не админ - пропускаем
try:
replied_msg = message.reply_to_message
deleted_count = 0
# Пытаемся найти сообщение в БД по telegram_message_id
async with async_session_maker() as session:
# Получаем admin user для deleted_by
admin_user = await UserService.get_user_by_telegram_id(
session,
message.from_user.id
)
if not admin_user:
logger.error(f"Админ {message.from_user.id} не найден в БД")
await message.answer("❌ Ошибка: пользователь не найден")
return
chat_message = await ChatMessageService.get_message_by_telegram_id(
session,
telegram_message_id=replied_msg.message_id,
chat_id=message.chat.id,
)
# Если нашли broadcast сообщение - удаляем у всех получателей
if chat_message and chat_message.forwarded_message_ids:
bot = message.bot
for user_telegram_id, forwarded_msg_id in chat_message.forwarded_message_ids.items():
try:
await bot.delete_message(
chat_id=int(user_telegram_id),
message_id=forwarded_msg_id
)
deleted_count += 1
except Exception as e:
logger.warning(f"Не удалось удалить сообщение у {user_telegram_id}: {e}")
# Помечаем как удалённое в БД (используем admin_user.id, а не telegram_id)
await ChatMessageService.delete_message(
session,
message_id=chat_message.id,
deleted_by=admin_user.id
)
logger.info(
f"Администратор {message.from_user.id} удалил broadcast сообщение "
f"{replied_msg.message_id} у {deleted_count} получателей"
)
# Удаляем исходное сообщение (на которое ответили)
try:
await replied_msg.delete()
except Exception as e:
logger.warning(f"Не удалось удалить исходное сообщение: {e}")
# Удаляем команду админа
try:
await message.delete()
except Exception as e:
logger.warning(f"Не удалось удалить команду админа: {e}")
# Если было broadcast удаление - показываем статистику
if deleted_count > 0:
try:
status_msg = await message.answer(
f"✅ Сообщение удалено у {deleted_count} получателей",
reply_to_message_id=None
)
# Удаляем статус через 3 секунды
import asyncio
await asyncio.sleep(3)
await status_msg.delete()
except Exception as e:
logger.warning(f"Не удалось показать/удалить статус: {e}")
except Exception as e:
logger.error(f"Ошибка при быстром удалении сообщения: {e}")
try:
# Пытаемся удалить хотя бы команду админа
await message.delete()
except:
pass