qw-push - 企业微信群机器人消息推送
通过企业微信群机器人 webhook 推送各类消息。只需一个 webhook 地址即可发送,支持文本、Markdown、图片、图文、文件、模版卡片等。
何时使用
当用户提到或暗示以下需求时使用:企业微信/企微/群机器人/webhook/推送消息/发通知/告警/构建结果通知/发到群里/提醒@某人/qw-push/WeChat Work bot。
前置条件
- python3(macOS 系统自带,无需第三方库)
- 一个企业微信群机器人 webhook 地址,格式形如:
https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=<KEY> - 获取方式:企微群聊 → 右上角
…→ 群机器人 → 添加机器人 → 复制 webhook 地址
webhook 地址的两种给法
- 作为第一个参数直接传入(推荐,"直接给一个 webhook 地址"):
bash python3 "$QW_PUSH" https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=XXX text "hello" - 设置环境变量
QW_WEBHOOK,后续命令无需再传:
bash export QW_WEBHOOK="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=XXX" python3 "$QW_PUSH" text "hello"
> ⚠️ 安全:webhook 地址等价于一把"发消息的钥匙",切勿提交到 git / 贴到公开博客 / 发给不可信的人。若怀疑泄漏,请在企微后台删掉该机器人重建。
入口脚本
QW_PUSH="/Users/wuliang/workspace/qw-push-skill/qw-push.py"
> 该脚本自包含、仅依赖 python3 标准库,从任意路径调用均可。
所有命令格式:python3 "$QW_PUSH" [webhook] <command> [options]
通用选项:-n/--dry-run 只打印将发送的 JSON 载荷、不实际发送(适合先校验再发)。
消息模板(templates/)
各类消息的完整载荷模板存放在 Skill 目录下 templates/,避免每次凭记忆拼 JSON:
| 模板名 | 类型 |
|---|---|
text |
文本消息 |
markdown |
Markdown 消息 |
news |
图文消息 |
card-text-notice |
模版卡片·文本通知型 |
card-news-notice |
模版卡片·图文展示型 |
card-button-interaction |
模版卡片·按钮交互型 |
card-vote-interaction |
模版卡片·投票型 |
card-multiple-interaction |
模版卡片·多项选择型 |
news / card / raw 三个命令支持 -t/--template <名称> 直接加载模板;配合 --dry-run 可先查看结构再按需改值。
# 列出可用模板(无需 webhook)
python3 "$QW_PUSH" templates
# 预览某个卡片的载荷结构(不发送)
python3 "$QW_PUSH" --dry-run card -t text-notice
# 直接发送模板
python3 "$QW_PUSH" "$QW_WEBHOOK" card -t text-notice
> 典型工作流:--dry-run -t <名称> 看结构 → 复制改值 → 用 stdin 发送。模板为完整载荷(含 msgtype);card -t text-notice 会自动解析到 card-text-notice.json。
> ⚠️ 三种交互型卡片(button_interaction / vote_interaction / multiple_interaction)需带 task_id,且必须由配置了回调 URL 的机器人才能发送;普通群机器人发送会被拒绝并返回 errcode=41106 not support template_card type by robot without callback(连展示都不行)。它们仍可用 --dry-run -t <名称> 预览载荷结构。普通群机器人可发的卡片仅 text_notice / news_notice 两种。
命令参考
1. 文本消息 text
python3 "$QW_PUSH" "$QW_WEBHOOK" text "构建完成 ✅" [-m userid1,@all] [-p 13800001111,@all]
| 参数 | 必填 | 说明 |
|---|---|---|
content |
✅ | 文本内容,≤2048 字节(UTF-8);- 表示从 stdin 读取 |
-m/--mentioned |
- | 被@的 userid,逗号分隔;@all=@所有人 |
-p/--mobile |
- | 被@的手机号,逗号分隔;@all=@所有人 |
2. Markdown 消息 markdown
支持标题 #、加粗 **、链接、行内代码、引用 >,以及三种字体颜色:
<font color="info">绿色</font> / <font color="comment">灰色</font> / <font color="warning">橙红</font>。≤4096 字节。
python3 "$QW_PUSH" "$QW_WEBHOOK" markdown $'## 发布通知\n**v1.2.0** 已上线\n<font color="info">成功</font>'
3. 图片消息 image
本地图片自动计算 base64 + md5。支持 JPG/PNG,≤2MB。
python3 "$QW_PUSH" "$QW_WEBHOOK" image /path/to/qrcode.png
4. 图文消息 news
1~8 条图文。json 参数可为:JSON 文件路径 / JSON 字符串 / stdin(-,默认)。
# 通过 stdin
cat <<EOF | python3 "$QW_PUSH" "$QW_WEBHOOK" news
{
"articles": [
{"title":"标题1","description":"摘要","url":"https://example.com/a","picurl":"https://example.com/a.png"},
{"title":"标题2","description":"摘要","url":"https://example.com/b"}
]
}
EOF
# 或直接传 JSON 文件
python3 "$QW_PUSH" "$QW_WEBHOOK" news articles.json
# 或用内置模板
python3 "$QW_PUSH" "$QW_WEBHOOK" news -t news
5. 文件消息 file(需先上传)
# 先上传,得到 media_id
MEDIA_ID=$(python3 "$QW_PUSH" "$QW_WEBHOOK" upload ./report.pdf)
# 再发送
python3 "$QW_PUSH" "$QW_WEBHOOK" file "$MEDIA_ID"
> upload 限制 5B~20MB;media_id 仅 3 天有效,且仅本机器人可用。
6. 模版卡片 card
支持 text_notice(文本通知)/ news_notice(图文展示)等卡片类型,card_action 为卡片整体跳转。
cat <<EOF | python3 "$QW_PUSH" "$QW_WEBHOOK" card
{
"card_type": "text_notice",
"source": {"icon_url":"https://example.com/icon.png","desc":"CI","desc_color":0},
"main_title": {"title":"部署成功","desc":"production"},
"emphasis_content": {"title":"100%","desc":"通过率"},
"sub_title_text": "所有测试通过",
"horizontal_content_list": [
{"keyname":"分支","value":"main"},
{"keyname":"提交","value":"a1b2c3d"}
],
"card_action": {"type":1,"url":"https://example.com/build/123"}
}
EOF
支持 card_type 为 text_notice(文本通知)/ news_notice(图文展示)/ button_interaction(按钮交互)/ vote_interaction(投票)/ multiple_interaction(多项选择);可直接用内置模板:
python3 "$QW_PUSH" "$QW_WEBHOOK" card -t text-notice # 文本通知卡片
python3 "$QW_PUSH" "$QW_WEBHOOK" card -t news-notice # 图文展示卡片
python3 "$QW_PUSH" "$QW_WEBHOOK" card -t button-interaction # 按钮交互卡片
python3 "$QW_PUSH" "$QW_WEBHOOK" card -t vote-interaction # 投票卡片
python3 "$QW_PUSH" "$QW_WEBHOOK" card -t multiple-interaction # 多项选择卡片
7. 原始 JSON raw(万能兜底)
发送任意符合企微 webhook 协议的 JSON 载荷,适合上述命令未覆盖的高级场景。
echo '{"msgtype":"text","text":{"content":"hi"}}' | python3 "$QW_PUSH" "$QW_WEBHOOK" raw
# 也可用模板(等价于发送 markdown 模板载荷)
python3 "$QW_PUSH" "$QW_WEBHOOK" raw -t markdown
交互型卡片与回调机器人
本 Skill 使用的 webhook 群机器人是"只发不收"的,实测发送交互型卡片会返回:
errcode=41106 not support template_card type by robot without callback
即:button_interaction / vote_interaction / multiple_interaction 必须由配置了回调的机器人发送,企业微信要把用户的点击/投票事件推回你的服务端,卡片才能下发。普通 webhook 群机器人没有回调,故被拒绝(连展示都不行)。
如何让交互型卡片可用
需要两件事(具体菜单以企业微信官方文档与管理后台为准):
- 配置一个带回调的机器人。两条常见路径:
- 自建应用机器人:在企业微信管理后台创建自建应用,通过应用消息接口(/cgi-bin/message/send,需corpid+secret换access_token,与 webhook 是不同接口)发送卡片,并在应用里配置"接收消息"回调来接收点击事件。
- 群机器人回调:部分版本的群机器人支持在机器人设置里直接配置回调地址(若你的后台有该项即可用)。 - 实现回调服务端,通过企业微信的 URL 验证并接收事件。
回调 URL 验证机制(企业微信通用)
配置回调时需填写 Token 与 EncodingAESKey。企业微信会向回调 URL 发 GET 请求(带 msg_signature、timestamp、nonce、echostr),服务端须:
- 校验签名:
SHA1("".join(sorted([token, timestamp, nonce])))应等于msg_signature; - 解密 echostr:用
EncodingAESKey(base64 解码补=后取 32 字节作 AES-256-CBC 密钥,IV 取密钥前 16 字节)解密echostr,去 PKCS7 填充; - 返回明文 echostr 作为响应体。
验证通过后,回调生效;后续点击事件以加密 POST 推送到同一 URL,解密后得到事件 JSON(含 TaskId、ButtonKey / 选项 Key 等)。
> ⚠️ 以上验证机制为企业微信应用回调的通用做法;群机器人若支持回调,流程类似但请以官方文档为准。
>
> 本 Skill 仅覆盖 webhook 推送,不含回调服务端。如需我帮你写一个"回调验证 + 接收点击事件"的 Python 服务端示例(含签名校验与 AES 解密),告诉我即可。
限制与注意
- 频率:每个机器人每分钟最多 20 条消息,超限被限流(
errcode=45009)。 - 内容大小:text ≤2048 字节,markdown ≤4096 字节,image ≤2MB,图文 1~8 条,文件 5B~20MB。
- media_id:仅 3 天有效,且仅对上传它的机器人可用。
- 错误处理:HTTP 非 2xx 或
errcode!=0时脚本以非零退出码结束,并向 stderr 打印企业微信返回的原始信息;stdout 仅在upload时输出 media_id,便于脚本捕获。
典型场景
- CI / 构建结果通知 →
markdown+ 颜色标记成功/失败 - 告警推送 →
text+mentioned_list@值班人 - 周报 / 精选图文 →
news - 截图 / 二维码 →
image - 发送报告附件 →
upload+file