from __future__ import annotations from dataclasses import dataclass, field from enum import StrEnum class KeyboardScope(StrEnum): OFF = "off" DM = "dm" GROUP = "group" ALL = "all" ALLOWLIST = "allowlist" @dataclass class InlineButton: text: str url: str | None = None callback_data: str | None = None copy_text: str | None = None style: str | None = None icon_custom_emoji_id: str | None = None @dataclass class InlineKeyboard: rows: list[list[InlineButton]] = field(default_factory=list) def to_telegram_markup(self) -> dict | None: if not self.rows: return None return { "inline_keyboard": [ [ _build_button_cell(btn) for btn in row ] for row in self.rows ] } def _build_button_cell(btn: InlineButton) -> dict: cell: dict = {"text": btn.text} if btn.url: cell["url"] = btn.url elif btn.callback_data: cell["callback_data"] = btn.callback_data if btn.copy_text: cell["copy_text"] = {"text": btn.copy_text} if btn.style: cell["style"] = btn.style if btn.icon_custom_emoji_id: cell["icon_custom_emoji_id"] = btn.icon_custom_emoji_id return cell def build_model_selector(models: list[str], selected: str | None = None, prefix: str = "model") -> InlineKeyboard: if not models: return InlineKeyboard() rows: list[list[InlineButton]] = [] current_row: list[InlineButton] = [] for i, model in enumerate(models): label = f"{'✅ ' if model == selected else ''}{model}" btn = InlineButton(text=label, callback_data=f"{prefix}:{model}") current_row.append(btn) if len(current_row) >= 2 or i == len(models) - 1: rows.append(current_row) current_row = [] if current_row: rows.append(current_row) return InlineKeyboard(rows=rows) def build_approval_keyboard(action_id: str) -> InlineKeyboard: return InlineKeyboard(rows=[ [ InlineButton(text="✅ 批准", callback_data=f"approval:approve:{action_id}"), InlineButton(text="❌ 拒绝", callback_data=f"approval:reject:{action_id}"), ] ])