Code Monkey home page Code Monkey logo

co-wechat's Introduction

co-wechat NPM version Build Status Dependencies Status Coverage Status

微信公众平台消息接口服务中间件

升级注意事项

支持 Koa2

如果仍然使用 Koa1,请使用[email protected]

middleware 方法变更

middleware() 方法接受一个 async function 作为参数。

app.use(wechat(config).middleware(async (message, ctx) => {
  // TODO
}));

上下文与返回值变更

现在的上下文不再是原始的 请求上下文,而仅仅是 message 对象。

业务的返回值现在直接返回即可,无需关注上下文。比如:

async (message, ctx) => {
  return 'Hello world!';
}

取消 session 的支持

不再支持 session 的功能。如需使用 session 功能,建议使用 redis 自行处理逻辑,取 message.FromUserName 作为 key,取一个合适的 ttl 时间即可。

功能列表

  • 自动回复(文本、图片、语音、视频、音乐、图文)

Installation

$ npm install co-wechat

Use with koa2

const wechat = require('co-wechat');

const config = {
  token: 'THE TOKEN',
  appid: 'THE APPID',
  encodingAESKey: 'THE ENCODING AES KEY'
};

app.use(wechat(config).middleware(async (message, ctx) => {
  // 微信输入信息就是这个 message
  if (message.FromUserName === 'diaosi') {
    // 回复屌丝(普通回复)
    return 'hehe';
  } else if (message.FromUserName === 'text') {
    //你也可以这样回复text类型的信息
    return {
      content: 'text object',
      type: 'text'
    };
  } else if (message.FromUserName === 'hehe') {
    // 回复一段音乐
    return {
      type: "music",
      content: {
        title: "来段音乐吧",
        description: "一无所有",
        musicUrl: "http://mp3.com/xx.mp3",
        hqMusicUrl: "http://mp3.com/xx.mp3"
      }
    };
  } else if (message.FromUserName === 'kf') {
    // 转发到客服接口
    return {
      type: "customerService",
      kfAccount: "test1@test"
    };
  } else {
    // 回复高富帅(图文回复)
    return [
      {
        title: '你来我家接我吧',
        description: '这是女神与高富帅之间的对话',
        picurl: 'http://nodeapi.cloudfoundry.com/qrcode.jpg',
        url: 'http://nodeapi.cloudfoundry.com/'
      }
    ];
  }
}));

备注:token 在微信平台的开发者中心申请

开启调试模式(用于微信公众平台接口调试工具调试)

//构造函数的第二个函数isDebug参数,为true或别的正值时表示开启,推荐用配置或是环境变量的方式传参
wechat(config, true)

配置成debug模式后,用微信公众平台接口调试工具发送POST请求(比如消息接口调试-文本消息)时返回结果不会提示Invalid signature

回复消息

当用户发送消息到微信公众账号,自动回复一条消息。这条消息可以是文本、图片、语音、视频、音乐、图文。详见:官方文档

回复文本

async (message, ctx) => {
  return 'Hello world!';
}
// 或者
async (message, ctx) => {
  return {type: "text", content: 'Hello world!'};
}

回复图片

async (message, ctx) => {
  return {
    type: "image",
    content: {
      mediaId: 'mediaId'
    }
  };
}

回复语音

async (message, ctx) => {
  return {
    type: "voice",
    content: {
      mediaId: 'mediaId'
    }
  };
}

回复视频

async (message, ctx) => {
  return {
    type: "video",
    content: {
      mediaId: 'mediaId',
      thumbMediaId: 'thumbMediaId'
    }
  };
}

回复音乐

async (message, ctx) => {
  return {
    title: "来段音乐吧",
    description: "一无所有",
    musicUrl: "http://mp3.com/xx.mp3",
    hqMusicUrl: "http://mp3.com/xx.mp3"
  };
}

回复图文

async (message, ctx) => {
  return [
    {
      title: '你来我家接我吧',
      description: '这是女神与高富帅之间的对话',
      picurl: 'http://nodeapi.cloudfoundry.com/qrcode.jpg',
      url: 'http://nodeapi.cloudfoundry.com/'
    }
  ];
}

回复空串

async (message, ctx) => {
  return '';
}

转发到客服接口

async (message, ctx) => {
  return {
    type: "customerService",
    kfAccount: "test1@test" //可选
  };
}

集成到 Egg.js

路由设置

// app/router.js
'use strict';

