61 lines
2.4 KiB
Python
61 lines
2.4 KiB
Python
"""WeCom (企业微信 / WeChat Work) sink — send parsed Markdown as an app message.
|
|
|
|
WeCom apps deliver content via the message-send API. The native ingestion path
|
|
is a ``markdown`` message from a self-built app: first an access token is fetched
|
|
with the corp id + secret, then the message is posted. WeCom's markdown is a
|
|
limited subset with a 2048-byte content cap and no inline images, so the body is
|
|
truncated to fit.
|
|
|
|
Docs: https://developer.work.weixin.qq.com/document/path/90236 (message/send),
|
|
https://developer.work.weixin.qq.com/document/path/91039 (gettoken).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from . import _http
|
|
from .base import ParsedDoc, Sink, SinkError, SinkResult, register
|
|
|
|
TOKEN_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
|
|
SEND_URL = "https://qyapi.weixin.qq.com/cgi-bin/message/send"
|
|
|
|
|
|
@register
|
|
class WeComSink(Sink):
|
|
name = "wecom"
|
|
aliases = ("企业微信", "wechatwork")
|
|
requires = ("WECOM_CORPID", "WECOM_CORPSECRET", "WECOM_AGENTID")
|
|
label = "WeCom app markdown (企业微信)"
|
|
|
|
def deliver(self, doc: ParsedDoc) -> SinkResult:
|
|
corpid = self.env("WECOM_CORPID")
|
|
secret = self.env("WECOM_CORPSECRET")
|
|
agentid = self.env("WECOM_AGENTID")
|
|
touser = self.env("WECOM_TOUSER", "@all")
|
|
|
|
# Step 1: fetch an access token.
|
|
token_url = f"{TOKEN_URL}?corpid={corpid}&corpsecret={secret}"
|
|
status, parsed = _http.request_json("GET", token_url)
|
|
if parsed.get("errcode") not in (0, None) or not parsed.get("access_token"):
|
|
raise SinkError(parsed.get("errmsg") or f"WeCom token fetch failed: {parsed}")
|
|
token = parsed["access_token"]
|
|
|
|
# Step 2: send the markdown message.
|
|
send_url = f"{SEND_URL}?access_token={token}"
|
|
payload = {
|
|
"touser": touser,
|
|
"msgtype": "markdown",
|
|
"agentid": int(agentid),
|
|
"markdown": {"content": doc.markdown[:2048]},
|
|
}
|
|
status, parsed = _http.request_json("POST", send_url, payload=payload)
|
|
if parsed.get("errcode") not in (0, None):
|
|
raise SinkError(parsed.get("errmsg") or f"WeCom send failed: {parsed}")
|
|
|
|
return SinkResult(
|
|
sink=self.name,
|
|
ok=True,
|
|
url=None,
|
|
detail="markdown notification (WeCom markdown is a limited subset, "
|
|
"2048-byte cap, no inline images)",
|
|
)
|