# 第三方客服系统接入- 发送客服消息
仅适用于对平台有过授权的下类账号类型 1. 已认证的服务号 2. 已认证的订阅号 3. 小程序 4. h5 5. 小程序插件
# 配置信息
例如:
- APPID: xxxxxxxxxxxxxxx
- TOKEN: xxxxxxxxxxxxxxx
- EncodingAESKey: xxxxxxxxxxxxxxx
# 发送客服消息接口:
https://chatbot.weixin.qq.com/openapi/sendmsg/{TOKEN}
接口类型:
POST请求
# 请求参数说明:
字段 | 类型 | 是否必填 | 描述 |
---|---|---|---|
appid | string | 否 | 当渠道为0、5、6时,请传入公众号或小程序的appid,渠道为7时非必填或传入token |
openid | string | 是 | 用户的微信openid |
msg | string | 是 | 消息内容 |
channel | number | 是 | 渠道ID,详见渠道对映表 |
event | string | 否 | 事件, 详见事件列表 |
kefuname | string | 否 | 客服人员的昵称 |
kefuavatar | string | 否 | 客服人员的头像 |
ans_node_name | string | 否 | 分类/技能名称 |
# 渠道说明(channel):
渠道ID | 渠道描述 |
---|---|
0 | 接入方式为,扫码绑定的公众号接入智能对话的渠道 |
1 | 接入方式为,扫码绑定的小程序接入智能对话的渠道 |
2 | 接入方式为,通过开放接口方式接入智能对话的渠道 (暂未开放) |
3 | 接入方式为,企业微信群聊机器人(暂未开放) |
4 | 接入方式为,企业微信(面向企业内部的员工)客服机器人(暂未开放) |
5 | 接入方式为,公众号菜单H5机器人 |
6 | 接入方式为,小程序插件接入智能对话的渠道 |
7 | 接入方式为,网页H5机器人智能对话的渠道 |
9 | 接入方式为,绑定的微信客服接入智能对话的渠道 |
# 事件说明(event):
事件ID | 描述 |
---|---|
waiterQualityEvaluate | 客服请用户对其评价 |
waiterEnter | 客服接入 |
waiterSwitch | 客服转接 |
waiterQuit | 客服离开 |
# 消息加密:
比如参数为
<xml>
<appid><![CDATA[wxxxxx]]></appid>
<openid><![CDATA[xxxxx]]></openid>
<msg><![CDATA[您好,请问需要什么帮助]]></msg>
<channel>0</channel>
<kefuname><![CDATA[客服昵称]]></kefuname>
<kefuavatar><![CDATA[客服头像图片URL地址]]></kefuavatar>
<ans_node_name><![CDATA[分类或技能名称]]></ans_node_name>
</xml>
将加密后的数据,以字段 encrypt 放入body中
var cryptor = new WXBizMsgCrypt(TOKEN, EncodingAESKey, APPID);
var data = `<xml>
<appid><![CDATA[wxxxxx]]></appid>
<openid><![CDATA[xxxxx]]></openid>
<msg><![CDATA[您好,请问需要什么帮助]]></msg>
<channel>0</channel>
<kefuname><![CDATA[客服昵称:小红]]></kefuname>
<kefuavatar><![CDATA[客服头像图片地址:https://xxx]]></kefuavatar>
</xml>`
var encrypted = cryptor.encrypt(data);
curl -X post -d '{"encrypt": encrypted}' -H "content-type:application/json" "https://chatbot.weixin.qq.com/openapi/sendmsg/{TOKEN}"
注意: 在发送post的请求时,需要指定 content-type: "application/json"
加密算法示例
func Encrypt(appid, encodingKey string, plaintextMsg []byte) (string, error) {
// 算法:AES_Encrypt[random(16B) + msg_len(4B) + msg + $appid]
msg := bytes.Buffer{}
// 获取 16B 的随机字符串
randomString := getRandomBytes(16)
msg.Write(randomString)
// 获取 4B 的内容长度的网络字节序
length := len(plaintextMsg)
msgLength := make([]byte, 4)
msgLength[0] = byte(length >> 24)
msgLength[1] = byte(length >> 16)
msgLength[2] = byte(length >> 8)
msgLength[3] = byte(length)
msg.Write(msgLength)
// 写入消息内容
msg.Write(plaintextMsg)
msg.Write([]byte(appid))
// 加密
encryptedMsg, err := AesCBCEncrypt(encodingKey, msg.Bytes())
if err != nil {
return "", err
}
// base64 编码
base64Msg := make([]byte, base64.StdEncoding.EncodedLen(len(encryptedMsg)))
base64.StdEncoding.Encode(base64Msg, encryptedMsg)
return string(base64Msg), nil
}
func Decrypt(encodingAesKey, msg string) (string, error) {
// base64 解码
cipherText, err := base64.StdEncoding.DecodeString(msg)
if err != nil {
return "", err
}
// aes 解密
plainText, err := AesCBCDecrypt(encodingAesKey, []byte(cipherText))
if err != nil {
return "", err
}
// 去除随机数和
originalText := plainText[16:]
// 内容长度
length := int(originalText[0])<<24 + int(originalText[1])<<16 + int(originalText[2])<<8 + int(originalText[3])
// 去除 appid
originalText = originalText[4 : length+4]
return string(originalText), nil
}
AESCBC 部分的加解密算法细节可参考 第三方服务接口
# 返回值说明:
字段 | 类型 | 描述 |
---|---|---|
code | number | 错误码 |
msg | array | 接口调用信息 |
# 返回格式:
{
"errcode": 0,
"msg": "成功"
}
# 富文本支持
- 纯文本
const msg = "您好,请问需要什么帮助?"
- 文本类型(含推荐问法)
const msg = "暂未找到与之对应的答案,我们会尽快处理。 官方公众号:微信对话开放平台 <a href=\"weixin://bizmsgmenu?msgmenucontent=今天北京多少度&msgmenuid=今天北京多少度\">今天北京多少度</a>"
- 文本类型(含超级链接)
const msg = "请前往<a href=\"https://chatbot.weixin.qq.com/\">openai</a>进行配置"
- H5
const msg = {
"news": {
"articles": [{
"title": "实时更新:新型肺炎疫情最新动态",
"description": "腾讯新闻第一时间同步全国新型肺炎疫情动态,欢迎关注、转发",
"url": "https://news.qq.com/zt2020/page/feiyan.htm",
"picurl": "http://mmbiz.qpic.cn/mmbiz_jpg/W3gQtpV3j8D8kZRqfpTJlfVqubwgFQf47H0GWlGV6leaDF80ZpdtuFhQVsCsM3YKmwkujXzdjR2k6aWfA41ic7Q/0?wx_fmt=jpeg",
"type": "h5"
}]
}
}
news.articles数组中每一项的字段描述为:
字段 | 类型 | 描述 |
---|---|---|
title | string | 文章标题 |
description | string | 文章描述 |
url | string | 文章链接 |
picurl | string | 图片链接 |
type | string | 表示该文章的类型,有'h5'和'mp'两种,mp表示公众号文章,h5表示在对话开放平台配置的h5 |
- 公众号图片
const msg = {
"image": {
"media_id": "KegpipQG9t-klMo25My4e4BCZFcmKvgMcpMFAkC-VFE",
"url": "http://mmbiz.qpic.cn/mmbiz_jpg/W3gQtpV3j8Bax22dhRiccWAb2AtVjal28XmqnhDW22dMn3RA5EoGkpolMO3tD9kQC1Hf9AjEJI66K40xQsNtXgQ/0?wx_fmt=jpeg"
}
}
image中每个字段描述为:
字段 | 类型 | 描述 |
---|---|---|
media_id | string | 公众号图片素材ID |
url | string | 公众号图片url |
- 小程序
const msg ={
"miniprogrampage": {
"title": "openai对话插件",
"appid": "wx8c631f7e9f2465e1",
"pagepath": "pages/index/index",
"thumb_media_id": "KegpipQG9t-klMo25My4e8zpBjhjg3JMrMSpgjikB4U",
"thumb_url": "http://mmbiz.qpic.cn/mmbiz_png/W3gQtpV3j8BYhWgfHT5Hfg6auN94c2ec4BBhDOMtPQrx6vEMc1rR4iaDKxDLOfZ1jBUqIEEY4YpvEj6ktSyXT7g/0?wx_fmt=png"
}
}
miniprogrampage中每个字段描述为:
字段 | 类型 | 描述 |
---|---|---|
title | string | 小程序标题 |
appid | string | appid |
pagepath | string | 小程序跳转页面路径 |
thumb_media_id | string | 公众号图片素材ID |
thumb_url | string | 公众号图片url |
- 合并回答
{
"multimsg": ["您好", {
"image": {
"media_id": "KegpipQG9t-klMo25My4e4BCZFcmKvgMcpMFAkC-VFE",
"url": "http://mmbiz.qpic.cn/mmbiz_jpg/W3gQtpV3j8Bax22dhRiccWAb2AtVjal28XmqnhDW22dMn3RA5EoGkpolMO3tD9kQC1Hf9AjEJI66K40xQsNtXgQ/0?wx_fmt=jpeg"
}
}, {
"miniprogrampage": {
"title": "openai对话插件",
"appid": "wx8c631f7e9f2465e1",
"pagepath": "pages/index/index",
"thumb_media_id": "KegpipQG9t-klMo25My4e8zpBjhjg3JMrMSpgjikB4U",
"thumb_url": "http://mmbiz.qpic.cn/mmbiz_png/W3gQtpV3j8BYhWgfHT5Hfg6auN94c2ec4BBhDOMtPQrx6vEMc1rR4iaDKxDLOfZ1jBUqIEEY4YpvEj6ktSyXT7g/0?wx_fmt=png"
}]
}
字段 | 类型 | 描述 |
---|---|---|
multimsg | array | 合并回答列表,数组中的每一项为以上单一类型的集合,每一个类型参考其对应的数据结构 |
# 错误码说明:
{
errcode: 1001,
errmsg: "TOKEN is not valid"
}
错误码 | 描述 |
---|---|
1001 | token无效 |
1002 | 机器人审核没有通过 |
1003 | 签名缺少userid字段 |
1004 | 签名字段为空 |
1005 | 签名过期或无效 |
1006 | 签名校验失败,缺少userid字段 |
1007 | appid, category,label, desc 字段不能未空 |
1008 | appid, openid,msg, 字段不能未空 |