In the last article: Development of Noejs Wechat Public Number (1) Access to Wechat Public Number On this basis, this article will implement a simple reply function.
1. Optimizing Access Code
Before, we simply and roughly realized the access of Wechat Public Number. The access code was written directly in app.js file. From the point of view of project development, it is not easy to maintain the code in the future. So we separated this part of code and wrote it as a middleware according to koa's style.
Create a new wechat folder and a new generator.js file under the root directory.
var sha1 = require('sha1'); module.exports = function(opts){ return function *(next){ var token = opts.token; var signature = this.query.signature; var nonce = this.query.nonce; var timestamp = this.query.timestamp; var echostr = this.query.echostr; var str = [token,timestamp,nonce].sort().join(''); var sha = sha1(str); this.body = (sha === signature) ? echostr + '' : 'failed'; }; }
At this point, the content of app.js becomes:
'use strict' var Koa = require('koa'); var wechat = require('./wechat/generator'); var config = { wechat:{ appID:'...', appSecret:'...', token:'...' } }; var app = new Koa(); app.use(wechat(config.wechat)); app.listen(8080);
console.log('Listening 8080...')
2. Get access_token
access_token is a key for developing programs to interact with the wexin public platform. access_token is used to call most interfaces.
The features of access_token are:
The validity period is 2 hours (7200s). The expiration time is automatically invalid and needs to be retrieved.
As long as access_token is updated, the previous access_token will automatically fail.
Solution:
The system automatically fetches the value of access_token every two hours to ensure that access_token is always valid.
To facilitate frequent calls, access_token is stored in a single place (database, file, etc.) where all subsystems can access it.
In the process of creating an instance and completing the initialization work, the program uses the constructor to read the bill stored in the config/wechat.txt file, determine whether it is empty and expired, selectively retrieve the number and save it in the original file. The official document about access_token is visible. : Get access_token.
FunctionWechat (opts) {// constructor to generate instances, complete initialization, read and write bills var that = this; this.appID = opts.appID; this.appSecret = opts.appSecret; this.getAccessToken = opts.getAccessToken; this.saveAccessToken = opts.saveAccessToken; this.getAccessToken().then(function(data){ try{ data = JSON.parse(data); }catch(e){ return that.updateAccessToken(); } if(that.isvalidAccessToken(data)){ Promise.resolve(data); }else{ return that.updateAccessToken(); } }).then(function(data){ that.access_token = data.access_token; that.expires_in = data.expires_in; that.saveAccessToken(JSON.stringify(data)); }); }
We instantiate a Wechat in moudle.exports:
var wechat = new Wechat(opts);
This ensures that the validity of access_token is checked every time the program starts, and a new access_token is automatically acquired every time.
3. Processing steps of Wechat messages
Whether event push or message push, Wechat servers send requests by post. The data type of push is not json but xml. There are five steps to process push messages:
Processing POST type control logic, receiving xml data packets;
Parse the data package and get the message type or data type of the data package.
Assemble custom messages;
Packaged in xml format;
Return the message in 5 seconds.
3.1 Receiving xml data
request object in http module can be obtained by raw-body module, and data can be assembled to get an xml object of buffer.
var data = yield rawBody(this.req,{ length:this.length, limit:'1mb', encoding:this.charset }); console.log('data:'+data);
3.2 parsing xml data
Using xml2js module to parse XML data into object format
var content = yield util.parseXMLAsync(data);
The parseXMLAsync method in util:
exports.parseXMLAsync = function(xml){ return new Promise(function(resolve,reject){ xml2js.parseString(xml,{trim:true},function(err,content){ err ? reject(err) : resolve(content); }) }); }
3.3 Format xml data
From the parsed xml data, although the data has presented the form of key-value pairs, its values are in the form of arrays and need to be flattened.
var message = util.formatMessage(content.xml);
Its essence is to traverse the values in the array, because there is nesting in multi-text messages:
function formatMessage(result){ var message = {}; if(typeof result === 'object'){ var keys = Object.keys(result); for(var i=0;i<keys.length;i++){ var key = keys[i]; var item = result[key]; if(!(item instanceof Array) || item.length === 0) continue; if (item.length === 1){ var val = item[0]; if (typeof val === 'object') message[key] = formatMessage(val); else message[key] = (val || '').trim(); }else{ message[key] = []; for(var j=0,k=item.length;j<k;j++) message[key].push(formatMessage(item[j])); } } } return message; }
3.4 Judge message type and reply
Here for subscribe event, after new attention, automatic reply text message finally waits for you, but fortunately I didn't give up.
if(message.MsgType === 'event'){ if(message.Event === 'subscribe'){ var createTime = new Date().getTime(); that.status = 200; that.type = 'application/xml'; that.body = '<xml>'+ '<ToUserName><![CDATA['+ message.FromUserName +']]></ToUserName>'+ '<FromUserName><![CDATA['+ message.ToUserName +']]></FromUserName>'+ '<CreateTime>'+createTime+'</CreateTime>'+ '<MsgType><![CDATA[text]]></MsgType>'+ '<Content><![CDATA[At last, I waited for you. Fortunately, I didn't give up.]]></Content>'+ '</xml>' return; } }
Note: Here is just a simple implementation of the automatic response function. This way of splicing strings is still very inconvenient. It will be encapsulated as an interface later.
Scanning the two-dimensional code of the test account with the mobile phone Wechat, you can pay attention to it, and receive the message of the test public number push at the same time! _________
Come on, a simple attention response is done.