nodejs 实现微信公众号消息管理

2,369 阅读3分钟

我们经常可以看到,在关注一个公众号的时候,有的公众号会自动给我们回复一条消息,在给公众号发消息的时候,公众号也会针对我们发送的消息进行特定的回复。 那么这样的功能是怎么用代码实现的呢?

接下来我们一步一步的实现:

1. 服务器配置

文档参看

首先我们进入公众号的后台,找到开发->基本配置->服务器配置,根据提示填入服务器地址、Token、EncodingAESKey、选择加密方式。

  • 服务器地址:自己开发服务器上一个可访问的地址,微信服务器会在接收到消息或事件的时候把消息推送到我们的这个服务器地址上面

  • Token: 这个是我们自己随便填的一个字符中,在验证服务器地址的时候需要用到

  • EncodingAESKey: 消息加解密密钥将用于消息体加解密过程

  • 加密方式:这里我们使用明文模式,其它2个模板在解析消息的时候需要用EncodingAESKey进行解密

内容填写完提交的时候微信会往我们的服务器地址发送一个GET请求来进行校验, 我们的服务器需要返回请求参数里面的 echostr 字符串配置才可以通过。 代码如下:

  router.route('/url').get(function (req, res) {
    var signature = req.query.signature;
    var timestamp = req.query.timestamp;
    var nonce = req.query.nonce;
    var echostr = req.query.echostr;
    var cr = wxmessage.checkSignature(signature, timestamp, nonce);
    if (cr) {
      res.send(echostr);
    } else {
      res.send(false);
    }
  });

checkSignature函数如下:

  function checkSignature(signature, timestamp, nonce) {
    var arr = [config.wxtoken, timestamp, nonce].sort();
    var str = arr.join('');
    var ciphertext = CryptoJs.SHA1(str);
    if (signature == ciphertext.toString()) {
      return true;
    }

    return false;
  }

注意checkSignature函数中使用的config.wetoken就是上面我们填的Token字符串。

保存成功后我们点击 [启用] 按钮就可以了, 启用后微信的消息就会转发到我们的服务器地址上面了。

2. 关注/取消事件通知 消息接收

事件推送文档参看

普通消息文档参看

对于事件和消息微信服务器都会以 POST 形式推送到我们的服务器地址上面,所以我们对同一个地址在增加一个post类型的请求函数:

  router.route('/uirl').post(function (req, res) {
    var buffer = [];
    req.on('data', function (data) {
      buffer.push(data);
    });

    req.on('end', async function () {
      try {
        const r = await wxmessage.msgHandler(buffer);
        console.log('send Data:', r);
        res.send(r);
      } catch (error) {
        console.log('公众号消息事件Error:', error);
        res.send('error');
      }
    });
  });

这里我们接收完推送数据后把数据交给了 msgHandler 进行处理,

  function msgHandler(msgbufer) {
    var parser = new xml2js.Parser({ trim: true, explicitArray: false, explicitRoot: false });
    var builder = new xml2js.Builder({ headless: true, cdata: true, explicitRoot: false, rootName: 'xml' });

    return new Promise((resolve, reject) => {
      parser.parseString(msgbufer.toString(), async function (err, result) {
        if (err) {
          reject({
            code: -1,
            msg: 'error',
            data: err,
          });
        }
        var baseData = {
          ToUserName: result.FromUserName,
          FromUserName: result.ToUserName,
          CreateTime: Date.now(),
        }

        switch (result.MsgType) {
          case 'text':
            switch (result.Content.toLowerCase()) {
              case 'help':
                // 返回帮助内容
                var helpTxt = [
                  '1. 在公众号对话框中输入任意商品名称,点击返回的链接即可筛选购买.',
                  '2. 输入关键字『入口』可以得到网站的入口链接.'
                ]
                var data = Object.assign({
                  MsgType: 'text',
                  Content: helpTxt.join('\n'),
                }, baseData);

                resolve(builder.buildObject(data));
                break;
              default:
                break;
            }
            break;
          case 'event':
            if (result.Event === 'subscribe') {
              // 关注
              var data = Object.assign({
                MsgType: 'news',
                ArticleCount: 1,
                Articles: {
                  item: {
                    Title: '淘淘乐',
                    Description: '丸子带你买,店内领取各种淘宝天猫优惠券',
                    PicUrl: 'http://weixin.tangsj.com/dataoke/wx.jpg',
                    Url: 'http://weixin.tangsj.com/dataoke/',
                  },
                },
              }, baseData);

              resolve(builder.buildObject(data));
            } else if (result.Event === 'unsubscribe') {
              // 取消关注
              var data = Object.assign({
                MsgType: 'text',
                Content: '在下没能满足客官的需求,实在抱歉~~',
              }, baseData);

              resolve(builder.buildObject(data));
            }
            resolve('');
            break;
          default:
            resolve('');
            break;
        }
      });
    });
  },

微信服务器和我们的服务器数据交换是用 xml 形式进行的, 所以这里我们引入的 xml2js 库进行处理

微信的这一次post 链接会保持5s, 5s内我们可以给微信服务器回复消息内容。5s 内没有回复微信会重新发起请求, 总共尝试3次。

3. 回复内容

回复的内容我们组装好对象 然后得用xml2js builder 转换成 xml 最后通过 res.send(xmlcontent) 返回给微信服务器就完成了,具体看代码看msgHandler。

原文地址 CodeCook