#!/usr/bin/env python3 from __future__ import annotations import argparse import json import os from urllib.error import HTTPError, URLError from urllib.request import Request, urlopen def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() parser.add_argument("--app-id", required=True) parser.add_argument("--app-secret", required=True) parser.add_argument("--chat-id", required=True) parser.add_argument("--host", required=True) parser.add_argument("--environment", required=True) parser.add_argument("--check", required=True) parser.add_argument("--failure", required=True) parser.add_argument("--details", default="") return parser.parse_args() def post_json(url: str, payload: dict[str, object], headers: dict[str, str] | None = None) -> dict[str, object]: merged_headers = {"Content-Type": "application/json; charset=utf-8"} if headers: merged_headers.update(headers) request = Request( url, data=json.dumps(payload).encode("utf-8"), headers=merged_headers, method="POST", ) try: with urlopen(request, timeout=30) as response: return json.loads(response.read().decode("utf-8")) except HTTPError as exc: body = exc.read().decode("utf-8", errors="replace") raise RuntimeError(f"HTTP {exc.code} calling {url}: {body}") from exc except URLError as exc: raise RuntimeError(f"network error calling {url}: {exc}") from exc def fetch_tenant_access_token(app_id: str, app_secret: str) -> str: payload = post_json( "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", { "app_id": app_id, "app_secret": app_secret, }, ) token = str(payload.get("tenant_access_token", "")).strip() if not token: raise RuntimeError(f"missing tenant_access_token in Feishu response: {json.dumps(payload, ensure_ascii=True)}") return token def build_message(args: argparse.Namespace) -> str: lines = [ "NexusCRM offline host guard failed", f"host: {args.host}", f"environment: {args.environment}", f"check: {args.check}", f"failure: {args.failure}", ] if args.details: lines.append(f"details: {args.details}") return "\n".join(lines) def resolve_header_template(environment: str) -> str: normalized = environment.strip().lower() env_key = f"FEISHU_GUARD_{normalized.upper()}_HEADER_TEMPLATE" configured = os.environ.get(env_key, "").strip() if configured: return configured default_configured = os.environ.get("FEISHU_GUARD_DEFAULT_HEADER_TEMPLATE", "").strip() if default_configured: return default_configured if normalized == "staging": return "orange" if normalized == "prod": return "red" return "grey" def build_card(args: argparse.Namespace) -> dict[str, object]: elements: list[dict[str, object]] = [ { "tag": "div", "fields": [ {"is_short": True, "text": {"tag": "lark_md", "content": f"**Host**\n{args.host}"}}, {"is_short": True, "text": {"tag": "lark_md", "content": f"**Environment**\n{args.environment}"}}, {"is_short": True, "text": {"tag": "lark_md", "content": f"**Check**\n{args.check}"}}, {"is_short": True, "text": {"tag": "lark_md", "content": f"**Failure**\n{args.failure}"}}, ], } ] if args.details: elements.extend( [ {"tag": "hr"}, {"tag": "div", "text": {"tag": "plain_text", "content": args.details}}, ] ) return { "config": {"wide_screen_mode": True}, "header": { "template": resolve_header_template(args.environment), "title": {"tag": "plain_text", "content": "NexusCRM offline host guard failed"}, }, "elements": elements, } def send_chat_message(token: str, chat_id: str, args: argparse.Namespace) -> None: payload = { "receive_id": chat_id, "msg_type": "interactive", "content": json.dumps(build_card(args), ensure_ascii=False), } response = post_json( "https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id", payload, headers={"Authorization": f"Bearer {token}"}, ) if response.get("code") not in (0, "0", None): raise RuntimeError(f"Feishu send message failed: {json.dumps(response, ensure_ascii=True)}") def main() -> int: args = parse_args() token = fetch_tenant_access_token(args.app_id, args.app_secret) send_chat_message(token, args.chat_id, args) return 0 if __name__ == "__main__": raise SystemExit(main())