├── .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 |
10 | 11 | 12 |
13 | 18 | 19 | 20 | --------------------------------------------------------------------------------