""" Описание кнопок как данных. Чтобы добавить или убрать кнопку — редактируй TASK_BUTTONS. Чтобы добавить новый тип клавиатуры — добавь список ButtonSpec + функцию build_*_keyboard(). Keyboard (агностичный тип) конвертируется в нативный формат в адаптере (см. bot/notifier.py). """ from __future__ import annotations import hashlib import hmac import time from dataclasses import dataclass, field from typing import Callable import config from bot.notifier import Keyboard, KeyboardButton # ── Спецификация кнопки ──────────────────────────────────────────────────────── @dataclass class ButtonSpec: """ Описание одной кнопки главной клавиатуры задачи. url_factory — функция(ctx) → str, для URL-кнопок callback_prefix — строковый префикс callback_data, для inline-кнопок condition — функция(ctx) → bool, показывать ли кнопку (по умолчанию: всегда) """ label: str url_factory: Callable[[dict], str] | None = None callback_prefix: str | None = None condition: Callable[[dict], bool] = field( default_factory=lambda: (lambda _ctx: True) ) # ── Вспомогательная функция: подписанная ссылка на Mini App ─────────────────── def _sign_task_url(task_number: str) -> str: """Генерирует HMAC-подписанную ссылку на Mini App (TTL 30 дней).""" expires = int(time.time()) + 30 * 24 * 3600 msg = f"{task_number}:{expires}".encode() sig = hmac.new(config.MINIAPP_SECRET.encode(), msg, hashlib.sha256).hexdigest()[:16] return f"{config.PUBLIC_URL}/miniapp?task={task_number}&exp={expires}&sig={sig}" # ── Главная клавиатура задачи ───────────────────────────────────────────────── # # Добавить кнопку → добавь ButtonSpec в TASK_BUTTONS # Убрать кнопку → удали строку из TASK_BUTTONS # Временно скрыть → раскомментируй condition или закомментируй весь ButtonSpec # TASK_BUTTONS: list[ButtonSpec] = [ ButtonSpec( label="📄 Предпросмотр", url_factory=lambda ctx: _sign_task_url(ctx["task_number"]), condition=lambda ctx: bool(config.PUBLIC_URL), ), ButtonSpec( label="🔗 Открыть в IntraDesk", url_factory=lambda ctx: config.BASE_TASK_URL + ctx["task_number"], ), # ── Управляющие кнопки (раскомментируй для включения) ───────────────────── # ButtonSpec( # label="📋 Статус", # callback_prefix="st", # condition=lambda ctx: not ctx.get("closed", False), # ), # ButtonSpec( # label="👤 Исполнитель", # callback_prefix="ex", # condition=lambda ctx: not ctx.get("closed", False), # ), # ButtonSpec( # label="🙋 Взять", # callback_prefix="take", # condition=lambda ctx: not ctx.get("closed", False), # ), # ButtonSpec( # label="❌ Снять", # callback_prefix="drop", # condition=lambda ctx: not ctx.get("closed", False), # ), ] def _make_callback_data(spec: ButtonSpec, ctx: dict) -> str: task_number = ctx["task_number"] group_id = ctx.get("group_id", 0) # take/drop — без group_id if spec.callback_prefix in ("take", "drop"): return f"{spec.callback_prefix}:{task_number}" return f"{spec.callback_prefix}:{task_number}:{group_id}" def build_task_keyboard(ctx: dict) -> Keyboard: """ Строит главную клавиатуру задачи по TASK_BUTTONS. ctx ожидает: task_number : str group_id : int (опционально) closed : bool (опционально) Раскладка: по 2 кнопки в ряд. """ buttons: list[KeyboardButton] = [] for spec in TASK_BUTTONS: if spec.condition(ctx): if spec.url_factory: buttons.append(KeyboardButton( label=spec.label, url=spec.url_factory(ctx), )) elif spec.callback_prefix: buttons.append(KeyboardButton( label=spec.label, callback_data=_make_callback_data(spec, ctx), )) return [buttons[i:i + 2] for i in range(0, len(buttons), 2)] # ── Клавиатура выбора статуса ───────────────────────────────────────────────── def build_status_keyboard(task_number: str, group_id: int) -> Keyboard: from intradesk import reference rows: Keyboard = [] for sid, name in reference.STATUSES.items(): rows.append([KeyboardButton( label=name, callback_data=f"ss:{task_number}:{group_id}:{sid}", )]) rows.append([KeyboardButton( label="↩ Назад", callback_data=f"back:{task_number}:{group_id}", )]) return rows # ── Клавиатура выбора исполнителя ───────────────────────────────────────────── def build_executor_keyboard(task_number: str, group_id: int) -> Keyboard: import database rows: Keyboard = [] for uid in config.EXECUTOR_USER_IDS: name = database.resolve("employees", uid) or f"ID {uid}" rows.append([KeyboardButton( label=name, callback_data=f"se:{task_number}:{group_id}:{uid}", )]) rows.append([KeyboardButton( label="➖ Только группа", callback_data=f"su:{task_number}:{group_id}", )]) rows.append([KeyboardButton( label="↩ Назад", callback_data=f"back:{task_number}:{group_id}", )]) return rows