1. 抽离alt文本截断逻辑为公共工具函数 2. 重构快速回复构建函数,支持更多action类型 3. 新增 narrowcast 消息发送与进度查询能力 4. 新增用户粉丝数、好友人口统计API调用 5. 优化回复消息逻辑,支持quote token 6. 改进审批ID生成逻辑,避免重复 7. 调整部分配置与异常处理逻辑 8. 新增熔断器与相关指标统计
121 lines
4.6 KiB
Python
121 lines
4.6 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
from yuxi.channels.adapters.line.flex_templates.common import truncate_alt_text
|
|
from yuxi.channels.adapters.line.quick_reply import build_quick_reply_items
|
|
|
|
QUICK_REPLIES_RE = re.compile(r"\[\[quick_replies:\s*(.+?)\]\]")
|
|
CONFIRM_RE = re.compile(r"\[\[confirm:\s*(.+?)\|\s*(.+?)\|\s*(.+?)\]\]")
|
|
BUTTONS_RE = re.compile(r"\[\[buttons:\s*(.+?)\]\]")
|
|
LOCATION_RE = re.compile(r"\[\[location:\s*(.+?)\]\]")
|
|
|
|
_MAX_DIRECTIVES = 5
|
|
|
|
|
|
def parse_line_directives(text: str) -> list[dict] | None:
|
|
if not text or not text.strip():
|
|
return None
|
|
|
|
result: list[dict] = []
|
|
|
|
qr_match = QUICK_REPLIES_RE.search(text)
|
|
if qr_match and len(result) < _MAX_DIRECTIVES:
|
|
labels = [lbl.strip() for lbl in qr_match.group(1).split(",") if lbl.strip()]
|
|
main_text = QUICK_REPLIES_RE.sub("", text).strip()
|
|
items = build_quick_reply_items(labels)
|
|
if items:
|
|
result.append(
|
|
{
|
|
"type": "text",
|
|
"text": main_text[:5000],
|
|
"quickReply": {"items": items},
|
|
}
|
|
)
|
|
|
|
cf_match = CONFIRM_RE.search(text)
|
|
if cf_match and len(result) < _MAX_DIRECTIVES:
|
|
question = cf_match.group(1).strip()
|
|
yes_label = cf_match.group(2).strip()
|
|
no_label = cf_match.group(3).strip()
|
|
main_text = CONFIRM_RE.sub("", text).strip()
|
|
if main_text and question:
|
|
result.append({"type": "text", "text": main_text[:5000]})
|
|
if question:
|
|
result.append(
|
|
{
|
|
"type": "template",
|
|
"altText": truncate_alt_text(question),
|
|
"template": {
|
|
"type": "confirm",
|
|
"text": question[:240],
|
|
"actions": [
|
|
{"type": "message", "label": yes_label[:20], "text": "yes"},
|
|
{"type": "message", "label": no_label[:20], "text": "no"},
|
|
],
|
|
},
|
|
}
|
|
)
|
|
|
|
btn_match = BUTTONS_RE.search(text)
|
|
if btn_match and len(result) < _MAX_DIRECTIVES:
|
|
parts = [p.strip() for p in btn_match.group(1).split("|")]
|
|
if len(parts) >= 1:
|
|
title = parts[0] if len(parts) > 0 else ""
|
|
description = parts[1] if len(parts) > 1 else ""
|
|
btn_defs = parts[2] if len(parts) > 2 else ""
|
|
actions = []
|
|
if btn_defs.strip():
|
|
for btn_def in btn_defs.split(",")[:4]:
|
|
btn_def = btn_def.strip()
|
|
if not btn_def:
|
|
continue
|
|
if ":" in btn_def:
|
|
b_label, b_data = btn_def.split(":", 1)
|
|
b_label, b_data = b_label.strip(), b_data.strip()
|
|
if b_data.startswith("https://"):
|
|
actions.append({"type": "uri", "label": b_label[:20], "uri": b_data})
|
|
else:
|
|
actions.append({"type": "message", "label": b_label[:20], "text": b_data})
|
|
|
|
main_text = BUTTONS_RE.sub("", text).strip()
|
|
if main_text:
|
|
result.append({"type": "text", "text": main_text[:5000]})
|
|
if title and actions:
|
|
result.append(
|
|
{
|
|
"type": "template",
|
|
"altText": truncate_alt_text(title),
|
|
"template": {
|
|
"type": "buttons",
|
|
"title": title[:40],
|
|
"text": description[:160],
|
|
"actions": actions,
|
|
},
|
|
}
|
|
)
|
|
|
|
loc_match = LOCATION_RE.search(text)
|
|
if loc_match and len(result) < _MAX_DIRECTIVES:
|
|
parts = [p.strip() for p in loc_match.group(1).split("|")]
|
|
if len(parts) >= 4:
|
|
title, address = parts[0], parts[1]
|
|
try:
|
|
lat, lon = float(parts[2]), float(parts[3])
|
|
except ValueError:
|
|
lat, lon = 0.0, 0.0
|
|
main_text = LOCATION_RE.sub("", text).strip()
|
|
if main_text:
|
|
result.append({"type": "text", "text": main_text[:5000]})
|
|
result.append(
|
|
{
|
|
"type": "location",
|
|
"title": title[:100],
|
|
"address": address[:100],
|
|
"latitude": lat,
|
|
"longitude": lon,
|
|
}
|
|
)
|
|
|
|
return result[:_MAX_DIRECTIVES] if result else None
|