import re from aiogram import F, Router from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from core.settings.web_config import is_email_binding_enabled from database.identities import get_identity_by_email, get_or_create_identity_for_tg from handlers.buttons import BACK from handlers.utils import edit_or_send_message from logger import logger router = Router(name="email_binding") EMAIL_RE = re.compile(r"^[^\s@]+@[^\s@]+\.[^\s@]+$") class EmailBindingState(StatesGroup): waiting_for_email = State() @router.callback_query(F.data == "bind_email") async def prompt_email(callback: CallbackQuery, state: FSMContext, session) -> None: if not is_email_binding_enabled(): await callback.answer("Привязка почты отключена", show_alert=True) return identity = await get_or_create_identity_for_tg(session, callback.from_user.id) if identity.email: await callback.answer("Почта уже привязана", show_alert=True) return builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text=BACK, callback_data="profile")) await edit_or_send_message( target_message=callback.message, text=( "📧 Привязка почты\n\n" "Укажите email — он понадобится для входа на сайт, " "если возникнут проблемы с Telegram.\n\n" "Отправьте адрес сообщением." ), reply_markup=builder.as_markup(), ) await state.set_state(EmailBindingState.waiting_for_email) await callback.answer() @router.message(EmailBindingState.waiting_for_email) async def receive_email(message: Message, state: FSMContext, session) -> None: raw = (message.text or "").strip().lower() if not EMAIL_RE.match(raw) or len(raw) > 255: await message.answer("❌ Неверный формат email. Попробуйте ещё раз.") return existing = await get_identity_by_email(session, raw) if existing and existing.tg_id and existing.tg_id != message.from_user.id: await message.answer("❌ Этот email уже занят другим пользователем.") return identity = await get_or_create_identity_for_tg(session, message.from_user.id) if identity.email: await state.clear() await message.answer("ℹ️ Почта уже была привязана.") return if existing and existing.id != identity.id: logger.warning( "email_binding: identity collision tg_id=%s wants email=%s already on identity_id=%s", message.from_user.id, raw, existing.id, ) await message.answer("❌ Этот email уже занят. Используйте другой.") return identity.email = raw await session.flush() await state.clear() builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="👤 В кабинет", callback_data="profile")) await message.answer( f"✅ Почта {raw} привязана.", reply_markup=builder.as_markup(), )