195 lines
7.2 KiB
Python
195 lines
7.2 KiB
Python
"""
|
||
Реестр 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)
|