domovoy_bot/utils/redirect.py

169 lines
7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Универсальный шлюз перенаправления в личные сообщения (Zero-Spam Redirect)
Все ответы на команды и меню отправляются строго в ЛС, не засоряя группу.
"""
import asyncio
import logging
import os
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton, FSInputFile
logger = logging.getLogger(__name__)
async def delete_after_delay(msg: Message, delay: int = 15):
"""Фоновое удаление временного уведомления через delay секунд"""
try:
await asyncio.sleep(delay)
await msg.delete()
except Exception:
pass
async def respond_pm_or_redirect(
event: Message | CallbackQuery,
text: str,
reply_markup=None,
photo_path: str = None,
parse_mode: str = 'HTML',
start_param: str = 'start'
):
"""
Отправляет контент пользователю строго в личные сообщения.
В групповом чате команда тихо удаляется, а пользователю отправляется карточка в ЛС.
Если бот не может написать в ЛС первым (пользователь не нажал /start),
выводится короткая временная плашка с кнопкой перехода в ЛС, удаляемая через 15 сек.
"""
bot = event.bot
# --- Обработка CallbackQuery ---
if isinstance(event, CallbackQuery):
message = event.message
# Если инлайн-кнопку нажали в группе
if message and message.chat.type in ('group', 'supergroup'):
user_id = event.from_user.id
try:
if photo_path and os.path.exists(photo_path):
await bot.send_photo(
chat_id=user_id,
photo=FSInputFile(photo_path),
caption=text,
reply_markup=reply_markup,
parse_mode=parse_mode
)
else:
await bot.send_message(
chat_id=user_id,
text=text,
reply_markup=reply_markup,
parse_mode=parse_mode
)
await event.answer("Отправил вам в личные сообщения 📩", show_alert=False)
except Exception as e:
logger.info(f"Cannot PM user {user_id} on callback: {e}")
await event.answer(
"Пожалуйста, сначала откройте диалог с ботом в личке (@lkmthreeseven_bot)!",
show_alert=True
)
return
# Если инлайн-кнопку нажали в ЛС
await event.answer()
has_media = False
if message:
has_media = bool(
(hasattr(message, 'photo') and message.photo) or
(hasattr(message, 'video') and message.video) or
(hasattr(message, 'document') and message.document) or
(hasattr(message, 'content_type') and message.content_type != 'text')
)
if has_media or (photo_path and os.path.exists(photo_path)):
if message:
try:
await message.delete()
except Exception:
pass
if photo_path and os.path.exists(photo_path):
await bot.send_photo(
chat_id=event.from_user.id,
photo=FSInputFile(photo_path),
caption=text,
reply_markup=reply_markup,
parse_mode=parse_mode
)
else:
await bot.send_message(
chat_id=event.from_user.id,
text=text,
reply_markup=reply_markup,
parse_mode=parse_mode
)
else:
try:
await message.edit_text(text, reply_markup=reply_markup, parse_mode=parse_mode)
except Exception:
await message.answer(text, reply_markup=reply_markup, parse_mode=parse_mode)
return
# --- Обработка Message ---
if isinstance(event, Message):
user_id = event.from_user.id
# Если сообщение получено в личке — штатный ответ
if event.chat.type == 'private':
if photo_path and os.path.exists(photo_path):
await event.answer_photo(
FSInputFile(photo_path),
caption=text,
reply_markup=reply_markup,
parse_mode=parse_mode
)
else:
await event.answer(text, reply_markup=reply_markup, parse_mode=parse_mode)
return
# Если сообщение получено в группе или супергруппе:
# 1. Пытаемся тихо удалить сообщение-команду жильца
try:
await event.delete()
except Exception:
pass
# 2. Отправляем карточку/меню пользователю в ЛС
sent_to_pm = False
try:
if photo_path and os.path.exists(photo_path):
await bot.send_photo(
chat_id=user_id,
photo=FSInputFile(photo_path),
caption=text,
reply_markup=reply_markup,
parse_mode=parse_mode
)
else:
await bot.send_message(
chat_id=user_id,
text=text,
reply_markup=reply_markup,
parse_mode=parse_mode
)
sent_to_pm = True
except Exception as e:
logger.info(f"Cannot PM user {user_id}: {e}")
# 3. Если в личку отправить не удалось (пользователь не начинал диалог с ботом)
if not sent_to_pm:
bot_user = await bot.get_me()
url = f"https://t.me/{bot_user.username}?start={start_param}"
kb = InlineKeyboardMarkup(inline_keyboard=[[
InlineKeyboardButton(text="💬 Открыть в личке", url=url)
]])
user_mention = event.from_user.mention_html()
hint = await bot.send_message(
chat_id=event.chat.id,
text=f"👋 {user_mention}, чтобы не спамить в общий чат, перейдите в диалог с ботом:",
reply_markup=kb,
parse_mode='HTML'
)
asyncio.create_task(delete_after_delay(hint, 15))