# -*- coding: utf-8 -*- """ 推送模块:企业微信 Webhook + 飞书 Webhook 支持推送文本消息、图片消息、文件消息 """ import json import base64 import requests from pathlib import Path from .logger import get_logger logger = get_logger() WECHAT_WEBHOOK_BASE = "https://qyapi.weixin.qq.com/cgi-bin/webhook" def _send_wechat_message(payload, webhook_key, target): url = f"{WECHAT_WEBHOOK_BASE}/send?key={webhook_key}" if target: payload["chatid"] = target try: resp = requests.post(url, json=payload, timeout=(5, 15)) resp.raise_for_status() result = resp.json() if result.get("errcode") == 0: logger.info("企业微信推送成功") return True else: logger.warning("企业微信推送失败: errcode=%s, errmsg=%s", result.get("errcode"), result.get("errmsg")) return False except requests.exceptions.ConnectionError: logger.warning("企业微信推送失败:无法连接到服务器") return False except requests.exceptions.Timeout: logger.warning("企业微信推送失败:请求超时") return False except requests.exceptions.HTTPError as e: logger.warning("企业微信推送失败:HTTP错误 %s", e) return False except (ValueError, requests.exceptions.JSONDecodeError): logger.warning("企业微信推送失败:服务器返回非JSON响应") return False except Exception as e: logger.warning("企业微信推送异常: %s", e) return False def push_wechat_text(content, webhook_key, target): payload = { "msgtype": "text", "text": {"content": content} } return _send_wechat_message(payload, webhook_key, target) def push_wechat_image(image_path, webhook_key, target): if not Path(image_path).exists(): logger.warning("图片文件不存在,跳过推送: %s", image_path) return False with open(image_path, "rb") as f: img_b64 = base64.b64encode(f.read()).decode("utf-8") img_md5 = __import__("hashlib").md5( base64.b64decode(img_b64) ).hexdigest() payload = { "msgtype": "image", "image": {"base64": img_b64, "md5": img_md5} } return _send_wechat_message(payload, webhook_key, target) def push_wechat_markdown(content, webhook_key, target): payload = { "msgtype": "markdown", "markdown": {"content": content} } return _send_wechat_message(payload, webhook_key, target) def push_feishu_text(content, webhook_url): if not webhook_url: logger.info("飞书 webhook 未配置,跳过推送") return False try: payload = {"msg_type": "text", "content": {"text": content}} resp = requests.post(webhook_url, json=payload, timeout=(5, 15)) resp.raise_for_status() result = resp.json() if result.get("code") == 0: logger.info("飞书推送成功") return True else: logger.warning("飞书推送失败: code=%s, msg=%s", result.get("code"), result.get("msg")) return False except requests.exceptions.ConnectionError: logger.warning("飞书推送失败:无法连接到飞书服务器") return False except requests.exceptions.Timeout: logger.warning("飞书推送失败:请求超时") return False except requests.exceptions.HTTPError as e: logger.warning("飞书推送失败:HTTP错误 %s", e) return False except (ValueError, requests.exceptions.JSONDecodeError): logger.warning("飞书推送失败:服务器返回非JSON响应") return False except Exception as e: logger.warning("飞书推送异常: %s", e) return False def send_daily_push(now, date_str, total_count, top3_articles, output_dir, push_config): webhook_key = push_config.get("wechat_key", "") target = push_config.get("wechat_target", "") enable_wechat = push_config.get("enable_wechat_push", False) enable_feishu = push_config.get("enable_feishu_push", False) feishu_url = push_config.get("feishu_webhook", "") if not enable_wechat and not enable_feishu: logger.info("未启用任何推送渠道,跳过") return if not webhook_key and not feishu_url: logger.info("推送已启用但均未配置 webhook") return date_label = now.strftime("%m月%d日") # 文本摘要 summary_lines = [ f"🪖 军事科技每日摘报 — {date_label}", f"📊 今日共 {total_count} 篇精选文章", "", "🔥 今日必看 TOP3:", ] for i, a in enumerate(top3_articles, 1): title = a.get("translated_title") or a.get("title", "")[:40] score = a.get("final_score", 0) summary_lines.append(f" {i}. [{score:.1f}] {title}") summary_lines.append("") summary_lines.append(f"📄 完整报告已生成,包含装备动态/地区冲突/战略政策三分类深度洞察。") text_content = "\n".join(summary_lines) if enable_wechat and webhook_key: push_wechat_text(text_content, webhook_key, target) webzine_image = output_dir / f"military_webzine_{date_str}.png" push_wechat_image(str(webzine_image), webhook_key, target) if enable_feishu and feishu_url: push_feishu_text(text_content, feishu_url)