"""
Обработчики команд /start и /help
"""
from datetime import datetime
from aiogram import Router, F
from aiogram.types import Message
from aiogram.filters import Command, CommandStart
from aiogram.fsm.context import FSMContext
from config import config
from models.user import User
from services.infrastructure.database import DatabaseService
from services.auth.auth_new import AuthService
from services.utils import UtilsService
from services.business.user_service import UserService
from services.business.message_service import MessageService
from services.infrastructure.logger import get_logger
from services.infrastructure.metrics import get_metrics_service, track_message_processing
from keyboards.reply import get_main_keyboard_for_user, get_admin_reply_keyboard
from keyboards.inline import get_admin_keyboard
from dependencies import inject_start_services, inject_link_services, inject_main_menu_services
logger = get_logger(__name__)
router = Router()
async def _create_welcome_message(user: User, referral_link: str) -> str:
"""Создание приветственного сообщения"""
welcome_text = f"👋 Добро пожаловать, {user.display_name}!\n\n"
welcome_text += "🤖 Я бот для анонимных вопросов.\n\n"
welcome_text += "📝 Как это работает:\n"
welcome_text += "• Поделитесь своей ссылкой с друзьями\n"
welcome_text += "• Они смогут задать вам анонимные вопросы\n"
welcome_text += "• Вы получите уведомления и сможете ответить\n\n"
welcome_text += f"🔗 Ваша персональная ссылка:\n"
welcome_text += f"{referral_link}\n\n"
welcome_text += "💡 Совет: Скопируйте ссылку и поделитесь ею в социальных сетях!"
return welcome_text
async def _process_start_command(
message: Message,
user_service: UserService,
auth: AuthService,
utils: UtilsService,
message_service: MessageService,
validator
) -> User:
"""Обработка команды /start без аргументов"""
# Валидируем Telegram ID пользователя
user_id_validation = validator.validate_telegram_id(message.from_user.id)
if not user_id_validation:
logger.error(f"❌ Невалидный Telegram ID: {message.from_user.id}")
await message_service.send_message(
message,
"❌ Ошибка: недопустимый ID пользователя.",
get_main_keyboard_for_user(message.from_user.id)
)
raise ValueError(f"Invalid Telegram ID: {message.from_user.id}")
# Создаем или обновляем пользователя
user = await user_service.create_or_update_user(message.from_user, message.chat.id)
# Проверяем, является ли пользователь админом
is_admin = auth.is_admin(user.telegram_id)
# Генерируем персональную ссылку
bot_info = await message.bot.get_me()
referral_link = user_service.generate_referral_link(bot_info.username, user)
# Создаем приветственное сообщение
welcome_text = await _create_welcome_message(user, referral_link)
# Выбираем клавиатуру в зависимости от роли
if is_admin:
keyboard = get_admin_reply_keyboard()
else:
keyboard = get_main_keyboard_for_user(user.telegram_id)
logger.info(f"⌨️ Создана клавиатура для пользователя {user.telegram_id}: {type(keyboard).__name__}")
# Отправляем сообщение
await message_service.send_message(message, welcome_text, keyboard)
logger.info(f"✅ Приветственное сообщение отправлено пользователю {user.telegram_id}")
return user
@router.message(CommandStart())
@track_message_processing("start_command")
@inject_start_services
async def cmd_start(
message: Message,
state: FSMContext,
user_service: UserService,
auth: AuthService,
utils: UtilsService,
message_service: MessageService,
validator,
**kwargs
):
"""Обработчик команды /start"""
try:
logger.info(f"🚀 Команда /start от пользователя {message.from_user.id} ({message.from_user.first_name})")
# Сбрасываем состояние FSM при команде /start
await state.clear()
logger.info(f"🔄 Состояние FSM сброшено для пользователя {message.from_user.id}")
# Получаем аргументы команды
args = message.text.split()[1:] if len(message.text.split()) > 1 else []
# Обрабатываем deep linking если есть аргументы
if args:
logger.info(f"🔗 Обработка deep link: {args[0]}")
await handle_deep_link(message, args[0], user_service, state, message_service, validator)
else:
# Обрабатываем обычную команду /start
await _process_start_command(message, user_service, auth, utils, message_service, validator)
except Exception as e:
logger.error(f"💥 Ошибка в обработчике /start: {e}")
await message_service.send_error_message(
message,
"❌ Произошла ошибка при запуске бота. Попробуйте позже."
)
async def handle_deep_link(
message: Message,
ref_code: str,
user_service: UserService,
state: FSMContext,
message_service: MessageService,
validator
):
"""Обработка deep linking для анонимных вопросов"""
try:
# Валидируем Telegram ID пользователя
user_id_validation = validator.validate_telegram_id(message.from_user.id)
if not user_id_validation:
logger.error(f"❌ Невалидный Telegram ID: {message.from_user.id}")
await message_service.send_message(
message,
"❌ Ошибка: недопустимый ID пользователя.",
get_main_keyboard_for_user(message.from_user.id)
)
return
# Создаем или обновляем пользователя (важно для любого /start)
current_user = await user_service.create_or_update_user(message.from_user, message.chat.id)
logger.info(f"✅ Пользователь создан/обновлен при deep link: {current_user.telegram_id}")
# Валидируем deep link
validation_result = validator.validate_deep_link(ref_code)
if not validation_result:
logger.warning(f"⚠️ Невалидный deep link от пользователя {message.from_user.id}: {ref_code}")
await message_service.send_message(
message,
f"❌ {validation_result.error_message}",
get_main_keyboard_for_user(message.from_user.id)
)
return
# Используем санитизированное значение
ref_code = validation_result.sanitized_value
if not ref_code.startswith('ref_'):
await message_service.send_message(
message,
"❌ Неверная ссылка.",
get_main_keyboard_for_user(message.from_user.id)
)
return
# Извлекаем анонимный ID из реферального кода
anonymous_id = ref_code[4:] # Убираем 'ref_'
# Ищем пользователя по profile_link
target_user = await user_service.get_user_by_profile_link(anonymous_id)
if not target_user:
await message_service.send_message(
message,
"❌ Пользователь, на которого вы перешли, не найден.",
get_main_keyboard_for_user(message.from_user.id)
)
return
# Отправляем сообщение о переходе по ссылке
deep_link_text = (
f"👋 Вы перешли по ссылке пользователя {target_user.display_name}!\n\n"
f"📝 Теперь вы можете задать анонимный вопрос.\n"
f"Просто отправьте ваше сообщение, и оно будет передано получателю."
)
await message_service.send_message(
message,
deep_link_text,
get_main_keyboard_for_user(message.from_user.id)
)
# Устанавливаем состояние ожидания вопроса
from aiogram.fsm.state import State, StatesGroup
class QuestionStates(StatesGroup):
waiting_for_question = State()
await state.set_state(QuestionStates.waiting_for_question)
await state.update_data(target_user_id=target_user.telegram_id)
except Exception as e:
logger.error(f"Ошибка при обработке deep link: {e}")
await message_service.send_error_message(
message,
"❌ Произошла ошибка при обработке ссылки."
)
@router.message(Command("help"))
async def cmd_help(message: Message):
"""Обработчик команды /help"""
help_text = "📖 Справка по боту\n\n"
help_text += "🤖 Основные команды:\n"
help_text += "/start - Запуск бота и получение персональной ссылки\n"
help_text += "/help - Показать эту справку\n"
help_text += "/stats - Показать статистику (только для админов)\n\n"
help_text += "📝 Как задать анонимный вопрос:\n"
help_text += "1. Перейдите по персональной ссылке пользователя\n"
help_text += "2. Отправьте ваш вопрос боту\n"
help_text += "3. Вопрос будет передан получателю анонимно\n\n"
help_text += "💬 Как ответить на вопрос:\n"
help_text += "1. Получите уведомление о новом вопросе\n"
help_text += "2. Нажмите кнопку 'Ответить'\n"
help_text += "3. Введите ваш ответ\n"
help_text += "4. Ответ будет отправлен анонимно\n\n"
help_text += "🔗 Ваша персональная ссылка:\n"
help_text += "Используйте кнопку 'Моя ссылка' для получения ссылки\n\n"
help_text += "❓ Нужна помощь?\n"
help_text += "Обратитесь к администратору бота: @Kerrad1"
await message.answer(help_text, parse_mode="HTML")
@router.message(F.text == "ℹ️ Помощь")
async def help_button(message: Message):
"""Обработчик кнопки 'Помощь'"""
await cmd_help(message)
@router.message(F.text == "🔗 Моя ссылка")
@inject_link_services
async def my_link_button(
message: Message,
user_service: UserService,
message_service: MessageService,
**kwargs
):
"""Обработчик кнопки 'Моя ссылка'"""
try:
# Получаем пользователя из БД
user = await user_service.get_user_by_telegram_id(message.from_user.id)
if not user:
await message_service.send_message(
message,
"❌ Пользователь не найден. Используйте /start для регистрации."
)
return
# Получаем информацию о боте
bot_info = await message.bot.get_me()
referral_link = user_service.generate_referral_link(bot_info.username, user)
link_text = "🔗 Ваша персональная ссылка:\n\n"
link_text += f"{referral_link}\n\n"
link_text += "📝 Как использовать:\n"
link_text += "• Скопируйте ссылку\n"
link_text += "• Поделитесь ею в социальных сетях\n"
link_text += "• Друзья смогут задать вам анонимные вопросы\n\n"
link_text += "💡 Совет: Добавьте ссылку в описание профиля или поделитесь в Stories!"
await message_service.send_message(message, link_text)
except Exception as e:
logger.error(f"Ошибка при получении ссылки: {e}")
await message_service.send_error_message(
message,
"❌ Произошла ошибка при получении ссылки. Попробуйте позже."
)
@router.message(F.text == "⬅️ Главное меню")
@inject_main_menu_services
async def back_to_main(
message: Message,
state: FSMContext,
auth: AuthService,
message_service: MessageService,
**kwargs
):
"""Обработчик кнопки 'Главное меню'"""
# Сбрасываем состояние FSM при возврате в главное меню
await state.clear()
logger.info(f"🔄 Состояние FSM сброшено для пользователя {message.from_user.id} (кнопка 'Главное меню')")
is_admin = auth.is_admin(message.from_user.id)
if is_admin:
keyboard = get_admin_reply_keyboard()
else:
keyboard = get_main_keyboard_for_user(message.from_user.id)
await message_service.send_message(
message,
"🏠 Главное меню\n\nВыберите действие:",
keyboard
)
@router.message(F.text == "⚙️ Админ панель")
@inject_main_menu_services
async def admin_panel_button(
message: Message,
auth: AuthService,
message_service: MessageService,
**kwargs
):
"""Обработчик кнопки 'Админ панель'"""
# Проверяем, является ли пользователь админом
if not auth.is_admin(message.from_user.id):
await message_service.send_message(
message,
"❌ У вас нет прав для доступа к админ панели."
)
return
# Получаем inline клавиатуру для админов
admin_keyboard = get_admin_keyboard()
await message_service.send_message(
message,
"👑 Админ панель\n\nВыберите действие:",
admin_keyboard
)