首页 > 编程 > JavaScript > 正文

NodeJS实现微信公众号关注后自动回复功能

2019-11-19 16:27:33
字体:
来源:转载
供稿:网友

一 实先自动回复功能的逻辑步骤

  1 处理POST类型的控制逻辑,接收XML的数据包;

  2 解析XML数据包(获得数据包的消息类型或者是事件类型);

  3 拼装我们定义好的消息;

  4 包装成XML格式;

  5 在5秒内返回回去

二 代码实操

  本节代码参照上节课继续修改和完善,目录结构跟之前相同,新引入的模块raw-body使用npm install安装一下即可,app.js启动文件和util.js不做变动,主要修改一下generator.js文件,以及在generator.js同级目录下新建wechat.js文件和tools.js文件。

  wechat.js文件是将前一节generator.js文件中票据部分的代码抽离出来单独放在一个文件中,具体代码如下:

'use strict';// 引入模块var Promise = require('bluebird');var request = Promise.promisify(require('request'));//增加url配置项var prefix = 'https://api.weixin.qq.com/cgi-bin/';var api = {  accessToken: prefix + 'token?grant_type=client_credential'};//利用构造函数生成实例 完成票据存储逻辑function weChat(opts) {  var that = this;  this.appID = opts.appID;  this.appSecret = opts.appSecret;  this.getAccessToken = opts.getAccessToken;  this.saveAccessToken = opts.saveAccessToken;  //获取票据的方法  this.getAccessToken()    .then(function(data) {      //从静态文件获取票据,JSON化数据,如果有异常,则尝试更新票据      try {        data = JSON.parse(data);      } catch (e) {        return that.updateAccessToken();      }      //判断票据是否在有效期内,如果合法,向下传递票据,如果不合法,更新票据      if (that.isValidAccessToken(data)) {        Promise.resolve(data);      } else {        return that.updateAccessToken();      }    })    //将拿到的票据信息和有效期信息存储起来    .then(function(data) {      //console.log(data);      that.access_token = data.access_token;      that.expires_in = data.expires_in;      that.saveAccessToken(data);    })};//在weChat的原型链上增加验证有效期的方法weChat.prototype.isValidAccessToken = function(data) {  //进行判断,如果票据不合法,返回false  if (!data || !data.access_token || !data.expires_in) {    return false;  }  //拿到票据和过期时间的数据  var access_token = data.access_token;  var expires_in = data.expires_in;  //获取当前时间  var now = (new Date().getTime());  //如果当前时间小于票据过期时间,返回true,否则返回false  if (now < expires_in) {    return true;  } else {    return false;  };};//在weChat的原型链上增加更新票据的方法weChat.prototype.updateAccessToken = function() {  var appID = this.appID;  var appSecret = this.appSecret;  var url = api.accessToken + '&appid=' + appID + '&secret=' + appSecret;  return new Promise(function(resolve, reject) {    //使用request发起请求    request({      url: url,      json: true    }).then(function(response) {      var data = response.body;      var now = (new Date().getTime());      var expires_in = now + (data.expires_in - 20) * 1000;      //把新票据的有效时间赋值给data      data.expires_in = expires_in;      resolve(data);    })  })};//向外暴露weChatmodule.exports = weChat;

  generator.js文件进行精简后,添加判断对xml数据的格式化方法以及判断事件,添加关注事件测试信息,具体代码如下:

