af229e2a7b
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
43 lines
2.1 KiB
Python
43 lines
2.1 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)
|