""" Реестр callback-обработчиков. Добавить новый callback: @callback("my_prefix") def _cb_my_action(call, bot): ... Диспетчер telegram.py вызывает dispatch(call, bot), который находит нужный обработчик по префиксу callback_data. """ from __future__ import annotations import logging from typing import Callable import database from intradesk import api as intradesk_api from intradesk import reference from bot.keyboards import ( build_executor_keyboard, build_status_keyboard, build_task_keyboard, ) from bot import poster log = logging.getLogger(__name__) _registry: dict[str, Callable] = {} def callback(prefix: str): """Декоратор: регистрирует функцию как обработчик callback с данным префиксом.""" def decorator(fn: Callable) -> Callable: _registry[prefix] = fn return fn return decorator def dispatch(call, bot) -> None: """Роутер: разбирает префикс из callback_data и вызывает нужный обработчик.""" prefix = call.data.split(":")[0] handler = _registry.get(prefix) if handler: handler(call, bot) else: log.debug(f"Unhandled callback prefix: {prefix!r}") # ── Вспомогательные функции ─────────────────────────────────────────────────── def _parse2(s: str) -> tuple[str, int]: """Парсит '{task_number}:{group_id}' → (task_number, group_id).""" parts = s.split(":", 1) return parts[0], int(parts[1]) if len(parts) > 1 and parts[1].isdigit() else 0 def _get_message_id(task_number: str, call) -> int: """Возвращает актуальный message_id поста из БД, иначе — из callback.""" task_id = database.get_task_id_by_number(task_number) if task_id: data = database.get_message_data(task_id) if data: return data["message_id"] return call.message.message_id def _restore_keyboard(bot, call, task_number: str, group_id: int): """Восстанавливает главную клавиатуру задачи на актуальном посте.""" notifier = poster.get_notifier() msg_id = _get_message_id(task_number, call) try: notifier.edit_keyboard(msg_id, build_task_keyboard({ "task_number": task_number, "group_id": group_id, })) except Exception as e: if not notifier.is_stale_error(e): log.warning(f"restore_keyboard failed: {e}") def _answer_stale_or_error(bot, call, exc: Exception): notifier = poster.get_notifier() if notifier.is_stale_error(exc): bot.answer_callback_query(call.id, "⚠️ Пост обновился, попробуйте ещё раз") else: bot.answer_callback_query(call.id, str(exc), show_alert=True) # ── Обработчики ─────────────────────────────────────────────────────────────── @callback("st") def _cb_show_statuses(call, bot): task_number, group_id = _parse2(call.data[3:]) notifier = poster.get_notifier() msg_id = _get_message_id(task_number, call) try: notifier.edit_keyboard(msg_id, build_status_keyboard(task_number, group_id)) bot.answer_callback_query(call.id) except Exception as e: _answer_stale_or_error(bot, call, e) @callback("ss") def _cb_set_status(call, bot): # ss:{task_number}:{group_id}:{status_id} parts = call.data.split(":") task_number = parts[1] group_id = int(parts[2]) status_id = int(parts[3]) status_name = reference.STATUSES.get(status_id, f"#{status_id}") if intradesk_api.change_status(int(task_number), status_id): bot.answer_callback_query(call.id, f"✅ Статус → {status_name}") _restore_keyboard(bot, call, task_number, group_id) else: bot.answer_callback_query(call.id, "❌ Ошибка изменения статуса", show_alert=True) @callback("ex") def _cb_show_executors(call, bot): task_number, group_id = _parse2(call.data[3:]) notifier = poster.get_notifier() msg_id = _get_message_id(task_number, call) try: notifier.edit_keyboard(msg_id, build_executor_keyboard(task_number, group_id)) bot.answer_callback_query(call.id) except Exception as e: _answer_stale_or_error(bot, call, e) @callback("se") def _cb_set_executor(call, bot): # se:{task_number}:{group_id}:{emp_id} parts = call.data.split(":") task_number = parts[1] group_id = int(parts[2]) emp_id = int(parts[3]) emp_name = database.resolve("employees", emp_id) or f"ID {emp_id}" if intradesk_api.assign_executor(int(task_number), user_id=emp_id, group_id=group_id or None): bot.answer_callback_query(call.id, f"✅ Исполнитель → {emp_name}") _restore_keyboard(bot, call, task_number, group_id) else: bot.answer_callback_query(call.id, "❌ Ошибка при назначении", show_alert=True) @callback("su") def _cb_unset_executor(call, bot): """Убрать конкретного исполнителя, оставить только группу.""" task_number, group_id = _parse2(call.data[3:]) if not group_id: bot.answer_callback_query(call.id, "Группа не определена", show_alert=True) return if intradesk_api.assign_executor(int(task_number), group_id=group_id): bot.answer_callback_query(call.id, "✅ Исполнитель снят, группа сохранена") _restore_keyboard(bot, call, task_number, group_id) else: bot.answer_callback_query(call.id, "❌ Ошибка", show_alert=True) @callback("take") def _cb_take(call, bot): task_number = call.data[5:] task_id = database.get_task_id_by_number(task_number) if not task_id: bot.answer_callback_query(call.id, "Заявка не найдена", show_alert=True) return user_id = call.from_user.id user_name = (call.from_user.full_name or call.from_user.username or str(user_id)).strip() database.add_responsible(task_id, user_id, user_name) bot.answer_callback_query(call.id, f"🙋 Вы взяли заявку #{task_number} в работу") poster.refresh_responsible(task_id, task_number) @callback("drop") def _cb_drop(call, bot): task_number = call.data[5:] task_id = database.get_task_id_by_number(task_number) if not task_id: bot.answer_callback_query(call.id, "Заявка не найдена", show_alert=True) return database.remove_responsible(task_id, call.from_user.id) bot.answer_callback_query(call.id, f"✅ Вы сняты с заявки #{task_number}") poster.refresh_responsible(task_id, task_number) @callback("back") def _cb_back(call, bot): task_number, group_id = _parse2(call.data[5:]) bot.answer_callback_query(call.id) _restore_keyboard(bot, call, task_number, group_id)