'use strict';// 引入模块var sha1 = require('sha1');var getRawBody = require('raw-body');var weChat = require('./wechat');var tools = require('./tools');// 建立中间件函数并暴露出去module.exports = function(opts) {  //实例化weChat()函数  //var wechat = new weChat(opts);  return function*(next) {    //console.log(this.query);    var that = this;    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.method对请求方法进行判断    if (this.method === 'GET') {      // 如果是get请求 判断加密后的值是否等于签名值      if (sha === signature) {        this.body = echostr + '';      } else {        this.body = 'wrong';      };    } else if (this.method === 'POST') {      //如果是post请求 也是先判断签名是否合法 如果不合法 直接返回wrong      if (sha !== signature) {        this.body = 'wrong';        return false;      };      //通过raw-body模块 可以把把this上的request对象 也就是http模块中的request对象 去拼装它的数据 最终拿到一个buffer的xml数据      //通过yield关键字 获取到post过来的原始的XML数据      var data = yield getRawBody(this.req, {        length: this.length,        limit: '1mb',        encoding: this.charset      });      //console.log(data.toString());打印XML数据(当微信公众号有操作的时候 终端可以看到返回的XML数据)      //tools为处理XML数据的工具包 使用tools工具包的parseXMLAsync方法 把XML数据转化成数组对象      var content = yield tools.parseXMLAsync(data);      //console.log(content);打印转化后的数组对象      //格式化content数据为json对象      var message = tools.formatMessage(content.xml);      console.log(message);      //打印message      //判断message的MsgType 如果是event 则是一个事件      if (message.MsgType === 'event') {        //如果是订阅事件        if (message.Event === 'subscribe') {          //获取当前时间戳          var now = new Date().getTime();          //设置回复状态是200          that.status = 200;          //设置回复的类型是xml格式          that.type = 'application/xml';          //设置回复的主体          that.body = '<xml>' +            '<ToUserName><![CDATA[' + message.FromUserName + ']]></ToUserName>' +            '<FromUserName><![CDATA[' + message.ToUserName + ']]></FromUserName>' +            '<CreateTime>' + now + '</CreateTime>' +            '<MsgType><![CDATA[text]]></MsgType>' +            '<Content><![CDATA[你好,同学!]]></Content>' +            '</xml>';          return;        }      }    }  }};

  tools.js是处理XML数据的工具文件:

'use strict';//引入模块var xml2js = require('xml2js');var Promise = require('bluebird');//导出解析XML的方法exports.parseXMLAsync = function(xml) {  return new Promise(function(resolve, reject) {    xml2js.parseString(xml, { trim: true }, function(err, content) {      if (err) {        reject(err);      } else {        resolve(content);      };    });  });};//因为value值可能是嵌套多层的 所以先对value值进行遍历function formatMessage(result) {  //声明空对象message  var message = {};  //对result类型进行判断  if (typeof result === 'object') {    //如果是object类型 通过Object.keys()方法拿到result所有的key 并存入keys变量中    var keys = Object.keys(result);    //对keys进行循环遍历    for (var i = 0; i < keys.length; i++) {      //拿到每个key对应的value值      var item = result[keys[i]];      //拿到key      var key = keys[i];      //判断item是否为数组或者长度是否为0      if (!(item instanceof Array) || item.length === 0) {        //如果item不是数组或者长度为0 则跳过继续向下解析        continue;      }      //如果长度为1      if (item.length === 1) {        //拿到value值存入val变量        var val = item[0];        //判断val是否为对象        if (typeof val === 'object') {          //如果val为对象 则进一步进行遍历          message[key] = formatMessage(val);        } else {          //如果不是对象 就把值赋给当前的key放入message里 并去除收尾空格          message[key] = (val || '').trim();        }      }      //如果item的长度既不是0也不是1 则说明它是一个数组      else {        //把message的key设置为空数组        message[key] = [];        //对数组进行遍历        for (var j = 0, k = item.length; j < k; j++) {          message[key].push(formatMessage(item[j]));        }      }    }  }  return message;}exports.formatMessage = function(xml) {  return new Promise(function(resolve, reject) {    xml2js.parseString(xml, { trim: true }, function(err, content) {      if (err) {        reject(err);      } else {        resolve(content);      };    });  });};exports.formatMessage = formatMessage;

  完成这节的代码后,当关注微信测试公众号的时候,会自动回复『你好,同学!』的提示信息。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表