module.exports = app => {
  // 将 get/post 请求都转给 home.wechat
  app.all('/', 'home.wechat');
};

控制器

'use strict';

const wechat = require('co-wechat');

module.exports = app => {
  class HomeController extends app.Controller {}

  // 因为 Egg 需要用类的形式来组织,而 wechat 是通过 middleware 方法来生成中间件
  HomeController.prototype.wechat = wechat({
    token: 'token',
    appid: 'appid',
    encodingAESKey: ''
  }).middleware(async (message, ctx) => {
    // TODO
  });

  return HomeController;
};

相同路由支持多账号

// app/router.js
'use strict';

module.exports = app => {
  // 将 get/post 请求都转给 home.wechat
  app.all('/wechat/:appid', 'home.prehandle', 'home.wechat');
};

在前置中间件中预先设置 ctx.wx_token 或 ctx.wx_cryptor:

'use strict';

const WXBizMsgCrypt = require('wechat-crypto');
const wechat = require('co-wechat');

module.exports = app => {
  class HomeController extends app.Controller {
    async prehandle(ctx, next) {
      const appid = ctx.params.appid;
      const token = getTokenByAppid(appid);
      ctx.wx_token = token
      // 或者
      const encodingAESKey = getEncodingAESKeyByAppid(appid);

      ctx.wx_cryptor = new WXBizMsgCrypt(token, encodingAESKey, appid);
      await next();
    }
  }

  HomeController.prototype.wechat = wechat({
    // 当有前置中间件设置 ctx.wx_token 和 ctx.wx_cryptor 时,这里配置随意填写
    // token: 'token',
    // appid: 'appid',
    // encodingAESKey: ''
  }).middleware(async (message, ctx) => {
    // TODO
  });

  return HomeController;
};

注意,上述的 getTokenByAppid 和 getEncodingAESKeyByAppid 方法根据自己情况请自行提供。

Show cases

Node.js API 自动回复

Node.js API自动回复机器人

欢迎关注。

代码:https://github.com/JacksonTian/api-doc-service

你可以在CloudFoundryappfogBAE等搭建自己的机器人。

详细API

原始API文档请参见:消息接口指南

交流群

QQ群:157964097,使用疑问,开发,贡献代码请加群。

捐赠

如果您觉得Wechat对您有帮助,欢迎请作者一杯咖啡

捐赠wechat

或者

License

The MIT license.

Contributors


 project  : co-wechat
 repo age : 3 years, 1 month
 active   : 21 days
 commits  : 59
 files    : 10
 authors  :
    46  Jackson Tian  78.0%
     6  ifeiteng      10.2%
     3  lixiaojun     5.1%
     2  Andrew Lyu    3.4%
     1  Jealee3000    1.7%
     1  fancyoung     1.7%

co-wechat's People

Contributors

apemost avatar bellchet58 avatar fancyoung avatar jacksontian avatar lixiaojun avatar terry-fei avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

co-wechat's Issues

作者你好

非常感谢你开源这个插件,我有一个问题如下

router.use('/aaa',wechat(config).middleware(async (message, ctx) => {
  return '啊啊啊啊';
}))

为什么,这个路由不走呢?

koa2.x ctx.request.body === 'string' 而是object

if (ctx.request.body && typeof ctx.request.body === 'string') {
xml = ctx.request.body;
} else {
// 取原始数据
xml = await getRawBody(ctx.req, {
length: ctx.request.length,
limit: '1mb',
encoding: ctx.request.charset || 'utf-8'
});
}

在koa2.0下用的时候

ctx.request.body === 'string'

这个不是string
而是 object

集成到eggjs 1.8,接收消息报错 Can't set headers after they are sent.

2017-09-28 17:27:06,846 ERROR 1014 nodejs.unhandledRejectionError: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:489:11)
at ServerResponse.setHeader (_http_outgoing.js:496:3)
at Object.set (/usr/src/app/node_modules/koa/lib/response.js:436:16)
at Object.set length [as length] (/usr/src/app/node_modules/egg/app/extend/response.js:11:10)
at Object.set body [as body] (/usr/src/app/node_modules/koa/lib/response.js:152:19)
at Object.body (/usr/src/app/node_modules/delegates/index.js:92:31)
at /usr/src/app/node_modules/co-wechat/lib/wechat.js:278:20
at
at process._tickCallback (internal/process/next_tick.js:188:7)
name: 'unhandledRejectionError'
pid: 1014
hostname: 6f6a5b08ca67

