一直想在日誌中多穿插些旅行日誌或者平常雜記,可是一來冬天實在沒有什麼可供娛樂的選項,二來今日隨着新型冠狀病毒的爆發,娛樂業基本上都進入停滯狀態,索性仍是老老實實跟家敲代碼吧……其實也是所謂的敲代碼,佩服本身的自律性,總不自覺地點開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 是一個簡單的 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
首先,咱們須要先配置路由,來接收服務器發送的請求和數據;數組
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
等……app
這裏有一個坑要特別注意!!!注意!!注意!重要的事說三遍:這裏得到的 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
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 發佈!