Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,74 @@ List.add('view', [

如果用户触发等待回复事务后,没有按照`{}`中的进行回复,那么将会由原有的默认函数进行处理。在原有函数中,可以选择调用`res.nowait()`中断事务。`nowait()`除了能中断事务外,与`reply`的行为一致。

### 菜单操作
#### 获取口令
获取访问口令,用于进一步操作菜单。

```
var API = require('wechat').API;
var api = new API('appid', 'secret');
api.getAccessToken(function (err, token) {
// token
// {"access_token":"ACCESS_TOKEN","expires_in":7200}
});
```

#### 创建菜单
获取口令之后,就能创建菜单了。

```
var menu = {
"button":[
{
"type":"click",
"name":"今日歌曲",
"key":"V1001_TODAY_MUSIC"
},
{
"type":"click",
"name":"歌手简介",
"key":"V1001_TODAY_SINGER"
},
{
"name":"菜单",
"sub_button":[
{
"type":"click",
"name":"hello word",
"key":"V1001_HELLO_WORLD"
},
{
"type":"click",
"name":"赞一下我们",
"key":"V1001_GOOD"
}
]
}]
};
api.createMenu(menu, function (err, data) {
// TODO
});
```

#### 获取菜单
创建菜单之后,就可以获取菜单了:

```
api.getMenu(function (err, menu) {
// menu
});
```

#### 删除菜单
也可以删除掉菜单:

```
api.removeMenu(function (err, data) {
// TODO
});
```

## Show cases
### Node.js API自动回复

Expand Down
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
var wechat = require('./lib/wechat');
wechat.List = require('./lib/list');
module.exports = wechat;
wechat.API = require('./lib/common');
module.exports = wechat;
98 changes: 98 additions & 0 deletions lib/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
var urllib = require('urllib');

// http://mp.weixin.qq.com/wiki/index.php?title=%E8%BF%94%E5%9B%9E%E7%A0%81%E8%AF%B4%E6%98%8E
var wrapper = function (callback) {
return function (err, data, res) {
if (err) {
err.name = 'WeChatAPI' + err.name;
return callback(err, data, res);
}
if (data.errcode) {
err = new Error(data.errmsg);
err.name = 'WeChatAPIError';
return callback(err, data, res);
}
callback(null, data, res);
};
};

var API = function (appid, appsecret) {
this.appid = appid;
this.appsecret = appsecret;
this.prefix = 'https://api.weixin.qq.com/cgi-bin/';
};

/**
* 根据appid和appsecret获取access token
* @param {String} appid appid
* @param {String} appsecret secret
* @param {Function} callback 回调函数
*/
API.prototype.getAccessToken = function (callback) {
var that = this;
var url = this.prefix + 'token?grant_type=client_credential&appid=' + this.appid + '&secret=' + this.appsecret;
urllib.request(url, {dataType: 'json'}, wrapper(function (err, data) {
if (err) {
return callback(err);
}
that.token = data.access_token;
callback(err, data);
}));
return this;
};

/**
* 创建菜单
* @param {Object} menu 菜单对象
* @param {Function} callback 回调函数
*/
API.prototype.createMenu = function (menu, callback) {
var url = this.prefix + 'menu/create?access_token=' + this.token;
var args = {
dataType: 'json',
type: 'POST',
data: menu,
headers: {
'Content-Type': 'application/json'
}
};
urllib.request(url, args, wrapper(callback));
};

/**
* 获取菜单
* @param {Function} callback 回调函数
*/
API.prototype.getMenu = function (callback) {
var url = this.prefix + 'menu/get?access_token=' + this.token;
urllib.request(url, {dataType: 'json'}, wrapper(callback));
};

/**
* 删除菜单
* @param {Function} callback 回调函数
*/
API.prototype.removeMenu = function (callback) {
var url = this.prefix + 'menu/delete?access_token=' + this.token;
urllib.request(url, {dataType: 'json'}, wrapper(callback));
};

/**
* 获取二维码
* @param {Object} data 二维码数据
* @param {Function} callback 回调函数
*/
API.prototype.getQRCode = function (data, callback) {
var url = this.prefix + 'qrcode/get?access_token=' + this.token;
var args = {
dataType: 'json',
type: 'POST',
data: data,
headers: {
'Content-Type': 'application/json'
}
};
urllib.request(url, args, wrapper(callback));
};

module.exports = API;
18 changes: 18 additions & 0 deletions lib/menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
var Menu = function () {
this.buttons = [];
};

Menu.prototype.addButton = function (button, subButtons) {
if (subButtons) {
delete button.type;
delete button.key;
button.sub_button = subButtons;
}
this.buttons.push(button);
return this;
};

Menu.prototype.toString = function () {
return JSON.stringify({"button": this.buttons});
};
exports.Menu = Menu;
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wechat",
"version": "0.4.8",
"version": "0.5.0",
"description": "微信公共平台自动回复接口服务",
"main": "index.js",
"scripts": {
Expand All @@ -26,7 +26,8 @@
"dependencies": {
"xml2js": "0.2.6",
"ejs": ">=0.8.3",
"bufferhelper": ">=0.2.0"
"bufferhelper": ">=0.2.0",
"urllib": ">=0.3.4"
},
"devDependencies": {
"supertest": "*",
Expand All @@ -36,7 +37,8 @@
"blanket": "*",
"travis-cov": "*",
"coveralls": "*",
"mocha-lcov-reporter": "*"
"mocha-lcov-reporter": "*",
"muk": "*"
},
"author": "Jackson Tian",
"license": "MIT",
Expand Down
114 changes: 114 additions & 0 deletions test/common.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
var should = require('should');
var urllib = require('urllib');
var muk = require('muk');
var API = require('../').API;

describe('common.js', function () {
describe('getAccessToken', function () {
var api = new API('appid', 'secret');

it('should not ok', function (done) {
api.getAccessToken(function (err, token) {
should.exist(err);
err.name.should.be.equal('WeChatAPIError');
err.message.should.be.equal('invalid appid');
done();
});
});

describe('mock urllib err', function () {
before(function () {
muk(urllib, 'request', function (url, args, callback) {
var err = new Error('Urllib Error');
err.name = 'UrllibError';
callback(err);
});
});

after(function () {
muk.restore();
});

it('should get mock error', function (done) {
api.getAccessToken(function (err, token) {
should.exist(err);
err.name.should.be.equal('WeChatAPIUrllibError');
err.message.should.be.equal('Urllib Error');
done();
});
});
});

describe('mock token', function () {
before(function () {
muk(urllib, 'request', function (url, args, callback) {
process.nextTick(function () {
callback(null, {"access_token": "ACCESS_TOKEN","expires_in": 7200});
});
});
});
after(function () {
muk.restore();
});

it('should ok', function (done) {
api.getAccessToken(function (err, token) {
should.not.exist(err);
token.should.have.property('access_token', 'ACCESS_TOKEN');
token.should.have.property('expires_in', 7200);
done();
});
});
});
});

describe('invalid token', function () {
var api = new API('appid', 'secret');
before(function (done) {
api.getAccessToken(function (err) {
done();
});
});

it('createMenu should not ok', function (done) {
api.createMenu('{}', function (err, menu) {
should.exist(err);
err.name.should.be.equal('WeChatAPIError');
err.message.should.be.equal('invalid credential');
done();
});
});

it('getMenu should not ok', function (done) {
api.getMenu(function (err, menu) {
should.exist(err);
err.name.should.be.equal('WeChatAPIError');
err.message.should.be.equal('invalid credential');
done();
});
});

it('removeMenu should not ok', function (done) {
api.removeMenu(function (err, token) {
should.exist(err);
err.name.should.be.equal('WeChatAPIError');
err.message.should.be.equal('invalid credential');
done();
});
});

it('getQRCode should not ok', function (done) {
var ticket = {"expire_seconds": 1800, "action_name": "SCAN_SCENE", "action_info": {"scene": {"scene_id": 123}}};
api.getQRCode(ticket, function (err, data, res) {
should.exist(err);
err.name.should.be.equal('WeChatAPIError');
err.message.should.be.equal('invalid credential');
done();
});
});
});

describe('mock', function () {

});
});