395 lines
17 KiB
Python
395 lines
17 KiB
Python
import html
|
||
import time
|
||
import traceback
|
||
from datetime import datetime
|
||
|
||
from aiogram import F, Router
|
||
from aiogram.filters import MagicData
|
||
from aiogram.fsm.context import FSMContext
|
||
from aiogram.types import CallbackQuery
|
||
|
||
from helper_bot.handlers.admin.utils import format_user_info
|
||
from helper_bot.handlers.voice.constants import CALLBACK_DELETE, CALLBACK_SAVE
|
||
from helper_bot.handlers.voice.services import AudioFileService
|
||
from helper_bot.keyboards.keyboards import (
|
||
create_keyboard_for_ban_reason,
|
||
create_keyboard_with_pagination,
|
||
get_reply_keyboard_admin,
|
||
)
|
||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
||
from helper_bot.utils.helper_func import get_banned_users_buttons, get_banned_users_list
|
||
|
||
# Local imports - metrics
|
||
from helper_bot.utils.metrics import (
|
||
db_query_time,
|
||
track_errors,
|
||
track_file_operations,
|
||
track_time,
|
||
)
|
||
from logs.custom_logger import logger
|
||
|
||
from .constants import (
|
||
CALLBACK_BAN,
|
||
CALLBACK_DECLINE,
|
||
CALLBACK_PAGE,
|
||
CALLBACK_PUBLISH,
|
||
CALLBACK_RETURN,
|
||
CALLBACK_UNLOCK,
|
||
ERROR_BOT_BLOCKED,
|
||
MESSAGE_DECLINED,
|
||
MESSAGE_ERROR,
|
||
MESSAGE_PUBLISHED,
|
||
MESSAGE_USER_BANNED,
|
||
MESSAGE_USER_UNLOCKED,
|
||
)
|
||
from .dependency_factory import get_ban_service, get_post_publish_service
|
||
from .exceptions import (
|
||
BanError,
|
||
PostNotFoundError,
|
||
PublishError,
|
||
UserBlockedBotError,
|
||
UserNotFoundError,
|
||
)
|
||
|
||
callback_router = Router()
|
||
|
||
|
||
@callback_router.callback_query(F.data == CALLBACK_PUBLISH)
|
||
@track_time("post_for_group", "callback_handlers")
|
||
@track_errors("callback_handlers", "post_for_group")
|
||
async def post_for_group(call: CallbackQuery, settings: MagicData("settings")):
|
||
publish_service = get_post_publish_service()
|
||
# TODO: переделать на MagicData
|
||
logger.info(
|
||
f"Получен callback-запрос с действием: {call.data} от пользователя {call.from_user.full_name} (ID сообщения: {call.message.message_id})"
|
||
)
|
||
|
||
try:
|
||
await publish_service.publish_post(call)
|
||
await call.answer(text=MESSAGE_PUBLISHED, cache_time=3)
|
||
except UserBlockedBotError:
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
except (PostNotFoundError, PublishError) as e:
|
||
logger.error(f"Ошибка при публикации поста: {str(e)}")
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
except Exception as e:
|
||
if str(e) == ERROR_BOT_BLOCKED:
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
else:
|
||
important_logs = settings["Telegram"]["important_logs"]
|
||
await call.bot.send_message(
|
||
chat_id=important_logs,
|
||
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}",
|
||
)
|
||
logger.error(f"Неожиданная ошибка при публикации поста: {str(e)}")
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
|
||
|
||
@callback_router.callback_query(F.data == CALLBACK_DECLINE)
|
||
@track_time("decline_post_for_group", "callback_handlers")
|
||
@track_errors("callback_handlers", "decline_post_for_group")
|
||
async def decline_post_for_group(call: CallbackQuery, settings: MagicData("settings")):
|
||
publish_service = get_post_publish_service()
|
||
# TODO: переделать на MagicData
|
||
logger.info(
|
||
f"Получен callback-запрос с данными: {call.data} от пользователя {call.from_user.full_name} (ID: {call.from_user.id})"
|
||
)
|
||
try:
|
||
await publish_service.decline_post(call)
|
||
await call.answer(text=MESSAGE_DECLINED, cache_time=3)
|
||
except UserBlockedBotError:
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
except (PostNotFoundError, PublishError) as e:
|
||
logger.error(f"Ошибка при отклонении поста: {str(e)}")
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
except Exception as e:
|
||
if str(e) == ERROR_BOT_BLOCKED:
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
else:
|
||
important_logs = settings["Telegram"]["important_logs"]
|
||
await call.bot.send_message(
|
||
chat_id=important_logs,
|
||
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}",
|
||
)
|
||
logger.error(f"Неожиданная ошибка при отклонении поста: {str(e)}")
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
|
||
|
||
@callback_router.callback_query(F.data == CALLBACK_BAN)
|
||
@track_time("ban_user_from_post", "callback_handlers")
|
||
@track_errors("callback_handlers", "ban_user_from_post")
|
||
async def ban_user_from_post(call: CallbackQuery, **kwargs):
|
||
ban_service = get_ban_service()
|
||
# TODO: переделать на MagicData
|
||
try:
|
||
await ban_service.ban_user_from_post(call)
|
||
await call.answer(text=MESSAGE_USER_BANNED, cache_time=3)
|
||
except UserBlockedBotError:
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
except (UserNotFoundError, BanError) as e:
|
||
logger.error(f"Ошибка при блокировке пользователя: {str(e)}")
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
except Exception as e:
|
||
if str(e) == ERROR_BOT_BLOCKED:
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
else:
|
||
logger.error(f"Неожиданная ошибка при блокировке пользователя: {str(e)}")
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
|
||
|
||
@callback_router.callback_query(F.data.contains(CALLBACK_BAN))
|
||
@track_time("process_ban_user", "callback_handlers")
|
||
@track_errors("callback_handlers", "process_ban_user")
|
||
async def process_ban_user(
|
||
call: CallbackQuery, state: FSMContext, bot_db: MagicData("bot_db"), **kwargs
|
||
):
|
||
ban_service = get_ban_service()
|
||
# TODO: переделать на MagicData
|
||
user_id = call.data[4:]
|
||
logger.info(
|
||
f"Вызов функции process_ban_user. Данные callback: {call.data} пользователь: {user_id}"
|
||
)
|
||
|
||
# Проверяем, что user_id является валидным числом
|
||
try:
|
||
user_id_int = int(user_id)
|
||
except ValueError:
|
||
logger.error(f"Некорректный user_id в callback: {user_id}")
|
||
await call.answer(
|
||
text="Ошибка: некорректный ID пользователя", show_alert=True, cache_time=3
|
||
)
|
||
return
|
||
|
||
try:
|
||
# Получаем username пользователя
|
||
username = await ban_service.ban_user(str(user_id_int), "")
|
||
if not username:
|
||
raise UserNotFoundError(f"Пользователь с ID {user_id_int} не найден в базе")
|
||
|
||
# Получаем full_name пользователя из базы данных
|
||
full_name = await bot_db.get_full_name_by_id(user_id_int)
|
||
if not full_name:
|
||
full_name = "Неизвестно"
|
||
|
||
# Сохраняем данные в формате, совместимом с admin_handlers
|
||
await state.update_data(
|
||
target_user_id=user_id_int,
|
||
target_username=username,
|
||
target_full_name=full_name,
|
||
)
|
||
|
||
# Используем единый формат отображения информации о пользователе
|
||
user_info = format_user_info(user_id_int, username, full_name)
|
||
markup = create_keyboard_for_ban_reason()
|
||
|
||
await call.message.answer(
|
||
text=f"{user_info}\n\nВыбери причину бана из списка или напиши ее в чат",
|
||
reply_markup=markup,
|
||
)
|
||
await state.set_state("AWAIT_BAN_DETAILS")
|
||
logger.info(
|
||
f"process_ban_user: Состояние изменено на AWAIT_BAN_DETAILS для пользователя {user_id_int}"
|
||
)
|
||
except UserNotFoundError:
|
||
markup = get_reply_keyboard_admin()
|
||
await call.message.answer(
|
||
text="Пользователь с таким ID не найден в базе", reply_markup=markup
|
||
)
|
||
await state.set_state("ADMIN")
|
||
|
||
|
||
@callback_router.callback_query(F.data.contains(CALLBACK_UNLOCK))
|
||
@track_time("process_unlock_user", "callback_handlers")
|
||
@track_errors("callback_handlers", "process_unlock_user")
|
||
async def process_unlock_user(call: CallbackQuery, **kwargs):
|
||
ban_service = get_ban_service()
|
||
# TODO: переделать на MagicData
|
||
user_id = call.data[7:]
|
||
|
||
# Проверяем, что user_id является валидным числом
|
||
try:
|
||
user_id_int = int(user_id)
|
||
except ValueError:
|
||
logger.error(f"Некорректный user_id в callback: {user_id}")
|
||
await call.answer(
|
||
text="Ошибка: некорректный ID пользователя", show_alert=True, cache_time=3
|
||
)
|
||
return
|
||
|
||
try:
|
||
username = await ban_service.unlock_user(str(user_id_int))
|
||
await call.answer(f"{MESSAGE_USER_UNLOCKED} {username}", show_alert=True)
|
||
except UserNotFoundError:
|
||
await call.answer(
|
||
text="Пользователь не найден в базе", show_alert=True, cache_time=3
|
||
)
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при разблокировке пользователя: {str(e)}")
|
||
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
|
||
|
||
|
||
@callback_router.callback_query(F.data == CALLBACK_RETURN)
|
||
@track_time("return_to_main_menu", "callback_handlers")
|
||
@track_errors("callback_handlers", "return_to_main_menu")
|
||
async def return_to_main_menu(call: CallbackQuery, **kwargs):
|
||
await call.message.delete()
|
||
logger.info(f"Запуск админ панели для пользователя: {call.message.from_user.id}")
|
||
markup = get_reply_keyboard_admin()
|
||
await call.message.answer(
|
||
"Добро пожаловать в админку. Выбери что хочешь:", reply_markup=markup
|
||
)
|
||
|
||
|
||
@callback_router.callback_query(F.data.contains(CALLBACK_PAGE))
|
||
@track_time("change_page", "callback_handlers")
|
||
@track_errors("callback_handlers", "change_page")
|
||
async def change_page(call: CallbackQuery, bot_db: MagicData("bot_db"), **kwargs):
|
||
try:
|
||
page_number = int(call.data[5:])
|
||
except ValueError:
|
||
logger.error(f"Некорректный номер страницы в callback: {call.data}")
|
||
await call.answer(
|
||
text="Ошибка: некорректный номер страницы", show_alert=True, cache_time=3
|
||
)
|
||
return
|
||
|
||
logger.info(f"Переход на страницу {page_number}")
|
||
|
||
if call.message.text == "Список пользователей которые последними обращались к боту":
|
||
list_users = await bot_db.get_last_users(30)
|
||
keyboard = create_keyboard_with_pagination(
|
||
page_number, len(list_users), list_users, "ban"
|
||
)
|
||
await call.bot.edit_message_reply_markup(
|
||
chat_id=call.message.chat.id,
|
||
message_id=call.message.message_id,
|
||
reply_markup=keyboard,
|
||
)
|
||
else:
|
||
message_user = await get_banned_users_list(int(page_number) * 7 - 7, bot_db)
|
||
await call.bot.edit_message_text(
|
||
chat_id=call.message.chat.id,
|
||
message_id=call.message.message_id,
|
||
text=message_user,
|
||
)
|
||
|
||
buttons = await get_banned_users_buttons(bot_db)
|
||
keyboard = create_keyboard_with_pagination(
|
||
page_number, len(buttons), buttons, "unlock"
|
||
)
|
||
await call.bot.edit_message_reply_markup(
|
||
chat_id=call.message.chat.id,
|
||
message_id=call.message.message_id,
|
||
reply_markup=keyboard,
|
||
)
|
||
|
||
|
||
@callback_router.callback_query(F.data == CALLBACK_SAVE)
|
||
@track_time("save_voice_message", "callback_handlers")
|
||
@track_errors("callback_handlers", "save_voice_message")
|
||
@track_file_operations("voice")
|
||
@db_query_time("save_voice_message", "audio_moderate", "mixed")
|
||
async def save_voice_message(
|
||
call: CallbackQuery,
|
||
bot_db: MagicData("bot_db"),
|
||
settings: MagicData("settings"),
|
||
**kwargs,
|
||
):
|
||
try:
|
||
logger.info(
|
||
f"Начинаем сохранение голосового сообщения. Message ID: {call.message.message_id}"
|
||
)
|
||
|
||
# Создаем сервис для работы с аудио файлами
|
||
audio_service = AudioFileService(bot_db)
|
||
|
||
# Получаем ID пользователя из базы
|
||
user_id = await bot_db.get_user_id_by_message_id_for_voice_bot(
|
||
call.message.message_id
|
||
)
|
||
logger.info(f"Получен user_id: {user_id}")
|
||
|
||
# Генерируем имя файла
|
||
file_name = await audio_service.generate_file_name(user_id)
|
||
logger.info(f"Сгенерировано имя файла: {file_name}")
|
||
|
||
# Собираем инфо о сообщении
|
||
time_UTC = int(time.time())
|
||
date_added = datetime.fromtimestamp(time_UTC)
|
||
|
||
# Получаем file_id из voice сообщения
|
||
file_id = call.message.voice.file_id if call.message.voice else ""
|
||
logger.info(f"Получен file_id: {file_id}")
|
||
|
||
# ВАЖНО: Сначала скачиваем и сохраняем файл на диск
|
||
logger.info("Начинаем скачивание и сохранение файла на диск...")
|
||
await audio_service.download_and_save_audio(call.bot, call.message, file_name)
|
||
logger.info("Файл успешно скачан и сохранен на диск")
|
||
|
||
# Только после успешного сохранения файла - сохраняем в базу данных
|
||
logger.info("Начинаем сохранение информации в базу данных...")
|
||
await audio_service.save_audio_file(file_name, user_id, date_added, file_id)
|
||
logger.info("Информация успешно сохранена в базу данных")
|
||
|
||
# Удаляем сообщение из предложки
|
||
logger.info("Удаляем сообщение из предложки...")
|
||
await call.bot.delete_message(
|
||
chat_id=settings["Telegram"]["group_for_posts"],
|
||
message_id=call.message.message_id,
|
||
)
|
||
logger.info("Сообщение удалено из предложки")
|
||
|
||
# Удаляем запись из таблицы audio_moderate
|
||
logger.info("Удаляем запись из таблицы audio_moderate...")
|
||
await bot_db.delete_audio_moderate_record(call.message.message_id)
|
||
logger.info("Запись удалена из таблицы audio_moderate")
|
||
|
||
await call.answer(text="Сохранено!", cache_time=3)
|
||
logger.info(f"Голосовое сообщение успешно сохранено: {file_name}")
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при сохранении голосового сообщения: {e}")
|
||
logger.error(f"Traceback: {traceback.format_exc()}")
|
||
|
||
# Дополнительная информация для диагностики
|
||
try:
|
||
if "call" in locals() and call.message:
|
||
logger.error(f"Message ID: {call.message.message_id}")
|
||
logger.error(
|
||
f"User ID: {user_id if 'user_id' in locals() else 'не определен'}"
|
||
)
|
||
logger.error(
|
||
f"File name: {file_name if 'file_name' in locals() else 'не определен'}"
|
||
)
|
||
except:
|
||
pass
|
||
|
||
await call.answer(text="Ошибка при сохранении!", cache_time=3)
|
||
|
||
|
||
@callback_router.callback_query(F.data == CALLBACK_DELETE)
|
||
@track_time("delete_voice_message", "callback_handlers")
|
||
@track_errors("callback_handlers", "delete_voice_message")
|
||
@db_query_time("delete_voice_message", "audio_moderate", "delete")
|
||
async def delete_voice_message(
|
||
call: CallbackQuery,
|
||
bot_db: MagicData("bot_db"),
|
||
settings: MagicData("settings"),
|
||
**kwargs,
|
||
):
|
||
try:
|
||
# Удаляем сообщение из предложки
|
||
await call.bot.delete_message(
|
||
chat_id=settings["Telegram"]["group_for_posts"],
|
||
message_id=call.message.message_id,
|
||
)
|
||
|
||
# Удаляем запись из таблицы audio_moderate
|
||
await bot_db.delete_audio_moderate_record(call.message.message_id)
|
||
|
||
await call.answer(text="Удалено!", cache_time=3)
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при удалении голосового сообщения: {e}")
|
||
await call.answer(text="Ошибка при удалении!", cache_time=3)
|