如何通过企业微信发送消息通知

蚊子前端博客
发布于 2023-02-10 14:52
企业微信为我们提供了多种消息通知的方式,每种方式都有哪些特点,怎么接入呢?

通过企业微信发送消息通知有两种方式:

  1. 群机器人;

  2. 企业内部自建应用;

两者的对比

接入难易程度发送范围是否可以接收回调
群机器人简单,任何人都可以创建,并添加到群组中(默认任何人,但管理员可以开启白名单)只能往群组中发送消息不能,只能发送,不能接收
企业自建应用需要管理员添加,并需要配置发送消息服务器的 IP 白名单可以指定给某人或某几人或某部门发送消息(在管理员指定范围内)可以接收消息,实现互动

适用范围

  • 群机器人:适合小范围推送,或者群内所有人或大部分人都关心的消息,否则容易对群内其他人造成困扰。如服务的监控消息、每周一次的科技信息等。

  • 企业自建应用:适合精准推送,推送一些偏私密性的消息,或者只需要让他自己知道就足够了。比如禅道的任务、bug 状态变动、内容增删等的通知;比如流水线的通知(谁触发谁接收通知)等;

群机器人

群机器人,顾名思义,是只能在群组中才能添加的机器人。二人对话的聊天框中是无法添加的,但我们可以通过一些技巧来实现。

如何创建小群测试

有时候我们想单独测试下我们的机器人,又不想打扰别人,可以用如下的方式操作。

先默默拉几个人建一个群,注意,不要发送消息、不要改群名。不要进行任何操作。然后再默默把其他人踢掉,就可以形成二人群或者一人群了。其他人是完全不感知的,他们是不知道自己被拉群了,然后又被踢掉了。

比如这个群,这群里只有我一个人:

群里只有一个人-蚊子的前端博客

添加群机器人

群里任何人都可以添加机器人。

添加成功后,就会有一个对应的 webhook 地址,其他应用使用这个地址,就可以通过群机器人发送消息了。 注意,不要泄露您的 webhook 地址,避免他人通过该地址发送垃圾消息。

如何发送消息

群机器人发送消息还是比较简单的,按照文档配置即可。开发者可以按以下说明向这个地址发起 HTTP POST 请求,即可实现给该群组发送消息。

官方发送不同格式消息的地址:https://developer.work.weixin.qq.com/document/path/91770

如在 shell 脚本中:

COPYSHELL

curl 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxx-yyyyyy-zzzzzz' \ -H 'Content-Type: application/json' \ -d ' { "msgtype": "text", "text": { "content": "hello world" } }'

比如我在流水线是用的群机器人,来提示我流水线的启动和结束。gitlab 流水线可以执行 shell 脚本,我们直接编写 shell 脚本即可。

通过shell命令向机器人发通知-蚊子的前端博客

很奇怪,这段代码在博客里一直发布不成功,就改成截图了。

有的同学会通过后台服务发送一些信息,如在 nodejs 中:

COPYJAVASCRIPT

