155 lines
5.4 KiB
Python
155 lines
5.4 KiB
Python
# -*- 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)
|