Node微信公衆號開發 自定義回覆

一直想在日誌中多穿插些旅行日誌或者平常雜記,可是一來冬天實在沒有什麼可供娛樂的選項,二來今日隨着新型冠狀病毒的爆發,娛樂業基本上都進入停滯狀態,索性仍是老老實實跟家敲代碼吧……其實也是所謂的敲代碼,佩服本身的自律性,總不自覺地點開steam……html

這幾天一直在對本身的公衆號進行完善、優化,如今能夠繼續更新一篇關於自定義被動回覆的開發記錄。node

官方文檔截取

被動回覆用戶消息分爲如下6種回覆類型,咱們須要將所需的回覆類型按照官方API拼成固定字符串返回給服務器,如下是不一樣類型及其對應的字符串:npm

  • 回覆文本消息
<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[video]]></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 對象轉換器,官方使用方法以下:json

var parseString = require('xml2js').parseString;
var xml = "<root>Hello xml2js!</root>"
parseString(xml, function (err, result) {
    console.dir(result);
});複製代碼

更多的使用方法,還請另行移步只 www.npmjs.com/package/xml…api

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 等……app

這裏有一個坑要特別注意!!!注意!!注意!重要的事說三遍:這裏得到的 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 使用

這裏存在着另外一個坑,官方因爲某些緣由將圖文回覆作了限制:

  • 被動回覆只能回覆一條圖文消息
  • 對於開發的公衆號(不是操做公衆號後臺運維的),被動回覆沒法使用大圖方式,大小圖對好比圖

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 發佈!

相關文章
相關標籤/搜索