fix quality code

This commit is contained in:
2026-02-01 23:03:23 +03:00
parent 731e68a597
commit f8962225ee
106 changed files with 8456 additions and 5810 deletions

View File

@@ -1,23 +1,34 @@
from .callback_handlers import callback_router
from .constants import (CALLBACK_BAN, CALLBACK_DECLINE, CALLBACK_PAGE,
CALLBACK_PUBLISH, CALLBACK_RETURN, CALLBACK_UNLOCK)
from .exceptions import (BanError, PostNotFoundError, PublishError,
UserBlockedBotError, UserNotFoundError)
from .constants import (
CALLBACK_BAN,
CALLBACK_DECLINE,
CALLBACK_PAGE,
CALLBACK_PUBLISH,
CALLBACK_RETURN,
CALLBACK_UNLOCK,
)
from .exceptions import (
BanError,
PostNotFoundError,
PublishError,
UserBlockedBotError,
UserNotFoundError,
)
from .services import BanService, PostPublishService
__all__ = [
'callback_router',
'PostPublishService',
'BanService',
'UserBlockedBotError',
'PostNotFoundError',
'UserNotFoundError',
'PublishError',
'BanError',
'CALLBACK_PUBLISH',
'CALLBACK_DECLINE',
'CALLBACK_BAN',
'CALLBACK_UNLOCK',
'CALLBACK_RETURN',
'CALLBACK_PAGE'
"callback_router",
"PostPublishService",
"BanService",
"UserBlockedBotError",
"PostNotFoundError",
"UserNotFoundError",
"PublishError",
"BanError",
"CALLBACK_PUBLISH",
"CALLBACK_DECLINE",
"CALLBACK_BAN",
"CALLBACK_UNLOCK",
"CALLBACK_RETURN",
"CALLBACK_PAGE",
]

View File

@@ -7,28 +7,49 @@ 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.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)
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 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 .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)
from .exceptions import (
BanError,
PostNotFoundError,
PublishError,
UserBlockedBotError,
UserNotFoundError,
)
callback_router = Router()
@@ -36,65 +57,61 @@ 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")
):
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})')
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)}')
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']
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()}"
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}",
)
logger.error(f'Неожиданная ошибка при публикации поста: {str(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_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")
):
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})')
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)}')
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']
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()}"
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}",
)
logger.error(f'Неожиданная ошибка при отклонении поста: {str(e)}')
logger.error(f"Неожиданная ошибка при отклонении поста: {str(e)}")
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
@@ -110,65 +127,75 @@ async def ban_user_from_post(call: CallbackQuery, **kwargs):
except UserBlockedBotError:
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
except (UserNotFoundError, BanError) as e:
logger.error(f'Ошибка при блокировке пользователя: {str(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)}')
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):
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}")
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)
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 = 'Неизвестно'
full_name = "Неизвестно"
# Сохраняем данные в формате, совместимом с admin_handlers
await state.update_data(
target_user_id=user_id_int,
target_username=username,
target_full_name=full_name
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
reply_markup=markup,
)
await state.set_state("AWAIT_BAN_DETAILS")
logger.info(
f"process_ban_user: Состояние изменено на AWAIT_BAN_DETAILS для пользователя {user_id_int}"
)
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')
await call.message.answer(
text="Пользователь с таким ID не найден в базе", reply_markup=markup
)
await state.set_state("ADMIN")
@callback_router.callback_query(F.data.contains(CALLBACK_UNLOCK))
@@ -178,22 +205,26 @@ 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)
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)
await call.answer(f"{MESSAGE_USER_UNLOCKED} {username}", show_alert=True)
except UserNotFoundError:
await call.answer(text='Пользователь не найден в базе', show_alert=True, cache_time=3)
await call.answer(
text="Пользователь не найден в базе", show_alert=True, cache_time=3
)
except Exception as e:
logger.error(f'Ошибка при разблокировке пользователя: {str(e)}')
logger.error(f"Ошибка при разблокировке пользователя: {str(e)}")
await call.answer(text=MESSAGE_ERROR, show_alert=True, cache_time=3)
@@ -204,48 +235,52 @@ 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)
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
):
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)
await call.answer(
text="Ошибка: некорректный номер страницы", show_alert=True, cache_time=3
)
return
logger.info(f"Переход на страницу {page_number}")
if call.message.text == 'Список пользователей которые последними обращались к боту':
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')
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,
chat_id=call.message.chat.id,
message_id=call.message.message_id,
reply_markup=keyboard
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,
chat_id=call.message.chat.id,
message_id=call.message.message_id,
text=message_user
text=message_user,
)
buttons = await get_banned_users_buttons(bot_db)
keyboard = create_keyboard_with_pagination(page_number, len(buttons), buttons, 'unlock')
keyboard = create_keyboard_with_pagination(
page_number, len(buttons), buttons, "unlock"
)
await call.bot.edit_message_reply_markup(
chat_id=call.message.chat.id,
chat_id=call.message.chat.id,
message_id=call.message.message_id,
reply_markup=keyboard
reply_markup=keyboard,
)
@@ -255,73 +290,81 @@ async def change_page(
@track_file_operations("voice")
@db_query_time("save_voice_message", "audio_moderate", "mixed")
async def save_voice_message(
call: CallbackQuery,
call: CallbackQuery,
bot_db: MagicData("bot_db"),
settings: MagicData("settings"),
**kwargs
):
**kwargs,
):
try:
logger.info(f"Начинаем сохранение голосового сообщения. Message ID: {call.message.message_id}")
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)
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
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)
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:
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 'не определен'}")
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)
await call.answer(text="Ошибка при сохранении!", cache_time=3)
@callback_router.callback_query(F.data == CALLBACK_DELETE)
@@ -329,23 +372,23 @@ async def save_voice_message(
@track_errors("callback_handlers", "delete_voice_message")
@db_query_time("delete_voice_message", "audio_moderate", "delete")
async def delete_voice_message(
call: CallbackQuery,
call: CallbackQuery,
bot_db: MagicData("bot_db"),
settings: MagicData("settings"),
**kwargs
):
**kwargs,
):
try:
# Удаляем сообщение из предложки
await call.bot.delete_message(
chat_id=settings['Telegram']['group_for_posts'],
message_id=call.message.message_id
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)
await call.answer(text="Удалено!", cache_time=3)
except Exception as e:
logger.error(f"Ошибка при удалении голосового сообщения: {e}")
await call.answer(text='Ошибка при удалении!', cache_time=3)
await call.answer(text="Ошибка при удалении!", cache_time=3)

