329 lines
14 KiB
Python
329 lines
14 KiB
Python
"""
|
||
Обработчики команд /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"👋 <b>Добро пожаловать, {user.display_name}!</b>\n\n"
|
||
welcome_text += "🤖 Я бот для анонимных вопросов.\n\n"
|
||
welcome_text += "📝 <b>Как это работает:</b>\n"
|
||
welcome_text += "• Поделитесь своей ссылкой с друзьями\n"
|
||
welcome_text += "• Они смогут задать вам анонимные вопросы\n"
|
||
welcome_text += "• Вы получите уведомления и сможете ответить\n\n"
|
||
welcome_text += f"🔗 <b>Ваша персональная ссылка:</b>\n"
|
||
welcome_text += f"<code>{referral_link}</code>\n\n"
|
||
welcome_text += "💡 <b>Совет:</b> Скопируйте ссылку и поделитесь ею в социальных сетях!"
|
||
|
||
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:
|
||
# Валидируем 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 = "📖 <b>Справка по боту</b>\n\n"
|
||
help_text += "🤖 <b>Основные команды:</b>\n"
|
||
help_text += "/start - Запуск бота и получение персональной ссылки\n"
|
||
help_text += "/help - Показать эту справку\n"
|
||
help_text += "/stats - Показать статистику (только для админов)\n\n"
|
||
help_text += "📝 <b>Как задать анонимный вопрос:</b>\n"
|
||
help_text += "1. Перейдите по персональной ссылке пользователя\n"
|
||
help_text += "2. Отправьте ваш вопрос боту\n"
|
||
help_text += "3. Вопрос будет передан получателю анонимно\n\n"
|
||
help_text += "💬 <b>Как ответить на вопрос:</b>\n"
|
||
help_text += "1. Получите уведомление о новом вопросе\n"
|
||
help_text += "2. Нажмите кнопку 'Ответить'\n"
|
||
help_text += "3. Введите ваш ответ\n"
|
||
help_text += "4. Ответ будет отправлен анонимно\n\n"
|
||
help_text += "🔗 <b>Ваша персональная ссылка:</b>\n"
|
||
help_text += "Используйте кнопку 'Моя ссылка' для получения ссылки\n\n"
|
||
help_text += "❓ <b>Нужна помощь?</b>\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 = "🔗 <b>Ваша персональная ссылка:</b>\n\n"
|
||
link_text += f"<code>{referral_link}</code>\n\n"
|
||
link_text += "📝 <b>Как использовать:</b>\n"
|
||
link_text += "• Скопируйте ссылку\n"
|
||
link_text += "• Поделитесь ею в социальных сетях\n"
|
||
link_text += "• Друзья смогут задать вам анонимные вопросы\n\n"
|
||
link_text += "💡 <b>Совет:</b> Добавьте ссылку в описание профиля или поделитесь в 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,
|
||
"🏠 <b>Главное меню</b>\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,
|
||
"👑 <b>Админ панель</b>\n\nВыберите действие:",
|
||
admin_keyboard
|
||
)
|