feat:完成微信公众号的自动化工具

This commit is contained in:
Daniel
2026-04-01 14:21:10 +08:00
commit 124a5f0192
16 changed files with 675 additions and 0 deletions

72
app/services/wechat.py Normal file
View File

@@ -0,0 +1,72 @@
from __future__ import annotations
import time
import httpx
import markdown2
from app.config import settings
from app.schemas import PublishResponse, WechatPublishRequest
class WechatPublisher:
def __init__(self) -> None:
self._access_token = None
self._expires_at = 0
async def publish_draft(self, req: WechatPublishRequest) -> PublishResponse:
if not settings.wechat_appid or not settings.wechat_secret:
return PublishResponse(ok=False, detail="缺少 WECHAT_APPID / WECHAT_SECRET 配置")
token = await self._get_access_token()
if not token:
return PublishResponse(ok=False, detail="获取微信 access_token 失败")
html = markdown2.markdown(req.body_markdown)
payload = {
"articles": [
{
"title": req.title,
"author": req.author or settings.wechat_author,
"digest": req.summary,
"content": html,
"content_source_url": "",
"need_open_comment": 0,
"only_fans_can_comment": 0,
}
]
}
async with httpx.AsyncClient(timeout=25) as client:
url = f"https://api.weixin.qq.com/cgi-bin/draft/add?access_token={token}"
r = await client.post(url, json=payload)
data = r.json()
if data.get("errcode", 0) != 0:
return PublishResponse(ok=False, detail=f"微信发布失败: {data}", data=data)
return PublishResponse(ok=True, detail="已发布到公众号草稿箱", data=data)
async def _get_access_token(self) -> str | None:
now = int(time.time())
if self._access_token and now < self._expires_at - 60:
return self._access_token
async with httpx.AsyncClient(timeout=20) as client:
r = await client.get(
"https://api.weixin.qq.com/cgi-bin/token",
params={
"grant_type": "client_credential",
"appid": settings.wechat_appid,
"secret": settings.wechat_secret,
},
)
data = r.json()
token = data.get("access_token")
if not token:
return None
self._access_token = token
self._expires_at = now + int(data.get("expires_in", 7200))
return token