feat: 接入企微智能机器人——异常报警/数据核验日报/设备健康日报

- flows/shared/wecom.py: WSClient 按需连接(连接→认证→发送→断开),全容错,超长分段
- flows/monitor/alert_failure.py: Prefect 事件触发(Failed/Crashed→run deployment),发群报警
- flows/monitor/daily_report.py: 每日 18:30 近7天 RIIN/SDS 核验(复用 verify)+商品完整性+OCR+失败回顾
- flows/monitor/device_check.py: 每日 09:00 内存/磁盘/GPU(80%阈值)+Docker 容器异常+服务探活
- db.py: 新增 get_goods_integrity/ping; compose worker 挂载 docker.sock+/:/host:ro+GPU
- scripts/wecom_capture_chatid.py: 一次性捕获群 chat_id 工具
This commit is contained in:
yeuimu
2026-08-27 09:40:53 +00:00
parent b9e0bc42be
commit 1d0da2a00b
12 changed files with 836 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
"""一次性工具:捕获目标群的 chat_id 并写回 Secret Block / 发测试消息。
用法:
# 1. 初始化 Secret Block(凭据可从环境变量 WECOM_BOT_ID / WECOM_SECRET 读取)
uv run python scripts/wecom_capture_chatid.py --init
# 2. 监听模式:运行后在目标群里 @机器人 说任意一句话,即可捕获 chat_id
uv run python scripts/wecom_capture_chatid.py --listen
# 3. 捕获到 chat_id 后写回 Secret Block(替换 <chatid>)
uv run python scripts/wecom_capture_chatid.py --save-chatid <chatid>
# 4. 验证收发链路
uv run python scripts/wecom_capture_chatid.py --test "hello"
"""
from __future__ import annotations
import argparse
import asyncio
import json
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from prefect.blocks.system import Secret # noqa: E402
SECRET_BLOCK = "wecom-aibot-credentials"
def _read_block() -> dict[str, str]:
value = Secret.load(SECRET_BLOCK).get()
if isinstance(value, str):
value = json.loads(value)
return value
def _write_block(creds: dict[str, str]) -> None:
Secret(value=json.dumps(creds, ensure_ascii=False)).save(SECRET_BLOCK, overwrite=True)
shown = {k: (v[:8] + "..." if k == "secret" and v else v) for k, v in creds.items()}
print(f"已保存 Secret Block {SECRET_BLOCK}: {shown}")
def do_init() -> None:
creds = {
"bot_id": os.environ.get("WECOM_BOT_ID", ""),
"secret": os.environ.get("WECOM_SECRET", ""),
"chat_id": "",
}
missing = [k for k in ("bot_id", "secret") if not creds[k]]
if missing:
sys.exit(f"缺少环境变量: {missing},请先 export WECOM_BOT_ID=... WECOM_SECRET=...")
_write_block(creds)
def do_save_chatid(chatid: str) -> None:
creds = _read_block()
creds["chat_id"] = chatid
_write_block(creds)
def do_test(message: str) -> None:
from flows.shared.wecom import send_markdown
ok = send_markdown(f"# 机器人连通性测试\n\n{message}\n\n来自 inkreach-prefect 通知链路。")
sys.exit(0 if ok else 1)
async def _listen(creds: dict[str, str]) -> None:
from wecom_aibot_sdk import WSClient
ws = WSClient(bot_id=creds["bot_id"], secret=creds["secret"])
def on_any(frame) -> None:
body = frame.get("body") or {}
chatid = body.get("chatid") or ""
sender = (body.get("from") or {}).get("userid", "")
msgtype = body.get("msgtype", "")
text = ((body.get("text") or {}).get("content") or "")[:50]
print(f"收到帧: msgtype={msgtype} chatid={chatid} from={sender} text={text}")
if chatid:
print(f"\n*** chat_id = {chatid} ***\n")
print(f"写回命令: uv run python scripts/wecom_capture_chatid.py --save-chatid {chatid}")
ws.on("message", on_any)
ws.on("event.enter_chat", on_any)
ws.on("authenticated", lambda: print("已认证,等待消息(请在目标群 @机器人 发一句话)...Ctrl+C 退出"))
await ws.connect()
await asyncio.Event().wait()
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--init", action="store_true", help="初始化 Secret Block(需 WECOM_BOT_ID/WECOM_SECRET 环境变量)")
parser.add_argument("--listen", action="store_true", help="监听消息以捕获 chat_id")
parser.add_argument("--save-chatid", metavar="CHATID", help="把捕获的 chat_id 写回 Secret Block")
parser.add_argument("--test", metavar="MSG", help="向已保存的 chat_id 发送测试消息")
args = parser.parse_args()
if args.init:
do_init()
elif args.save_chatid:
do_save_chatid(args.save_chatid)
elif args.test:
do_test(args.test)
elif args.listen:
# Secret.load 是 sync_compatible,须在进入事件循环前同步调用
creds = _read_block()
asyncio.run(_listen(creds))
else:
parser.print_help()
if __name__ == "__main__":
main()