73 lines
2.4 KiB
Python
73 lines
2.4 KiB
Python
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
|