Node微信公众号开发 自定义回复

Node微信公众号开发 自定义回复一直想在日志中多穿插些旅行日志或者日常杂记,但是一来冬天实在没有什么可供娱乐的选项,二来今日随着新型冠状病毒的爆发,娱乐业基本上都进入停滞状态,索性还是老老实实跟家敲代码吧……其实也是所谓的敲代码,佩

一直想在日志中多穿插些旅行日志或者日常杂记,但是一来冬天实在没有什么可供娱乐的选项,二来今日随着新型冠状病毒的爆发,娱乐业基本上都进入停滞状态,索性还是老老实实跟家敲代码吧……其实也是所谓的敲代码,佩服自己的自律性,总不自觉地点开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 ,这里面包含了提交回复时所用到的 ToUserNameFromUserName 等……

这里有一个坑要特别注意!!!注意!!注意!重要的事说三遍:这里获得的 ToUserNameFromUserName 与提交模板中的 ToUserNameFromUserName 对应关系正好相反,也就是说 result.xml.ToUserName 对应的模板数据应该是 FromUserName ,而 result.xml.FromUserName 则对应模板数据 ToUserName

回复功能除了以上需要加入 app.js 中的部分,我另外做了两个脚本用于单独对回复功能进行处理,分别为 reply.jsreplyType.jsreplyType.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 使用

这里存在着另一个坑,官方由于某些原因将图文回复做了限制:

  • 被动回复只能回复一条图文消息
  • 对于开发的公众号(不是操作公众号后台运维的),被动回复无法使用大图方式,大小图对比如图

Node微信公众号开发 自定义回复

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 中不仅包含了 ToUserNameFromUserName 还包含了用户的操作信息:

  • 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微信公众号开发 自定义菜单

相关文章:

资料参考:

本文由博客一文多发平台 OpenWrite 发布!

今天的文章Node微信公众号开发 自定义回复分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/18403.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注