- 调整依赖导入顺序与清理冗余空行 - 修复setup wizard步骤存储逻辑 - 新增消息编辑/转发标记、卡片控件支持 - 完善目标校验、媒体上传、配对存储功能 - 优化流式响应与错误处理逻辑 - 增强SSRF防护与配置灵活性 - 新增/扩展内置命令与卡片处理能力 - 调整媒体大小限制与类型参数
266 lines
7.2 KiB
Python
266 lines
7.2 KiB
Python
from __future__ import annotations
|
|
|
|
|
|
def build_approval_card(title: str, action_id: str, confirm_text: str = "确认", reject_text: str = "拒绝") -> dict:
|
|
return {
|
|
"cards_v2": [
|
|
{
|
|
"card_id": action_id,
|
|
"card": {
|
|
"header": {"title": title, "imageType": "SQUARE"},
|
|
"sections": [
|
|
{
|
|
"widgets": [
|
|
{
|
|
"buttonList": {
|
|
"buttons": [
|
|
_make_button(confirm_text, "approve", green=True),
|
|
_make_button(reject_text, "reject", green=False),
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
],
|
|
},
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
def build_poll_card(question: str, options: list[str], action_id: str) -> dict:
|
|
buttons = [_make_button(opt, f"poll_vote_{i}_{action_id}") for i, opt in enumerate(options)]
|
|
|
|
sections: list[dict] = []
|
|
while buttons:
|
|
chunk = buttons[:2]
|
|
buttons = buttons[2:]
|
|
sections.append({"widgets": [{"buttonList": {"buttons": chunk}}]})
|
|
|
|
return {
|
|
"cards_v2": [
|
|
{
|
|
"card_id": action_id,
|
|
"card": {
|
|
"header": {"title": question, "imageType": "SQUARE"},
|
|
"sections": sections,
|
|
},
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
def build_info_card(title: str, fields: dict[str, str]) -> dict:
|
|
widgets = []
|
|
for key, value in fields.items():
|
|
widgets.append(
|
|
{
|
|
"decoratedText": {
|
|
"topLabel": key,
|
|
"text": value,
|
|
}
|
|
}
|
|
)
|
|
|
|
return {
|
|
"cards_v2": [
|
|
{
|
|
"card": {
|
|
"header": {"title": title},
|
|
"sections": [{"widgets": widgets}],
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
def build_form_card(title: str, fields: list[dict], submit_action: str) -> dict:
|
|
widgets = []
|
|
for field in fields:
|
|
field_input_type = field.get("input_type", "SINGLE_LINE")
|
|
|
|
if field_input_type in ("SINGLE_SELECT", "MULTI_SELECT"):
|
|
widgets.append(
|
|
{
|
|
"selectionInput": {
|
|
"label": field.get("label", ""),
|
|
"name": field.get("name", ""),
|
|
"type": field_input_type,
|
|
"items": _build_selection_items(field.get("options", [])),
|
|
}
|
|
}
|
|
)
|
|
elif field_input_type in ("DATE_AND_TIME", "DATE_ONLY", "TIME_ONLY"):
|
|
widgets.append(
|
|
_make_date_time_picker(
|
|
name=field.get("name", ""),
|
|
label=field.get("label", ""),
|
|
type_=field_input_type,
|
|
value_ms_epoch=field.get("value_ms_epoch"),
|
|
)
|
|
)
|
|
elif field_input_type == "DIVIDER":
|
|
widgets.append({"divider": {}})
|
|
else:
|
|
widgets.append(
|
|
{
|
|
"textInput": {
|
|
"label": field.get("label", ""),
|
|
"name": field.get("name", ""),
|
|
"type": field_input_type,
|
|
}
|
|
}
|
|
)
|
|
|
|
widgets.append({"buttonList": {"buttons": [_make_button("提交", submit_action, green=True)]}})
|
|
|
|
return {
|
|
"cards_v2": [
|
|
{
|
|
"card": {
|
|
"header": {"title": title},
|
|
"sections": [{"widgets": widgets}],
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
def build_exec_approval_card(
|
|
title: str,
|
|
action_id: str,
|
|
detail_fields: dict[str, str] | None = None,
|
|
) -> dict:
|
|
widgets = []
|
|
|
|
if detail_fields:
|
|
for key, value in detail_fields.items():
|
|
widgets.append(
|
|
{
|
|
"decoratedText": {
|
|
"topLabel": key,
|
|
"text": value,
|
|
}
|
|
}
|
|
)
|
|
|
|
widgets.append(
|
|
{
|
|
"buttonList": {
|
|
"buttons": [
|
|
_make_button("确认执行", f"exec_approve_{action_id}", green=True),
|
|
_make_button("拒绝执行", f"exec_reject_{action_id}", green=False),
|
|
]
|
|
}
|
|
}
|
|
)
|
|
|
|
return {
|
|
"cards_v2": [
|
|
{
|
|
"card": {
|
|
"header": {"title": title, "imageType": "SQUARE"},
|
|
"sections": [{"widgets": widgets}],
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
def build_selection_card(
|
|
title: str,
|
|
selection_name: str,
|
|
label: str,
|
|
options: list[dict[str, str]],
|
|
selection_type: str = "SINGLE_SELECT",
|
|
submit_action: str | None = None,
|
|
) -> dict:
|
|
widgets = [
|
|
{
|
|
"selectionInput": {
|
|
"label": label,
|
|
"name": selection_name,
|
|
"type": selection_type,
|
|
"items": _build_selection_items(options),
|
|
}
|
|
}
|
|
]
|
|
|
|
if submit_action:
|
|
widgets.append({"buttonList": {"buttons": [_make_button("提交", submit_action, green=True)]}})
|
|
|
|
return {
|
|
"cards_v2": [
|
|
{
|
|
"card": {
|
|
"header": {"title": title},
|
|
"sections": [{"widgets": widgets}],
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
def _make_divider() -> dict:
|
|
return {"divider": {}}
|
|
|
|
|
|
def _build_selection_items(options: list[dict[str, str]]) -> list[dict]:
|
|
items = []
|
|
for opt in options:
|
|
items.append(
|
|
{
|
|
"text": opt.get("text", opt.get("label", "")),
|
|
"value": opt.get("value", ""),
|
|
"selected": opt.get("selected", False),
|
|
}
|
|
)
|
|
return items
|
|
|
|
|
|
def _make_button(text: str, action: str, green: bool = False) -> dict:
|
|
color = {"red": 0, "green": 0.6, "blue": 0} if green else {"red": 0.6, "green": 0, "blue": 0}
|
|
return {
|
|
"text": text,
|
|
"color": color,
|
|
"onClick": {"action": {"function": action}},
|
|
}
|
|
|
|
|
|
def _make_date_time_picker(
|
|
name: str,
|
|
label: str = "",
|
|
type_: str = "DATE_AND_TIME",
|
|
value_ms_epoch: int | None = None,
|
|
) -> dict:
|
|
widget = {
|
|
"dateTimePicker": {
|
|
"name": name,
|
|
"label": label,
|
|
"type": type_,
|
|
}
|
|
}
|
|
if value_ms_epoch is not None:
|
|
widget["dateTimePicker"]["valueMsEpoch"] = str(value_ms_epoch)
|
|
return widget
|
|
|
|
|
|
def _make_grid(column_count: int, items: list[dict]) -> dict:
|
|
return {
|
|
"grid": {
|
|
"columnCount": column_count,
|
|
"items": [{"widgets": [item]} for item in items],
|
|
}
|
|
}
|
|
|
|
|
|
def _make_columns(column_items: list[list[dict]]) -> dict:
|
|
return {"columns": {"columnItems": [{"widgets": col_widgets} for col_widgets in column_items]}}
|
|
|
|
|
|
def _make_link_button(text: str, url: str) -> dict:
|
|
return {
|
|
"text": text,
|
|
"onClick": {"openLink": {"url": url}},
|
|
}
|