# 自定义机器人指南
# 一、功能场景
企业存在给
特定群组
自动推送消息的需求,比如:监控报警推送、销售线索推送、运营内容推送等。
你可以在群聊中添加一个自定义机器人
,通过服务端调用webhook
地址,即可将外部系统的通知消息即时推送到群聊中。我们也提供了自定义关键词
、IP白名单
和签名
三种维度的安全配置,控制webhook
的调用范围。
注意:
- 你需有
一定的服务端开发基础
,通过请求调用自定义机器人
的webhook
地址,实现消息推送功能。 自定义机器人
添加进群后即可使用,不需租户管理员审核。这提升了开发机器人的便捷性,但出于租户数据安全考虑,也限制了自定义机器人
的使用场景。自定义机器人
不具有
任何数据访问权限。- 如果希望实现机器人群管理、获取用户信息等深度的应用场景,建议你参考 开发教程,通过
机器人应用
实现。自定义机器人
和机器人应用
的能力对比参考如下:
能力 | 自定义机器人 | 机器人应用 |
---|---|---|
向所在群推送消息 | ✅ | ✅ |
实现包含链接跳转的消息卡片 | ✅ | ✅ |
实现将点击操作提交到服务端的消息卡片 | ❌ | ✅ |
响应用户@机器人的消息 | ❌ | ✅ |
向用户发送单聊消息 | ❌ | ✅ |
创建群、管理群、获取群信息 | ❌ | ✅ |
访问通讯录、管理云文档等其他丰富的开放能力 | ❌ | ✅ |
# 二、操作流程
# 第一步:邀请自定义机器人入群
进入您的目标群聊,打开
聊天设置
,找到群机器人
,并点击添加机器人
,选择自定义机器人
加入群聊。
为你的机器人输入一个合适的机器人名称和描述,然后点击
添加
。
# 第二步:配置 webhook
在添加机器人后,您会获取该机器人的 webhook 地址,格式如下:
https://hi-open.zhipin.com/open-apis/bot/hook/xxxxxxxxxxxxxxxxx
请妥善保存好此 webhook 地址
,不要公布在Gitlab、博客等可公开查阅的网站上,避免地址泄露后被恶意调用发送垃圾消息
# 第三步:调用webhook发送消息
用任意方式向该 webhook 发起 HTTP POST 请求,即可向这个自定义机器人所在的群聊发送消息。
注意:
你需要一定的服务端开发基础,通过服务端请求方式调用webhook地址。
以curl指令为例,请求示例如下:
不开启安全设置签名校验时的调用示例,开启的签名校验的示例请参照下方安全设置部分
curl -X POST -H "Content-Type: application/json" \
-d '{"msg_type":"text","content":{"text":"request example"}}' \
https://hi-open.zhipin.com/open-apis/bot/hook/xxxxxxxxxxxxxxxxx
2
3
你可以把上述指令复制到 macOS系统的终端应用(或Windows系统的控制台应用)中进行测试。
请将上述示例中的url示例部分
https://hi-open.zhipin.com/open-apis/bot/hook/xxxxxxxxxxxxxxxxx
如 请求成功
,返回体为:
{
"code": 0,
"msg": "success",
"data": true
}
2
3
4
5
如 请求体格式错误
,返回体如下。
{
"code": 400,
"msg": "参数有误",
"data": {}
}
2
3
4
5
请检查:
- 请求体内容格式是否与各消息类型的示例代码一致
- 请求体大小不能超过20k
# 三、添加安全设置
如果未妥善保管webhook地址,可能存在webhook地址泄露后,被恶意开发者调用,发送垃圾信息的风险,我们强烈建议对其进行安全设置。安全设置目前提供了 3 种方式,可根据需求选择一种及以上方式进行配置。
# 方式一:自定义关键词
最多设置 10 个关键词。设定后,只有包含至少一个关键词的消息才会被发送。
如,将自定义关键词设为应用报警和项目更新后,自定义机器人发送的消息至少包含其中一个词,才会被发送到群聊。
发送请求后,若自定义关键词校验失败,将返回以下信息:
关键词校验失败
{
"code": 200401,
"msg": "群安全策略校验失败"
}
2
3
4
# 方式二:IP 白名单
最多设置 10 个 IP 地址或地址段。设定后,只处理来自所设 IP 地址范围的请求。支持段输入,如 123.12.1.* 或 123.1.1.1/24
IP校验失败
{
"code": 200401,
"msg": "群安全策略校验失败"
}
2
3
4
# 方式三:签名校验
设定后,发送的请求是需要签名验证来保障来源可信。
签名的算法:把 timestamp + "\n" + 密钥 当做签名字符串,使用 HmacSHA256 算法计算签名,再进行 Base64 编码。
需要注意的是
- timestamp为距当前时间不超过 1 小时(3600s)的时间戳,时间单位s,如:1599360473
- 密钥在创建机器人时会自动生成,可直接从机器人信息界面上复制
Go语言示例代码如下:
func GenSign(secret string, timestamp int64) (string, error) {
//timestamp + key 做sha256, 再进行base64 encode
stringToSign := fmt.Sprintf("%v", timestamp) + "\\n" + secret
var data []byte
h := hmac.New(sha2New, []byte(stringToSign))
_, err := h.Write(data)
if err != nil {
return "", err
}
signature := baseStdEncoding.EncodeToString(h.Sum(nil))
return signature, nil
}
2
3
4
5
6
7
8
9
10
11
12
Java示例代码如下:
package sign;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Base64;
public class SignDemo {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
String secret = "demosecret";
int timestamp = 1686887084;
System.out.printf("sign: %s", GenSign(secret, timestamp));
}
private static String GenSign(String secret, int timestamp) throws NoSuchAlgorithmException, InvalidKeyException {
//把timestamp+"\n"+密钥当做签名字符串
String stringToSign = timestamp + "\n" + secret;
//使用HmacSHA256算法计算签名
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
return URLEncoder.encode(new String(Base64.encodeBase64(signData)));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Python示例代码如下:
import base64
import hashlib
import hmac
# 签名
def gen_sign(secret, timestamp):
# 把timestamp+"\n"+密钥当做签名字符串
string_to_sign = f"{timestamp}\n{secret}"
# 使用HmacSHA256算法计算签名
hmac_code = hmac.new(secret.encode('utf-8'), msg=string_to_sign.encode('utf-8'), digestmod=hashlib.sha256).digest()
# 对签名数据进行Base64编码,并进行URL编码
sign = base64.b64encode(hmac_code).decode('utf-8')
# 对sign进行urlencode
return urllib.parse.quote(sign, safe='')
2
3
4
5
6
7
8
9
10
11
12
13
获得签名后,在发送请求时需加上 timestamp(单位秒:s) 和 sign (即从机器人管理页面获得的签名密钥)字段。发送文本消息的请求例如下:
开启签名验证后发送文本消息
{
"timestamp": "1599360473",
"sign": "xxxxxxxxxxxxxxxxxxxxx(签名密钥)",
"msg_type": "text",
"content": {
"text": "request example"
}
}
2
3
4
5
6
7
8
发送请求后,若签名校验失败,请排查以下原因:
- 时间戳距发送时已超过 1 小时,签名已过期;
- 服务器时间与标准时间有比较大的偏差,导致签名过期。请注意检查、校准你的服务器时间;
- 签名不匹配,校验不通过。将返回以下信息:
签名校验失败
{
"code": 200401,
"msg": "群安全策略校验失败"
}
2
3
4
# 四、发送更加个性化的消息
自定义机器人添加完成后,就能向其 webhook 地址发送 POST 请求,从而在群聊中推送消息了。目前支持推送的消息格式有
文本
、图片消息
、消息卡片
等。
在消息体中,参数 msg_type和对应的消息类型的映射关系如下:
参数(msg_type) | 消息类型 | 参考文档 |
---|---|---|
text | 文本 | |
compressive_card | 消息卡片 | 新消息卡片消息 |
# 发送文本消息
请求的消息体示例:
{
"msg_type": "text",
"content": {
"text": "新更新提醒"
}
}
2
3
4
5
6
# 发送消息卡片
消息卡片可由按钮等多种组件类型搭建而成。关于消息卡片的设计规范、格式等信息,请详见开放平台文档- 消息卡片,或咨询Bosshi开放平台相关同学。
- 自定义机器人发送的消息卡片,只支持通过按钮、文字链方式跳转url,不支持点击后回调信息到服务端的 回传交互
- 在消息卡片中如果要@提到某用户,请注意:自定义机器人仅支持通过
open_id
的方式实现,暂不支持 email、 user_id等其他方式。 - 在新消息卡片中如果要@提到某用户,请注意:自定义机器人支持通过
open_id
email
、user_id
三种方式进行,详情参考compressive_card的语法规则
请求的消息体示例:compressiveCardContent为在消息卡片搭建平台上出的卡片json
{
"msg_type": "compressive_card",
"content": {
"compressiveCardContent": "{\"modules\":[{\"tag\":\"div\",\"text\":{\"content\":\"这是新的消息卡片,现在你可以像搭建乐高一样来搭建消息卡片,同时还可以添加button、下拉交互,\n\t<at email='zenglinhong@kanzhun.com'></at><font color='red'>赶快来尝试吧</font>!\",\"tag\":\"hi_md\"}}],\"header\":{\"text\":{\"content\":\"这是webhook发送消息卡片的标题\",\"tag\":\"plain_text\"},\"color\":\"blue\"}}"
}
}
2
3
4
5
6
7
# 五、删除自定义机器人
在
群设置 - 群机器人
中,点击已有的机器人,点击右侧删除机器人
按钮即可。
# 六、常见问题
# 如何实现@指定人、@所有人
可以在机器人发送的普通文本消息(text)中,使用at标签实现@人效果。具体请求示意如下:
请求体示意:
{
"msg_type": "text",
"content": {
"text": "新更新提醒",
"atIds": "userOpenId"
}
}
2
3
4
5
6
7
8
在富文本卡片(compressive_card)中使用@指定人(暂不支持所有人)请参考文档富文本卡片语法规则 (opens new window)
# 如何获得 @指定人 时所需要的open_id?
自定义机器人不需要租户管理员审核即可向所在的群(包括外部群)发送消息。这一开发上的灵活性也限制自定义机器人不具有任何数据访问权限。否则会在管理员不知情的条件下,泄露租户的隐私信息。
基于这个前提,自定义机器人本身不能调用接口获取用户的open_id,或直接通过用户的邮箱、手机号来@人(恶意开发者可能用这种方式扫出群成员的头像、姓名等隐私信息)。但你可以开发一个机器人应用,使用以下受管控的方案获得用户的 open_id
,然后参考 怎么实现机器人@人,在自定义机器人推送的消息中@人。
方案一:通过邮箱或手机号反查用户的open_id
- 首先,你需要 创建一个自建应用;
- 接着,为应用申请权限:通过手机号或邮箱获取用户 ID(contact:user.id:readonly)。并创建应用版本,提交发版审核。
- 最后,在版本发布审核通过后,调用 通过手机号或邮箱获取用户 ID接口,即可通过用户的手机号或邮箱获取用户的
open_id
。
方案二:解析用户发送给机器人的带@人内容的消息,获取目标用户的open_id
- 首先,你需要 创建一个自建应用;
- 接着,
- 为应用申请权限:获取用户发给机器人的单聊消息(im:message.p2p_msg)、获取与发送单聊、群组消息(im:message);
- 订阅 消息与群组 分类下的 接收消息事件;
- 为这个自建应用创建应用版本,提交发版审核。
- 最后,在版本审核发布后,你可以在同机器人的单聊中发送 @某某某 用户的消息。解析 接收消息事件的返回内容,其中的消息体内上报了被
@用户 的
open_id
信息。
← 如何在群组中使用机器人? 斜杠指令帮助文档 →