最近跟着大帅搞得小程序开通流量主之后,想着给公众号引流一下,就要从小程序能够去关注公众号咯。但是小程序的机制大家懂的,不能直接跳转到公众号哦,那就只能找客服回复一个二维码图片咯!!!
配置一下
unicloud配置
- 先在项目uniCloud下面的云函数文件夹中右键添加云函数
- 在弹出层中新建一个名称为 contact 的云函数(名称随便取都行)
- 创建好之后,在contact云函数文件夹上右键上传云函数
- 云函数代码如下:
- 暂时不写云函数代码,先去unicloud后台配置小程序需要的URL数据
- 在右侧目录找到云函数 - 函数列表之后,找到
contact
云函数,点击详情按钮
- 找到
云函数URL化模块
,点击编辑
- 在输入框中输入
/contact
注意:此处必须用/开头,名称可以随便取
- 点击确定之后,在点击PATH后面的复制路径按钮
此时,uniCloud配置完成,下面开始配置小程序
小程序配置
- 微信公众平台登录自己的小程序账号
- 在左侧菜单栏找到开发 - 开发管理
- 进入开发管理之后切换tab到开发设置
- 找到消息推送,点击启用
- 跳转到消息推送配置页面之后,按照图片方式进行配置
此时点击提交,会提示你Token校验失败,请检查确认
,不要慌,只是云函数里面没有写而已。这个时候就可以去写云函数的代码了~~
开始写云函数
如果对消息来源要求不高,或者不考虑安全性,可以直接在云函数中返回 event.queryStringParameters.echostr
云函数代码如下:
'use strict'; exports.main = async (event, context) => { //event为客户端上传的参数 console.log('event : ', event) //返回数据给客户端 return event.queryStringParameters.echostr; }; 复制代码
校验安全性也很简单的
1. 将token、timestamp、nonce三个参数进行字典序排序
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
云函数代码如下:
'use strict'; //npm install sha1 const sha1 = require("sha1"); const token = "123qweASD"; function verifyMSGSender(params){ const mysignature = sha1([token,params.timestamp,params.nonce].sort().join("")); console.log(mysignature===params.signature?"校验通过":"校验失败"); if(mysignature===params.signature)return true; else return false; } exports.main = async (event, context) => { //event为客户端上传的参数 if(!verifyMSGSender(event.queryStringParameters))return "来源校验失败"; //校验通过后,下面这行返回echostr的代码注释掉 else return event.queryStringParameters.echostr; //返回数据给客户端 return; }; 复制代码
注意:token需要和消息配置页面的token保持一致哦~ 此时就可以去消息配置里面点击保存按钮啦!!!
配置完成后是这样的
校验通过之后,代码里面的 echostr
代码就可以注释掉了
//校验通过后,下面这行返回echostr的代码注释掉 else return event.queryStringParameters.echostr; 复制代码
此时给客服发消息之后,在云函数日志里面可以看到返回结果了
但是这个结果是一个字符串的,我们需要把字符串转为json格式
// 解析json格式字符串 const receiveMsg = JSON.parse(event.body); 复制代码
数据包返回格式
根据用户发送的消息,会有以下格式
{// 文本消息返回数据包 "ToUserName": "toUser", "FromUserName": "fromUser",//客服消息发起者的openid "CreateTime": 1482048670, "MsgType": "text",//该条消息类型 "Content": "this is a test", "MsgId": 1234567890123456 } 复制代码
{// 图片消息返回数据包 "ToUserName": "toUser", "FromUserName": "fromUser",//客服消息发起者的openid "CreateTime": 1482048670, "MsgType": "image",//该条消息类型 "PicUrl": "this is a url", "MediaId": "media_id",//微信媒体资源id "MsgId": 1234567890123456 } 复制代码
// 小程序卡片消息button按钮配置 <button size="mini" type="primary" :plain="true" open-type="contact" :session-from="" :show-message-card="true" :send-message-title="title"> 查看 </button> {// 小程序卡片消息返回数据包 "ToUserName": "toUser", "FromUserName": "fromUser",//客服消息发起者的openid "CreateTime": 1482048670, "MsgType": "miniprogrampage",//该条消息类型 "MsgId": 1234567890123456, "Title":"title",//小程序卡片标题 "AppId":"appid",//小程序appid "PagePath":"path",//小程序卡片跳转页面 "ThumbUrl":"",//小程序卡片缩略图 "ThumbMediaId":"" } 复制代码
{// 客服消息进入会话事件 "ToUserName": "toUser", "FromUserName": "fromUser",//客服消息发起者的openid "CreateTime": 1482048670, "MsgType": "event",//该条消息类型 "Event": "user_enter_tempsession", "SessionFrom": "sessionFrom"//开发者在客服会话按钮设置的 session-from 属性 } 复制代码
回复客服消息
先获取access_token
注意:APPID和APPSECRET配置在微信公众平台开发管理里面获取
const tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + APPID + '&secret=' + APPSECRET; // uniCloud.httpclient 发起请求 const res1 = await uniCloud.httpclient.request(tokenUrl, { method: 'GET', dataType:"json" }); //返回数据给客户端 const access_token = res1.data.access_token; 复制代码
开始回复消息
const access_token = res1.data.access_token const res2 = await uniCloud.httpclient.request("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token="+access_token,{ method:"POST", headers:{ "Content-Type":"application/json" }, dataType:"json", data:{ touser:touser,//接收此消息用户的openid msgtype:"text",//此消息的类型 text:{ content:"回复的文本内容", } } }); 复制代码
回复消息的种类有很多,text文本消息,img图片消息,link链接消息,miniprogrampage小程序卡片消息。下面只说回复图片消息(这个在大部分教程里面都没写过,其他的可以自行在掘金上搜索)
在做图片消息自动回复之前,根据微信文档描述,需要现将图片上传到临时文件服务器,而且图片保存时间有效期只有三天
上传图片信息
在请求参数中可以看到,我们需要传一个media
的参数,而且是FormData
类型的,但是我们不会在小程序上添加一个input框来用作上传图片,所以需要借助nodeJS的form-data模块
注意:在微信小程序中,不能直接在代码中写 new FormData() ,需要自行安装模块
- 首先将需要的图片上传到unicloud云存储中,获取到对应的URL
const img_url = 'https://云存储路径.jpg'
- 将图片链接转为Buffer
const url = await uniCloud.httpclient.request(img_url) let buff = new Buffer(url.data); 复制代码
- 将buff传入formData
// 此处的FormData需要安装到当前云函数文件夹中 // npm install form-data let form = new FormData() // 将 media 参数、buff信息、formdata中需要包含的filename、图片信息打包 form.append('media', buff, { filename: `${Date.now()}.jpg`, contentType: 'image/jpeg' }) 复制代码
- 请求
上传客服临时文件
接口,将formdata信息上传,获取到media_id
// 请求微信服务器API,将formdata信息上传,获取到media_id const imgRes = await uniCloud.httpclient.request("https://api.weixin.qq.com/cgi-bin/media/upload?access_token=" + access_token + '&type=image', { method: "POST", headers: form.getHeaders(), dataType: 'json', content: form.getBuffer() }); return imgRes.data.media_id 复制代码
- 判断用户发送的消息,然后回复图片
注意:我这里判断的是用户发送 2 之后,回复消息
// 判断用户发送的消息内容 if(receiveMsg.Content === '2'){ // 调用获取media_id的方法 // 因为方法是一个promise,所以调用的时候需要加上 await 前缀 let media_id = await uploadTempImg(url, access_token) if(media_id){ // 发送消息 const res2 = await uniCloud.httpclient.request("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + access_token, { method: "POST", headers: { "Content-Type": "application/json" }, dataType: "json", data: { touser: receiveMsg.FromUserName, //接收此消息用户的openid msgtype: "image", //此消息的类型 image: { media_id: media_id } } }); } } 复制代码
自动回复的小机器人就出现了
完整代码
'use strict'; // 需要先执行初始化 npm init -y 生成packages.json文件后 // 再执行安装 npm install sha1 // 安装在当前目录哦。。不是全局安装的 const sha1 = require("sha1"); const FormData = require("form-data"); const token = "123qweASD"; const db = uniCloud.database(); function verifyMSGSender(params) { const mysignature = sha1([token, params.timestamp, params.nonce].sort().join("")); console.log(mysignature === params.signature ? "校验通过" : "校验失败"); if (mysignature === params.signature) return true; else return false; } async function uploadTempImg(img_url, access_token){ // 现将图片链接转为buffer const url = await uniCloud.httpclient.request(img_url) let buff = new Buffer(url.data); // 此处的FormData需要安装到当前云函数文件夹中 // npm install form-data let form = new FormData() // 将 media 参数、buff信息、formdata中需要包含的filename、图片信息打包 form.append('media', buff, { filename: `${Date.now()}.jpg`, contentType: 'image/jpeg' }) // 请求微信服务器API,将formdata信息上传,获取到media_id const imgRes = await uniCloud.httpclient.request( "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=" + access_token + '&type=image', { method: "POST", headers: form.getHeaders(), dataType: 'json', content: form.getBuffer() }); return imgRes.data.media_id } exports.main = async (event, context) => { // 获取config中配置的appid和appSecret let appId = await db.collection('wx_config').where({ 'key': 'wxId' }).get() let appSecret = await db.collection('wx_config').where({ 'key': 'wxSecret' }).get() const APPID = appId.data[0].val const APPSECRET = appSecret.data[0].val const tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + APPID + '&secret=' + APPSECRET; // uniCloud.httpclient 发起请求 const res1 = await uniCloud.httpclient.request(tokenUrl, { method: 'GET', dataType:"json" }); //返回数据给客户端 const access_token = res1.data.access_token; //event为客户端上传的参数 if (!verifyMSGSender(event.queryStringParameters)) return "来源校验失败"; //校验通过后,下面这行返回echostr的代码注释掉 // else return event.queryStringParameters.echostr; // 解析json格式字符串 const receiveMsg = JSON.parse(event.body); const url = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-a8a16cef-f2b9-4644-b216-3b95bcb12602/eed2e97a-be28-4e6d-beb3-ca338d195858.jpg' // 判断用户发送的消息内容 if(receiveMsg.Content === '2'){ // 调用获取media_id的方法 // 因为方法是一个promise,所以调用的时候需要加上 await 前缀 let media_id = await uploadTempImg(url, access_token) if(media_id){ // 发送消息 const res2 = await uniCloud.httpclient.request( "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + access_token, { method: "POST", headers: { "Content-Type": "application/json" }, dataType: "json", data: { touser: receiveMsg.FromUserName, //接收此消息用户的openid msgtype: "image", //此消息的类型 image: { media_id: media_id } } }); } } //返回数据给客户端 return; };