View File

@@ -33,9 +33,9 @@ ERROR_BOT_BLOCKED = "Forbidden: bot was blocked by the user"
# Callback to command mapping for metrics
CALLBACK_COMMAND_MAPPING: Final[Dict[str, str]] = {
"publish": "publish",
"decline": "decline",
"decline": "decline",
"ban": "ban",
"unlock": "unlock",
"return": "return",
"page": "page"
"page": "page",
}

View File

@@ -3,6 +3,7 @@ from typing import Callable
from aiogram import Bot
from aiogram.client.default import DefaultBotProperties
from aiogram.fsm.context import FSMContext
from helper_bot.utils.base_dependency_factory import get_global_instance
from .services import BanService, PostPublishService
@@ -21,7 +22,7 @@ def get_post_publish_service() -> PostPublishService:
def get_ban_service() -> BanService:
"""Фабрика для BanService"""
bdf = get_global_instance()
db = bdf.get_db()
settings = bdf.settings
return BanService(None, db, settings)

View File

@@ -1,23 +1,28 @@
class UserBlockedBotError(Exception):
"""Исключение, возникающее когда пользователь заблокировал бота"""
pass
class PostNotFoundError(Exception):
"""Исключение, возникающее когда пост не найден в базе данных"""
pass
class UserNotFoundError(Exception):
"""Исключение, возникающее когда пользователь не найден в базе данных"""
pass
class PublishError(Exception):
"""Общее исключение для ошибок публикации"""
pass
class BanError(Exception):
"""Исключение для ошибок бана/разбана пользователей"""
pass

View File