const axios = require("axios"); const send = async () => { const result = await axios({ url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxx-yyyyyy-zzzzzz", method: "post", data: { msgtype: "news", news: { articles: [ { title: "中秋节礼品领取", description: "今年中秋节公司有豪礼相送", url: "www.qq.com", picurl: "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png", }, ], }, }, }); if (result.status === 200 && result.data?.errcode === 0) { console.log("success", result.data); } else { console.error("fail"); } }; send();

把机器人发布到公司

若您觉得您的群机器人功能很不错,后台服务也比较稳定,有的同学会搞一些笑话机器人、每周信息汇总机器人、每日一语机器人等等。就可以将其发布到公司范围内,其他人可以就把他添加到别的群组里。

将机器人发布到公司-蚊子的前端博客

添加机器人时,可以从这里选择已发布到公司的机器人。

发布到公司的机器人列表-蚊子的前端博客

对于没有发布到公司的群机器人,只能靠大家的口口相传了。

点评

企业微信机器人引入简单,使用方便。相应的,能力也有很大的局限性,比如无法鉴权,谁拿到这个地址,都能发送消息;同时,只有在有这个机器人的群里,才能收到消息,否则就无法感知。

比如上面的那个群里,群里只有我自己,我就可以接收到通知,但其他人,虽然他们触发流水产生的群机器人消息,但因为他们不在这个群里,就无法收到通知。

企业自建应用

如何创建企业自建应用

这需要拥有该企业微信的管理员权限,才能创建自建应用。

一. 进入管理后台,选择应用管理,下拉到底部,点击创建应用;

二. 填入提前准备好的 logo 图片(建议使用 750*750,1M 以内的 jpg、png 图片)、应用名称和应用介绍(选填),然后选择可见范围;

-蚊子的前端博客

同时,还要准备好域名和几个 IP,这里需要从管理后台下载一个验证文件,放到该域名对应的服务器根目录中,域名验证通过后就可以添加 IP 白名单了。

  • 主动推送消息:可以在设置了本地机器 IP 白名单后,在本地机器就可以发起测试;

  • 接收并回复消息:只能在已验证的域名上操作;

三. 创建成功;得到如下的几条数据(获取方式:https://developer.work.weixin.qq.com/document/path/90665):

  • corpId: 即企业 ID,在“我的企业”tab 的最底部;

  • secret: 即该应用的密钥,点击进入刚才创建的应用内进行查看;

  • agentId: 该应用的 id(主动发送消息时用不到,但接收消息时需要);

若您不是管理员,还请将第 2 序列中准备好的数据给到公司的管理员,待管理员创建成功后,得到第 3 序列中需要的数据。

自建应用的特点:

  1. 只有能由有管理员权限的人员,进行创建;

  2. 可以定向给企业内的任何成员发送消息,对其他成员无干扰;

  3. 可以接收消息,并进行相应的回复(关键词的自动回复和接入程序的更复杂的回复等);

  4. 需要验证域名、IP 白名单,并且需要该企业对应的 corpId,才能正常使用;

获取 access_token

企业自建应用的任何功能,都要首先获取到 access_token。

官网地址:https://developer.work.weixin.qq.com/document/path/91039

通过已获取到的 corpId 和 secret 就可以拿到 access_token 了。不过 token 接口有请求频率的限制,并且获取到的 token 有 7200s(2 小时)的有效期,程序需要缓存该 token。

COPYJAVASCRIPT

const axios = require("axios"); const { corpId, secret } = require("./utils/secret"); const cache = require("./utils/cache"); const getAccessToken = async () => { const cacheToken = await cache.get("token"); if (cacheToken) { return cacheToken; } const { status, data } = await axios({ url: "https://qyapi.weixin.qq.com/cgi-bin/gettoken", params: { corpid: corpId, corpsecret: secret, }, }); if (status === 200 && data.errcode === 0) { cache.set("token", data.access_token); return data.access_token; } return null; }; module.exports = getAccessToken;

发送消息推送

企业自建应用,除消息推送外,还有很多其他的功能,如通讯录管理、身份验证、消息推送、创建群聊等,不过我们这里主讲消息推送这块。

跟群机器人类似,消息也有很多种类型,每种类型的消息所需要的字段也不一样,大家可自行查阅文档。这里仅举一个发送文本消息的例子:

COPYJAVASCRIPT

const axios = require("axios"); const getAccessToken = require("./src/get-access-token"); const { agentId } = require("./src/utils/secret"); const sendTextMsg = async () => { const accessToken = await getAccessToken(); const { status, data } = await axios({ url: `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${accessToken}`, method: "post", data: { touser: "xiaowenzi", // 多个用户,用 | 隔开 // toparty: 'PartyID1|PartyID2', // 部门 id // totag: 'TagID1 | TagID2', msgtype: "text", agentid: agentId, text: { content: '你的快递已到,请携带工卡前往邮件中心领取。\n 出发前可查看<a href="https://www.xiabingbao.com">邮件中心视频实况</a>,聪明避开排队。', }, }, }); console.log(status, data); };

我们在创建好「消息提醒」的自建应用后,其他的内网服务(如禅道、知识库、流水线、错误率警告等服务),都可以通过该应用向用户或部门发送通知,方便周知各种内容的变更。

接收并回复消息

自建应用还有一个很重要的功能,就是可以接收每个用户发送过来的任何消息(包括底部菜单的点击),然后再针对该消息,进行相应的回复。

这里的配置相对来说比较麻烦一些:

  1. 所有的操作只能在管理后台配置的链接进行;

  2. 接收和要回复的消息均是 xml 格式或 xml string 格式的;

在接收消息之前,首先要在管理后台配置接收消息的线上地址,在保存地址时,就会校验这个地址的有效性,即企业微信会以 GET 请求的方式,携带一些参数,请求该 url,若能正常解密 url 参数中的内容并返回,即为配置成功。

加密和解密的算法与官方已有库

在接收并回复消息模块中,有三个过程:

  1. 验证 url:接收参数并解析出参数中的内容,然后返回;主要是在保存 url 地址时使用;

  2. 接收消息:企业微信会以 POST 方式并携带参数请求我们的保存的地址 ,根据参数和 body 中的数据,解析出真正的消息;

  3. 回复消息:把要回复的消息,连同时间戳、随机数等进行加密,然后返回给企业微信;

第 2、3 过程是连续的,若要回复消息,则直接返回即可;若不想回复消息或回复需要很长的时间(官方会等待 5 秒时间),可以直接在第 2 步中返回 200(即以空串为返回包),然后再通过上面的“发送消息推送”,主动给相关用户推送消息。

各种加密解密算法也挺绕的,官方也出了一些相关语言的包(库),可以直接拿来使用(https://developer.work.weixin.qq.com/document/path/90307)。我这里只有一个能运行 php 的主机,因此选择了 php 语言的库。各位可以根据自己的需要,选择相应的库即可。

我这里使用了 codeigniter 框架。

验证 url

COPYPHP

// 检测 url 的合法性 private function checkValidUrl() { $this->load->library('WXBizMsgCrypt'); $this->wxbizmsgcrypt->init($this->token, $this->encodingAesKey, $this->corpId); $sVerifyMsgSig = $this->input->get("msg_signature"); $sVerifyTimeStamp = $this->input->get("timestamp"); $sVerifyNonce = $this->input->get("nonce"); $sVerifyEchoStr = $this->input->get("echostr"); // 需要返回的明文 $sEchoStr = ""; $errCode = $this->wxbizmsgcrypt->VerifyURL($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sVerifyEchoStr, $sEchoStr); if ($errCode == 0) { echo $sEchoStr; } else { print("ERR: " . $errCode . "\n\n"); } }

接收消息

接收消息这里很特殊,企业微信发送过来的是一个 xml string 类型的。我也是好久没写过 php 了,不知道怎么接收这个数据,用 post 方式尝试了 N 多次,也没成功。后来才查到相关资料是用file_get_contents("php://input")的方式来接收。

接收到所有的数据,再通过官方提供的解密函数,解析出真实的 xml 信息。注意,这里并不是单纯的消息,还有各种如发送用户、发送的消息类型、发送的时间等信息。还得需要通过 xml 的进一步解析,才能解析出各个字段的值。

COPYPHP

/** * 接收消息 * 参数接收一些加密参数,具体消息是通过post的body传过来的, * 在php中,若body是一个纯字符串,需要用 file_get_contents('php://input') 的方式来接收 * * 关于file_get_contents和post的区别: * @see https://www.cnblogs.com/phpper/p/9574419.html */ private function decodeMsg() { $this->load->library('WXBizMsgCrypt'); $this->wxbizmsgcrypt->init($this->token, $this->encodingAesKey, $this->corpId); $sReqMsgSig = $this->input->get("msg_signature"); $sReqTimeStamp = $this->input->get("timestamp"); $sReqNonce = $this->input->get("nonce"); // post请求的密文数据 $sReqData = file_get_contents('php://input'); $sMsg = ""; // 解析之后的明文 $errCode = $this->wxbizmsgcrypt->DecryptMsg($sReqMsgSig, $sReqTimeStamp, $sReqNonce, $sReqData, $sMsg); if ($errCode == 0) { // 解密成功,sMsg即为xml格式的明文 echo ($sMsg); return $sMsg; // TODO: 对明文的处理 /* "<xml><ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName> <FromUserName><![CDATA[mycreate]]></FromUserName> <CreateTime>1409659813</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[hello]]></Content> <MsgId>4561255354251345929</MsgId> <AgentID>218</AgentID> </xml>" */ } else { print("ERR: " . $errCode . "\n\n"); } }

回复消息

回复消息与接收消息差不多,根据官方要求的字段格式,拼接 xml,然后再以 string 类型进行加密。

COPYPHP

private function sendMsg() { $this->load->library('WXBizMsgCrypt'); $this->wxbizmsgcrypt->init($this->token, $this->encodingAesKey, $this->corpId); // 接收消息 $getMsg = $this->decodeMsg(); // 接收到消息后,经过处理,然后需要返回给用户消息了 $now = time(); // 需要发送的明文 $sRespData = "<xml><ToUserName><![CDATA[xiaowenzi]]></ToUserName><FromUserName><![CDATA[{$this->corpId}]]></FromUserName><CreateTime>{$now}</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[{$getMsg}]]></Content><AgentID>{$this->agentId}</AgentID></xml>"; $sReqTimeStamp = $now; $sReqNonce = rand(); $sEncryptMsg = ""; //xml格式的密文 $errCode = $this->wxbizmsgcrypt->EncryptMsg($sRespData, $sReqTimeStamp, $sReqNonce, $sEncryptMsg); if ($errCode == 0) { echo ($sEncryptMsg); // print("done \n"); // TODO: // 加密成功,企业需要将加密之后的sEncryptMsg返回 // HttpUtils.SetResponce($sEncryptMsg); //回复加密之后的密文 } else { print("ERR: " . $errCode . "\n\n"); // exit(-1); } }

在群聊会话中发通知

比如一些活动报名、或者中奖名单等,有一长串的用户名单,需要拉群知会一些消息。若要手动创建的话,那每次都得搜索拉入,需要很长时间。但若通过接口创建的话,几秒钟就可以。

创建群聊

创建群聊官方地址:https://developer.work.weixin.qq.com/document/path/90245

COPYJSON

{ "name": "NAME", "owner": "userid1", "userlist": ["userid1", "userid2", "userid3"], "chatid": "CHATID" }

设置好群聊名称、群主、群成员、群 Id(可选),就可以创建了。创建成功后,会返回该群聊的 id:

COPYJAVASCRIPT

// 创建群聊 const createGroupChat = async () => { const accessToken = await getAccessToken(); const { status, data } = await axios({ url: `https://qyapi.weixin.qq.com/cgi-bin/appchat/create?access_token=${accessToken}`, method: "post", data: { // chatid: Date.now(), name: `企业内建应用创建的群聊-${Date.now().toString(36)}`, owner: "xiaowenzi", userlist: ["xiaowenzi", "dawenzi"], // 成员名单至少需要2个人 }, }); // 创建成功后,不会立即在聊天框中展示出来,需要通过发送消息,来激活该群聊 console.log("createGroupChat", status, data); // chatid: wruVG5OwAAQF_MwwspiyXmD8T7NQt8yA };

群聊创建成功后,会返回该群聊的 id,用于后续的比如修改群聊标题、群聊成员、发送群聊消息等操作。而且,群聊刚创建成功时,是不会立即在聊天框中展示出来的,需要通过发送消息,来激活该群聊。

向群聊中发送消息

这里也有一个发送消息的接口,但这里的接口跟上面的“发送消息推送”不是同一个接口。而且,这里的接口还需要指定群 id(即 chatid)才能发送消息。

COPYJAVASCRIPT

const sendMsgToGroup = async () => { const accessToken = await getAccessToken(); const { status, data } = await axios({ url: `https://qyapi.weixin.qq.com/cgi-bin/appchat/send?access_token=${accessToken}`, method: "post", data: { chatid: "chatid", // 修改自己的群聊id msgtype: "markdown", markdown: { content: '# 你的快递已到\n请携带工卡前往邮件中心领取\n<a href="http://work.weixin.qq.com">邮件中心视频实况</a>,聪明避开排队', }, safe: 0, }, }); console.log("sendMsgToGroup", status, data); };

这里发送消息的格式,也是有多种格式。

总结

群机器人和企业自建应用有着不同的接入难度和接入场景,各位可以根据自己的需要,来选择适合自己的方式。

标签:
阅读(1723)

公众号:

qrcode

微信公众号:前端小茶馆

公众号:

qrcode

微信公众号:前端小茶馆