一直想在日志中多穿插些旅行日志或者日常杂记,但是一来冬天实在没有什么可供娱乐的选项,二来今日随着新型冠状病毒的爆发,娱乐业基本上都进入停滞状态,索性还是老老实实跟家敲代码吧……其实也是所谓的敲代码,佩服自己的自律性,总不自觉地点开steam……
这几天一直在对自己的公众号进行完善、优化,现在可以继续更新一篇关于自定义被动回复
的开发记录。
官方文档截取
被动回复用户消息分为以下6种回复类型,我们需要将所需的回复类型按照官方API拼成固定字符串返回给服务器,以下是不同类型及其对应的字符串:
- 回复文本消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>
参数 | 是否必须 | 描述 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | 消息类型,文本为text |
Content | 是 | 回复的消息内容(换行:在content中能够换行,微信客户端就支持换行显示) |
- 回复图片消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image>
<MediaId><![CDATA[media_id]]></MediaId>
</Image>
</xml>
参数 | 是否必须 | 说明 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | 消息类型,图片为image |
MediaId | 是 | 通过素材管理中的接口上传多媒体文件,得到的id |
- 回复语音消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[voice]]></MsgType>
<Voice>
<MediaId><![CDATA[media_id]]></MediaId>
</Voice>
</xml>
参数 | 是否必须 | 说明 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间戳 (整型) |
MsgType | 是 | 消息类型,语音为voice |
MediaId | 是 | 通过素材管理中的接口上传多媒体文件,得到的id |
- 回复视频消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA]></MsgType>
<Video>
<MediaId><![CDATA[media_id]]></MediaId>
<Title><![CDATA[title]]></Title>
<Description><![CDATA[description]]></Description>
</Video>
</xml>
参数 | 是否必须 | 说明 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | 消息类型,视频为video |
MediaId | 是 | 通过素材管理中的接口上传多媒体文件,得到的id |
Title | 否 | 视频消息的标题 |
Description | 否 | 视频消息的描述 |
- 回复音乐消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[music]]></MsgType>
<Music>
<Title><![CDATA[TITLE]]></Title>
<Description><![CDATA[DESCRIPTION]]></Description>
<MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl>
<HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl>
<ThumbMediaId><![CDATA[media_id]]></ThumbMediaId>
</Music>
</xml>
参数 | 是否必须 | 说明 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | 消息类型,音乐为music |
Title | 否 | 音乐标题 |
Description | 否 | 音乐描述 |
MusicURL | 否 | 音乐链接 |
HQMusicUrl | 否 | 高质量音乐链接,WIFI环境优先使用该链接播放音乐 |
ThumbMediaId | 是 | 缩略图的媒体id,通过素材管理中的接口上传多媒体文件,得到的id |
- 回复图文消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>1</ArticleCount>
<Articles>
<item>
<Title><![CDATA[title1]]></Title>
<Description><![CDATA[description1]]></Description>
<PicUrl><![CDATA[picurl]]></PicUrl>
<Url><![CDATA[url]]></Url>
</item>
</Articles>
</xml>
参数 | 是否必须 | 说明 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | 消息类型,图文为news |
ArticleCount | 是 | 图文消息个数;当用户发送文本、图片、视频、图文、地理位置这五种消息时,开发者只能回复1条图文消息;其余场景最多可回复8条图文消息 |
Articles | 是 | 图文消息信息,注意,如果图文数超过限制,则将只发限制内的条数 |
Title | 是 | 图文消息标题 |
Description | 是 | 图文消息描述 |
PicUrl | 是 | 图片链接,支持JPG、PNG格式,较好的效果为大图360200,小图200200 |
Url | 是 | 点击图文消息跳转链接 |
xml2js
xml2js 是一个简单的 XML 到 JavaScript 对象转换器,官方使用方法如下:
var parseString = require('xml2js').parseString;
var xml = "<root>Hello xml2js!</root>"
parseString(xml, function (err, result) {
console.dir(result);
});
更多的使用方法,还请另行移步只 www.npmjs.com/package/xml…
Node.js 自定义被动回复开发
首先,我们需要先配置路由,来接收服务器发送的请求和数据;
app.post("/", (req,res, next)=> { …… })
需要使用事件监听来监视和控制数据;
app.post("/", (req,res, next)=> {
// 获取到微信返回的二进制数据
var buffer = []
req.on('data', (data) => {
buffer.push(data)
})
req.on('end', () => {
// 将数据转化成 utf-8 格式
var msgXml = Buffer.concat(buffer).toString('utf-8')
})
})
我们先将微信返回来的二进制数据存放到 buffer
数组中,并在请求结束时将其处理为普通 XML
数据;
这里存在一个问题,Node.js 无法直接将 XML 转换成 Javascript 对象
,这时就需要前文介绍的组件 xml2js
来帮我们实现这一步操作;
var reply = require('./reply') // 自定义回复
……
req.on('end', () => {
// 将数据转化成 utf-8 格式
var msgXml = Buffer.concat(buffer).toString('utf-8')
// 调用 xml2js 模块的 parseString 方法
parseString(msgXml, { explicitArray: false }, (error, result) => {
// 如果有错误直接抛出
if (error) {
console.log(error)
return
}
result = result.xml // result.xml 是获取到的真实数据
reply(result, (resultXml) => { // 调用回复功能
res.send(resultXml)
})
})
})
……
通过组件我们就可以获得到真正能够使用的数据了 result.xml
,这里面包含了提交回复时所用到的 ToUserName
、FromUserName
等……
这里有一个坑要特别注意!!!注意!!注意!重要的事说三遍:这里获得的 ToUserName
、FromUserName
与提交模板中的 ToUserName
、FromUserName
对应关系正好相反,也就是说 result.xml.ToUserName
对应的模板数据应该是 FromUserName
,而 result.xml.FromUserName
则对应模板数据 ToUserName
;
回复功能除了以上需要加入 app.js 中的部分,我另外做了两个脚本用于单独对回复功能进行处理,分别为 reply.js
和 replyType.js
, replyType.js
存放了所有涉及的模板,辅助 reply.js
完成自定义回复的工作;
以下是 replyType.js
中的代码:
// 核心模块
var fs = require('fs')
// 引入开发模块
var request = require('./request')
// 回复文本消息
exports.textMsg = (result, content) => {
console.log('reply type text !')
var xmlContent = '<xml><ToUserName><![CDATA[' + result.FromUserName + ']]></ToUserName>'
xmlContent += '<FromUserName><![CDATA[' + result.ToUserName + ']]></FromUserName>'
xmlContent += '<CreateTime>' + new Date().getTime() + '</CreateTime>'
xmlContent += '<MsgType><![CDATA[text]]></MsgType>'
xmlContent += '<Content><![CDATA[' + content + ']]></Content></xml>'
return xmlContent
}
// 回复图片
exports.imgMsg = function (result, urlPath, callback) {
uploadFile(urlPath, 'image').then((media_id) => {
console.log('reply type image !', media_id)
var xmlContent = '<xml><ToUserName><![CDATA[' + result.FromUserName + ']]></ToUserName>'
xmlContent += '<FromUserName><![CDATA[' + result.ToUserName + ']]></FromUserName>'
xmlContent += '<CreateTime>' + new Date().getTime() + '</CreateTime>'
xmlContent += '<MsgType><![CDATA[image]]></MsgType>'
xmlContent += '<Image><MediaId><![CDATA[' + media_id + ']]></MediaId></Image></xml>'
callback(xmlContent)
})
}
// 回复图文消息
exports.graphicMsg = (result, contentArr) => {
console.log('reply type image and text !')
var xmlContent = '<xml><ToUserName><![CDATA[' + result.FromUserName + ']]></ToUserName>'
xmlContent += '<FromUserName><![CDATA[' + result.ToUserName + ']]></FromUserName>'
xmlContent += '<CreateTime>' + new Date().getTime() + '</CreateTime>'
xmlContent += '<MsgType><![CDATA[news]]></MsgType>'
xmlContent += '<ArticleCount>' + contentArr.length + '</ArticleCount>'
xmlContent += '<Articles>'
contentArr.map((item, index) => {
xmlContent += '<item>'
xmlContent += '<Title><![CDATA[' + item.Title + ']]></Title>'
xmlContent += '<Description><![CDATA[' + item.Description + ']]></Description>'
xmlContent += '<PicUrl><![CDATA[' + item.PicUrl + ']]></PicUrl>'
xmlContent += '<Url><![CDATA[' + item.Url + ']]></Url>'
xmlContent += '</item>'
})
xmlContent += '</Articles></xml>'
return xmlContent
}
上述代码中图片回复模板由于需要获得 media_id
,所以在开发过程中无法单独使用,需要取得公众号素材使用的权限,也就是上述代码中的 uploadFile
方法,稍后我将进行说明
上述代码中,除了单纯的文本回复,其他都用到了异步函数,所以此处使用回调函数的方法将最终的模板抛出给 reply.js
使用
这里存在着另一个坑,官方由于某些原因将图文回复做了限制:
- 被动回复只能回复一条图文消息
- 对于开发的公众号(不是操作公众号后台运维的),被动回复无法使用大图方式,大小图对比如图
replyType.js
引入 reply.js
页面,使用方法如下:
- 图片回复
var urlPath = path.join(__dirname, '自定义图片.jpg')
resultXml = replyType.imgMsg(result, urlPath, (resultXml) => {
callback(resultXml)
})
- 文本回复
resultXml = replyType.textMsg(result, '回复内容')
callback(resultXml)
- 图文回复
resultXml = replyType.graphicMsg(result, contentArr) // contentArr 为图文数组对象
使用自定义回复
前文提到的 result.xml
中不仅包含了 ToUserName
、FromUserName
还包含了用户的操作信息:
result.xml.MsgType === 'event'
说明用户事件触发的,比如result.xml.Event === 'subscribe'
代表用户关注了result.xml.Event === 'CLICK'
代表用户是通过自定义菜单项操作result.xml.EventKey
可以获取到自定义菜单的key
属性
result.xml.MsgType === 'text'
说明用户是通过输入关键词触发的result.xml.Content
获得用户所输入的具体内容
—
上传素材,获取 media_id
这里的素材可以是图片、语音等等,官方说明如下:
- 请求方式:POST
- 请求地址:https://api.weixin.qq.com/cgi-bin/media/upload?accesstoken=ACCESSTOKEN&type=TYPE
// 素材上传获取 media_id
function uploadFile (urlPath, type) {
return new Promise((resolve, reject) => {
fs.readFile('./config.json', 'utf-8', (error, data) => {
if (error) {
console.log('uploadFile read accessToken fail', error)
return
}
var form = { //构造表单
media: fs.createReadStream(urlPath)
}
var url = 'https://api.weixin.qq.com/cgi-bin/media/upload?access_token=' + JSON.parse(data).setAccessToken.accessToken + '&type=' + type
request.post(url, form).then((result) => {
resolve(JSON.parse(result).media_id)
})
})
})
}
我的 access_token
保存在了 config.json
中,所以先通过 fs.readFile
获取到,之后拼接处接口,接口中的 type
为需要创建的临时媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb,主要用于视频与音乐格式的缩略图)
fs.createReadStream
用于获得这个媒体文件的文件流,将这个文件流已请求参数形式发送个公众号,此时服务器将会返回一串字符串,该字符串就是媒体文件的对应 media_id
关于自定义回复的笔记就到这里。
我的数据源于我的博客站点,起初使用的是 WordPress 提供的 wp-json 接口文件,但后来由于安全性的考虑对接口获取方式进行了修改,目前我的公众号数据源于爬取站点获得,之后我将会分享关于 Node 爬取站点的学习记录。
还望各位客官能够喜欢!
文章已同步我的个人博客:《Node微信公众号开发 自定义菜单》
相关文章:
—
资料参考:
- [微信公众号官方文档]
- [微信公众平台接口调试工具]
- [微信公众平台测试账号]
- [Node开发微信公众号(1)——微信公众号接入]
- [Node开发微信公众号(2)——微信回复]
- [Node开发微信公众号(3)——微信菜单]
- [Node开发微信公众号(4)——素材上传]
本文由博客一文多发平台 OpenWrite 发布!
今天的文章Node微信公众号开发 自定义回复分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/18403.html