wechat_alert.py 2.7 KB
#!/usr/bin/env python
"""
Lightweight Enterprise WeChat webhook sender for service monitor alerts.

This module is intentionally small and focused so that Bash-based monitors
can invoke it without pulling in the full application stack.

Usage example:
  python scripts/wechat_alert.py --service backend --level error --message "backend restarted"
"""

import argparse
import json
import os
import sys
from datetime import datetime

import requests


def get_webhook_url() -> str:
    """
    Resolve webhook URL from environment, with optional default.

    Priority:
      1. SERVICE_MONITOR_WECHAT_WEBHOOK
      2. Built-in default (provided by ops)
    """
    env_url = os.getenv("SERVICE_MONITOR_WECHAT_WEBHOOK", "").strip()
    if env_url:
        return env_url
    # Fallback to the URL provided in ops configuration.
    return (
        "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?"
        "key=2d9e38ef-9242-4e2e-82cc-fab060322871"
    )


def build_text_payload(message: str) -> dict:
    return {
        "msgtype": "text",
        "text": {
            "content": message,
        },
    }


def send_wechat_message(message: str) -> None:
    url = get_webhook_url()
    if not url:
        # No webhook configured; fail silently to avoid breaking callers.
        return

    payload = build_text_payload(message)
    headers = {"Content-Type": "application/json"}

    try:
        resp = requests.post(url, headers=headers, data=json.dumps(payload), timeout=5)
    except Exception:
        # Swallow all exceptions to avoid impacting the caller.
        return

    try:
        if resp.status_code != 200:
            return
        data = resp.json()
        # errcode == 0 means success per WeCom docs
        if int(data.get("errcode", -1)) != 0:
            return
    except Exception:
        return


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description="Send Enterprise WeChat alert message")
    parser.add_argument("--service", help="service name", default="")
    parser.add_argument("--level", help="alert level (info|warn|error)", default="info")
    parser.add_argument(
        "--message",
        required=True,
        help="alert message body (short, human-readable)",
    )

    args = parser.parse_args(argv)

    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    parts = [
        f"【服务监控告警】",
        f"时间: {ts}",
    ]
    if args.service:
        parts.append(f"服务: {args.service}")
    if args.level:
        parts.append(f"级别: {args.level}")
    parts.append(f"详情: {args.message}")

    full_message = "\n".join(parts)
    send_wechat_message(full_message)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())