File size: 1,892 Bytes
3291500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
"""
钉钉自定义机器人:加签并 POST 发送 Markdown 消息。
"""
import base64
import hashlib
import hmac
import os
import time
import urllib.parse
from pathlib import Path

import requests
from dotenv import load_dotenv

# 从项目根目录加载 .env
_project_root = Path(__file__).resolve().parent.parent
load_dotenv(_project_root / ".env")


def _sign(secret: str) -> tuple[str, str]:
    timestamp = str(round(time.time() * 1000))
    string_to_sign = f"{timestamp}\n{secret}"
    hmac_code = hmac.new(
        secret.encode("utf-8"),
        string_to_sign.encode("utf-8"),
        digestmod=hashlib.sha256,
    ).digest()
    sign = urllib.parse.quote(base64.b64encode(hmac_code))
    return timestamp, sign


def build_signed_url(webhook: str, secret: str) -> str:
    """在 Webhook URL 后追加 timestamp 与 sign 参数。"""
    timestamp, sign = _sign(secret)
    sep = "&" if "?" in webhook else "?"
    return f"{webhook}{sep}timestamp={timestamp}&sign={sign}"


def send_markdown(title: str, text: str) -> tuple[bool, str]:
    """
    发送钉钉 Markdown 消息。
    返回 (成功与否, 错误信息)。
    """
    webhook = os.environ.get("DINGTALK_WEBHOOK", "").strip()
    secret = os.environ.get("DINGTALK_SECRET", "").strip()
    if not webhook:
        return False, "未配置 DINGTALK_WEBHOOK"
    url = build_signed_url(webhook, secret) if secret else webhook
    body = {
        "msgtype": "markdown",
        "markdown": {
            "title": title,
            "text": text,
        },
    }
    try:
        r = requests.post(url, json=body, headers={"Content-Type": "application/json"}, timeout=10)
        data = r.json()
        if r.status_code != 200 or data.get("errcode") != 0:
            return False, data.get("errmsg", r.text) or str(r.status_code)
        return True, ""
    except Exception as e:
        return False, str(e)