node(egg)完成微信公众号自动回复功能

2020-11-03  本文已影响0人  w晚风

项目背景:

我这里是在使用微信公众号获取用户openid的时候希望不经过网页授权方式获取。所以就想到通过事件消息推送的方式来进行,也就是说用户关注,取消关注,发送消息,返回回来的数据都会携带openid,这是我想要的。这边使用的是node来作为服务端

1.首先需要在微信公众平台 - 基础设置 - 设置服务器地址

image.png image.png

2.配置服务器地址时可能出现“参数错误,。。。。”的情况,可能是因为域名被拦截,不能使用。

那么首先就需要在后台实现这么个接口来开启这个url接口。这个地方填写服务器地址接口域名,就是用来跟公众号交互的。

首先实现一个get请求通过这个配置鉴权

async index() {
        const { ctx, app } = this;
        let { signature, echostr, timestamp, nonce } = ctx.query;
        
        console.log(ctx.query)
        
        const token = 'zwp123'; // 这个地方就是公众平台上所谓的token 必须一样
        
        // 将token、timestamp、nonce三个参数进行字典序排序
        const arr = [token, timestamp, nonce];
        arr.sort();
        
        // 将三个参数字符串拼接成一个字符串进行sha1加密
        const str = arr[0]+ arr[1] + arr[2];
        var sha1 = require('sha1');
        console.log(sha1(str))
        
        // 开发者获得加密后的字符串可与signature对比
        if(sha1(str) === signature){
            ctx.body = echostr;
        }else{
            ctx.body = false;
        }
    }

配置好服务器地址等信息,提交成功,点击启用完成服务器配置

3. 回复消息,分为关注后自动回复以及粉丝向公众号发消息时的回复等等情况

如何捕获用户。实际上都是微信服务器post请求配置好的服务器地址

async index2() {
    const { ctx, app } = this;
    
    console.log(this.ctx.request.body)
    
    // 用xml2js模块来处理xml
    var parseString = require('xml2js').parseString;
    
    var xml = this.ctx.request.body;
    
    parseString(xml, function (err, result) {
        // console.log(result.xml)
        
        const xmlData = result.xml;
        
        const createTime = Date.parse(new Date());
        const msgType = xmlData.MsgType[0]; // 消息类型,event
        const toUserName = xmlData.ToUserName[0]; // 开发人员微信号
        const toFromName = xmlData.FromUserName[0]; // 发送方帐号(一个OpenID)
        const event = xmlData.Event ? xmlData.Event[0] : ''; // 事件类型,subscribe(订阅)、unsubscribe(取消订阅)
        
        console.log(event)
        
        if(msgType == 'event' && event == 'subscribe'){ // 关注后
            ctx.body = `<xml>
                             <ToUserName><![CDATA[${toFromName}]]></ToUserName>
                             <FromUserName><![CDATA[${toUserName}]]></FromUserName>
                             <CreateTime>${createTime}</CreateTime>
                             <MsgType><![CDATA[text]]></MsgType>
                             <Content><![CDATA[欢迎关注w满城不及她公众号,下面请开始你的表演!]]></Content>
                        </xml>`;
        }
        else{// 其他情况
            ctx.body = `<xml>
                             <ToUserName><![CDATA[${toFromName}]]></ToUserName>
                             <FromUserName><![CDATA[${toUserName}]]></FromUserName>
                             <CreateTime>${createTime}</CreateTime>
                             <MsgType><![CDATA[text]]></MsgType>
                             <Content><![CDATA[啊~啊~啊~你在发什么消息?]]></Content>
                        </xml>`;
        }   
    })
}

来解释下,当用户点击关注,取消,发送消息的时候,会调用post接口推送xml数据信息,格式如下所示

<xml>
  <ToUserName><![CDATA[gh_a0df2b564971]]></ToUserName>
  <FromUserName><![CDATA[opNck1VdVevJsMeTfICgyHpW-Y_k]]></FromUserName>
  <CreateTime>1604312087</CreateTime>
  <MsgType><![CDATA[event]]></MsgType>
  <Event><![CDATA[subscribe]]></Event>
  <EventKey><![CDATA[]]></EventKey>
</xml>

在使用node的过程中打印接收body里面的数据为空,这是格式位xml数据,需要进行处理,我这里用的egg。

解决办法:config.default.js 文件中配置

// 覆盖egg自带的配置 使支持接收xml参数
  config.bodyParser = {
    enable: true,
    encoding: 'utf8',
    formLimit: '100kb',
    jsonLimit: '100kb',
    strict: true,
    // @see https://github.com/hapijs/qs/blob/master/lib/parse.js#L8 for more options
    queryString: {
      arrayLimit: 100,
      depth: 5,
      parameterLimit: 1000,
    },
    enableTypes: ['json', 'form', 'text'],
    extendTypes: {
      text: ['text/xml', 'application/xml'],
    },
  };

这样就能看到xml数据了。
再然后就要去解析xml数据了,我这里用到了一个插件xml2js
地址:https://www.npmjs.com/package/xml2js

安装:

npm i xml2js

简单用法:

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

再解析数据后就可以捕获用户的动作,返回各种信息了


image.png
image.png

这样就好了。

注意:我这里配置的路由是这样的

// 公众号测试
  router.get('/index', controller.demo.index);
  router.post('/index', controller.demo.index2);

可以看到是相同的接口名称,只是接口请求方式不一样,一个是get,一个是post
get请求是用来通过配置服务器地址的
post请求是用来捕获用户在微信公众号各种操作的

可能讲的不是清楚,请见谅,也是这两天才琢磨出来的,因此记录下来,以便后续不忘记

推荐文章:
https://www.cnblogs.com/kakayang/p/9729541.html
https://blog.csdn.net/m0_37805167/article/details/84327354

上一篇下一篇

猜你喜欢

热点阅读