dandysuper-telegram-colored-choices-buttons
为 Telegram 机器人选择按钮自动分配颜色样式。根据选项关键性、不可逆性与优先级分类为 default/destructive/secondary。
安装 / 下载方式
TotalClaw CLI推荐
totalclaw skills install shop:totalclaw:totalclaw~dandysuper-telegram-colored-choices-buttonscURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/totalclaw%3Atotalclaw~dandysuper-telegram-colored-choices-buttons/file -o dandysuper-telegram-colored-choices-buttons.md## 概述(中文)
为 Telegram 机器人选择按钮自动分配颜色样式。根据选项关键性、不可逆性与优先级分类为 default/destructive/secondary。
## 技能正文
# Telegram 机器人 — 彩色选择按钮技能
## 目的
每当机器人向用户展示选项时,**必须**根据各选项的关键性、不可逆性或差异自动为每个按钮着色。机器人绝不发送无样式/纯文本选择按钮——颜色始终应用。
## 核心规则
**每当机器人向用户发送 2 个以上选项时,必须对每个选项分类并自动分配 `style`。这不是可选项。**
---
## Telegram Bot API — 按钮样式
`InlineKeyboardButton` 与 `KeyboardButton` 上的两个字段:
- **`style`**(String,可选)— 按钮颜色:
- *(省略)* — 默认强调色/蓝色。**推荐 / 安全 / 主要操作。**
- `"destructive"` — 红色。**不可逆、危险或高风险操作。**
- `"secondary"` — 灰色/弱化。**低优先级、关闭、跳过或中性操作。**
- **`icon_custom_emoji_id`**(String,可选)— 按钮上的自定义表情图标。
---
## 自动分类规则
机器人构建一组选择按钮时,**必须**在发送前将每个选项分为三个层级之一:
### 层级 1 — 默认(强调色/蓝色):推荐路径
适用于:
- 最安全或最常见的操作
- 积极确认("Yes"、"Continue"、"Accept"、"Start")
- 机器人会推荐的操作
- 流程中向前推进
**做法:** 省略 `style` 字段(或设为 `null`)。
### 层级 2 — 破坏性(红色):高风险或不可逆
适用于:
- 删除、移除或永久更改某物
- 取消进行中的操作并丢失工作
- 封禁、拉黑或限制用户
- 拒绝、婉拒重要事项
- 花费金钱、代币或积分
- 难以轻易撤销
**做法:** 设置 `"style": "destructive"`。
### 层级 3 — 次要(灰色):低优先级或逃生口
适用于:
- 跳过、关闭或推迟("Maybe later"、"Not now")
- 中性退路("Back"、无损失时的 "Cancel")
- 展示更多信息而不承诺("Details"、"Help")
- 选项集中最不重要的项
**做法:** 设置 `"style": "secondary"`。
---
## 如何决策 — 对比很重要
当选项关键性不同时,颜色**必须**反映该对比:
**高对比** — 选项后果差异很大:
```
"Delete my account" → destructive(红色)
"Keep my account" → default(蓝色)
```
**中对比** — 一个主操作,一个逃生:
```
"Subscribe" → default(蓝色)
"Not now" → secondary(灰色)
```
**低对比** — 选项大致平等:
```
"Option A" → default(蓝色)
"Option B" → default(蓝色)
"Skip" → secondary(灰色)
```
**一组内多个层级:**
```
"Confirm purchase" → default(蓝色) — 推荐
"Change amount" → secondary(灰色) — 中性/返回
"Cancel order" → destructive(红色) — 丢失进度
```
---
## 分类示例
机器人问:"Approve this document?"
```json
[
[{"text": "✅ Approve", "callback_data": "approve"},
{"text": "❌ Reject", "callback_data": "reject", "style": "destructive"}],
[{"text": "⏭ Review later", "callback_data": "skip", "style": "secondary"}]
]
```
机器人问:"Pick a plan:"
```json
[
[{"text": "Free Plan", "callback_data": "free"},
{"text": "Pro Plan", "callback_data": "pro"}],
[{"text": "Compare plans", "callback_data": "compare", "style": "secondary"}]
]
```
(平等选项 = 均为默认;信息链接 = 次要)
机器人问:"Delete all messages in this chat?"
```json
[
[{"text": "🗑 Delete all", "callback_data": "delete_all", "style": "destructive"}],
[{"text": "Keep messages", "callback_data": "keep"}]
]
```
(破坏性操作为红色;安全操作为默认蓝色)
机器人问:"Transfer 500 tokens to @user?"
```json
[
[{"text": "Send 500 tokens", "callback_data": "send", "style": "destructive"},
{"text": "Cancel", "callback_data": "cancel", "style": "secondary"}]
]
```
(花费 = 破坏性,因为有成本;取消 = 次要)
---
## 实现 — Python 自动分类器
机器人**必须**使用分类函数确定样式。参考实现如下:
```python
import re
# 各层级信号关键词(不区分大小写,匹配按钮文本 + callback_data)
DESTRUCTIVE_SIGNALS = [
r"\bdelete\b", r"\bremove\b", r"\bban\b", r"\bblock\b",
r"\breject\b", r"\bdecline\b", r"\brevoke\b", r"\bterminate\b",
r"\bcancel order\b", r"\bcancel subscription\b",
r"\bunsubscribe\b", r"\bdestroy\b", r"\bpurge\b",
r"\bspend\b", r"\btransfer\b", r"\bpay\b", r"\bsend.*tokens?\b",
r"\breset\b", r"\bclear all\b", r"\bwipe\b",
r"\bleave\b", r"\bquit\b", r"\bdisconnect\b",
]
SECONDARY_SIGNALS = [
r"\bskip\b", r"\bnot now\b", r"\bmaybe later\b", r"\blater\b",
r"\bback\b", r"\bdismiss\b", r"\bclose\b",
r"\bdetails\b", r"\bmore info\b", r"\bhelp\b", r"\babout\b",
r"\bno thanks\b", r"\bnevermind\b",
r"\bcancel$", # 纯 "cancel"(无工作损失)= 次要,非破坏性
]
def classify_button_style(text: str, callback_data: str = "", context_hint: str = "") -> str | None:
"""
根据文本与上下文自动确定按钮样式。
返回:
"destructive" — 红色按钮(不可逆 / 高风险)
"secondary" — 灰色按钮(低优先级 / 关闭)
None — 默认蓝色按钮(主要 / 推荐)
context_hint: 可选额外上下文,如 "this action costs money"
"""
combined = f"{text} {callback_data} {context_hint}".lower()
# 先检查破坏性(更高优先级)
for pattern in DESTRUCTIVE_SIGNALS:
if re.search(pattern, combined):
return "destructive"
# 再检查次要
for pattern in SECONDARY_SIGNALS:
if re.search(pattern, combined):
return "secondary"
# 默认 = 主要(蓝色)
return None
def build_choice_buttons(choices: list[dict]) -> list[list[dict]]:
"""
接收原始选项列表,返回已自动分配样式的 Bot API inline_keyboard 行。
每个 choice dict:
text (str): 按钮标签(必需)
data (str): callback_data(必需,除非设置了 url)
url (str): URL 按钮(可选,与 data 互斥)
style (str|None): 覆盖样式 — 若设置则跳过自动分类
context (str): 分类器额外提示(如 "costs money")
emoji_id (str): 自定义表情 ID(可选)
row (int): 强制放入指定行(可选)
返回适用于 inline_keyboard 的行列表。
"""
# 按行分组
row_map: dict[int, list[dict]] = {}
auto_row = 0
for i, choice in enumerate(choices):
btn: dict = {"text": choice["text"]}
# 操作
if "url" in choice:
btn["url"] = choice["url"]
else:
btn["callback_data"] = choice.get("data", choice["text"].lower().replace(" ", "_"))
# 样式 — 有覆盖则用覆盖,否则自动分类
if "style" in choice and choice["style"] is not None:
btn["style"] = choice["style"]
else:
auto_style = classify_button_style(
choice["text"],
choice.get("data", ""),
choice.get("context", ""),
)
if auto_style:
btn["style"] = auto_style
# 自定义表情
if "emoji_id" in choice:
btn["icon_custom_emoji_id"] = choice["emoji_id"]
# 行分配
target_row = choice.get("row", auto_row)
row_map.setdefault(target_row, []).append(btn)
# 每 2 个按钮自动换行
if len(row_map.get(auto_row, [])) >= 2:
auto_row += 1
return [row_map[k] for k in sorted(row_map.keys())]
```
### 在机器人中使用分类器:
```python
import requests
def send_choices(bot_token, chat_id, text, choices, parse_mode="HTML"):
"""发送带自动着色选择按钮的消息。"""
keyboard = build_choice_buttons(choices)
payload = {
"chat_id": chat_id,
"text": text,
"parse_mode": parse_mode,
"reply_markup": {"inline_keyboard": keyboard},
}
url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
resp = requests.post(url, json=payload)
resp.raise_for_status()
return resp.json()
# 机器人只需传入原始选项 — 颜色自动分配:
send_choices(TOKEN, chat_id, "Approve this document?", [
{"text": "✅ Approve", "data": "approve"},
{"text": "❌ Reject", "data": "reject"}, # 自动 → destructive(红色)
{"text": "⏭ Review later", "data": "later"}, # 自动 → secondary(灰色)
])
```
---
## 与 python-telegram-bot 库配合使用
> 若库版本尚未暴露 `style`,通过 `api_kwargs` 传入。
```python
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
def auto_button(text, callback_data, context=""):
style = classify_button_style(text, callback_data, context)
kwargs = {"style": style} if style else {}
return InlineKeyboardButton(text, callback_data=callback_data, api_kwargs=kwargs)
keyboard = InlineKeyboardMarkup([
[auto_button("Approve", "approve"),
auto_button("Reject", "reject")],
[auto_button("Skip", "skip")],
])
await update.message.reply_text("Pick an option:", reply_markup=keyboard)
```
---
## 回复键盘 — 同样规则适用
```json
{
"chat_id": "<CHAT_ID>",
"text": "Delete your data?",
"reply_markup": {
"keyboard": [
[
{"text": "Keep my data"},
{"text": "Delete everything", "style": "destructive"}
]
],
"resize_keyboard": true,
"one_time_keyboard": true
}
}
```
---
## 按钮上的自定义表情
可与同一按钮上的 `style` 组合使用:
```json
{"text": "Boost", "callback_data": "boost", "style": "destructive", "icon_custom_emoji_id": "5368324170671202286"}
```
---
## Shell 辅助工具
快速测试彩色按钮:
```bash
./SKILL.sh <BOT_TOKEN> <CHAT_ID>
```
## 参考
- Telegram Bot API: https://core.telegram.org/bots/api
- Bot API 更新日志: https://core.telegram.org/bots/api-changelog
- Telegram 博客公告: https://telegram.org/blog/crafting-android-design-and-more