项目初始化

This commit is contained in:
2026-07-02 11:31:16 +08:00
commit eef4c76e3f
64 changed files with 13369 additions and 0 deletions

96
daily/webhook.py Normal file
View File

@@ -0,0 +1,96 @@
"""推送早报至企业微信群 webhook超长自动分多条"""
from __future__ import annotations
import sys
import time
from pathlib import Path
import certifi
import httpx
from daily.config import OUTPUT_DIR, ROOT, env_int, wecom_chunk_bytes
from daily.wecom_split import split_wecom_messages
_PUSH_GAP_MS = 300
def _load_webhook_key() -> str:
from daily.config import env
key = (env("WECOM_WEBHOOK_KEY") or "").strip()
if not key:
raise RuntimeError("请设置 WECOM_WEBHOOK_KEY项目根 .env")
return key
def _resolve_report_path(arg: str | None) -> Path:
if arg:
path = Path(arg)
if not path.is_absolute():
path = ROOT / path
return path
candidates = sorted(
OUTPUT_DIR.glob("*.wecom.md"),
key=lambda p: p.stat().st_mtime,
reverse=True,
)
if candidates:
return candidates[0]
legacy = sorted(ROOT.glob("*.wecom.md"), key=lambda p: p.stat().st_mtime, reverse=True)
if legacy:
return legacy[0]
raise RuntimeError("未找到 .wecom.md 报告,请先运行 python -m daily")
def _post_markdown(client: httpx.Client, url: str, content: str) -> None:
payload = {"msgtype": "markdown", "markdown": {"content": content}}
resp = client.post(url, json=payload)
resp.raise_for_status()
data = resp.json()
if data.get("errcode", 0) != 0:
raise RuntimeError(f"推送失败: errcode={data.get('errcode')} errmsg={data.get('errmsg')}")
def send_report(report_path: Path | None = None) -> None:
path = _resolve_report_path(str(report_path) if report_path else None)
if not path.exists():
raise RuntimeError(f"报告文件不存在: {path}")
content = path.read_text(encoding="utf-8").strip()
if not content:
raise RuntimeError(f"报告内容为空: {path}")
chunk_limit = wecom_chunk_bytes()
max_parts = env_int("DAILY_WECOM_MAX_PARTS", 5)
parts = split_wecom_messages(content, chunk_limit)
if len(parts) > max_parts:
raise RuntimeError(
f"早报需 {len(parts)} 条消息,超过 DAILY_WECOM_MAX_PARTS={max_parts},请调小各区块条数"
)
key = _load_webhook_key()
url = f"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={key}"
with httpx.Client(timeout=20.0, verify=certifi.where()) as client:
for i, part in enumerate(parts, 1):
if i > 1:
time.sleep(_PUSH_GAP_MS / 1000.0)
_post_markdown(client, url, part)
total_bytes = len(content.encode("utf-8"))
if len(parts) == 1:
print(f"已推送至企业微信: {path.name} ({total_bytes} bytes)")
else:
sizes = ", ".join(str(len(p.encode("utf-8"))) for p in parts)
print(f"已推送至企业微信: {path.name} ({total_bytes} bytes → {len(parts)} 条: {sizes})")
def main(argv: list[str] | None = None) -> int:
args = argv if argv is not None else sys.argv[1:]
try:
send_report(Path(args[0]) if args else None)
return 0
except Exception as exc:
print(f"ERROR: {exc}", file=sys.stderr)
return 1