1273 lines
59 KiB
Python
1273 lines
59 KiB
Python
"""
|
||
Обработчики для работы с анонимными вопросами
|
||
"""
|
||
from datetime import datetime
|
||
from aiogram import Router, F
|
||
from aiogram.types import Message, CallbackQuery
|
||
from aiogram.filters import StateFilter
|
||
from aiogram.fsm.context import FSMContext
|
||
from aiogram.fsm.state import State, StatesGroup
|
||
|
||
from config import config
|
||
from models.user import User
|
||
from models.question import Question, QuestionStatus
|
||
from services.infrastructure.database import DatabaseService
|
||
from services.business.question_service import QuestionService
|
||
from services.business.user_service import UserService
|
||
from services.business.message_service import MessageService
|
||
from services.business.pagination_service import PaginationService
|
||
from services.utils import is_valid_answer_text, send_answer_to_author
|
||
from services.utils import is_valid_question_text, format_question_info, escape_html
|
||
from services.infrastructure.logger import get_logger
|
||
from services.infrastructure.metrics import get_metrics_service, track_question_processing, track_answer_processing
|
||
from services.infrastructure.logging_decorators import log_function_call, log_business_event, log_fsm_transition, log_utility
|
||
from services.infrastructure.logging_utils import log_user_action, log_business_operation, log_fsm_event
|
||
from dependencies import inject_question_services, inject_answer_services
|
||
from keyboards.inline import get_answer_keyboard, get_question_view_keyboard, get_user_questions_keyboard
|
||
from keyboards.reply import get_main_keyboard_for_user, get_cancel_keyboard
|
||
from keyboards.inline import get_admin_keyboard
|
||
|
||
logger = get_logger(__name__)
|
||
router = Router()
|
||
|
||
|
||
@log_function_call(log_params=True, log_result=False)
|
||
async def _format_questions_list(
|
||
questions_with_authors: list,
|
||
page: int,
|
||
per_page: int,
|
||
user_id: int,
|
||
question_service: QuestionService
|
||
) -> str:
|
||
"""Форматирование списка вопросов для отображения (оптимизированная версия)"""
|
||
try:
|
||
# Проверяем, является ли пользователь суперпользователем
|
||
is_superuser = False
|
||
if user_id:
|
||
try:
|
||
from dependencies import get_auth
|
||
auth_service = get_auth()
|
||
if auth_service:
|
||
is_superuser = await auth_service.is_superuser(user_id)
|
||
except Exception as e:
|
||
logger.warning(f"Ошибка при проверке суперпользователя: {e}")
|
||
is_superuser = False
|
||
|
||
# Формируем текст сообщения
|
||
questions_text = ""
|
||
|
||
for i, (question, author_user) in enumerate(questions_with_authors, page * per_page + 1):
|
||
# Проверяем, что question не None
|
||
if not question:
|
||
logger.warning(f"Найден None question в списке на позиции {i}")
|
||
continue
|
||
|
||
# Дополнительная проверка на наличие атрибута status
|
||
if not hasattr(question, 'status') or question.status is None:
|
||
logger.warning(f"Вопрос {question.id if hasattr(question, 'id') else 'unknown'} не имеет статуса")
|
||
continue
|
||
|
||
status_emoji = {
|
||
'pending': '⏳',
|
||
'answered': '✅',
|
||
'rejected': '❌',
|
||
'deleted': '🗑️'
|
||
}
|
||
|
||
emoji = status_emoji.get(question.status.value, '❓')
|
||
preview = question_service.get_question_preview(question, 40)
|
||
|
||
# Используем user_question_number для отображения, если он есть
|
||
display_number = question.user_question_number if question.user_question_number is not None else i
|
||
questions_text += f"{i}. {emoji} <b>#{display_number}</b>"
|
||
|
||
# Для суперпользователей добавляем информацию об авторе вопроса
|
||
if is_superuser and author_user:
|
||
try:
|
||
from services.utils import format_user_display_name
|
||
author_info = format_user_display_name(author_user)
|
||
questions_text += f" Вопрос от {author_info}"
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при форматировании информации об авторе вопроса: {e}")
|
||
elif is_superuser and not author_user:
|
||
questions_text += " (автор неизвестен)"
|
||
|
||
questions_text += "\n"
|
||
questions_text += f" {preview}\n"
|
||
questions_text += f" 📅 {question.created_at.strftime('%d.%m.%Y %H:%M')}\n\n"
|
||
|
||
questions_text += "💡 Нажмите на номер вопроса для просмотра деталей."
|
||
|
||
return questions_text
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при форматировании списка вопросов: {e}")
|
||
return "❌ Ошибка при форматировании списка вопросов."
|
||
|
||
|
||
|
||
@router.message(F.text == "❌ Отмена")
|
||
@log_fsm_transition(to_state="cancelled")
|
||
@log_function_call(log_params=True)
|
||
async def cancel_action(message: Message, state: FSMContext):
|
||
"""Обработчик кнопки 'Отмена'"""
|
||
await state.clear()
|
||
|
||
# Используем новую систему авторизации
|
||
is_admin = False
|
||
try:
|
||
from dependencies import get_auth
|
||
auth_service = get_auth()
|
||
if auth_service:
|
||
is_admin = auth_service.is_admin(message.from_user.id)
|
||
except Exception as e:
|
||
logger.warning(f"Ошибка при проверке админа: {e}")
|
||
is_admin = False
|
||
keyboard = get_admin_keyboard() if is_admin else get_main_keyboard_for_user(message.from_user.id)
|
||
|
||
await message.answer(
|
||
"❌ Действие отменено.",
|
||
reply_markup=keyboard
|
||
)
|
||
|
||
|
||
@log_function_call(log_params=True, log_result=True)
|
||
async def _send_questions_page(
|
||
target,
|
||
questions_text: str,
|
||
keyboard,
|
||
message_service: MessageService
|
||
) -> bool:
|
||
"""Отправка страницы с вопросами"""
|
||
try:
|
||
if isinstance(target, CallbackQuery):
|
||
# Это CallbackQuery
|
||
logger.info("Отправляем CallbackQuery через edit_text")
|
||
try:
|
||
await message_service.edit_message(target, questions_text, keyboard)
|
||
except Exception as edit_error:
|
||
# Если не удалось отредактировать, отправляем новое сообщение
|
||
logger.warning(f"Не удалось отредактировать сообщение: {edit_error}")
|
||
await message_service.send_message(target, questions_text, keyboard)
|
||
else:
|
||
# Это Message - используем answer() напрямую
|
||
logger.info("Отправляем Message через answer()")
|
||
await message_service.send_message(target, questions_text, keyboard)
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при отправке страницы вопросов: {e}")
|
||
return False
|
||
|
||
|
||
@log_business_event("show_questions_page", log_params=True, log_result=False)
|
||
async def show_questions_page(
|
||
message_or_callback,
|
||
questions: list,
|
||
page: int = 0,
|
||
per_page: int = 9,
|
||
user_id: int = None,
|
||
question_service: QuestionService = None,
|
||
user_service: UserService = None,
|
||
message_service: MessageService = None,
|
||
pagination_service: PaginationService = None
|
||
):
|
||
"""
|
||
Показать страницу с вопросами
|
||
|
||
Args:
|
||
message_or_callback: Message или CallbackQuery объект
|
||
questions: Список всех вопросов
|
||
page: Номер страницы (начиная с 0)
|
||
per_page: Количество вопросов на странице
|
||
user_id: ID пользователя для проверки прав суперпользователя
|
||
question_service: Сервис для работы с вопросами
|
||
user_service: Сервис для работы с пользователями
|
||
message_service: Сервис для отправки сообщений
|
||
pagination_service: Сервис для пагинации
|
||
"""
|
||
try:
|
||
# Логируем тип объекта для отладки
|
||
object_type = type(message_or_callback).__name__
|
||
questions_count = len(questions) if questions is not None else 0
|
||
logger.info(f"show_questions_page вызвана с объектом типа: {object_type}, page: {page}, questions: {questions_count}")
|
||
|
||
# Используем сервисы если они переданы, иначе создаем временные
|
||
if not question_service:
|
||
from dependencies import get_question_service
|
||
question_service = get_question_service()
|
||
|
||
if not user_service:
|
||
from dependencies import get_user_service
|
||
user_service = get_user_service()
|
||
|
||
if not message_service:
|
||
from dependencies import get_message_service
|
||
message_service = get_message_service()
|
||
|
||
if not pagination_service:
|
||
from dependencies import get_pagination_service
|
||
pagination_service = get_pagination_service()
|
||
|
||
# Получаем общее количество вопросов для пагинации
|
||
total_questions_count = await user_service.database.get_user_questions_count(user_id)
|
||
|
||
if total_questions_count == 0:
|
||
empty_text = (
|
||
"📭 У вас пока нет вопросов.\n\n"
|
||
"🔗 Поделитесь своей ссылкой, чтобы получать анонимные вопросы!"
|
||
)
|
||
|
||
if message_service:
|
||
await message_service.send_message(message_or_callback, empty_text)
|
||
else:
|
||
if isinstance(message_or_callback, CallbackQuery):
|
||
await message_or_callback.message.edit_text(empty_text, reply_markup=None)
|
||
else:
|
||
await message_or_callback.answer(empty_text)
|
||
return
|
||
|
||
# Рассчитываем пагинацию на основе БД
|
||
total_questions, current_page, total_pages, offset = await pagination_service.calculate_pagination_from_db(
|
||
total_questions_count, page, per_page
|
||
)
|
||
|
||
# Получаем вопросы с авторами для текущей страницы (оптимизированный запрос)
|
||
questions_with_authors = await user_service.database.get_user_questions_with_authors(
|
||
user_id, limit=per_page, offset=offset
|
||
)
|
||
|
||
# Формируем информацию о пагинации
|
||
start_idx = current_page * per_page
|
||
end_idx = min(start_idx + per_page, total_questions)
|
||
pagination_info = pagination_service.format_pagination_info(
|
||
current_page, total_pages, start_idx, end_idx, total_questions
|
||
)
|
||
|
||
# Формируем текст сообщения
|
||
questions_text = f"📋 <b>Ваши вопросы</b>\n\n"
|
||
questions_text += pagination_info
|
||
|
||
# Добавляем список вопросов
|
||
questions_list_text = await _format_questions_list(
|
||
questions_with_authors, current_page, per_page, user_id, question_service
|
||
)
|
||
questions_text += questions_list_text
|
||
|
||
# Создаем клавиатуру с пагинацией, используя реальные вопросы из базы данных
|
||
keyboard = get_user_questions_keyboard(questions_with_authors, current_page, per_page, total_questions)
|
||
|
||
# Отправляем страницу
|
||
await _send_questions_page(message_or_callback, questions_text, keyboard, message_service)
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при отображении страницы вопросов: {e},")
|
||
try:
|
||
if message_service:
|
||
await message_service.send_error_message(
|
||
message_or_callback,
|
||
"❌ Ошибка при загрузке вопросов"
|
||
)
|
||
else:
|
||
if isinstance(message_or_callback, CallbackQuery):
|
||
await message_or_callback.answer("❌ Ошибка при загрузке вопросов", show_alert=True)
|
||
else:
|
||
await message_or_callback.answer("❌ Ошибка при загрузке вопросов")
|
||
except Exception as answer_error:
|
||
logger.error(f"Не удалось отправить сообщение об ошибке: {answer_error}")
|
||
|
||
|
||
class QuestionStates(StatesGroup):
|
||
"""Состояния для работы с вопросами"""
|
||
waiting_for_question = State()
|
||
waiting_for_answer = State()
|
||
editing_answer = State()
|
||
|
||
|
||
@router.message(F.text == "📋 Мои вопросы")
|
||
@log_business_event("my_questions_button", log_params=True)
|
||
async def my_questions_button(
|
||
message: Message,
|
||
state: FSMContext,
|
||
question_service: QuestionService,
|
||
message_service: MessageService
|
||
):
|
||
"""Обработчик кнопки 'Мои вопросы'"""
|
||
try:
|
||
await state.clear()
|
||
|
||
# Получаем все вопросы пользователя
|
||
all_questions = await question_service.get_user_questions(message.from_user.id, limit=1000)
|
||
|
||
if not all_questions:
|
||
await message_service.send_message(
|
||
message,
|
||
"📭 У вас пока нет вопросов.\n\n"
|
||
"🔗 Поделитесь своей ссылкой, чтобы получать анонимные вопросы!",
|
||
get_main_keyboard_for_user(message.from_user.id)
|
||
)
|
||
else:
|
||
# Показываем первую страницу
|
||
await show_questions_page(
|
||
message,
|
||
all_questions,
|
||
page=0,
|
||
user_id=message.from_user.id,
|
||
question_service=question_service,
|
||
message_service=message_service
|
||
)
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при получении вопросов: {e}")
|
||
await message_service.send_error_message(
|
||
message,
|
||
"❌ Произошла ошибка при получении вопросов. Попробуйте позже."
|
||
)
|
||
|
||
|
||
@log_utility
|
||
def _has_attachments(message: Message) -> bool:
|
||
"""Проверяет, содержит ли сообщение вложения"""
|
||
return (
|
||
message.photo is not None or
|
||
message.video is not None or
|
||
message.audio is not None or
|
||
message.document is not None or
|
||
message.sticker is not None or
|
||
message.animation is not None or
|
||
message.voice is not None or
|
||
message.video_note is not None or
|
||
message.contact is not None or
|
||
message.location is not None or
|
||
message.media_group_id is not None or
|
||
message.caption is not None
|
||
)
|
||
|
||
|
||
@router.message(StateFilter(QuestionStates.waiting_for_question))
|
||
@inject_question_services
|
||
@log_fsm_transition(to_state="processing_question")
|
||
@log_business_event("process_anonymous_question", log_params=True)
|
||
async def process_anonymous_question(
|
||
message: Message,
|
||
state: FSMContext,
|
||
question_service: QuestionService,
|
||
user_service: UserService,
|
||
message_service: MessageService,
|
||
validator,
|
||
**kwargs
|
||
):
|
||
"""Обработка анонимного вопроса"""
|
||
# Убираем dispatcher из kwargs, если он есть
|
||
kwargs.pop('dispatcher', None)
|
||
try:
|
||
# Получаем данные из состояния
|
||
data = await state.get_data()
|
||
target_user_id = data.get('target_user_id')
|
||
|
||
if not target_user_id:
|
||
await message_service.send_message(
|
||
message,
|
||
"❌ Ошибка: не найден получатель вопроса.",
|
||
get_main_keyboard_for_user(message.from_user.id)
|
||
)
|
||
await state.clear()
|
||
return
|
||
|
||
# Проверяем, содержит ли сообщение вложения
|
||
if _has_attachments(message):
|
||
logger.warning(f"⚠️ Пользователь {message.from_user.id} отправил сообщение с вложениями")
|
||
await message_service.send_message(
|
||
message,
|
||
"❌ <b>Вложения не допускаются</b>\n\n"
|
||
"📝 Вопрос должен состоять только из текста.\n"
|
||
"🚫 Фотографии, видео, документы, аудио и другие вложения не принимаются.\n\n"
|
||
"Попробуйте отправить вопрос еще раз:",
|
||
get_cancel_keyboard()
|
||
)
|
||
return
|
||
|
||
# Проверяем, что сообщение содержит текст
|
||
if not message.text:
|
||
logger.warning(f"⚠️ Пользователь {message.from_user.id} отправил пустое сообщение")
|
||
await message_service.send_message(
|
||
message,
|
||
"❌ Вопрос не может быть пустым\n\n"
|
||
"Попробуйте отправить вопрос еще раз:",
|
||
get_cancel_keyboard()
|
||
)
|
||
return
|
||
|
||
# Валидируем текст вопроса
|
||
validation_result = validator.validate_question_text(
|
||
message.text,
|
||
config.MAX_QUESTION_LENGTH
|
||
)
|
||
|
||
if not validation_result:
|
||
logger.warning(f"⚠️ Невалидный вопрос от пользователя {message.from_user.id}: {validation_result.error_message}")
|
||
await message_service.send_message(
|
||
message,
|
||
f"❌ {validation_result.error_message}\n\n"
|
||
"Попробуйте отправить вопрос еще раз:",
|
||
get_cancel_keyboard()
|
||
)
|
||
return
|
||
|
||
# Используем санитизированный текст
|
||
sanitized_question_text = validation_result.sanitized_value
|
||
|
||
# Проверяем, не заблокирован ли отправитель получателем
|
||
if await user_service.is_user_blocked(target_user_id, message.from_user.id):
|
||
await message_service.send_message(
|
||
message,
|
||
"🚫 <b>Вы заблокированы этим пользователем</b>\n\n"
|
||
"К сожалению, вы не можете отправлять вопросы этому пользователю.",
|
||
get_main_keyboard_for_user(message.from_user.id)
|
||
)
|
||
await state.clear()
|
||
return
|
||
|
||
# Создаем вопрос с санитизированным текстом
|
||
question = await question_service.create_question(
|
||
message.from_user.id,
|
||
target_user_id,
|
||
sanitized_question_text
|
||
)
|
||
|
||
# Отправляем уведомление получателю
|
||
await _send_question_notification(
|
||
message.bot,
|
||
question,
|
||
target_user_id,
|
||
user_service,
|
||
message_service
|
||
)
|
||
|
||
# Отправляем подтверждение отправителю
|
||
confirmation_text = (
|
||
"✅ <b>Вопрос отправлен!</b>\n\n"
|
||
"📝 Ваш вопрос был передан получателю анонимно.\n"
|
||
"🔔 Он получит уведомление и сможет ответить на него.\n\n"
|
||
"💡 Спасибо за использование нашего бота!"
|
||
)
|
||
|
||
await message_service.send_message(
|
||
message,
|
||
confirmation_text,
|
||
get_main_keyboard_for_user(message.from_user.id)
|
||
)
|
||
|
||
await state.clear()
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при обработке вопроса: {e}")
|
||
await message_service.send_error_message(
|
||
message,
|
||
"❌ Произошла ошибка при отправке вопроса. Попробуйте позже."
|
||
)
|
||
await state.clear()
|
||
|
||
|
||
@log_function_call(log_params=True, log_result=True)
|
||
async def _send_question_notification(
|
||
bot,
|
||
question: Question,
|
||
target_user_id: int,
|
||
user_service: UserService,
|
||
message_service: MessageService
|
||
) -> bool:
|
||
"""Отправка уведомления о новом вопросе"""
|
||
try:
|
||
# Получаем информацию о получателе
|
||
target_user = await user_service.get_user_by_telegram_id(target_user_id)
|
||
if not target_user:
|
||
return False
|
||
|
||
# Проверяем, является ли получатель суперпользователем
|
||
is_target_superuser = False
|
||
try:
|
||
from dependencies import get_auth
|
||
auth_service = get_auth()
|
||
if auth_service:
|
||
is_target_superuser = await auth_service.is_superuser(target_user_id)
|
||
except Exception as e:
|
||
logger.warning(f"Ошибка при проверке суперпользователя для уведомления: {e}")
|
||
is_target_superuser = False
|
||
|
||
notification_text = "❓ <b>Новый анонимный вопрос!</b>\n\n"
|
||
|
||
# Для суперпользователей добавляем информацию об авторе
|
||
if is_target_superuser and question.from_user_id:
|
||
try:
|
||
from services.utils import format_user_display_name
|
||
author_user = await user_service.get_user_by_telegram_id(question.from_user_id)
|
||
if author_user:
|
||
author_info = format_user_display_name(author_user)
|
||
notification_text = f"❓ <b>Новый вопрос от {author_info}!</b>\n\n"
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при получении информации об авторе для уведомления: {e}")
|
||
|
||
notification_text += f"📝 <b>Вопрос:</b>\n{escape_html(question.message_text)}\n\n"
|
||
notification_text += f"📅 {question.created_at.strftime('%d.%m.%Y %H:%M')}"
|
||
|
||
# Отправляем уведомление
|
||
await message_service.send_bot_message(
|
||
bot,
|
||
target_user_id,
|
||
notification_text,
|
||
get_answer_keyboard(question.id, question.from_user_id)
|
||
)
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
logger.error(f"Не удалось отправить уведомление пользователю {target_user_id}: {e}")
|
||
return False
|
||
|
||
|
||
@router.callback_query(F.data.startswith("answer_"))
|
||
@log_fsm_transition(to_state="waiting_for_answer")
|
||
@log_business_event("answer_question_callback", log_params=True)
|
||
async def answer_question_callback(callback: CallbackQuery, state: FSMContext, validator = None):
|
||
"""Обработчик кнопки 'Ответить' на вопрос"""
|
||
try:
|
||
# Валидируем callback data
|
||
if validator:
|
||
validation_result = validator.validate_callback_data(callback.data)
|
||
if not validation_result:
|
||
logger.warning(f"⚠️ Невалидный callback data от пользователя {callback.from_user.id}: {callback.data}")
|
||
await callback.answer("❌ Неверные данные", show_alert=True)
|
||
return
|
||
|
||
# Извлекаем и валидируем question_id
|
||
question_id_str = callback.data.split("_")[1]
|
||
try:
|
||
question_id = int(question_id_str)
|
||
if question_id <= 0:
|
||
logger.warning(f"⚠️ Невалидный question_id в callback: {question_id}")
|
||
await callback.answer("❌ Неверный ID вопроса", show_alert=True)
|
||
return
|
||
except ValueError:
|
||
logger.warning(f"⚠️ Неверный формат question_id в callback: {question_id_str}")
|
||
await callback.answer("❌ Неверный формат ID", show_alert=True)
|
||
return
|
||
|
||
from dependencies import get_database
|
||
db = get_database()
|
||
|
||
# Получаем вопрос
|
||
question = await db.get_question(question_id)
|
||
|
||
if not question:
|
||
await callback.answer("❌ Вопрос не найден", show_alert=True)
|
||
return
|
||
|
||
if question.to_user_id != callback.from_user.id:
|
||
await callback.answer("❌ У вас нет прав на этот вопрос", show_alert=True)
|
||
return
|
||
|
||
if question.status != QuestionStatus.PENDING:
|
||
await callback.answer("❌ На этот вопрос уже отвечено", show_alert=True)
|
||
return
|
||
|
||
# Устанавливаем состояние ожидания ответа
|
||
await state.set_state(QuestionStates.waiting_for_answer)
|
||
await state.update_data(question_id=question_id)
|
||
|
||
# Отправляем сообщение с просьбой ввести ответ
|
||
await callback.message.edit_text(
|
||
f"💬 <b>Ответ на вопрос #{question_id}</b>\n\n"
|
||
f"📝 <b>Вопрос:</b>\n{escape_html(question.message_text)}\n\n"
|
||
f"✍️ <b>Введите ваш ответ:</b>",
|
||
reply_markup=None,
|
||
parse_mode="HTML"
|
||
)
|
||
|
||
await callback.answer()
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при обработке ответа на вопрос: {e}")
|
||
await callback.answer("❌ Произошла ошибка", show_alert=True)
|
||
|
||
|
||
@router.message(StateFilter(QuestionStates.waiting_for_answer))
|
||
@inject_answer_services
|
||
@log_fsm_transition(to_state="processing_answer")
|
||
@log_business_event("process_answer", log_params=True)
|
||
async def process_answer(
|
||
message: Message,
|
||
state: FSMContext,
|
||
validator,
|
||
message_service: MessageService,
|
||
**kwargs
|
||
):
|
||
"""Обработка ответа на вопрос"""
|
||
try:
|
||
# Получаем данные из состояния
|
||
data = await state.get_data()
|
||
question_id = data.get('question_id')
|
||
|
||
if not question_id:
|
||
await message.answer(
|
||
"❌ Ошибка: не найден вопрос для ответа.",
|
||
reply_markup=get_main_keyboard_for_user(message.from_user.id)
|
||
)
|
||
await state.clear()
|
||
return
|
||
|
||
# Валидируем текст ответа
|
||
validation_result = validator.validate_answer_text(
|
||
message.text,
|
||
config.MAX_ANSWER_LENGTH
|
||
)
|
||
|
||
if not validation_result:
|
||
logger.warning(f"⚠️ Невалидный ответ от пользователя {message.from_user.id}: {validation_result.error_message}")
|
||
await message.answer(
|
||
f"❌ {validation_result.error_message}\n\n"
|
||
"Попробуйте отправить ответ еще раз:",
|
||
reply_markup=get_cancel_keyboard()
|
||
)
|
||
return
|
||
|
||
# Используем санитизированный текст
|
||
sanitized_answer_text = validation_result.sanitized_value
|
||
|
||
# Сохраняем ответ
|
||
from dependencies import get_database
|
||
db = get_database()
|
||
|
||
question = await db.get_question(question_id)
|
||
if question:
|
||
question.mark_as_answered(sanitized_answer_text)
|
||
await db.update_question(question)
|
||
|
||
# Обновляем статистику пользователя (если нужно)
|
||
# user = await db.get_user(message.from_user.id)
|
||
# if user:
|
||
# user.increment_questions_answered()
|
||
# await db.update_user(user)
|
||
|
||
# Отправляем ответ автору вопроса
|
||
logger.info(f"Попытка отправить ответ автору вопроса {question.id}, from_user_id: {question.from_user_id}")
|
||
await send_answer_to_author(message.bot, question, question.answer_text)
|
||
|
||
# Отправляем подтверждение
|
||
await message_service.send_message(
|
||
message,
|
||
"✅ <b>Ответ сохранен!</b>\n\n"
|
||
"💬 Ваш ответ был сохранен и будет показан при просмотре вопроса.\n\n"
|
||
"📋 Используйте 'Мои вопросы' для просмотра всех вопросов и ответов.",
|
||
get_main_keyboard_for_user(message.from_user.id)
|
||
)
|
||
|
||
await state.clear()
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при обработке ответа: {e}")
|
||
await message_service.send_message(
|
||
message,
|
||
"❌ Произошла ошибка при сохранении ответа. Попробуйте позже.",
|
||
get_main_keyboard_for_user(message.from_user.id)
|
||
)
|
||
await state.clear()
|
||
|
||
|
||
@router.callback_query(F.data.startswith("reject_"))
|
||
@log_business_event("reject_question_callback", log_params=True)
|
||
async def reject_question_callback(callback: CallbackQuery, validator = None):
|
||
"""Обработчик кнопки 'Отклонить' вопрос"""
|
||
try:
|
||
# Валидируем callback data
|
||
if validator:
|
||
validation_result = validator.validate_callback_data(callback.data)
|
||
if not validation_result:
|
||
logger.warning(f"⚠️ Невалидный callback data от пользователя {callback.from_user.id}: {callback.data}")
|
||
await callback.answer("❌ Неверные данные", show_alert=True)
|
||
return
|
||
|
||
# Извлекаем и валидируем question_id
|
||
question_id_str = callback.data.split("_")[1]
|
||
try:
|
||
question_id = int(question_id_str)
|
||
if question_id <= 0:
|
||
logger.warning(f"⚠️ Невалидный question_id в callback: {question_id}")
|
||
await callback.answer("❌ Неверный ID вопроса", show_alert=True)
|
||
return
|
||
except ValueError:
|
||
logger.warning(f"⚠️ Неверный формат question_id в callback: {question_id_str}")
|
||
await callback.answer("❌ Неверный формат ID", show_alert=True)
|
||
return
|
||
|
||
from dependencies import get_database
|
||
db = get_database()
|
||
|
||
# Получаем вопрос
|
||
question = await db.get_question(question_id)
|
||
|
||
if not question:
|
||
await callback.answer("❌ Вопрос не найден", show_alert=True)
|
||
return
|
||
|
||
if question.to_user_id != callback.from_user.id:
|
||
await callback.answer("❌ У вас нет прав на этот вопрос", show_alert=True)
|
||
return
|
||
|
||
if question.status != QuestionStatus.PENDING:
|
||
await callback.answer("❌ На этот вопрос уже отвечено", show_alert=True)
|
||
return
|
||
|
||
# Отклоняем вопрос
|
||
question.mark_as_rejected()
|
||
await db.update_question(question)
|
||
|
||
# Обновляем сообщение
|
||
await callback.message.edit_text(
|
||
f"❌ <b>Вопрос #{question.user_question_number if question.user_question_number is not None else question_id} отклонен</b>\n\n"
|
||
f"📝 <b>Вопрос:</b>\n{escape_html(question.message_text)}\n\n"
|
||
f"📅 {question.created_at.strftime('%d.%m.%Y %H:%M')}\n"
|
||
f"❌ Отклонен: {question.answered_at.strftime('%d.%m.%Y %H:%M')}",
|
||
reply_markup=None,
|
||
parse_mode="HTML"
|
||
)
|
||
|
||
await callback.answer("❌ Вопрос отклонен")
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при отклонении вопроса: {e}")
|
||
await callback.answer("❌ Произошла ошибка", show_alert=True)
|
||
|
||
|
||
@router.callback_query(F.data.startswith("delete_"))
|
||
@log_business_event("delete_question_callback", log_params=True)
|
||
async def delete_question_callback(callback: CallbackQuery, validator = None):
|
||
"""Обработчик кнопки 'Удалить' вопрос"""
|
||
try:
|
||
# Валидируем callback data
|
||
if validator:
|
||
validation_result = validator.validate_callback_data(callback.data)
|
||
if not validation_result:
|
||
logger.warning(f"⚠️ Невалидный callback data от пользователя {callback.from_user.id}: {callback.data}")
|
||
await callback.answer("❌ Неверные данные", show_alert=True)
|
||
return
|
||
|
||
# Извлекаем и валидируем question_id
|
||
question_id_str = callback.data.split("_")[1]
|
||
try:
|
||
question_id = int(question_id_str)
|
||
if question_id <= 0:
|
||
logger.warning(f"⚠️ Невалидный question_id в callback: {question_id}")
|
||
await callback.answer("❌ Неверный ID вопроса", show_alert=True)
|
||
return
|
||
except ValueError:
|
||
logger.warning(f"⚠️ Неверный формат question_id в callback: {question_id_str}")
|
||
await callback.answer("❌ Неверный формат ID", show_alert=True)
|
||
return
|
||
|
||
from dependencies import get_database
|
||
db = get_database()
|
||
|
||
# Получаем вопрос
|
||
question = await db.get_question(question_id)
|
||
|
||
if not question:
|
||
await callback.answer("❌ Вопрос не найден", show_alert=True)
|
||
return
|
||
|
||
if question.to_user_id != callback.from_user.id:
|
||
await callback.answer("❌ У вас нет прав на этот вопрос", show_alert=True)
|
||
return
|
||
|
||
# Удаляем вопрос
|
||
question.mark_as_deleted()
|
||
await db.update_question(question)
|
||
|
||
# Обновляем сообщение
|
||
await callback.message.edit_text(
|
||
f"🗑️ <b>Вопрос #{question.user_question_number if question.user_question_number is not None else question_id} удален</b>\n\n"
|
||
f"📅 Удален: {question.answered_at.strftime('%d.%m.%Y %H:%M')}",
|
||
reply_markup=None,
|
||
parse_mode="HTML"
|
||
)
|
||
|
||
await callback.answer("🗑️ Вопрос удален")
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при удалении вопроса: {e}")
|
||
await callback.answer("❌ Произошла ошибка", show_alert=True)
|
||
|
||
|
||
@router.callback_query(F.data.startswith("questions_page_"))
|
||
@log_business_event("questions_pagination_handler", log_params=True)
|
||
async def questions_pagination_handler(callback: CallbackQuery):
|
||
"""Обработчик пагинации списка вопросов (оптимизированная версия)"""
|
||
try:
|
||
# Извлекаем номер страницы из callback_data
|
||
page = int(callback.data.split("_")[-1])
|
||
|
||
# Получаем сервисы
|
||
from dependencies import get_user_service
|
||
user_service = get_user_service()
|
||
|
||
# Получаем общее количество вопросов для пагинации (оптимизированный запрос)
|
||
total_questions_count = await user_service.database.get_user_questions_count(callback.from_user.id)
|
||
|
||
if total_questions_count == 0:
|
||
await callback.answer("❌ Вопросы не найдены", show_alert=True)
|
||
return
|
||
|
||
# Показываем нужную страницу (show_questions_page сам загрузит данные из БД)
|
||
logger.info(f"Показываем страницу {page} для пользователя {callback.from_user.id}, всего вопросов: {total_questions_count}")
|
||
await show_questions_page(callback, None, page, user_id=callback.from_user.id)
|
||
await callback.answer()
|
||
|
||
except ValueError as e:
|
||
logger.error(f"Ошибка парсинга номера страницы: {e}")
|
||
await callback.answer("❌ Неверный номер страницы", show_alert=True)
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при пагинации вопросов: {e}")
|
||
await callback.answer("❌ Ошибка при загрузке страницы", show_alert=True)
|
||
|
||
|
||
@router.callback_query(F.data == "back_to_questions")
|
||
@log_business_event("back_to_questions_handler", log_params=True)
|
||
async def back_to_questions_handler(callback: CallbackQuery):
|
||
"""Обработчик кнопки 'Назад к списку вопросов'"""
|
||
try:
|
||
# Получаем базу данных
|
||
from dependencies import get_database
|
||
db = get_database()
|
||
|
||
# Получаем все вопросы пользователя
|
||
all_questions = await db.get_user_questions(callback.from_user.id, limit=1000)
|
||
|
||
if not all_questions:
|
||
await callback.answer("❌ Вопросы не найдены", show_alert=True)
|
||
return
|
||
|
||
# Показываем первую страницу списка
|
||
await show_questions_page(callback, all_questions, page=0, user_id=callback.from_user.id)
|
||
await callback.answer()
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при возврате к списку вопросов: {e}")
|
||
await callback.answer("❌ Ошибка при загрузке списка", show_alert=True)
|
||
|
||
|
||
@router.callback_query(F.data.startswith("view_question_"))
|
||
@log_business_event("view_question_handler", log_params=True)
|
||
async def view_question_handler(callback: CallbackQuery, validator = None):
|
||
"""Обработчик просмотра конкретного вопроса"""
|
||
try:
|
||
# Валидируем callback data
|
||
if validator:
|
||
validation_result = validator.validate_callback_data(callback.data)
|
||
if not validation_result:
|
||
logger.warning(f"⚠️ Невалидный callback data от пользователя {callback.from_user.id}: {callback.data}")
|
||
await callback.answer("❌ Неверные данные", show_alert=True)
|
||
return
|
||
|
||
# Извлекаем и валидируем question_id
|
||
question_id_str = callback.data.split("_")[-1]
|
||
try:
|
||
question_id = int(question_id_str)
|
||
if question_id <= 0:
|
||
logger.warning(f"⚠️ Невалидный question_id в callback: {question_id}")
|
||
await callback.answer("❌ Неверный ID вопроса", show_alert=True)
|
||
return
|
||
except ValueError:
|
||
logger.warning(f"⚠️ Неверный формат question_id в callback: {question_id_str}")
|
||
await callback.answer("❌ Неверный формат ID", show_alert=True)
|
||
return
|
||
|
||
# Получаем базу данных
|
||
from dependencies import get_database
|
||
db = get_database()
|
||
|
||
# Получаем вопрос
|
||
question = await db.get_question(question_id)
|
||
if not question:
|
||
await callback.answer("❌ Вопрос не найден", show_alert=True)
|
||
return
|
||
|
||
# Форматируем информацию о вопросе
|
||
question_text = format_question_info(question, show_answer=True)
|
||
|
||
# Создаем клавиатуру для просмотра вопроса
|
||
keyboard = get_question_view_keyboard(question)
|
||
|
||
await callback.message.edit_text(
|
||
question_text,
|
||
reply_markup=keyboard,
|
||
parse_mode="HTML"
|
||
)
|
||
|
||
await callback.answer()
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при просмотре вопроса: {e}")
|
||
await callback.answer("❌ Ошибка при загрузке вопроса", show_alert=True)
|
||
|
||
|
||
@router.callback_query(F.data.startswith("block_"))
|
||
@log_business_event("block_user_callback", log_params=True)
|
||
async def block_user_callback(callback: CallbackQuery, validator = None):
|
||
"""Обработчик блокировки пользователя"""
|
||
try:
|
||
# Валидируем callback data
|
||
if validator:
|
||
validation_result = validator.validate_callback_data(callback.data)
|
||
if not validation_result:
|
||
logger.warning(f"⚠️ Невалидный callback data от пользователя {callback.from_user.id}: {callback.data}")
|
||
await callback.answer("❌ Неверные данные", show_alert=True)
|
||
return
|
||
|
||
# Парсим данные: block_{user_id}_{question_id}
|
||
data_parts = callback.data.split("_")
|
||
if len(data_parts) != 3:
|
||
await callback.answer("❌ Ошибка в данных", show_alert=True)
|
||
return
|
||
|
||
# Валидируем user_id и question_id
|
||
try:
|
||
blocked_user_id = int(data_parts[1])
|
||
question_id = int(data_parts[2])
|
||
|
||
if validator:
|
||
# Валидируем Telegram ID
|
||
user_id_validation = validator.validate_telegram_id(blocked_user_id)
|
||
if not user_id_validation:
|
||
logger.warning(f"⚠️ Невалидный blocked_user_id в callback: {blocked_user_id}")
|
||
await callback.answer("❌ Неверный ID пользователя", show_alert=True)
|
||
return
|
||
|
||
# Валидируем question_id
|
||
if question_id <= 0:
|
||
logger.warning(f"⚠️ Невалидный question_id в callback: {question_id}")
|
||
await callback.answer("❌ Неверный ID вопроса", show_alert=True)
|
||
return
|
||
except ValueError:
|
||
logger.warning(f"⚠️ Неверный формат ID в callback: {callback.data}")
|
||
await callback.answer("❌ Неверный формат ID", show_alert=True)
|
||
return
|
||
|
||
blocker_id = callback.from_user.id
|
||
|
||
# Проверяем, не блокирует ли пользователь сам себя
|
||
if blocked_user_id == blocker_id:
|
||
await callback.answer("❌ Нельзя заблокировать самого себя", show_alert=True)
|
||
return
|
||
|
||
# Получаем базу данных
|
||
from dependencies import get_database
|
||
db = get_database()
|
||
|
||
# Проверяем, не заблокирован ли уже пользователь
|
||
if await db.is_user_blocked(blocker_id, blocked_user_id):
|
||
await callback.answer("❌ Пользователь уже заблокирован", show_alert=True)
|
||
return
|
||
|
||
# Блокируем пользователя
|
||
await db.block_user(blocker_id, blocked_user_id)
|
||
|
||
# Получаем информацию о заблокированном пользователе
|
||
blocked_user = await db.get_user(blocked_user_id)
|
||
blocked_name = blocked_user.display_name if blocked_user else f"ID: {blocked_user_id}"
|
||
|
||
# Обновляем сообщение с вопросом
|
||
question_text = f"🚫 <b>Пользователь заблокирован</b>\n\n"
|
||
question_text += f"👤 <b>Заблокирован:</b> {blocked_name}\n"
|
||
question_text += f"📅 <b>Дата блокировки:</b> {datetime.now().strftime('%d.%m.%Y %H:%M')}\n\n"
|
||
question_text += "✅ Пользователь больше не сможет отправлять вам вопросы."
|
||
|
||
# Создаем клавиатуру с кнопкой разблокировки
|
||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||
from aiogram.types import InlineKeyboardButton
|
||
builder = InlineKeyboardBuilder()
|
||
builder.add(
|
||
InlineKeyboardButton(
|
||
text="🔓 Разблокировать",
|
||
callback_data=f"unblock_{blocked_user_id}_{question_id}"
|
||
)
|
||
)
|
||
builder.add(
|
||
InlineKeyboardButton(
|
||
text="⬅️ К списку вопросов",
|
||
callback_data="back_to_questions"
|
||
)
|
||
)
|
||
builder.adjust(1)
|
||
|
||
await callback.message.edit_text(
|
||
question_text,
|
||
reply_markup=builder.as_markup(),
|
||
parse_mode="HTML"
|
||
)
|
||
|
||
await callback.answer("✅ Пользователь заблокирован", show_alert=True)
|
||
|
||
except ValueError:
|
||
await callback.answer("❌ Неверный формат данных", show_alert=True)
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при блокировке пользователя: {e}")
|
||
await callback.answer("❌ Произошла ошибка при блокировке", show_alert=True)
|
||
|
||
|
||
@router.callback_query(F.data.startswith("unblock_") & ~F.data.startswith("unblock_from_list_"))
|
||
@log_business_event("unblock_user_callback", log_params=True)
|
||
async def unblock_user_callback(callback: CallbackQuery):
|
||
"""Обработчик разблокировки пользователя"""
|
||
try:
|
||
# Логируем входящие данные для отладки
|
||
logger.info(f"🔓 Попытка разблокировки пользователя. Callback data: '{callback.data}'")
|
||
|
||
# Парсим данные: unblock_{user_id}_{question_id}
|
||
data_parts = callback.data.split("_")
|
||
logger.info(f"🔍 Разобранные части данных: {data_parts}, количество частей: {len(data_parts)}")
|
||
|
||
if len(data_parts) != 3:
|
||
logger.error(f"❌ Неверное количество частей в данных: {len(data_parts)}, ожидалось 3. Данные: {callback.data}")
|
||
await callback.answer("❌ Ошибка в данных", show_alert=True)
|
||
return
|
||
|
||
try:
|
||
unblocked_user_id = int(data_parts[1])
|
||
question_id = int(data_parts[2])
|
||
unblocker_id = callback.from_user.id
|
||
|
||
logger.info(f"🔍 Парсинг успешен: unblocked_user_id={unblocked_user_id}, question_id={question_id}, unblocker_id={unblocker_id}")
|
||
except ValueError as e:
|
||
logger.error(f"❌ Ошибка парсинга ID: {e}. Данные: {data_parts}")
|
||
await callback.answer("❌ Ошибка в данных пользователя", show_alert=True)
|
||
return
|
||
|
||
# Получаем базу данных
|
||
from dependencies import get_database
|
||
db = get_database()
|
||
|
||
# Проверяем, заблокирован ли пользователь
|
||
is_blocked = await db.is_user_blocked(unblocker_id, unblocked_user_id)
|
||
logger.info(f"🔍 Проверка блокировки: is_blocked={is_blocked} для unblocker_id={unblocker_id}, unblocked_user_id={unblocked_user_id}")
|
||
|
||
if not is_blocked:
|
||
logger.warning(f"⚠️ Попытка разблокировать незаблокированного пользователя: unblocker_id={unblocker_id}, unblocked_user_id={unblocked_user_id}")
|
||
await callback.answer("❌ Пользователь не заблокирован", show_alert=True)
|
||
return
|
||
|
||
# Разблокируем пользователя
|
||
logger.info(f"🔓 Начинаем разблокировку: unblocker_id={unblocker_id}, unblocked_user_id={unblocked_user_id}")
|
||
success = await db.unblock_user(unblocker_id, unblocked_user_id)
|
||
logger.info(f"🔓 Результат разблокировки: success={success}")
|
||
|
||
if not success:
|
||
logger.error(f"❌ Не удалось разблокировать пользователя: unblocker_id={unblocker_id}, unblocked_user_id={unblocked_user_id}")
|
||
await callback.answer("❌ Не удалось разблокировать пользователя", show_alert=True)
|
||
return
|
||
|
||
# Получаем информацию о разблокированном пользователе
|
||
unblocked_user = await db.get_user(unblocked_user_id)
|
||
unblocked_name = unblocked_user.display_name if unblocked_user else f"ID: {unblocked_user_id}"
|
||
|
||
# Обновляем сообщение
|
||
question_text = f"🔓 <b>Пользователь разблокирован</b>\n\n"
|
||
question_text += f"👤 <b>Разблокирован:</b> {unblocked_name}\n"
|
||
question_text += f"📅 <b>Дата разблокировки:</b> {datetime.now().strftime('%d.%m.%Y %H:%M')}\n\n"
|
||
question_text += "✅ Пользователь снова может отправлять вам вопросы."
|
||
|
||
# Создаем клавиатуру с кнопкой блокировки
|
||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||
from aiogram.types import InlineKeyboardButton
|
||
builder = InlineKeyboardBuilder()
|
||
builder.add(
|
||
InlineKeyboardButton(
|
||
text="🚫 Заблокировать",
|
||
callback_data=f"block_{unblocked_user_id}_{question_id}"
|
||
)
|
||
)
|
||
builder.add(
|
||
InlineKeyboardButton(
|
||
text="⬅️ К списку вопросов",
|
||
callback_data="back_to_questions"
|
||
)
|
||
)
|
||
builder.adjust(1)
|
||
|
||
await callback.message.edit_text(
|
||
question_text,
|
||
reply_markup=builder.as_markup(),
|
||
parse_mode="HTML"
|
||
)
|
||
|
||
logger.info(f"✅ Пользователь успешно разблокирован: unblocked_user_id={unblocked_user_id}")
|
||
await callback.answer("✅ Пользователь разблокирован", show_alert=True)
|
||
|
||
except ValueError as e:
|
||
logger.error(f"❌ Ошибка ValueError при разблокировке: {e}")
|
||
await callback.answer("❌ Неверный формат данных", show_alert=True)
|
||
except Exception as e:
|
||
logger.error(f"❌ Неожиданная ошибка при разблокировке пользователя: {e}")
|
||
await callback.answer("❌ Произошла ошибка при разблокировке", show_alert=True)
|
||
|
||
|
||
@router.message(F.text == "🚫 Заблокированные")
|
||
@log_business_event("blocked_users_button", log_params=True)
|
||
async def blocked_users_button(message: Message):
|
||
"""Обработчик кнопки 'Заблокированные'"""
|
||
try:
|
||
from dependencies import get_database
|
||
db = get_database()
|
||
|
||
# Получаем список заблокированных пользователей
|
||
blocked_user_ids = await db.user_blocks.get_blocked_users(message.from_user.id)
|
||
|
||
if not blocked_user_ids:
|
||
await message.answer(
|
||
"📝 <b>Заблокированные пользователи</b>\n\n"
|
||
"У вас нет заблокированных пользователей.",
|
||
parse_mode="HTML"
|
||
)
|
||
return
|
||
|
||
# Получаем информацию о заблокированных пользователях
|
||
blocked_users = []
|
||
for user_id in blocked_user_ids:
|
||
user = await db.get_user(user_id)
|
||
if user:
|
||
blocked_users.append(user)
|
||
|
||
if not blocked_users:
|
||
await message.answer(
|
||
"📝 <b>Заблокированные пользователи</b>\n\n"
|
||
"Заблокированные пользователи не найдены в системе.",
|
||
parse_mode="HTML"
|
||
)
|
||
return
|
||
|
||
# Формируем сообщение со списком заблокированных
|
||
text = "🚫 <b>Заблокированные пользователи</b>\n\n"
|
||
|
||
for i, user in enumerate(blocked_users, 1):
|
||
text += f"{i}. {user.display_name}\n"
|
||
if user.username:
|
||
text += f" @{user.username}\n"
|
||
text += f" ID: {user.telegram_id}\n\n"
|
||
|
||
text += f"📊 <b>Всего заблокировано:</b> {len(blocked_users)}"
|
||
|
||
# Создаем клавиатуру с кнопками разблокировки
|
||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||
from aiogram.types import InlineKeyboardButton
|
||
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
# Добавляем кнопки для каждого заблокированного пользователя
|
||
for user in blocked_users[:10]: # Ограничиваем до 10 кнопок
|
||
builder.add(
|
||
InlineKeyboardButton(
|
||
text=f"🔓 {user.display_name}",
|
||
callback_data=f"unblock_from_list_{user.telegram_id}"
|
||
)
|
||
)
|
||
|
||
# Добавляем кнопку "Разблокировать всех"
|
||
if len(blocked_users) > 1:
|
||
builder.add(
|
||
InlineKeyboardButton(
|
||
text="🔓 Разблокировать всех",
|
||
callback_data="unblock_all"
|
||
)
|
||
)
|
||
|
||
builder.adjust(1) # По одной кнопке в ряд
|
||
|
||
await message.answer(
|
||
text,
|
||
reply_markup=builder.as_markup(),
|
||
parse_mode="HTML"
|
||
)
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при получении списка заблокированных: {e}")
|
||
await message.answer(
|
||
"❌ Произошла ошибка при получении списка заблокированных пользователей.",
|
||
reply_markup=get_main_keyboard_for_user(message.from_user.id)
|
||
)
|
||
|
||
|
||
@router.callback_query(F.data.startswith("unblock_from_list_"))
|
||
@log_business_event("unblock_from_list_callback", log_params=True)
|
||
async def unblock_from_list_callback(callback: CallbackQuery):
|
||
"""Обработчик разблокировки пользователя из списка"""
|
||
try:
|
||
# Парсим данные: unblock_from_list_{user_id}
|
||
user_id = int(callback.data.split("_")[-1])
|
||
unblocker_id = callback.from_user.id
|
||
|
||
# Получаем базу данных
|
||
from dependencies import get_database
|
||
db = get_database()
|
||
|
||
# Проверяем, заблокирован ли пользователь
|
||
if not await db.is_user_blocked(unblocker_id, user_id):
|
||
await callback.answer("❌ Пользователь не заблокирован", show_alert=True)
|
||
return
|
||
|
||
# Разблокируем пользователя
|
||
success = await db.unblock_user(unblocker_id, user_id)
|
||
|
||
if not success:
|
||
await callback.answer("❌ Не удалось разблокировать пользователя", show_alert=True)
|
||
return
|
||
|
||
# Получаем информацию о разблокированном пользователе
|
||
unblocked_user = await db.get_user(user_id)
|
||
unblocked_name = unblocked_user.display_name if unblocked_user else f"ID: {user_id}"
|
||
|
||
await callback.answer(f"✅ {unblocked_name} разблокирован", show_alert=True)
|
||
|
||
# Обновляем сообщение со списком
|
||
await blocked_users_button(callback.message)
|
||
|
||
except ValueError:
|
||
await callback.answer("❌ Неверный формат данных", show_alert=True)
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при разблокировке из списка: {e}")
|
||
await callback.answer("❌ Произошла ошибка при разблокировке", show_alert=True)
|
||
|
||
|
||
@router.callback_query(F.data == "unblock_all")
|
||
@log_business_event("unblock_all_callback", log_params=True)
|
||
async def unblock_all_callback(callback: CallbackQuery):
|
||
"""Обработчик разблокировки всех пользователей"""
|
||
try:
|
||
unblocker_id = callback.from_user.id
|
||
|
||
# Получаем базу данных
|
||
from dependencies import get_database
|
||
db = get_database()
|
||
|
||
# Получаем список заблокированных пользователей
|
||
blocked_user_ids = await db.user_blocks.get_blocked_users(unblocker_id)
|
||
|
||
if not blocked_user_ids:
|
||
await callback.answer("❌ Нет заблокированных пользователей", show_alert=True)
|
||
return
|
||
|
||
# Разблокируем всех пользователей
|
||
unblocked_count = 0
|
||
for user_id in blocked_user_ids:
|
||
success = await db.unblock_user(unblocker_id, user_id)
|
||
if success:
|
||
unblocked_count += 1
|
||
|
||
await callback.answer(f"✅ Разблокировано {unblocked_count} пользователей", show_alert=True)
|
||
|
||
# Обновляем сообщение со списком
|
||
await blocked_users_button(callback.message)
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при разблокировке всех: {e}")
|
||
await callback.answer("❌ Произошла ошибка при разблокировке", show_alert=True)
|