136 lines
4.8 KiB
Python
136 lines
4.8 KiB
Python
"""События участников: добавление бота, вход и выход пользователей."""
|
||
|
||
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
|
||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||
|
||
from bot.services import alerts
|
||
from bot.services import users as users_service
|
||
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 "",
|
||
)
|
||
|
||
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})
|
||
|
||
# Подпись к фото ограничена 1024 символами; если не влезла — уйдёт в тело.
|
||
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:
|
||
"""Пользователь вышел или был исключён — удалить данные именно его."""
|
||
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 "",
|
||
)
|