Files
SmartUp/backend/app/utils/dingtalk.py
T
2026-05-29 17:51:12 +08:00

132 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""DingTalk webhook signing and message formatting (ported from monitor script)."""
import base64
import hashlib
import hmac
import json
import time
from typing import Any
from urllib.parse import quote_plus
def dingtalk_signed_url(webhook_url: str, secret: str) -> str:
timestamp = str(int(time.time() * 1000))
string_to_sign = f"{timestamp}\n{secret}".encode("utf-8")
digest = hmac.new(secret.encode("utf-8"), string_to_sign, hashlib.sha256).digest()
sign = quote_plus(base64.b64encode(digest).decode("utf-8"))
sep = "&" if "?" in webhook_url else "?"
return f"{webhook_url}{sep}timestamp={timestamp}&sign={sign}"
def format_dingtalk_rate_changed(upstream_name: str, changed_at: str, changes: list[dict[str, Any]]) -> dict[str, Any]:
lines = [
f"### 📊 {upstream_name} 分组倍率变更",
"",
f"- **时间**{changed_at}",
f"- **变化数量**{len(changes)}",
"",
]
for ch in changes:
name = ch.get("group_name") or ch.get("group_id") or "unknown"
platform = ch.get("platform") or "-"
old = ch.get("old_rate")
new = ch.get("new_rate")
lines.append(f"- `{name}` ({platform})`{old}` → `{new}`")
return {
"msgtype": "markdown",
"markdown": {
"title": f"{upstream_name} 分组倍率变更",
"text": "\n".join(lines),
},
}
def format_dingtalk_website_rate_changed(
website_name: str,
target_group_name: str,
changed_at: str,
old_rate: Any,
new_rate: Any,
) -> dict[str, Any]:
group_name = target_group_name or "unknown"
lines = [
f"### 网站倍率变更:{website_name}",
"",
f"- 时间:{changed_at}",
f"- 分组:`{group_name}`",
f"- 倍率:`{old_rate}` -> `{new_rate}`",
]
return {
"msgtype": "markdown",
"markdown": {
"title": f"{website_name} 网站倍率变更",
"text": "\n".join(lines),
},
}
def format_dingtalk_priority_changed(
website_name: str, upstream_name: str, changed_at: str,
updates: list[dict],
) -> dict[str, Any]:
success = sum(1 for u in updates if u.get("status") == "success")
failed = sum(1 for u in updates if u.get("status") == "failed")
skipped = sum(1 for u in updates if u.get("status") == "skipped")
lines = [
f"### 🔄 {website_name} 账号优先级变更",
"",
f"- **触发上游**{upstream_name}",
f"- **时间**{changed_at}",
f"- **摘要**{success} 更新 / {failed} 失败 / {skipped} 跳过",
"",
]
for u in updates:
emoji = {"success": "", "failed": "", "skipped": "⏭️"}.get(u.get("status", ""), "")
gid = u.get("group_id", "?")
priority = u.get("new_priority", "")
lines.append(f"{emoji} `{gid}` → priority={priority}")
return {
"msgtype": "markdown",
"markdown": {
"title": f"{website_name} 账号优先级变更",
"text": "\n".join(lines),
},
}
def format_dingtalk_balance_low(
upstream_name: str, balance: float, threshold: float, changed_at: str
) -> dict[str, Any]:
lines = [
f"### ⚠️ {upstream_name} 余额不足",
"",
f"- **当前余额**{balance:.2f}",
f"- **告警阈值**{threshold:.2f}",
f"- **时间**{changed_at}",
]
return {
"msgtype": "markdown",
"markdown": {
"title": f"{upstream_name} 余额不足",
"text": "\n".join(lines),
},
}
def format_dingtalk_status(upstream_name: str, event: str, changed_at: str, error: str = "") -> dict[str, Any]:
emoji = "🔴" if event == "upstream_unhealthy" else "🟢"
label = "服务异常" if event == "upstream_unhealthy" else "服务恢复"
lines = [
f"### {emoji} {upstream_name} {label}",
"",
f"- **时间**{changed_at}",
]
if error:
lines.append(f"- **错误**{error}")
return {
"msgtype": "markdown",
"markdown": {
"title": f"{upstream_name} {label}",
"text": "\n".join(lines),
},
}