getRawBody 参数传错了

co-wechat/lib/wechat.js

Lines 224 to 228 in 6dbb669

var xml = await getRawBody(ctx.req, {
length: ctx.length,
limit: '1mb',
encoding: ctx.charset
});

var xml = await getRawBody(ctx.req, {
  length: ctx.length,
  limit: '1mb',
  encoding: ctx.charset
});

此处的 ctx.length 是委托给 ctx.response.length 的,所以值是 undefined

ctx.charset 应该写成 ctx.request.charset 才对,并且按照 raw-body 文档,encoding: true 时默认为 'utf-8'。

参见 https://github.com/koajs/koa/blob/aaac09af1a6aa02161fead1422fac460fbdcce3e/lib/context.js#L154-L208

所以我认为这里应该写成:

var xml = await getRawBody(ctx.req, {
  length: ctx.request.length,
  limit: '1mb',
  encoding: ctx.request.charset || 'utf-8'
});

语法错误问题

babel默认配置会把node_modules过滤,所以编译时node_modules里的es6语法并未转换
我查看了一下babel插件的源码,都是经过编译后再上传到npm上的,所以我希望你们也可以把编译结果文件传到npm上,而不要把源码直接上传,这样对小白使用起来不太方便

Use template-literal instead of EJS

Template Literal is fastest, smallest and simplest template engine, because it use JS's literal template feature.

It's 55 times faster than EJS, and it also use less CPU and RAM ressources, so it may be a good idea to use it instead of EJS 😀

与egg配合如何调用service

作者你好,最近遇到一个问题:和egg一起使用在处理不同的消息时调用不同的service?

module.exports = app => {
  class HomeController extends app.Controller {}

  // 因为 Egg 需要用类的形式来组织,而 wechat 是通过 middleware 方法来生成中间件
  HomeController.prototype.wechat = wechat({
    token: 'token',
    appid: 'appid',
    encodingAESKey: ''
  }).middleware(async (message, ctx) => {
    // 此处能否调用不同的service?
  });

  return HomeController;
};

Non-whitespace before first tag

公众号发消息时,后端日志报下面的错误,导致后续代码未执行!
版本:2.4.0
koa版本:2.8.2
报错信息

Error: Non-whitespace before first tag.
    Line: 0
    Column: 1
    Char:
        at error (/home/github/repo/node_modules/co-wechat/node_modules/sax/lib/sax.js:642:8)
        at strictFail (/home/github/repo/node_modules/co-wechat/node_modules/sax/lib/sax.js:662:22)
        at SAXParser.write (/home/github/repo/node_modules/co-wechat/node_modules/sax/lib/sax.js:935:11)
        at Parser.exports.Parser.Parser.parseString (/home/github/repo/node_modules/co-wechat/node_modules/xml2js/lib/xml2js.js:403:31)
        at Parser.parseString (/home/github/repo/node_modules/co-wechat/node_modules/xml2js/lib/xml2js.js:6:61)
        at Object.exports.parseString (/home/github/repo/node_modules/co-wechat/node_modules/xml2js/lib/xml2js.js:433:19)
        at Promise (/home/github/repo/node_modules/co-wechat/lib/wechat.js:19:12)
        at new Promise (<anonymous>)
        at parseXML (/home/github/repo/node_modules/co-wechat/lib/wechat.js:18:10)
        at /home/github/repo/node_modules/co-wechat/lib/wechat.js:259:34
        at process._tickCallback (internal/process/next_tick.js:68:7)

代码:

import originWechat from 'co-wechat';

const middleware = () => {
  const { appid, aes, token } = wechatAccount;
  const wechat = originWechat({ appid, encodingAESKey: aes, token });
  return wechat.middleware(bot);
};

export const register = ({ router, wechat, open }) => {
  open.get('/wechat', middleware());
  open.post('/wechat', middleware());
};

TypeError: Cannot read property 'hasOwnProperty' of null

reply 方法下,content 可能为 null,而且 typeof null === 'object',这段代码(144行)可能会出错

if (Array.isArray(content)) {
    type = 'news';
  } else if (typeof content === 'object') { // content 可能为 null
    if (content.hasOwnProperty('type')) { // 如果 content 为 null,会报错
      if (content.type === 'customerService') {
        return reply2CustomerService(fromUsername, toUsername, content.kfAccount);
      }
      type = content.type;
      info.content = content.content;
    } else {
      type = 'music';
    }
  }

