115 lines
4.9 KiB
Python
115 lines
4.9 KiB
Python
from typing import Callable, Awaitable, Any, Dict
|
|
from aiogram import BaseMiddleware, Bot
|
|
from aiogram.types import TelegramObject, Message, CallbackQuery
|
|
from aiogram.exceptions import TelegramBadRequest
|
|
|
|
from middleware.loggers import loggers # ваш логгер
|
|
|
|
|
|
class SubscriptionMiddleware(BaseMiddleware):
|
|
"""
|
|
Middleware для проверки подписки пользователя на необходимые каналы.
|
|
Блокирует обработку команд, если пользователь не подписан.
|
|
|
|
Зачем нужен:
|
|
- Автоматическая проверка подписки для всех входящих сообщений
|
|
- Единая точка управления подписками
|
|
- Предотвращение доступа к функционалу без подписки
|
|
"""
|
|
|
|
def __init__(self, bot: Bot, channel_ids: list[int | str]):
|
|
"""
|
|
Инициализация middleware проверки подписки.
|
|
|
|
Args:
|
|
bot: Экземпляр бота
|
|
channel_ids: Список ID каналов/чатов для проверки подписки
|
|
"""
|
|
self.bot = bot
|
|
self.channel_ids = channel_ids
|
|
super().__init__()
|
|
|
|
async def __call__(
|
|
self,
|
|
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
|
|
event: TelegramObject,
|
|
data: Dict[str, Any]
|
|
) -> Any:
|
|
"""
|
|
Проверяет подписку пользователя перед обработкой команды.
|
|
"""
|
|
# Пропускаем не-сообщения и не-колбэки
|
|
if not isinstance(event, (Message, CallbackQuery)):
|
|
return await handler(event, data)
|
|
|
|
user_id: int = event.from_user.id
|
|
user_str: str = f"@{event.from_user.username}" if event.from_user.username else f"id{user_id}"
|
|
|
|
# Логируем начало проверки подписки
|
|
loggers.info(
|
|
text=f"Проверка подписки для пользователя",
|
|
log_type="SUBSCRIPTION_CHECK",
|
|
user=user_str
|
|
)
|
|
|
|
# Проверяем подписку на все required каналы
|
|
not_subscribed_channels: list[str] = []
|
|
|
|
for channel_id in self.channel_ids:
|
|
try:
|
|
member = await self.bot.get_chat_member(
|
|
chat_id=channel_id,
|
|
user_id=user_id
|
|
)
|
|
# Проверяем, что пользователь является участником
|
|
if member.status not in ['member', 'administrator', 'creator']:
|
|
not_subscribed_channels.append(str(channel_id))
|
|
|
|
except TelegramBadRequest as e:
|
|
loggers.error(
|
|
text=f"Ошибка проверки подписки на канал {channel_id}: {e}",
|
|
log_type="SUBSCRIPTION_ERROR",
|
|
user=user_str
|
|
)
|
|
|
|
# Если пользователь не подписан на некоторые каналы
|
|
if not_subscribed_channels:
|
|
loggers.warning(
|
|
text=f"Пользователь не подписан на каналы: {', '.join(not_subscribed_channels)}",
|
|
log_type="SUBSCRIPTION_FAILED",
|
|
user=user_str
|
|
)
|
|
|
|
warning_text = (
|
|
"📢 Для использования бота необходимо подписаться на наши каналы!\n\n"
|
|
"После подписки нажмите /start для продолжения."
|
|
)
|
|
|
|
# Создаем кнопку "Проверить подписку"
|
|
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
|
keyboard = InlineKeyboardMarkup(
|
|
inline_keyboard=[[
|
|
InlineKeyboardButton(
|
|
text="✅ Я подписался",
|
|
callback_data="check_subscription"
|
|
)
|
|
]]
|
|
)
|
|
|
|
if isinstance(event, Message):
|
|
await event.answer(warning_text, reply_markup=keyboard)
|
|
elif isinstance(event, CallbackQuery):
|
|
await event.message.answer(warning_text, reply_markup=keyboard)
|
|
await event.answer()
|
|
|
|
return None
|
|
|
|
# Логируем успешную проверку подписки
|
|
loggers.info(
|
|
text="Пользователь подписан на все required каналы",
|
|
log_type="SUBSCRIPTION_SUCCESS",
|
|
user=user_str
|
|
)
|
|
|
|
# Если подписка есть, продолжаем обработку
|
|
return await handler(event, data) |