- Added `ca-certificates` installation to Dockerfile for improved network security. - Updated health check command in Dockerfile to include better timeout handling. - Refactored `run_helper.py` to implement proper signal handling and logging during shutdown. - Transitioned database operations to an asynchronous model in `async_db.py`, improving performance and responsiveness. - Updated database schema to support new foreign key relationships and optimized indexing for better query performance. - Enhanced various bot handlers to utilize async database methods, improving overall efficiency and user experience. - Removed obsolete database and fix scripts to streamline the project structure.
278 lines
12 KiB
Python
278 lines
12 KiB
Python
import html
|
||
import traceback
|
||
import time
|
||
from datetime import datetime
|
||
|
||
from aiogram import Router, F
|
||
from aiogram.types import CallbackQuery
|
||
from aiogram.fsm.context import FSMContext
|
||
from aiogram.filters import MagicData
|
||
|
||
from helper_bot.handlers.voice.constants import CALLBACK_SAVE, CALLBACK_DELETE
|
||
from helper_bot.handlers.voice.services import AudioFileService
|
||
from helper_bot.keyboards.keyboards import create_keyboard_with_pagination, get_reply_keyboard_admin, \
|
||
create_keyboard_for_ban_reason
|
||
from helper_bot.utils.helper_func import get_banned_users_list, get_banned_users_buttons
|
||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
||
from .dependency_factory import get_post_publish_service, get_ban_service
|
||
from .exceptions import UserBlockedBotError, PostNotFoundError, UserNotFoundError, PublishError, BanError
|
||
from .constants import (
|
||
CALLBACK_PUBLISH, CALLBACK_DECLINE, CALLBACK_BAN, CALLBACK_UNLOCK,
|
||
CALLBACK_RETURN, CALLBACK_PAGE, MESSAGE_PUBLISHED, MESSAGE_DECLINED,
|
||
MESSAGE_USER_BANNED, MESSAGE_USER_UNLOCKED, MESSAGE_ERROR,
|
||
ERROR_BOT_BLOCKED
|
||
)
|
||
from logs.custom_logger import logger
|
||
|
||
callback_router = Router()
|
||
|
||
|
||
@callback_router.callback_query(F.data == CALLBACK_PUBLISH)
|
||
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)
|
||
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)
|
||
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))
|
||
async def process_ban_user(call: CallbackQuery, state: FSMContext, **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:
|
||
user_name = await ban_service.ban_user(str(user_id_int), "")
|
||
await state.update_data(user_id=user_id_int, user_name=user_name, message_for_user=None, date_to_unban=None)
|
||
markup = create_keyboard_for_ban_reason()
|
||
|
||
user_name_escaped = html.escape(str(user_name))
|
||
full_name_escaped = html.escape(str(call.message.from_user.full_name))
|
||
await call.message.answer(
|
||
text=f"<b>Выбран пользователь:\nid:</b> {user_id_int}\n<b>username:</b> {user_name_escaped}\nИмя:{full_name_escaped}\nВыбери причину бана из списка или напиши ее в чат",
|
||
reply_markup=markup
|
||
)
|
||
await state.set_state('BAN_2')
|
||
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))
|
||
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)
|
||
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))
|
||
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 = 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 = 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)
|
||
async def save_voice_message(
|
||
call: CallbackQuery,
|
||
bot_db: MagicData("bot_db"),
|
||
settings: MagicData("settings"),
|
||
**kwargs
|
||
):
|
||
try:
|
||
# Создаем сервис для работы с аудио файлами
|
||
audio_service = AudioFileService(bot_db)
|
||
|
||
# Получаем ID пользователя из базы
|
||
user_id = await bot_db.get_user_id_by_message_id_for_voice_bot(call.message.message_id)
|
||
|
||
# Генерируем имя файла
|
||
file_name = await audio_service.generate_file_name(user_id)
|
||
|
||
# Собираем инфо о сообщении
|
||
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 ""
|
||
|
||
# Сохраняем в базу данных
|
||
await audio_service.save_audio_file(file_name, user_id, date_added, file_id)
|
||
|
||
# Скачиваем и сохраняем файл
|
||
await audio_service.download_and_save_audio(call.bot, call.message, file_name)
|
||
|
||
# Удаляем сообщение из предложки
|
||
await call.bot.delete_message(
|
||
chat_id=settings['Telegram']['group_for_posts'],
|
||
message_id=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)
|
||
|
||
|
||
@callback_router.callback_query(F.data == CALLBACK_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
|
||
)
|
||
|
||
await call.answer(text='Удалено!', cache_time=3)
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при удалении голосового сообщения: {e}")
|
||
await call.answer(text='Ошибка при удалении!', cache_time=3)
|