请问co-wechat2.x版本只能运行在node7.x或更高版本?

问题由来:
我们公司环境是node6.x使用框架是koa2,集成co-wechat2.1运行是提示语法有问题,查看了co-wechat2.1源码发现里面使用了node7.x + 才支持的await等,所以 想要使用co-wechat 2.x是不是只能升级node7.x+ ,我们不能调整koa至1因为框架很多有依赖,所以想问下 是不是如此?假如是 请 作者在页面标注下,防止其他用户出现类似问题,谢谢啦!

co-wechat bug

lib/wechat.js:
182行: var encrypted =! !(query.encrypt_type && query.encrypt_type === 'aes' && query.msg_signature);
应 为:var encrypted = !(query.encrypt_type && query.encrypt_type === 'aes' && query.msg_signature);
283行:if (!query.encrypt_type || query.encrypt_type === 'raw')
应 为:if (!!query.encrypt_type || query.encrypt_type === 'raw')
都是感叹号,一行多了,一行少了

还有当在app.js页面(主页面)使用的时候没问题,但在其它页面做中间件使用的时候,必须要跟上路由器的get和post方法,co-wechat才能起作用,这可能是一个bug。我用的是koa-router

eggjs中使用时,如何在midlleware中获取原始的上下文请求

您好。
这边有个需求是这样的,收到微信的消息后,需要把消息存到数据库中。
我使用的是egg框架。
但看您的文档,middleware中,现在的上下文不再是原始的 请求上下文,而仅仅是 message 对象。
那我需要怎样才能获取到原始的请求上下文,然后调用service或者model中的方法呢?谢谢。

egg.js 的集成问题

'use strict';

const wechat = require('co-wechat');

module.exports = app => {
  class HomeController extends app.Controller { }

  // 因为 Egg 需要用类的形式来组织,而 wechat 是通过 middleware 方法来生成中间件
  HomeController.prototype.wechat = wechat({
    token: '',
    appid: '',
    encodingAESKey: ''
  }).middleware(async (message, ctx) => {
    // TODO
    var content = message.Content;
    var reply = '无法回复您的内容: ' + content;
    if (message.MsgType === 'event') {
      if (message.Event === 'subscribe') {
        if (message.EventKey) {
          //扫描二维码事件
          console.log('扫描二维码进入...');
        }

        reply = 'Thanks to subscribe. \r\n';
      } else if (message.Event === 'unsubscribe') {
        // 取消关注
        console.log('取消关注。。。');
        reply = '';
      } 
    } else if (message.MsgType === 'text') {


      // 用户输入文本选项
      switch (content) {
        case '1':
          reply = '您当前选项为 1。'
          break;

        case '2':
          reply = '您当前选项为 2。'
          break;

        case '3':
          reply = '您当前选项为 3。'
          break;

        default:
          break;
      }
      return reply;
    }

    await next();
  });

  return HomeController;
};

我用了微信公众平台接口调试工具进行调试,基础支持返回access_token,消息接口测试返回的是请求失败,手机上也是返回服务出现故障,刚刚上手egg.js,请问是哪里出现了问题?

重新修改了下代码

2.1.0处理xml被提前处理的问题

新增的处理xml提前处理的方法,可能需要加个判断body是不是字符串

我用的koa2+koa-bodyparser4.2.x,导致ctx.request.body是个{},所以就一直报错了。。

搞了一下午,刚刚突然发现是新增了那个判断。

koa2的wechat.middleware不可用

wechat("some token").middleware()中的配置的some token是Token(令牌)还是EncodingAESKey?
使用令牌的话报400错误,
使用EncodingAESKey的话是401

Use template-literal instead of EJS

Template Literal is fastest, smallest and simplest template engine, because it use JS's literal template feature.

It's 55 times faster than EJS, and it also use less CPU and RAM ressources, so it may be a good idea to use it instead of EJS 😀

请问关于路由的问题

这样的形式是否部署后会占用项目根目录,假如想要支持多个目录,例如:

  1. 项目地址/wechat 被用来监听 wechat那边的通知和回调
  2. 项目地址/其他 被用来监听自己项目的通知和回调这样如何配置呢?

请教下一个事件消息bug

使用koa2 。 当MsgType:event,Event:SCAN 即已关注的用户扫带参二维码进来之后,return "111"。那么会先收到111, 跟随着会出现 "该公众号提供的服务出现故障,请稍后再试" 。 测试号没问题, 其他事件也不会出现这个情况

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.