- Introduced `DependenciesMiddleware` and `BlacklistMiddleware` for enhanced request handling across all routers. - Refactored admin handlers to utilize new middleware, improving access control and error handling. - Updated the `admin_router` to include middleware for access checks and streamlined the process of banning users. - Enhanced the structure of admin handler imports for better organization and maintainability. - Improved error handling in various admin functions to ensure robust user interactions.
250 lines
13 KiB
Python
250 lines
13 KiB
Python
import html
|
||
from datetime import datetime, timedelta
|
||
from typing import Dict, Any
|
||
|
||
from aiogram import Bot
|
||
from aiogram.types import CallbackQuery
|
||
|
||
from helper_bot.utils.helper_func import (
|
||
send_text_message, send_photo_message, send_video_message,
|
||
send_video_note_message, send_audio_message, send_voice_message,
|
||
send_media_group_to_channel, delete_user_blacklist
|
||
)
|
||
from helper_bot.keyboards.keyboards import create_keyboard_for_ban_reason
|
||
from .exceptions import (
|
||
UserBlockedBotError, PostNotFoundError, UserNotFoundError,
|
||
PublishError, BanError
|
||
)
|
||
from .constants import (
|
||
CONTENT_TYPE_TEXT, CONTENT_TYPE_PHOTO, CONTENT_TYPE_VIDEO,
|
||
CONTENT_TYPE_VIDEO_NOTE, CONTENT_TYPE_AUDIO, CONTENT_TYPE_VOICE,
|
||
CONTENT_TYPE_MEDIA_GROUP, MESSAGE_POST_PUBLISHED, MESSAGE_POST_DECLINED,
|
||
MESSAGE_USER_BANNED_SPAM, ERROR_BOT_BLOCKED
|
||
)
|
||
from logs.custom_logger import logger
|
||
|
||
|
||
class PostPublishService:
|
||
def __init__(self, bot: Bot, db, settings: Dict[str, Any]):
|
||
self.bot = bot
|
||
self.db = db
|
||
self.settings = settings
|
||
self.group_for_posts = settings['Telegram']['group_for_posts']
|
||
self.main_public = settings['Telegram']['main_public']
|
||
self.important_logs = settings['Telegram']['important_logs']
|
||
|
||
async def publish_post(self, call: CallbackQuery) -> None:
|
||
"""Основной метод публикации поста"""
|
||
content_type = call.message.content_type
|
||
|
||
if content_type == CONTENT_TYPE_TEXT and call.message.text != CONTENT_TYPE_MEDIA_GROUP:
|
||
await self._publish_text_post(call)
|
||
elif content_type == CONTENT_TYPE_PHOTO:
|
||
await self._publish_photo_post(call)
|
||
elif content_type == CONTENT_TYPE_VIDEO:
|
||
await self._publish_video_post(call)
|
||
elif content_type == CONTENT_TYPE_VIDEO_NOTE:
|
||
await self._publish_video_note_post(call)
|
||
elif content_type == CONTENT_TYPE_AUDIO:
|
||
await self._publish_audio_post(call)
|
||
elif content_type == CONTENT_TYPE_VOICE:
|
||
await self._publish_voice_post(call)
|
||
elif call.message.text == CONTENT_TYPE_MEDIA_GROUP:
|
||
await self._publish_media_group(call)
|
||
else:
|
||
raise PublishError(f"Неподдерживаемый тип контента: {content_type}")
|
||
|
||
async def _publish_text_post(self, call: CallbackQuery) -> None:
|
||
"""Публикация текстового поста"""
|
||
text_post = html.escape(str(call.message.text))
|
||
author_id = self._get_author_id(call.message.message_id)
|
||
|
||
await send_text_message(self.main_public, call.message, text_post)
|
||
await self._delete_post_and_notify_author(call, author_id)
|
||
logger.info(f'Текст сообщения опубликован в канале {self.main_public}.')
|
||
|
||
async def _publish_photo_post(self, call: CallbackQuery) -> None:
|
||
"""Публикация поста с фото"""
|
||
text_post_with_photo = html.escape(str(call.message.caption))
|
||
author_id = self._get_author_id(call.message.message_id)
|
||
|
||
await send_photo_message(self.main_public, call.message, call.message.photo[-1].file_id, text_post_with_photo)
|
||
await self._delete_post_and_notify_author(call, author_id)
|
||
logger.info(f'Пост с фото опубликован в канале {self.main_public}.')
|
||
|
||
async def _publish_video_post(self, call: CallbackQuery) -> None:
|
||
"""Публикация поста с видео"""
|
||
text_post_with_photo = html.escape(str(call.message.caption))
|
||
author_id = self._get_author_id(call.message.message_id)
|
||
|
||
await send_video_message(self.main_public, call.message, call.message.video.file_id, text_post_with_photo)
|
||
await self._delete_post_and_notify_author(call, author_id)
|
||
logger.info(f'Пост с видео опубликован в канале {self.main_public}.')
|
||
|
||
async def _publish_video_note_post(self, call: CallbackQuery) -> None:
|
||
"""Публикация поста с кружком"""
|
||
author_id = self._get_author_id(call.message.message_id)
|
||
|
||
await send_video_note_message(self.main_public, call.message, call.message.video_note.file_id)
|
||
await self._delete_post_and_notify_author(call, author_id)
|
||
logger.info(f'Пост с кружком опубликован в канале {self.main_public}.')
|
||
|
||
async def _publish_audio_post(self, call: CallbackQuery) -> None:
|
||
"""Публикация поста с аудио"""
|
||
text_post_with_photo = html.escape(str(call.message.caption))
|
||
author_id = self._get_author_id(call.message.message_id)
|
||
|
||
await send_audio_message(self.main_public, call.message, call.message.audio.file_id, text_post_with_photo)
|
||
await self._delete_post_and_notify_author(call, author_id)
|
||
logger.info(f'Пост с аудио опубликован в канале {self.main_public}.')
|
||
|
||
async def _publish_voice_post(self, call: CallbackQuery) -> None:
|
||
"""Публикация поста с войсом"""
|
||
author_id = self._get_author_id(call.message.message_id)
|
||
|
||
await send_voice_message(self.main_public, call.message, call.message.voice.file_id)
|
||
await self._delete_post_and_notify_author(call, author_id)
|
||
logger.info(f'Пост с войсом опубликован в канале {self.main_public}.')
|
||
|
||
async def _publish_media_group(self, call: CallbackQuery) -> None:
|
||
"""Публикация медиагруппы"""
|
||
post_content = self.db.get_post_content_from_telegram_by_last_id(call.message.message_id)
|
||
pre_text = self.db.get_post_text_from_telegram_by_last_id(call.message.message_id)
|
||
post_text = html.escape(str(pre_text))
|
||
author_id = self._get_author_id_for_media_group(call.message.message_id)
|
||
|
||
await send_media_group_to_channel(bot=self.bot, chat_id=self.main_public, post_content=post_content, post_text=post_text)
|
||
await self._delete_media_group_and_notify_author(call, author_id)
|
||
|
||
async def decline_post(self, call: CallbackQuery) -> None:
|
||
"""Отклонение поста"""
|
||
content_type = call.message.content_type
|
||
|
||
if (content_type == CONTENT_TYPE_TEXT and call.message.text != CONTENT_TYPE_MEDIA_GROUP) or \
|
||
content_type in [CONTENT_TYPE_PHOTO, CONTENT_TYPE_AUDIO, CONTENT_TYPE_VOICE, CONTENT_TYPE_VIDEO, CONTENT_TYPE_VIDEO_NOTE]:
|
||
await self._decline_single_post(call)
|
||
elif call.message.text == CONTENT_TYPE_MEDIA_GROUP:
|
||
await self._decline_media_group(call)
|
||
else:
|
||
raise PublishError(f"Неподдерживаемый тип контента для отклонения: {content_type}")
|
||
|
||
async def _decline_single_post(self, call: CallbackQuery) -> None:
|
||
"""Отклонение одиночного поста"""
|
||
author_id = self._get_author_id(call.message.message_id)
|
||
await self.bot.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:
|
||
if str(e) == ERROR_BOT_BLOCKED:
|
||
raise UserBlockedBotError("Пользователь заблокировал бота")
|
||
raise
|
||
logger.info(f'Сообщение отклонено админом {call.from_user.full_name} (ID: {call.from_user.id}).')
|
||
|
||
async def _decline_media_group(self, call: CallbackQuery) -> None:
|
||
"""Отклонение медиагруппы"""
|
||
post_ids = self.db.get_post_ids_from_telegram_by_last_id(call.message.message_id)
|
||
message_ids = [row[0] for row in post_ids]
|
||
message_ids.append(call.message.message_id)
|
||
|
||
author_id = self._get_author_id_for_media_group(call.message.message_id)
|
||
await self.bot.delete_messages(chat_id=self.group_for_posts, message_ids=message_ids)
|
||
try:
|
||
await send_text_message(author_id, call.message, MESSAGE_POST_DECLINED)
|
||
except Exception as e:
|
||
if str(e) == ERROR_BOT_BLOCKED:
|
||
raise UserBlockedBotError("Пользователь заблокировал бота")
|
||
raise
|
||
|
||
def _get_author_id(self, message_id: int) -> int:
|
||
"""Получение ID автора по ID сообщения"""
|
||
author_id = self.db.get_author_id_by_message_id(message_id)
|
||
if not author_id:
|
||
raise PostNotFoundError(f"Автор не найден для сообщения {message_id}")
|
||
return author_id
|
||
|
||
def _get_author_id_for_media_group(self, message_id: int) -> int:
|
||
"""Получение ID автора для медиагруппы"""
|
||
author_id = self.db.get_author_id_by_helper_message_id(message_id)
|
||
if not author_id:
|
||
raise PostNotFoundError(f"Автор не найден для медиагруппы {message_id}")
|
||
return author_id
|
||
|
||
async def _delete_post_and_notify_author(self, call: CallbackQuery, author_id: int) -> None:
|
||
"""Удаление поста и уведомление автора"""
|
||
await self.bot.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:
|
||
if str(e) == ERROR_BOT_BLOCKED:
|
||
raise UserBlockedBotError("Пользователь заблокировал бота")
|
||
raise
|
||
|
||
async def _delete_media_group_and_notify_author(self, call: CallbackQuery, author_id: int) -> None:
|
||
"""Удаление медиагруппы и уведомление автора"""
|
||
post_ids = self.db.get_post_ids_from_telegram_by_last_id(call.message.message_id)
|
||
message_ids = [row[0] for row in post_ids]
|
||
message_ids.append(call.message.message_id)
|
||
await self.bot.delete_messages(chat_id=self.group_for_posts, message_ids=message_ids)
|
||
try:
|
||
await send_text_message(author_id, call.message, MESSAGE_POST_PUBLISHED)
|
||
except Exception as e:
|
||
if str(e) == ERROR_BOT_BLOCKED:
|
||
raise UserBlockedBotError("Пользователь заблокировал бота")
|
||
raise
|
||
|
||
|
||
class BanService:
|
||
def __init__(self, bot: Bot, db, settings: Dict[str, Any]):
|
||
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']
|
||
|
||
async def ban_user_from_post(self, call: CallbackQuery) -> None:
|
||
"""Бан пользователя за спам"""
|
||
author_id = self.db.get_author_id_by_message_id(call.message.message_id)
|
||
if not author_id:
|
||
raise UserNotFoundError(f"Автор не найден для сообщения {call.message.message_id}")
|
||
|
||
user_name = self.db.get_username(user_id=author_id)
|
||
current_date = datetime.now()
|
||
date_to_unban = current_date + timedelta(days=7)
|
||
|
||
self.db.set_user_blacklist(
|
||
user_id=author_id,
|
||
user_name=user_name,
|
||
message_for_user="Спам",
|
||
date_to_unban=date_to_unban
|
||
)
|
||
|
||
await self.bot.delete_message(chat_id=self.group_for_posts, message_id=call.message.message_id)
|
||
|
||
date_str = date_to_unban.strftime("%d.%m.%Y %H:%M")
|
||
try:
|
||
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}")
|
||
|
||
async def ban_user(self, user_id: str, user_name: str) -> str:
|
||
"""Бан пользователя по ID"""
|
||
user_name = self.db.get_username(user_id=user_id)
|
||
if not user_name:
|
||
raise UserNotFoundError(f"Пользователь с ID {user_id} не найден в базе")
|
||
|
||
return user_name
|
||
|
||
async def unlock_user(self, user_id: str) -> str:
|
||
"""Разблокировка пользователя"""
|
||
user_name = self.db.get_username(user_id=user_id)
|
||
if not user_name:
|
||
raise UserNotFoundError(f"Пользователь с ID {user_id} не найден в базе")
|
||
|
||
delete_user_blacklist(user_id, self.db)
|
||
logger.info(f"Разблокирован пользователь с ID: {user_id} username:{user_name}")
|
||
return user_name
|