# 第三方客服系统接入- 发送客服消息

仅适用于对平台有过授权的下类账号类型 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": "成功"
}

# 富文本支持

  1. 纯文本
    const msg = "您好,请问需要什么帮助?"
  1. 文本类型(含推荐问法)
    const msg = "暂未找到与之对应的答案,我们会尽快处理。 官方公众号:微信对话开放平台 <a href=\"weixin://bizmsgmenu?msgmenucontent=今天北京多少度&msgmenuid=今天北京多少度\">今天北京多少度</a>"
  1. 文本类型(含超级链接)
    const msg = "请前往<a href=\"https://chatbot.weixin.qq.com/\">openai</a>进行配置"
  1. 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
  1. 公众号图片
  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
  1. 小程序
  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
  1. 合并回答
  {
    "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, 字段不能未空