intra_max_chatbot/bot/handlers/start.py
2026-03-26 00:54:10 +03:00

232 lines
7.6 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.

"""
Обработчики авторизации:
- bot_started — первый запуск / перезапуск
- /start команда
- Шаринг контакта
"""
import re
from maxapi.dispatcher import Router
from maxapi.filters import F
from maxapi.filters.contact import ContactFilter
from maxapi.types.attachments.attachment import ContactAttachmentPayload
from maxapi.types.attachments.contact import Contact as ContactAttachment
from maxapi.types.updates.bot_started import BotStarted
from maxapi.types.updates.message_created import MessageCreated
from maxapi.context import BaseContext
from maxapi.filters.command import Command
from bot.keyboards import contact_request_kb, main_menu_kb
from bot.logging_setup import get_logger
from bot.send_helper import bot_send
from bot.states import AuthStates
logger = get_logger("bot.handlers.start")
from database.queries.bot_users import (
authorize_bot_user,
block_bot_user,
get_bot_user_by_max_id,
upsert_bot_user,
)
from database.queries.org_users import find_by_phone
from database.queries.message_log import log_message
router = Router(router_id="start")
def normalize_phone(raw: str) -> str:
"""Убираем всё кроме цифр, заменяем ведущую 8 → 7."""
digits = re.sub(r"\D", "", raw)
if digits.startswith("8") and len(digits) == 11:
digits = "7" + digits[1:]
return digits
@router.bot_started()
async def on_bot_started(event: BotStarted, context: BaseContext) -> None:
user = event.user
bot_user = await upsert_bot_user(
max_user_id=user.user_id,
max_chat_id=event.chat_id,
username=getattr(user, "username", None),
first_name=getattr(user, "name", None),
)
if bot_user["is_blocked"]:
logger.info("🚫 bot_started blocked | user_id=%s", user.user_id)
await bot_send(
event.bot,
chat_id=event.chat_id,
user_id=user.user_id,
text=(
"⛔ Ваш номер не найден в системе. Доступ заблокирован.\n"
"Обратитесь к администратору для подключения."
),
)
return
if bot_user["is_authorized"]:
logger.info("✅ bot_started returning user | user_id=%s", user.user_id)
await context.set_state(None)
await bot_send(
event.bot,
chat_id=event.chat_id,
user_id=user.user_id,
text="👋 С возвращением! Выберите действие:",
attachments=[main_menu_kb()],
)
return
# Новый пользователь — запрашиваем контакт
logger.info("👋 bot_started new user | user_id=%s", user.user_id)
await context.set_state(AuthStates.waiting_contact)
await bot_send(
event.bot,
chat_id=event.chat_id,
user_id=user.user_id,
text=(
"👋 Добро пожаловать в систему технической поддержки!\n\n"
"Для входа нажмите кнопку ниже и поделитесь своим номером телефона."
),
attachments=[contact_request_kb()],
)
@router.message_created(Command("start"))
async def on_start_command(event: MessageCreated, context: BaseContext) -> None:
chat_id, user_id = event.get_ids()
if user_id is None or chat_id is None:
return
bot_user = await get_bot_user_by_max_id(user_id)
if bot_user is None:
# Первый раз — как bot_started
await upsert_bot_user(
max_user_id=user_id,
max_chat_id=chat_id,
username=None,
first_name=None,
)
await context.set_state(AuthStates.waiting_contact)
await bot_send(
event.bot,
chat_id=chat_id,
user_id=user_id,
text=(
"👋 Добро пожаловать!\n\n"
"Поделитесь номером телефона для доступа к системе."
),
attachments=[contact_request_kb()],
)
return
if bot_user["is_blocked"]:
await bot_send(
event.bot,
chat_id=chat_id,
user_id=user_id,
text="⛔ Доступ заблокирован. Обратитесь к администратору.",
)
return
if not bot_user["is_authorized"]:
await context.set_state(AuthStates.waiting_contact)
await bot_send(
event.bot,
chat_id=chat_id,
user_id=user_id,
text="Поделитесь номером телефона для доступа:",
attachments=[contact_request_kb()],
)
return
await context.set_state(None)
await bot_send(
event.bot,
chat_id=chat_id,
user_id=user_id,
text="Главное меню:",
attachments=[main_menu_kb()],
)
@router.message_created(ContactFilter(), states=AuthStates.waiting_contact)
async def on_contact_shared(
event: MessageCreated,
contact: ContactAttachment,
context: BaseContext,
) -> None:
chat_id, user_id = event.get_ids()
if user_id is None or chat_id is None:
return
# Извлекаем телефон из VCF
payload = contact.payload
if not isinstance(payload, ContactAttachmentPayload):
await bot_send(
event.bot,
chat_id=chat_id,
user_id=user_id,
text="Не удалось прочитать контакт. Попробуйте ещё раз.",
attachments=[contact_request_kb()],
)
return
vcf = payload.vcf
raw_phone = vcf.phone
if not raw_phone:
await bot_send(
event.bot,
chat_id=chat_id,
user_id=user_id,
text="В контакте не найден номер телефона. Попробуйте ещё раз.",
attachments=[contact_request_kb()],
)
return
phone = normalize_phone(raw_phone)
display_name = vcf.full_name
# Логируем входящий контакт
await log_message(
direction="in",
max_user_id=user_id,
max_chat_id=chat_id,
message_id=None,
text=f"[Контакт: {raw_phone}]",
)
# Ищем в белом списке
org_user = await find_by_phone(phone)
if org_user is None:
logger.warning("🚫 phone not found, blocking | user_id=%s phone=%s", user_id, phone)
await block_bot_user(max_user_id=user_id, phone=phone)
await context.set_state(None)
await bot_send(
event.bot,
chat_id=chat_id,
user_id=user_id,
text=(
"⛔ Ваш номер не найден в системе.\n"
"Доступ заблокирован. Обратитесь к администратору для подключения."
),
)
return
# Авторизуем
logger.info("✅ authorized | user_id=%s phone=%s org_id=%s", user_id, phone, org_user["org_id"])
await authorize_bot_user(
max_user_id=user_id,
phone=phone,
org_id=org_user["org_id"],
first_name=display_name,
)
await context.set_state(None)
await bot_send(
event.bot,
chat_id=chat_id,
user_id=user_id,
text="✅ Вы успешно авторизованы! Выберите действие:",
attachments=[main_menu_kb()],
)