DispelEvilBot/bot/handlers/member_events.py

147 lines
5.2 KiB
Python
Raw Permalink 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.

"""События участников: добавление бота, вход и выход пользователей."""
from __future__ import annotations
import logging
from aiogram import Router
from aiogram.enums import ChatMemberStatus
from aiogram.filters import JOIN_TRANSITION, LEAVE_TRANSITION, ChatMemberUpdatedFilter
from aiogram.types import ChatMemberUpdated, FSInputFile, User
from aiogram.utils.keyboard import InlineKeyboardBuilder
from bot.services import alerts
from bot.services import users as users_service
from bot.services.sequences import send_sequence
from common.analytics import EventType, record
from common.config import get_settings
from common.texts import system, welcome
from common.texts.render import render
from common.texts.store import get_text
logger = logging.getLogger(__name__)
router = Router(name="member_events")
@router.my_chat_member(ChatMemberUpdatedFilter(member_status_changed=JOIN_TRANSITION))
async def bot_added_handler(event: ChatMemberUpdated) -> None:
"""Бот добавлен в чат."""
settings = get_settings()
if event.chat.id not in {settings.chat_id, settings.alert_chat_id}:
await event.bot.send_message(chat_id=event.chat.id, text=system.MSG_BOT_FOREIGN_CHAT)
await event.bot.leave_chat(event.chat.id)
else:
await event.bot.send_message(chat_id=event.chat.id, text=system.MSG_BOT_ADDED)
@router.chat_member(ChatMemberUpdatedFilter(member_status_changed=JOIN_TRANSITION))
async def welcome_handler(event: ChatMemberUpdated) -> None:
"""Пользователь вошёл в чат — приветствие."""
user = event.new_chat_member.user
if user.is_bot:
return
if event.chat.id != get_settings().chat_id:
return
await users_service.register_from_event(event)
await alerts.notify_join(
event.bot,
username=user.username,
user_id=user.id,
chat_title=event.chat.title or "",
)
if await send_sequence(event.bot, event.chat.id, "welcome", {"username": user.username}):
return
await _send_fallback_welcome(event, user)
async def _send_fallback_welcome(event: ChatMemberUpdated, user: User) -> None:
"""Приветствие по старым шаблонам, если последовательность не настроена."""
builder = InlineKeyboardBuilder()
builder.button(text=welcome.MSG_WELCOME_BUTTON_CAPTION, url=welcome.MSG_WELCOME_BUTTON_URL)
markup = builder.as_markup()
caption, body = welcome.split_welcome(
await get_text("welcome.text"),
await get_text("welcome.text2"),
await get_text("welcome.text3"),
)
caption = render(caption, {"username": user.username})
body = render(body, {"username": user.username})
if len(caption) > 1024:
body = caption + (f"\n\n{body}" if body else "")
caption = ""
if body:
await event.bot.send_photo(
chat_id=event.chat.id,
photo=FSInputFile("images/welcome_image.jpg"),
caption=caption or None,
)
await event.bot.send_message(
chat_id=event.chat.id,
text=body,
reply_markup=markup,
)
else:
await event.bot.send_photo(
chat_id=event.chat.id,
photo=FSInputFile("images/welcome_image.jpg"),
caption=caption or None,
reply_markup=markup,
)
@router.chat_member(ChatMemberUpdatedFilter(member_status_changed=LEAVE_TRANSITION))
async def goodbye_handler(event: ChatMemberUpdated) -> None:
"""Пользователь вышел или был исключён — удалить данные именно его."""
if event.chat.id != get_settings().chat_id:
return
leaver = event.old_chat_member.user
if leaver.is_bot:
return
actor = event.from_user
kicked = event.new_chat_member.status == ChatMemberStatus.KICKED
by_self = actor is None or actor.id == leaver.id
record(
EventType.LEAVE,
user_id=leaver.id,
username=leaver.username,
chat_id=event.chat.id,
payload={
"chat_title": event.chat.title or "",
"kicked": kicked,
"by_id": actor.id if actor and not by_self else 0,
"by_username": actor.username if actor and not by_self else "",
},
)
await users_service.delete_user(leaver.id)
logger.info("Удалён пользователь %s после выхода из чата", leaver.id)
# Исключение самим ботом (кнопка в админке): уведомление уже отправила админка.
if kicked and actor is not None and actor.id == event.bot.id:
return
if kicked and not by_self:
await alerts.notify_kick(
event.bot,
actor_username=actor.username if actor else None,
actor_id=actor.id if actor else 0,
username=leaver.username,
user_id=leaver.id,
chat_title=event.chat.title or "",
)
else:
await alerts.notify_leave(
event.bot,
username=leaver.username,
user_id=leaver.id,
chat_title=event.chat.title or "",
)