""" Хендлер верификации жильцов """ import logging from aiogram import Router, F from aiogram.types import Message, CallbackQuery from aiogram.filters import Command from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from database.db import AsyncSessionLocal from database.models import User, VerificationRequest from utils.validators import validate_apartment from utils.decorators import admin_only, admin_callback_only import config from datetime import datetime logger = logging.getLogger(__name__) router = Router() @router.message(Command('верификация')) @router.message(Command('verification')) async def cmd_verification(message: Message): """Команда /верификация - статус верификации""" logger.info(f'Команда /верификация от user_id={message.from_user.id}') async with AsyncSessionLocal() as session: stmt = select(User).where(User.user_id == message.from_user.id) result = await session.execute(stmt) user = result.scalar_one_or_none() if not user: await message.answer( '❌ Вы не зарегистрированы.\n\n' 'Нажмите /start или отправьте номер квартиры.' ) return if user.verified: await message.answer( f'✅ Вы верифицированы\n\n' f'Квартира: {user.apartment}\n' f'Сообщений: {user.message_count}', parse_mode='HTML' ) else: # Проверяем, есть ли заявка stmt = select(VerificationRequest).where( VerificationRequest.user_id == message.from_user.id, VerificationRequest.status == 'pending' ) result = await session.execute(stmt) request = result.scalar_one_or_none() if request: await message.answer( f'⏳ Заявка на проверке\n\n' f'Квартира: {request.apartment}\n' f'Статус: Ожидает подтверждения\n\n' f'Администратор скоро подтвердит вашу заявку.', parse_mode='HTML' ) else: await message.answer( f'⏳ Вы не верифицированы\n\n' f'📝 Как пройти верификацию:\n\n' f'1. Отправьте номер квартиры текстом\n' f'2. Дождитесь подтверждения администратора\n\n' f'Примеры: 45, 12А, 100-1', parse_mode='HTML' ) @router.message(F.text.regexp(r'^\s*\d+[А-ЯA-Z]?[\s/-]?\d*[А-ЯA-Z]?\s*$')) async def handle_apartment_number(message: Message): """ Обработка номера квартиры Срабатывает и в личке, и в группе """ text = message.text.strip() user_id = message.from_user.id # Если админ отправил номер квартиры — автоматически верифицируем if config.is_admin(user_id): logger.info(f'Админ отправил номер квартиры: {text}, автоматически верифицируем') await auto_verify_admin(message, text) return logger.info(f'Получен номер квартиры: {text} от user_id={user_id}') # Проверяем валидность if not validate_apartment(text): logger.info(f'Неверный формат квартиры: {text}') return # Просто игнорируем, это не номер квартиры await process_verification(message, text) async def auto_verify_admin(message: Message, apartment: str): """Автоматическая верификация админа""" user_id = message.from_user.id apartment_upper = apartment.strip().upper() async with AsyncSessionLocal() as session: try: # Проверяем пользователя в БД stmt = select(User).where(User.user_id == user_id) result = await session.execute(stmt) user = result.scalar_one_or_none() if not user: # Создаём нового пользователя (админа) user = User( user_id=user_id, username=message.from_user.username, first_name=message.from_user.first_name, last_name=message.from_user.last_name, apartment=apartment_upper, verified=True, is_admin=True, verification_date=datetime.utcnow(), ) session.add(user) logger.info(f'Создан новый админ с квартирой {apartment_upper}') else: # Обновляем данные админа user.apartment = apartment_upper user.verified = True user.is_admin = True if not user.verification_date: user.verification_date = datetime.utcnow() logger.info(f'Админ верифицирован с квартирой {apartment_upper}') await session.commit() await message.answer( f'✅ Администратор верифицирован!\n\n' f'Квартира: {apartment_upper}\n' f'Статус: Владелец/Админ\n\n' f'Теперь вы можете использовать все админские функции.', parse_mode='HTML' ) except Exception as e: logger.error(f'Ошибка при авто-верификации админа: {e}', exc_info=True) await message.answer('❌ Произошла ошибка при верификации админа.') @router.message(F.text, F.chat.type == 'private') async def handle_any_text_in_private(message: Message): """ Обработка любого текста в личке Пытаемся распознать номер квартиры """ # Пропускаем если это админ if config.is_admin(message.from_user.id): return text = message.text.strip() logger.info(f'Текст в личке: {text} от user_id={message.from_user.id}') # Проверяем, похоже ли на номер квартиры if validate_apartment(text): logger.info(f'Распознан номер квартиры: {text}') await process_verification(message, text) else: # Если не похоже на квартиру — подсказываем logger.info(f'Не похоже на номер квартиры: {text}') # Не отвечаем на каждое сообщение, чтобы не спамить async def process_verification(message: Message, apartment: str): """Обработка верификации""" user_id = message.from_user.id apartment_upper = apartment.strip().upper() logger.info(f'Начало верификации: user_id={user_id}, apartment={apartment_upper}') async with AsyncSessionLocal() as session: try: # Проверяем пользователя в БД stmt = select(User).where(User.user_id == user_id) result = await session.execute(stmt) user = result.scalar_one_or_none() if not user: logger.info(f'Создаём нового пользователя: user_id={user_id}') # Создаём нового пользователя user = User( user_id=user_id, username=message.from_user.username, first_name=message.from_user.first_name, last_name=message.from_user.last_name, apartment=apartment_upper, ) session.add(user) else: logger.info(f'Пользователь найден: verified={user.verified}, apartment={user.apartment}') # Если уже верифицирован if user.verified: # Но квартиры нет — сохраняем! if not user.apartment: user.apartment = apartment_upper await session.commit() logger.info(f'Сохранена квартира для верифицированного: {apartment_upper}') await message.answer( f'✅ Квартира сохранена!\n\n' f'Квартира: {apartment_upper}\n\n' f'Вы уже были верифицированы.\n' f'Теперь ваша квартира привязана к аккаунту!', parse_mode='HTML' ) return else: # Уже есть квартира await message.answer( f'✅ Вы уже верифицированы (кв. {user.apartment})\n\n' 'Если нужно изменить квартиру, обратитесь к администратору.', parse_mode='HTML' ) return # Обновляем квартиру для неверифицированных user.apartment = apartment_upper # Создаём заявку на верификацию (если ещё нет активной) stmt = select(VerificationRequest).where( VerificationRequest.user_id == user_id, VerificationRequest.status == 'pending' ) result = await session.execute(stmt) existing_request = result.scalar_one_or_none() if not existing_request: logger.info(f'Создаём заявку на верификацию') verification = VerificationRequest( user_id=user_id, apartment=apartment_upper, ) session.add(verification) await session.commit() logger.info(f'Верификация сохранена в БД') # Уведомляем пользователя await message.answer( f'✅ Заявка на верификацию создана!\n\n' f'Квартира: {apartment_upper}\n' f'Статус: На проверке\n\n' f'Администратор проверит данные в ближайшее время.\n' f'После подтверждения вы получите доступ ко всем функциям.', parse_mode='HTML' ) # Уведомляем админа try: user_name = message.from_user.first_name username = f'@{message.from_user.username}' if message.from_user.username else 'нет' await message.bot.send_message( config.ADMIN_USER_ID, f'🔔 Новая заявка на верификацию\n\n' f'Жилец: {user_name} {username}\n' f'Квартира: {apartment_upper}\n' f'ID: {user_id}\n\n' f'Для подтверждения используйте:\n' f'/verify_approve {user_id}', parse_mode='HTML' ) logger.info(f'Админ уведомлён') except Exception as e: logger.error(f'Не удалось уведомить админа: {e}') except Exception as e: logger.error(f'Ошибка при верификации: {e}', exc_info=True) await message.answer('❌ Произошла ошибка при верификации. Попробуйте позже.') @router.message(Command('verify')) @admin_only async def cmd_verify_list(message: Message): """Список заявок на верификацию (админ)""" logger.info(f'Админ запросил список заявок') async with AsyncSessionLocal() as session: stmt = ( select(VerificationRequest) .where(VerificationRequest.status == 'pending') .order_by(VerificationRequest.created_at) ) result = await session.execute(stmt) requests = list(result.scalars().all()) if not requests: await message.answer('✅ Все заявки обработаны') return text = f'📋 Заявки на верификацию ({len(requests)}):\n\n' for req in requests: # Получаем имя пользователя stmt = select(User).where(User.user_id == req.user_id) result = await session.execute(stmt) user = result.scalar_one_or_none() name = user.full_name if user else f'user{req.user_id}' text += f'• {name} — кв. {req.apartment}\n' text += f' ID: {req.user_id}\n\n' text += 'Для подтверждения используйте /verify_approve ' await message.answer(text, parse_mode='HTML') @router.message(Command('verify_approve')) @admin_only async def cmd_verify_approve(message: Message): """Подтверждение верификации (работает и в личке, и в группе)""" logger.info(f'Админ подтверждает верификацию в чате типа: {message.chat.type}') # Парсим ID пользователя из команды args = message.text.split() if len(args) < 2: await message.answer('Использование: /verify_approve ') return try: user_id = int(args[1]) except ValueError: await message.answer('❌ Неверный ID пользователя') return async with AsyncSessionLocal() as session: # Находим пользователя stmt = select(User).where(User.user_id == user_id) result = await session.execute(stmt) user = result.scalar_one_or_none() if not user: await message.answer('❌ Пользователь не найден') return # Верифицируем user.verified = True user.verification_date = datetime.utcnow() # Обновляем заявку stmt = select(VerificationRequest).where( VerificationRequest.user_id == user_id, VerificationRequest.status == 'pending' ) result = await session.execute(stmt) request = result.scalar_one_or_none() if request: request.status = 'approved' request.reviewed_at = datetime.utcnow() request.reviewed_by = message.from_user.id await session.commit() logger.info(f'Пользователь {user_id} верифицирован') # Уведомляем пользователя (в личку) try: await message.bot.send_message( user_id, f'✅ Верификация подтверждена!\n\n' f'Квартира: {user.apartment}\n' f'Теперь у вас есть доступ ко всем функциям бота.', parse_mode='HTML' ) except Exception as e: logger.error(f'Не удалось уведомить пользователя: {e}') # Отвечаем админу (в тот же чат где отправил команду) await message.answer( f'✅ Пользователь верифицирован!\n\n' f'Жилец: {user.full_name}\n' f'Квартира: {user.apartment}\n' f'ID: {user_id}', parse_mode='HTML' ) @router.message(Command('verify_reject')) @admin_only async def cmd_verify_reject(message: Message): """Отклонение верификации""" args = message.text.split(maxsplit=2) if len(args) < 2: await message.answer('Использование: /verify_reject [причина]') return try: user_id = int(args[1]) except ValueError: await message.answer('❌ Неверный ID пользователя') return reason = args[2] if len(args) > 2 else 'Без указания причины' async with AsyncSessionLocal() as session: # Находим пользователя stmt = select(User).where(User.user_id == user_id) result = await session.execute(stmt) user = result.scalar_one_or_none() if not user: await message.answer('❌ Пользователь не найден') return # Обновляем заявку stmt = select(VerificationRequest).where( VerificationRequest.user_id == user_id, VerificationRequest.status == 'pending' ) result = await session.execute(stmt) request = result.scalar_one_or_none() if request: request.status = 'rejected' request.reviewed_at = datetime.utcnow() request.reviewed_by = message.from_user.id request.reject_reason = reason await session.commit() # Уведомляем пользователя try: await message.bot.send_message( user_id, f'❌ Верификация отклонена\n\n' f'Причина: {reason}\n\n' f'Если вы считаете это ошибкой, обратитесь к администратору.' ) except Exception as e: logger.error(f'Не удалось уведомить пользователя: {e}') await message.answer(f'❌ Заявка пользователя отклонена')