@@ -4,28 +4,49 @@ from typing import Any, Dict
from aiogram import Bot, types
from aiogram.types import CallbackQuery
from helper_bot.keyboards.keyboards import create_keyboard_for_ban_reason
from helper_bot.utils.helper_func import (delete_user_blacklist,
get_text_message, send_audio_message,
send_media_group_to_channel,
send_photo_message,
send_text_message,
send_video_message,
send_video_note_message,
send_voice_message)
from helper_bot.utils.helper_func import (
delete_user_blacklist,
get_text_message,
send_audio_message,
send_media_group_to_channel,
send_photo_message,
send_text_message,
send_video_message,
send_video_note_message,
send_voice_message,
)
# Local imports - metrics
from helper_bot.utils.metrics import (db_query_time, track_errors,
track_media_processing, track_time)
from helper_bot.utils.metrics import (
db_query_time,
track_errors,
track_media_processing,
track_time,
)
from logs.custom_logger import logger
from .constants import (CONTENT_TYPE_AUDIO, CONTENT_TYPE_MEDIA_GROUP,
CONTENT_TYPE_PHOTO, CONTENT_TYPE_TEXT,
CONTENT_TYPE_VIDEO, CONTENT_TYPE_VIDEO_NOTE,
CONTENT_TYPE_VOICE, ERROR_BOT_BLOCKED,
MESSAGE_POST_DECLINED, MESSAGE_POST_PUBLISHED,
MESSAGE_USER_BANNED_SPAM)
from .exceptions import (BanError, PostNotFoundError, PublishError,
UserBlockedBotError, UserNotFoundError)
from .constants import (
CONTENT_TYPE_AUDIO,
CONTENT_TYPE_MEDIA_GROUP,
CONTENT_TYPE_PHOTO,
CONTENT_TYPE_TEXT,
CONTENT_TYPE_VIDEO,
CONTENT_TYPE_VIDEO_NOTE,
CONTENT_TYPE_VOICE,
ERROR_BOT_BLOCKED,
MESSAGE_POST_DECLINED,
MESSAGE_POST_PUBLISHED,
MESSAGE_USER_BANNED_SPAM,
)
from .exceptions import (
BanError,
PostNotFoundError,
PublishError,
UserBlockedBotError,
UserNotFoundError,
)
class PostPublishService:
@@ -35,10 +56,10 @@ class PostPublishService:
self.db = db
self.settings = settings
self.s3_storage = s3_storage
self.group_for_posts = settings['Telegram']['group_for_posts']
self.main_public = settings['Telegram']['main_public']
self.important_logs = settings['Telegram']['important_logs']
self.group_for_posts = settings["Telegram"]["group_for_posts"]
self.main_public = settings["Telegram"]["main_public"]
self.important_logs = settings["Telegram"]["important_logs"]
def _get_bot(self, message) -> Bot:
"""Получает бота из контекста сообщения или использует переданного"""
if self.bot:
@@ -53,14 +74,14 @@ class PostPublishService:
if call.message.text == CONTENT_TYPE_MEDIA_GROUP:
await self._publish_media_group(call)
return
# Проверяем, является ли сообщение частью медиагруппы (для обратной совместимости)
if call.message.media_group_id:
await self._publish_media_group(call)
return
content_type = call.message.content_type
if content_type == CONTENT_TYPE_TEXT:
await self._publish_text_post(call)
elif content_type == CONTENT_TYPE_PHOTO:
@@ -82,34 +103,50 @@ class PostPublishService:
"""Публикация текстового поста"""
author_id = await self._get_author_id(call.message.message_id)
updated_rows = await self.db.update_status_by_message_id(call.message.message_id, "approved")
updated_rows = await self.db.update_status_by_message_id(
call.message.message_id, "approved"
)
if updated_rows == 0:
logger.error(f"Не удалось обновить статус поста message_id={call.message.message_id} на 'approved'")
raise PostNotFoundError(f"Пост с message_id={call.message.message_id} не найден в базе данных")
logger.error(
f"Не удалось обновить статус поста message_id={call.message.message_id} на 'approved'"
)
raise PostNotFoundError(
f"Пост с message_id={call.message.message_id} не найден в базе данных"
)
# Получаем сырой текст и is_anonymous из базы
raw_text, is_anonymous = await self.db.get_post_text_and_anonymity_by_message_id(call.message.message_id)
raw_text, is_anonymous = (
await self.db.get_post_text_and_anonymity_by_message_id(
call.message.message_id
)
)
if raw_text is None:
raw_text = ""
# Получаем данные автора
user = await self.db.get_user_by_id(author_id)
if not user:
raise PostNotFoundError(f"Пользователь {author_id} не найден в базе данных")
# Формируем финальный текст с учетом is_anonymous
formatted_text = get_text_message(raw_text, user.first_name, user.username, is_anonymous)
sent_message = await send_text_message(self.main_public, call.message, formatted_text)
formatted_text = get_text_message(
raw_text, user.first_name, user.username, is_anonymous
)
sent_message = await send_text_message(
self.main_public, call.message, formatted_text
)
# Сохраняем published_message_id
await self.db.update_published_message_id(
original_message_id=call.message.message_id,
published_message_id=sent_message.message_id
published_message_id=sent_message.message_id,
)
await self._delete_post_and_notify_author(call, author_id)
logger.info(f'Текст сообщение опубликован в канале {self.main_public}, published_message_id={sent_message.message_id}.')
logger.info(
f"Текст сообщение опубликован в канале {self.main_public}, published_message_id={sent_message.message_id}."
)
@track_time("_publish_photo_post", "post_publish_service")
@track_errors("post_publish_service", "_publish_photo_post")
@@ -117,37 +154,58 @@ class PostPublishService:
"""Публикация поста с фото"""
author_id = await self._get_author_id(call.message.message_id)
updated_rows = await self.db.update_status_by_message_id(call.message.message_id, "approved")
updated_rows = await self.db.update_status_by_message_id(
call.message.message_id, "approved"
)
if updated_rows == 0:
logger.error(f"Не удалось обновить статус поста message_id={call.message.message_id} на 'approved'")
raise PostNotFoundError(f"Пост с message_id={call.message.message_id} не найден в базе данных")
logger.error(
f"Не удалось обновить статус поста message_id={call.message.message_id} на 'approved'"
)
raise PostNotFoundError(
f"Пост с message_id={call.message.message_id} не найден в базе данных"
)
# Получаем сырой текст и is_anonymous из базы
raw_text, is_anonymous = await self.db.get_post_text_and_anonymity_by_message_id(call.message.message_id)
raw_text, is_anonymous = (
await self.db.get_post_text_and_anonymity_by_message_id(
call.message.message_id
)
)
if raw_text is None:
raw_text = ""
# Получаем данные автора
user = await self.db.get_user_by_id(author_id)
if not user:
raise PostNotFoundError(f"Пользователь {author_id} не найден в базе данных")
# Формируем финальный текст с учетом is_anonymous
formatted_text = get_text_message(raw_text, user.first_name, user.username, is_anonymous)
sent_message = await send_photo_message(self.main_public, call.message, call.message.photo[-1].file_id, formatted_text)
formatted_text = get_text_message(
raw_text, user.first_name, user.username, is_anonymous
)
sent_message = await send_photo_message(
self.main_public,
call.message,
call.message.photo[-1].file_id,
formatted_text,
)
# Сохраняем published_message_id
await self.db.update_published_message_id(
original_message_id=call.message.message_id,
published_message_id=sent_message.message_id
published_message_id=sent_message.message_id,
)
# Сохраняем медиафайл из опубликованного поста (используем уже сохраненный файл)
await self._save_published_post_content(sent_message, sent_message.message_id, call.message.message_id)
await self._save_published_post_content(
sent_message, sent_message.message_id, call.message.message_id
)
await self._delete_post_and_notify_author(call, author_id)
logger.info(f'Пост с фото опубликован в канале {self.main_public}, published_message_id={sent_message.message_id}.')
logger.info(
f"Пост с фото опубликован в канале {self.main_public}, published_message_id={sent_message.message_id}."
)
@track_time("_publish_video_post", "post_publish_service")
@track_errors("post_publish_service", "_publish_video_post")
@@ -155,37 +213,55 @@ class PostPublishService:
"""Публикация поста с видео"""
author_id = await self._get_author_id(call.message.message_id)
updated_rows = await self.db.update_status_by_message_id(call.message.message_id, "approved")
updated_rows = await self.db.update_status_by_message_id(
call.message.message_id, "approved"
)
if updated_rows == 0:
logger.error(f"Не удалось обновить статус поста message_id={call.message.message_id} на 'approved'")
raise PostNotFoundError(f"Пост с message_id={call.message.message_id} не найден в базе данных")
logger.error(
f"Не удалось обновить статус поста message_id={call.message.message_id} на 'approved'"
)
raise PostNotFoundError(
f"Пост с message_id={call.message.message_id} не найден в базе данных"
)
# Получаем сырой текст и is_anonymous из базы
raw_text, is_anonymous = await self.db.get_post_text_and_anonymity_by_message_id(call.message.message_id)
raw_text, is_anonymous = (
await self.db.get_post_text_and_anonymity_by_message_id(
call.message.message_id
)
)
if raw_text is None:
raw_text = ""
# Получаем данные автора
user = await self.db.get_user_by_id(author_id)
if not user:
raise PostNotFoundError(f"Пользователь {author_id} не найден в базе данных")
# Формируем финальный текст с учетом is_anonymous
formatted_text = get_text_message(raw_text, user.first_name, user.username, is_anonymous)
sent_message = await send_video_message(self.main_public, call.message, call.message.video.file_id, formatted_text)
formatted_text = get_text_message(
raw_text, user.first_name, user.username, is_anonymous
)
sent_message = await send_video_message(
self.main_public, call.message, call.message.video.file_id, formatted_text
)
# Сохраняем published_message_id
await self.db.update_published_message_id(
original_message_id=call.message.message_id,
published_message_id=sent_message.message_id
published_message_id=sent_message.message_id,
)
# Сохраняем медиафайл из опубликованного поста (используем уже сохраненный файл)
await self._save_published_post_content(sent_message, sent_message.message_id, call.message.message_id)
await self._save_published_post_content(
sent_message, sent_message.message_id, call.message.message_id
)
await self._delete_post_and_notify_author(call, author_id)
logger.info(f'Пост с видео опубликован в канале {self.main_public}, published_message_id={sent_message.message_id}.')
logger.info(
f"Пост с видео опубликован в канале {self.main_public}, published_message_id={sent_message.message_id}."
)
@track_time("_publish_video_note_post", "post_publish_service")
@track_errors("post_publish_service", "_publish_video_note_post")
@@ -193,24 +269,36 @@ class PostPublishService:
"""Публикация поста с кружком"""
author_id = await self._get_author_id(call.message.message_id)
updated_rows = await self.db.update_status_by_message_id(call.message.message_id, "approved")
updated_rows = await self.db.update_status_by_message_id(
call.message.message_id, "approved"
)
if updated_rows == 0:
logger.error(f"Не удалось обновить статус поста message_id={call.message.message_id} на 'approved'")
raise PostNotFoundError(f"Пост с message_id={call.message.message_id} не найден в базе данных")
sent_message = await send_video_note_message(self.main_public, call.message, call.message.video_note.file_id)
logger.error(
f"Не удалось обновить статус поста message_id={call.message.message_id} на 'approved'"
)
raise PostNotFoundError(
f"Пост с message_id={call.message.message_id} не найден в базе данных"
)
sent_message = await send_video_note_message(
self.main_public, call.message, call.message.video_note.file_id
)
# Сохраняем published_message_id
await self.db.update_published_message_id(
original_message_id=call.message.message_id,
published_message_id=sent_message.message_id
published_message_id=sent_message.message_id,
)
# Сохраняем медиафайл из опубликованного поста (используем уже сохраненный файл)
await self._save_published_post_content(sent_message, sent_message.message_id, call.message.message_id)
await self._save_published_post_content(
sent_message, sent_message.message_id, call.message.message_id
)
await self._delete_post_and_notify_author(call, author_id)
logger.info(f'Пост с кружком опубликован в канале {self.main_public}, published_message_id={sent_message.message_id}.')
logger.info(
f"Пост с кружком опубликован в канале {self.main_public}, published_message_id={sent_message.message_id}."
)
@track_time("_publish_audio_post", "post_publish_service")
@track_errors("post_publish_service", "_publish_audio_post")
@@ -218,37 +306,55 @@ class PostPublishService:
"""Публикация поста с аудио"""
author_id = await self._get_author_id(call.message.message_id)
updated_rows = await self.db.update_status_by_message_id(call.message.message_id, "approved")
updated_rows = await self.db.update_status_by_message_id(
call.message.message_id, "approved"
)
if updated_rows == 0:
logger.error(f"Не удалось обновить статус поста message_id={call.message.message_id} на 'approved'")
raise PostNotFoundError(f"Пост с message_id={call.message.message_id} не найден в базе данных")
logger.error(
f"Не удалось обновить статус поста message_id={call.message.message_id} на 'approved'"
)
raise PostNotFoundError(
f"Пост с message_id={call.message.message_id} не найден в базе данных"
)
# Получаем сырой текст и is_anonymous из базы
raw_text, is_anonymous = await self.db.get_post_text_and_anonymity_by_message_id(call.message.message_id)
raw_text, is_anonymous = (
await self.db.get_post_text_and_anonymity_by_message_id(
call.message.message_id
)
)
if raw_text is None:
raw_text = ""
# Получаем данные автора
user = await self.db.get_user_by_id(author_id)
if not user:
raise PostNotFoundError(f"Пользователь {author_id} не найден в базе данных")
# Формируем финальный текст с учетом is_anonymous
formatted_text = get_text_message(raw_text, user.first_name, user.username, is_anonymous)
sent_message = await send_audio_message(self.main_public, call.message, call.message.audio.file_id, formatted_text)
formatted_text = get_text_message(
raw_text, user.first_name, user.username, is_anonymous
)
sent_message = await send_audio_message(
self.main_public, call.message, call.message.audio.file_id, formatted_text
)
# Сохраняем published_message_id
await self.db.update_published_message_id(
original_message_id=call.message.message_id,
published_message_id=sent_message.message_id
published_message_id=sent_message.message_id,
)
# Сохраняем медиафайл из опубликованного поста (используем уже сохраненный файл)
await self._save_published_post_content(sent_message, sent_message.message_id, call.message.message_id)
await self._save_published_post_content(
sent_message, sent_message.message_id, call.message.message_id
)
await self._delete_post_and_notify_author(call, author_id)
logger.info(f'Пост с аудио опубликован в канале {self.main_public}, published_message_id={sent_message.message_id}.')
logger.info(
f"Пост с аудио опубликован в канале {self.main_public}, published_message_id={sent_message.message_id}."
)
@track_time("_publish_voice_post", "post_publish_service")
@track_errors("post_publish_service", "_publish_voice_post")
@@ -256,24 +362,36 @@ class PostPublishService:
"""Публикация поста с войсом"""
author_id = await self._get_author_id(call.message.message_id)
updated_rows = await self.db.update_status_by_message_id(call.message.message_id, "approved")
updated_rows = await self.db.update_status_by_message_id(
call.message.message_id, "approved"
)
if updated_rows == 0:
logger.error(f"Не удалось обновить статус поста message_id={call.message.message_id} на 'approved'")
raise PostNotFoundError(f"Пост с message_id={call.message.message_id} не найден в базе данных")
sent_message = await send_voice_message(self.main_public, call.message, call.message.voice.file_id)
logger.error(
f"Не удалось обновить статус поста message_id={call.message.message_id} на 'approved'"
)
raise PostNotFoundError(
f"Пост с message_id={call.message.message_id} не найден в базе данных"
)
sent_message = await send_voice_message(
self.main_public, call.message, call.message.voice.file_id
)
# Сохраняем published_message_id
await self.db.update_published_message_id(
original_message_id=call.message.message_id,
published_message_id=sent_message.message_id
published_message_id=sent_message.message_id,
)
# Сохраняем медиафайл из опубликованного поста (используем уже сохраненный файл)
await self._save_published_post_content(sent_message, sent_message.message_id, call.message.message_id)
await self._save_published_post_content(
sent_message, sent_message.message_id, call.message.message_id
)
await self._delete_post_and_notify_author(call, author_id)
logger.info(f'Пост с войсом опубликован в канале {self.main_public}, published_message_id={sent_message.message_id}.')
logger.info(
f"Пост с войсом опубликован в канале {self.main_public}, published_message_id={sent_message.message_id}."
)
@track_time("_publish_media_group", "post_publish_service")
@track_errors("post_publish_service", "_publish_media_group")
@@ -282,91 +400,130 @@ class PostPublishService:
"""Публикация медиагруппы"""
try:
helper_message_id = call.message.message_id
media_group_message_ids = await self.db.get_post_ids_by_helper_id(helper_message_id)
media_group_message_ids = await self.db.get_post_ids_by_helper_id(
helper_message_id
)
if not media_group_message_ids:
logger.error(f"_publish_media_group: Не найдены message_id медиагруппы для helper_message_id={helper_message_id}")
logger.error(
f"_publish_media_group: Не найдены message_id медиагруппы для helper_message_id={helper_message_id}"
)
raise PublishError("Не найдены message_id медиагруппы в базе данных")
post_content = await self.db.get_post_content_by_helper_id(helper_message_id)
post_content = await self.db.get_post_content_by_helper_id(
helper_message_id
)
if not post_content:
logger.error(f"_publish_media_group: Контент медиагруппы не найден в базе данных для helper_message_id={helper_message_id}")
logger.error(
f"_publish_media_group: Контент медиагруппы не найден в базе данных для helper_message_id={helper_message_id}"
)
raise PublishError("Контент медиагруппы не найден в базе данных")
raw_text, is_anonymous = await self.db.get_post_text_and_anonymity_by_helper_id(helper_message_id)
raw_text, is_anonymous = (
await self.db.get_post_text_and_anonymity_by_helper_id(
helper_message_id
)
)
if raw_text is None:
raw_text = ""
author_id = await self.db.get_author_id_by_helper_message_id(helper_message_id)
author_id = await self.db.get_author_id_by_helper_message_id(
helper_message_id
)
if not author_id:
logger.error(f"_publish_media_group: Автор не найден для медиагруппы helper_message_id={helper_message_id}")
raise PostNotFoundError(f"Автор не найден для медиагруппы {helper_message_id}")
logger.error(
f"_publish_media_group: Автор не найден для медиагруппы helper_message_id={helper_message_id}"
)
raise PostNotFoundError(
f"Автор не найден для медиагруппы {helper_message_id}"
)
user = await self.db.get_user_by_id(author_id)
if not user:
raise PostNotFoundError(f"Пользователь {author_id} не найден в базе данных")
formatted_text = get_text_message(raw_text, user.first_name, user.username, is_anonymous)
raise PostNotFoundError(
f"Пользователь {author_id} не найден в базе данных"
)
formatted_text = get_text_message(
raw_text, user.first_name, user.username, is_anonymous
)
try:
await self._get_bot(call.message).delete_messages(
chat_id=self.group_for_posts,
message_ids=media_group_message_ids
chat_id=self.group_for_posts, message_ids=media_group_message_ids
)
except Exception as e:
logger.warning(f"_publish_media_group: Ошибка при удалении медиагруппы из чата модерации: {e}")
logger.warning(
f"_publish_media_group: Ошибка при удалении медиагруппы из чата модерации: {e}"
)
sent_messages = await send_media_group_to_channel(
bot=self._get_bot(call.message),
chat_id=self.main_public,
post_content=post_content,
bot=self._get_bot(call.message),
chat_id=self.main_public,
post_content=post_content,
post_text=formatted_text,
s3_storage=self.s3_storage
s3_storage=self.s3_storage,
)
if len(sent_messages) == len(media_group_message_ids):
for i, original_message_id in enumerate(media_group_message_ids):
published_message_id = sent_messages[i].message_id
try:
await self.db.update_published_message_id(
original_message_id=original_message_id,
published_message_id=published_message_id
published_message_id=published_message_id,
)
await self._save_published_post_content(
sent_messages[i], published_message_id, original_message_id
)
await self._save_published_post_content(sent_messages[i], published_message_id, original_message_id)
except Exception as e:
logger.warning(f"_publish_media_group: Ошибка при сохранении published_message_id для {original_message_id}: {e}")
logger.warning(
f"_publish_media_group: Ошибка при сохранении published_message_id для {original_message_id}: {e}"
)
else:
logger.warning(f"_publish_media_group: Количество опубликованных сообщений ({len(sent_messages)}) не совпадает с количеством оригинальных ({len(media_group_message_ids)})")
logger.warning(
f"_publish_media_group: Количество опубликованных сообщений ({len(sent_messages)}) не совпадает с количеством оригинальных ({len(media_group_message_ids)})"
)
await self.db.update_status_for_media_group_by_helper_id(
helper_message_id, "approved"
)
await self.db.update_status_for_media_group_by_helper_id(helper_message_id, "approved")
# Удаляем helper сообщение - это критично, делаем это всегда
try:
await self._get_bot(call.message).delete_message(
chat_id=self.group_for_posts,
message_id=helper_message_id
chat_id=self.group_for_posts, message_id=helper_message_id
)
except Exception as e:
logger.warning(f"_publish_media_group: Ошибка при удалении helper сообщения: {e}")
logger.warning(
f"_publish_media_group: Ошибка при удалении helper сообщения: {e}"
)
try:
await send_text_message(author_id, call.message, MESSAGE_POST_PUBLISHED)
except Exception as e:
if str(e) == ERROR_BOT_BLOCKED:
logger.warning(f"_publish_media_group: Пользователь {author_id} заблокировал бота")
logger.warning(
f"_publish_media_group: Пользователь {author_id} заблокировал бота"
)
raise UserBlockedBotError("Пользователь заблокировал бота")
logger.error(f"_publish_media_group: Ошибка при отправке уведомления автору: {e}")
logger.error(
f"_publish_media_group: Ошибка при отправке уведомления автору: {e}"
)
except Exception as e:
logger.error(f"_publish_media_group: Ошибка при публикации медиагруппы: {e}")
logger.error(
f"_publish_media_group: Ошибка при публикации медиагруппы: {e}"
)
# Пытаемся удалить helper сообщение даже при ошибке
try:
await self._get_bot(call.message).delete_message(
chat_id=self.group_for_posts,
message_id=call.message.message_id
chat_id=self.group_for_posts, message_id=call.message.message_id
)
except Exception as delete_error:
logger.warning(f"_publish_media_group: Не удалось удалить helper сообщение при ошибке: {delete_error}")
logger.warning(
f"_publish_media_group: Не удалось удалить helper сообщение при ошибке: {delete_error}"
)
raise PublishError(f"Не удалось опубликовать медиагруппу: {str(e)}")
@track_time("decline_post", "post_publish_service")
@@ -377,15 +534,25 @@ class PostPublishService:
if call.message.text == CONTENT_TYPE_MEDIA_GROUP:
await self._decline_media_group(call)
return
content_type = call.message.content_type
if content_type in [CONTENT_TYPE_TEXT, CONTENT_TYPE_PHOTO, CONTENT_TYPE_AUDIO,
CONTENT_TYPE_VOICE, CONTENT_TYPE_VIDEO, CONTENT_TYPE_VIDEO_NOTE]:
if content_type in [
CONTENT_TYPE_TEXT,
CONTENT_TYPE_PHOTO,
CONTENT_TYPE_AUDIO,
CONTENT_TYPE_VOICE,
CONTENT_TYPE_VIDEO,
CONTENT_TYPE_VIDEO_NOTE,
]:
await self._decline_single_post(call)
else:
logger.error(f"Неподдерживаемый тип контента для отклонения: {content_type}")
raise PublishError(f"Неподдерживаемый тип контента для отклонения: {content_type}")
logger.error(
f"Неподдерживаемый тип контента для отклонения: {content_type}"
)
raise PublishError(
f"Неподдерживаемый тип контента для отклонения: {content_type}"
)
@track_time("_decline_single_post", "post_publish_service")
@track_errors("post_publish_service", "_decline_single_post")
@@ -393,13 +560,21 @@ class PostPublishService:
"""Отклонение одиночного поста"""
author_id = await self._get_author_id(call.message.message_id)
updated_rows = await self.db.update_status_by_message_id(call.message.message_id, "declined")
updated_rows = await self.db.update_status_by_message_id(
call.message.message_id, "declined"
)
if updated_rows == 0:
logger.error(f"Не удалось обновить статус поста message_id={call.message.message_id} на 'declined'")
raise PostNotFoundError(f"Пост с message_id={call.message.message_id} не найден в базе данных")
await self._get_bot(call.message).delete_message(chat_id=self.group_for_posts, message_id=call.message.message_id)
logger.error(
f"Не удалось обновить статус поста message_id={call.message.message_id} на 'declined'"
)
raise PostNotFoundError(
f"Пост с message_id={call.message.message_id} не найден в базе данных"
)
await self._get_bot(call.message).delete_message(
chat_id=self.group_for_posts, message_id=call.message.message_id
)
try:
await send_text_message(author_id, call.message, MESSAGE_POST_DECLINED)
except Exception as e:
@@ -408,7 +583,9 @@ class PostPublishService:
raise UserBlockedBotError("Пользователь заблокировал бота")
logger.error(f"Ошибка при отправке уведомления автору {author_id}: {e}")
raise
logger.info(f'Сообщение отклонено админом {call.from_user.full_name} (ID: {call.from_user.id}).')
logger.info(
f"Сообщение отклонено админом {call.from_user.full_name} (ID: {call.from_user.id})."
)
@track_time("_decline_media_group", "post_publish_service")
@track_errors("post_publish_service", "_decline_media_group")
@@ -416,10 +593,14 @@ class PostPublishService:
async def _decline_media_group(self, call: CallbackQuery) -> None:
"""Отклонение медиагруппы"""
helper_message_id = call.message.message_id
await self.db.update_status_for_media_group_by_helper_id(helper_message_id, "declined")
media_group_message_ids = await self.db.get_post_ids_by_helper_id(helper_message_id)
await self.db.update_status_for_media_group_by_helper_id(
helper_message_id, "declined"
)
media_group_message_ids = await self.db.get_post_ids_by_helper_id(
helper_message_id
)
message_ids_to_delete = media_group_message_ids.copy()
message_ids_to_delete.append(helper_message_id)
@@ -428,19 +609,22 @@ class PostPublishService:
try:
await self._get_bot(call.message).delete_messages(
chat_id=self.group_for_posts,
message_ids=message_ids_to_delete
chat_id=self.group_for_posts, message_ids=message_ids_to_delete
)
except Exception as e:
logger.warning(f"_decline_media_group: Ошибка при удалении сообщений: {e}")
try:
await send_text_message(author_id, call.message, MESSAGE_POST_DECLINED)
except Exception as e:
if str(e) == ERROR_BOT_BLOCKED:
logger.warning(f"_decline_media_group: Пользователь {author_id} заблокировал бота")
logger.warning(
f"_decline_media_group: Пользователь {author_id} заблокировал бота"
)
raise UserBlockedBotError("Пользователь заблокировал бота")
logger.error(f"_decline_media_group: Ошибка при отправке уведомления автору {author_id}: {e}")
logger.error(
f"_decline_media_group: Ошибка при отправке уведомления автору {author_id}: {e}"
)
raise
@track_time("_get_author_id", "post_publish_service")
@@ -460,7 +644,7 @@ class PostPublishService:
author_id = await self.db.get_author_id_by_helper_message_id(message_id)
if author_id:
return author_id
# Если не найден, ищем по основному message_id медиагруппы
# Для этого нужно найти связанные сообщения медиагруппы
try:
@@ -474,7 +658,7 @@ class PostPublishService:
return author_id
except Exception as e:
logger.warning(f"Не удалось найти автора через связанные сообщения: {e}")
# Если все способы не сработали, ищем напрямую
author_id = await self.db.get_author_id_by_message_id(message_id)
if not author_id:
@@ -483,10 +667,14 @@ class PostPublishService:
@track_time("_delete_post_and_notify_author", "post_publish_service")
@track_errors("post_publish_service", "_delete_post_and_notify_author")
async def _delete_post_and_notify_author(self, call: CallbackQuery, author_id: int) -> None:
async def _delete_post_and_notify_author(
self, call: CallbackQuery, author_id: int
) -> None:
"""Удаление поста и уведомление автора"""
await self._get_bot(call.message).delete_message(chat_id=self.group_for_posts, message_id=call.message.message_id)
await self._get_bot(call.message).delete_message(
chat_id=self.group_for_posts, message_id=call.message.message_id
)
try:
await send_text_message(author_id, call.message, MESSAGE_POST_PUBLISHED)
except Exception as e:
@@ -497,16 +685,22 @@ class PostPublishService:
@track_time("_delete_media_group_and_notify_author", "post_publish_service")
@track_errors("post_publish_service", "_delete_media_group_and_notify_author")
@track_media_processing("media_group")
async def _delete_media_group_and_notify_author(self, call: CallbackQuery, author_id: int) -> None:
async def _delete_media_group_and_notify_author(
self, call: CallbackQuery, author_id: int
) -> None:
"""Удаление медиагруппы и уведомление автора (legacy метод, используется для обратной совместимости)"""
helper_message_id = call.message.message_id
media_group_message_ids = await self.db.get_post_ids_by_helper_id(helper_message_id)
media_group_message_ids = await self.db.get_post_ids_by_helper_id(
helper_message_id
)
message_ids_to_delete = media_group_message_ids.copy()
message_ids_to_delete.append(helper_message_id)
await self._get_bot(call.message).delete_messages(chat_id=self.group_for_posts, message_ids=message_ids_to_delete)
await self._get_bot(call.message).delete_messages(
chat_id=self.group_for_posts, message_ids=message_ids_to_delete
)
try:
await send_text_message(author_id, call.message, MESSAGE_POST_PUBLISHED)
except Exception as e:
@@ -516,30 +710,47 @@ class PostPublishService:
@track_time("_save_published_post_content", "post_publish_service")
@track_errors("post_publish_service", "_save_published_post_content")
async def _save_published_post_content(self, published_message: types.Message, published_message_id: int, original_message_id: int) -> None:
async def _save_published_post_content(
self,
published_message: types.Message,
published_message_id: int,
original_message_id: int,
) -> None:
"""Сохраняет ссылку на медиафайл из опубликованного поста (файл уже в S3 или на диске)."""
try:
# Получаем уже сохраненный путь/S3 ключ из оригинального поста
saved_content = await self.db.get_post_content_by_message_id(original_message_id)
saved_content = await self.db.get_post_content_by_message_id(
original_message_id
)
if saved_content and len(saved_content) > 0:
# Копируем тот же путь/S3 ключ
file_path, content_type = saved_content[0]
logger.debug(f"Копируем путь/S3 ключ для опубликованного поста: {file_path}")
logger.debug(
f"Копируем путь/S3 ключ для опубликованного поста: {file_path}"
)
success = await self.db.add_published_post_content(
published_message_id=published_message_id,
content_path=file_path, # Тот же путь/S3 ключ
content_type=content_type
content_type=content_type,
)
if success:
logger.info(f"Ссылка на файл сохранена для опубликованного поста: published_message_id={published_message_id}, path={file_path}")
logger.info(
f"Ссылка на файл сохранена для опубликованного поста: published_message_id={published_message_id}, path={file_path}"
)
else:
logger.warning(f"Не удалось сохранить ссылку на файл: published_message_id={published_message_id}")
logger.warning(
f"Не удалось сохранить ссылку на файл: published_message_id={published_message_id}"
)
else:
logger.warning(f"Контент не найден для оригинального поста message_id={original_message_id}")
logger.warning(
f"Контент не найден для оригинального поста message_id={original_message_id}"
)
except Exception as e:
logger.error(f"Ошибка при сохранении ссылки на контент опубликованного поста {published_message_id}: {e}")
logger.error(
f"Ошибка при сохранении ссылки на контент опубликованного поста {published_message_id}: {e}"
)
# Не прерываем публикацию, если сохранение контента не удалось
@@ -548,8 +759,8 @@ class BanService:
self.bot = bot
self.db = db
self.settings = settings
self.group_for_posts = settings['Telegram']['group_for_posts']
self.important_logs = settings['Telegram']['important_logs']
self.group_for_posts = settings["Telegram"]["group_for_posts"]
self.important_logs = settings["Telegram"]["important_logs"]
def _get_bot(self, message) -> Bot:
"""Получает бота из контекста сообщения или использует переданного"""
@@ -564,16 +775,22 @@ class BanService:
"""Бан пользователя за спам"""
# Если это helper-сообщение медиагруппы, используем специальный метод
if call.message.text == CONTENT_TYPE_MEDIA_GROUP:
author_id = await self.db.get_author_id_by_helper_message_id(call.message.message_id)
author_id = await self.db.get_author_id_by_helper_message_id(
call.message.message_id
)
else:
author_id = await self.db.get_author_id_by_message_id(call.message.message_id)
author_id = await self.db.get_author_id_by_message_id(
call.message.message_id
)
if not author_id:
raise UserNotFoundError(f"Автор не найден для сообщения {call.message.message_id}")
raise UserNotFoundError(
f"Автор не найден для сообщения {call.message.message_id}"
)
current_date = datetime.now()
date_to_unban = int((current_date + timedelta(days=7)).timestamp())
ban_author_id = call.from_user.id
await self.db.set_user_blacklist(
@@ -583,17 +800,21 @@ class BanService:
date_to_unban=date_to_unban,
ban_author=ban_author_id,
)
await self._get_bot(call.message).delete_message(chat_id=self.group_for_posts, message_id=call.message.message_id)
await self._get_bot(call.message).delete_message(
chat_id=self.group_for_posts, message_id=call.message.message_id
)
date_str = (current_date + timedelta(days=7)).strftime("%d.%m.%Y %H:%M")
try:
await send_text_message(author_id, call.message, MESSAGE_USER_BANNED_SPAM.format(date=date_str))
await send_text_message(
author_id, call.message, MESSAGE_USER_BANNED_SPAM.format(date=date_str)
)
except Exception as e:
if str(e) == ERROR_BOT_BLOCKED:
raise UserBlockedBotError("Пользователь заблокировал бота")
raise
logger.info(f"Пользователь {author_id} заблокирован за спам до {date_str}")
@track_time("ban_user", "ban_service")
@@ -603,7 +824,7 @@ class BanService:
user_name = await self.db.get_username(int(user_id))
if not user_name:
raise UserNotFoundError(f"Пользователь с ID {user_id} не найден в базе")
return user_name
@track_time("unlock_user", "ban_service")
@@ -614,7 +835,7 @@ class BanService:
user_name = await self.db.get_username(int(user_id))
if not user_name:
raise UserNotFoundError(f"Пользователь с ID {user_id} не найден в базе")
await delete_user_blacklist(int(user_id), self.db)
logger.info(f"Разблокирован пользователь с ID: {user_id} username:{user_name}")
return user_name