├── .gitignore
├── .jshintrc
├── README.md
├── app.js
├── cloud.js
├── nodemon.json
├── package.json
├── public
└── stylesheets
│ └── style.css
├── routes
└── wechatBot.js
├── server-cluster.js
├── server.js
└── views
├── error.ejs
├── index.ejs
└── todos.ejs
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | start.sh
3 |
4 | # VIM
5 | *~
6 | *.swp
7 | .avoscloud/
8 | .leancloud/
9 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true
3 | }
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 注意
2 |
3 | 该项目为 [微信公众平台开发指南](https://leancloud.cn/docs/webhosting_weixin.html) 文档所对应的代码,请参照文档进行开发,该项目不再维护。
4 |
5 | 此外你还可以查看 [leanengine-nodejs-demos](https://github.com/leancloud/leanengine-nodejs-demos) 了解云引擎的更多用法和最佳实践。
6 |
7 |
8 | 以下为原 README 内容
9 |
10 | # LeanEngine 微信自动问答机器人
11 | 使用 LeanEngine 的微信公众号自动问答机器人的演示项目。
12 |
13 |
14 | ## 文档
15 |
16 | * [微信公众平台开发指南](https://leancloud.cn/docs/webhosting_weixin.html)
17 |
18 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var express = require('express');
3 | var timeout = require('connect-timeout');
4 | var path = require('path');
5 | var cookieParser = require('cookie-parser');
6 | var bodyParser = require('body-parser');
7 | var AV = require('leanengine');
8 |
9 | // 加载云函数定义,你可以将云函数拆分到多个文件方便管理,但需要在主文件中加载它们
10 | require('./cloud');
11 |
12 | var app = express();
13 |
14 | // 设置模板引擎
15 | app.set('views', path.join(__dirname, 'views'));
16 | app.set('view engine', 'ejs');
17 |
18 | app.use(express.static('public'));
19 |
20 | // 设置默认超时时间
21 | app.use(timeout('15s'));
22 |
23 | // 加载云引擎中间件
24 | app.use(AV.express());
25 |
26 | app.enable('trust proxy');
27 | // 需要重定向到 HTTPS 可去除下一行的注释。
28 | //app.use(AV.Cloud.HttpsRedirect());
29 |
30 | app.use(bodyParser.json());
31 | app.use(bodyParser.urlencoded({
32 | extended: false
33 | }));
34 | app.use(cookieParser());
35 |
36 | app.get('/', function(req, res) {
37 | res.render('index', {
38 | currentTime: new Date()
39 | });
40 | });
41 |
42 | // 可以将一类的路由单独保存在一个文件中
43 | app.use('/wechat', require('./routes/wechatBot'));
44 |
45 | app.use(function(req, res, next) {
46 | // 如果任何一个路由都没有返回响应,则抛出一个 404 异常给后续的异常处理器
47 | if (!res.headersSent) {
48 | var err = new Error('Not Found');
49 | err.status = 404;
50 | next(err);
51 | }
52 | });
53 |
54 | // error handlers
55 | app.use(function(err, req, res, next) {
56 | if (req.timedout && req.headers.upgrade === 'websocket') {
57 | // 忽略 websocket 的超时
58 | return;
59 | }
60 |
61 | var statusCode = err.status || 500;
62 | if (statusCode === 500) {
63 | console.error(err.stack || err);
64 | }
65 | if (req.timedout) {
66 | console.error('请求超时: url=%s, timeout=%d, 请确认方法执行耗时很长,或没有正确的 response 回调。', req.originalUrl, err.timeout);
67 | }
68 | res.status(statusCode);
69 | // 默认不输出异常详情
70 | var error = {}
71 | if (app.get('env') === 'development') {
72 | // 如果是开发环境,则将异常堆栈输出到页面,方便开发调试
73 | error = err;
74 | }
75 | res.render('error', {
76 | message: err.message,
77 | error: error
78 | });
79 | });
80 |
81 | module.exports = app;
82 |
--------------------------------------------------------------------------------
/cloud.js:
--------------------------------------------------------------------------------
1 | var AV = require('leanengine');
2 |
3 | /**
4 | * 一个简单的云代码方法
5 | */
6 | AV.Cloud.define('hello', function(request, response) {
7 | response.success('Hello world!');
8 | });
9 |
--------------------------------------------------------------------------------
/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "ignore": ["views/*", "public/*"]
3 | }
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "LeanEngine_Weixin_Sample",
3 | "version": "1.1.0",
4 | "description": "使用 LeanCloud 云引擎轻松打造一个微信机器人",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js"
8 | ,"dev": "nodemon server.js"
9 | },
10 | "keywords": [
11 | "node",
12 | "LeanCloud",
13 | "LeanEngine",
14 | "express",
15 | "Weixin"
16 | ],
17 | "license": "MIT",
18 | "dependencies": {
19 | "async": "^1.5.2",
20 | "body-parser": "1.12.3",
21 | "connect-timeout": "^1.7.0",
22 | "cookie-parser": "^1.3.5",
23 | "ejs": "2.3.1",
24 | "express": "4.12.3",
25 | "leanengine": "^1.2.3",
26 | "request": "^2.69.0",
27 | "strformat": "0.0.7",
28 | "wechat": "^2.0.3",
29 | "wechat-api": "^1.24.0"
30 | },
31 | "devDependencies": {
32 | "nodemon": "^1.11.0"
33 | },
34 | "engines": {
35 | "node": "6.x"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/public/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4 | }
5 | a {
6 | color: #00b7ff;
7 | }
8 |
--------------------------------------------------------------------------------
/routes/wechatBot.js:
--------------------------------------------------------------------------------
1 | var router = require('express').Router();
2 | // 引用 wechat 库,详细请查看 https://github.com/node-webot/wechat
3 | var wechat = require('wechat');
4 | var config = {
5 | token: '请把微信后台要求输入的 token 填写在这里',
6 | appid: '请把微信的 AppID 填写在这里',
7 | encodingAESKey: '请把微信后台为您生成的 EncodingAESKey 填写在这里'
8 | };
9 |
10 | var WechatAPI = require('wechat-api');
11 | var api = new WechatAPI('请把微信的 AppID 填写在这里',
12 | '请把微信的 Secret Key 填写在这里');
13 |
14 | router.use('/', wechat(config).text(function(message, req, res, next) {
15 | // message为文本内容
16 | // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',
17 | // CreateTime: '1359125035',
18 | // MsgType: 'text',
19 | // Content: 'http',
20 | // MsgId: '5837397576500011341' }
21 | var keyArray = ['你好', '约吗'];
22 | var content = message.Content;
23 | var keyIndex = keyArray.indexOf(content);
24 | switch (keyIndex) {
25 | case 0:
26 | {
27 | res.reply({
28 | type: "text",
29 | content: '您好,大家好才是真的好!'
30 | });
31 |
32 | }
33 | break;
34 | case 1:
35 | {
36 | res.reply({
37 | type: "text",
38 | content: '不约,不约,叔叔我们不约!'
39 | });
40 |
41 | }
42 | break;
43 | default:
44 | res.reply({
45 | type: "text",
46 | content: '服务器挂掉了,你的要求暂时无法满足……'
47 | });
48 | break;
49 | }
50 | }).image(function(message, req, res, next) {
51 | // message为图片内容
52 | // { ToUserName: 'gh_d3e07d51b513',
53 | // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',
54 | // CreateTime: '1359124971',
55 | // MsgType: 'image',
56 | // PicUrl: 'http://mmsns.qpic.cn/mmsns/bfc815ygvIWcaaZlEXJV7NzhmA3Y2fc4eBOxLjpPI60Q1Q6ibYicwg/0',
57 | // MediaId: 'media_id',
58 | // MsgId: '5837397301622104395' }}).voice(function(message, req, res, next) {
59 | // TODO
60 | }).voice(function(message, req, res, next) {
61 | // message为音频内容
62 | // { ToUserName: 'gh_d3e07d51b513',
63 | // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',
64 | // CreateTime: '1359125022',
65 | // MsgType: 'voice',
66 | // MediaId: 'OMYnpghh8fRfzHL8obuboDN9rmLig4s0xdpoNT6a5BoFZWufbE6srbCKc_bxduzS',
67 | // Format: 'amr',
68 | // MsgId: '5837397520665436492' }
69 | }).video(function(message, req, res, next) {
70 | // message为视频内容
71 | // { ToUserName: 'gh_d3e07d51b513',
72 | // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',
73 | // CreateTime: '1359125022',
74 | // MsgType: 'video',
75 | // MediaId: 'OMYnpghh8fRfzHL8obuboDN9rmLig4s0xdpoNT6a5BoFZWufbE6srbCKc_bxduzS',
76 | // ThumbMediaId: 'media_id',
77 | // MsgId: '5837397520665436492' }
78 | // TODO
79 | }).shortvideo(function(message, req, res, next) {
80 | // message为短视频内容
81 | // { ToUserName: 'gh_d3e07d51b513',
82 | // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',
83 | // CreateTime: '1359125022',
84 | // MsgType: 'shortvideo',
85 | // MediaId: 'OMYnpghh8fRfzHL8obuboDN9rmLig4s0xdpoNT6a5BoFZWufbE6srbCKc_bxduzS',
86 | // ThumbMediaId: 'media_id',
87 | // MsgId: '5837397520665436492' }
88 | // TODO
89 | }).location(function(message, req, res, next) {
90 | // message为链接内容
91 | // { ToUserName: 'gh_d3e07d51b513',
92 | // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',
93 | // CreateTime: '1359125022',
94 | // MsgType: 'link',
95 | // Title: '公众平台官网链接',
96 | // Description: '公众平台官网链接',
97 | // Url: 'http://1024.com/',
98 | // MsgId: '5837397520665436492' }
99 | // TODO
100 | }).link(function(message, req, res, next) {
101 | // message为链接内容
102 | // { ToUserName: 'gh_d3e07d51b513',
103 | // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',
104 | // CreateTime: '1359125022',
105 | // MsgType: 'link',
106 | // Title: '公众平台官网链接',
107 | // Description: '公众平台官网链接',
108 | // Url: 'http://1024.com/',
109 | // MsgId: '5837397520665436492' }
110 | // TODO
111 | }).event(function(message, req, res, next) {
112 | // message为事件内容
113 | // { ToUserName: 'gh_d3e07d51b513',
114 | // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',
115 | // CreateTime: '1359125022',
116 | // MsgType: 'event',
117 | // Event: 'LOCATION',
118 | // Latitude: '23.137466',
119 | // Longitude: '113.352425',
120 | // Precision: '119.385040',
121 | // MsgId: '5837397520665436492' }
122 | // TODO
123 | }).device_text(function(message, req, res, next) {
124 | // message为设备文本消息内容
125 | // { ToUserName: 'gh_d3e07d51b513',
126 | // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',
127 | // CreateTime: '1359125022',
128 | // MsgType: 'device_text',
129 | // DeviceType: 'gh_d3e07d51b513'
130 | // DeviceID: 'dev1234abcd',
131 | // Content: 'd2hvc3lvdXJkYWRkeQ==',
132 | // SessionID: '9394',
133 | // MsgId: '5837397520665436492',
134 | // OpenID: 'oPKu7jgOibOA-De4u8J2RuNKpZRw' }
135 | // TODO
136 | }).device_event(function(message, req, res, next) {
137 | // message为设备事件内容
138 | // { ToUserName: 'gh_d3e07d51b513',
139 | // FromUserName: 'oPKu7jgOibOA-De4u8J2RuNKpZRw',
140 | // CreateTime: '1359125022',
141 | // MsgType: 'device_event',
142 | // Event: 'bind'
143 | // DeviceType: 'gh_d3e07d51b513'
144 | // DeviceID: 'dev1234abcd',
145 | // OpType : 0, //Event为subscribe_status/unsubscribe_status时存在
146 | // Content: 'd2hvc3lvdXJkYWRkeQ==', //Event不为subscribe_status/unsubscribe_status时存在
147 | // SessionID: '9394',
148 | // MsgId: '5837397520665436492',
149 | // OpenID: 'oPKu7jgOibOA-De4u8J2RuNKpZRw' }
150 | // TODO
151 | }).middlewarify());
152 |
153 | module.exports = router;
154 |
--------------------------------------------------------------------------------
/server-cluster.js:
--------------------------------------------------------------------------------
1 | var cluster = require('cluster');
2 |
3 | // 进程数量建议设置为可用的 CPU 数量
4 | var workers = process.env.LEANCLOUD_AVAILABLE_CPUS || 1;
5 |
6 | if (cluster.isMaster) {
7 | for (var i = 0; i < workers; i++) {
8 | cluster.fork();
9 | }
10 |
11 | cluster.on('exit', (worker, code, signal) => {
12 | console.log('worker %s died, restarting...', worker.process.pid);
13 | cluster.fork();
14 | });
15 | } else {
16 | require('./server.js');
17 | }
18 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var AV = require('leanengine');
3 |
4 | AV.init({
5 | appId: process.env.LEANCLOUD_APP_ID,
6 | appKey: process.env.LEANCLOUD_APP_KEY,
7 | masterKey: process.env.LEANCLOUD_APP_MASTER_KEY
8 | });
9 |
10 | // 如果不希望使用 masterKey 权限,可以将下面一行删除
11 | AV.Cloud.useMasterKey();
12 |
13 | var app = require('./app');
14 |
15 | // 端口一定要从环境变量 `LEANCLOUD_APP_PORT` 中获取。
16 | // LeanEngine 运行时会分配端口并赋值到该变量。
17 | var PORT = parseInt(process.env.LEANCLOUD_APP_PORT || process.env.PORT || 3000);
18 |
19 | app.listen(PORT, function (err) {
20 | console.log('Node app is running on port:', PORT);
21 |
22 | // 注册全局未捕获异常处理器
23 | process.on('uncaughtException', function(err) {
24 | console.error("Caught exception:", err.stack);
25 | });
26 | process.on('unhandledRejection', function(reason, p) {
27 | console.error("Unhandled Rejection at: Promise ", p, " reason: ", reason.stack);
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/views/error.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Error
5 |
6 |
7 |
8 | <%= message %>
9 | <%= error.status %>
10 | <%= error.stack %>
11 |
12 |
13 |
--------------------------------------------------------------------------------
/views/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | LeanEngine
5 |
6 |
7 |
8 | LeanEngine
9 | 这是 LeanEngine 微信自动问答机器人的演示项目。
10 |
11 |
12 |
--------------------------------------------------------------------------------
/views/todos.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Todo
5 |
6 |
7 |
8 | <%= title %>
9 |
13 |
14 | <% for(var i=0; i
15 | - <%= todos[i].get('content') %>
16 | <% } %>
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------