intrabot/bot/callbacks.py
2026-03-15 21:36:03 +03:00

195 lines
7.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Реестр 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)