Files
shaguabijia-app-server/app/integrations/feishu_notifier.py
T
guke 924e40a84e feat(compare-alert): 固化飞书卡片 table 格式(format_alert_card + send_feishu_card),worker 切换
- feishu_notifier: 新增 send_feishu_card(interactive msg_type,复用 _post_feishu)
- compare_alert_format: 新增 format_alert_card(schema 2.0, header red, markdown摘要+table 6列)
  - 列序: 时间/手机号/用时/失败原因/版本/trace(lark_md);无 width 属性
  - cost 列 helper _cost_cell: total_ms→Ns / step_count→M步 / 两者用" / "连 / 都无给"-"
  - 截断: 超 max_total 只出摘要; 空 hits 返回「本期无异常」卡片
  - 保留 format_alert_message / format_alert_post(有测试依赖)
- compare_alert_worker: _send(post) → _send_card(card); _scan_and_alert 调 format_alert_card
  - SEND_EMPTY 分支: 传空 hits 给 format_alert_card 得「本期无异常」卡片
  - webhook 空降级保留; build_hits/水位逻辑不动
- tests: format/feishu/worker 测试全适配新接口,86 passed 零回归
- 删除临时脚本 scripts/_test_alert_card.py

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-05 15:37:13 +08:00

49 lines
2.4 KiB
Python

"""飞书群自定义机器人发送(text / post 消息)。
自定义机器人「关键词」验证:消息 content.text 必须含机器人配置的关键词,否则飞书返回 code!=0
(如 19024 Key Words Not Found)——本项目消息由 compare_alert_format 生成,标题已含「比价失败报警」。
不需要签名(sign)/IP 白名单。文档:https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot
"""
from __future__ import annotations
import httpx
class FeishuNotifyError(Exception):
"""飞书发送失败(网络错误 / 非 2xx / 业务 code!=0,含关键词不匹配)。"""
def _post_feishu(webhook_url: str, payload: dict, timeout: float) -> None:
"""内部 helper:POST payload 到飞书 webhook 并校验响应。失败抛 FeishuNotifyError。"""
try:
resp = httpx.post(webhook_url, json=payload, timeout=timeout)
except httpx.HTTPError as e:
raise FeishuNotifyError(f"feishu request failed: {e}") from e
if resp.status_code >= 300:
raise FeishuNotifyError(f"feishu http {resp.status_code}: {resp.text[:200]}")
try:
data = resp.json()
except ValueError as e:
raise FeishuNotifyError(f"feishu bad json: {resp.text[:200]}") from e
code = data.get("code", data.get("StatusCode", 0))
if code not in (0, None):
raise FeishuNotifyError(f"feishu code={code} msg={data.get('msg') or data.get('StatusMessage')}")
def send_feishu_text(webhook_url: str, text: str, *, timeout: float = 10.0) -> None:
"""POST 一条 text 消息到飞书群机器人 webhook。失败(网络/HTTP/业务 code)抛 FeishuNotifyError。"""
payload = {"msg_type": "text", "content": {"text": text}}
_post_feishu(webhook_url, payload, timeout)
def send_feishu_post(webhook_url: str, title: str, content: list, *, timeout: float = 10.0) -> None:
"""发飞书富文本(post)。content 是段落数组,每段是元素数组[{tag:text/a,...}]。失败抛 FeishuNotifyError。"""
payload = {"msg_type": "post", "content": {"post": {"zh_cn": {"title": title, "content": content}}}}
_post_feishu(webhook_url, payload, timeout)
def send_feishu_card(webhook_url: str, card: dict, *, timeout: float = 10.0) -> None:
"""发飞书交互卡片(interactive)。card 为 schema 2.0 卡片 dict。失败抛 FeishuNotifyError。"""
payload = {"msg_type": "interactive", "card": card}
_post_feishu(webhook_url, payload, timeout)