Skip to content

Commit c11947e

Browse files
committed
working boilerplate
1 parent a62bd55 commit c11947e

26 files changed

+1134
-0
lines changed

.gitignore

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
lib-cov
2+
*.seed
3+
*.log
4+
*.csv
5+
*.dat
6+
*.out
7+
*.pid
8+
*.gz
9+
10+
.idea
11+
.tmp
12+
pids
13+
logs
14+
thumbs.db
15+
dump.rdb
16+
17+
.DS_Store
18+
npm-debug.log
19+
node_modules

actions/sample.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
const GraphAPI = require('../graphAPI');
4+
const sessionStore = require('../sessionStore');
5+
const debug = require('debug')('cbp:actions:sample');
6+
7+
8+
module.exports = function({sessionId, context, text, entities}) {
9+
10+
return sessionStore.get(sessionId)
11+
.then(session => {
12+
const recipientId = session.fbid;
13+
debug(`Session ${sessionId} received ${text}`);
14+
debug(`The current context is ${JSON.stringify(context)}`);
15+
debug(`Wit extracted ${JSON.stringify(entities)}`);
16+
17+
return GraphAPI.sendPlainMessage(recipientId, 'you got this');
18+
})
19+
.then(function() {
20+
return context;
21+
});
22+
}

app.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
3+
const debug = require('debug')('cbp:app');
4+
const express = require('express');
5+
const logger = require('morgan');
6+
const cookieParser = require('cookie-parser');
7+
const bodyParser = require('body-parser');
8+
9+
debug('loading configuration');
10+
const config = require('./config');
11+
require('./init')(config);
12+
13+
const app = express();
14+
15+
app.enable('trust proxy');
16+
app.set('port', process.env.PORT || 3000);
17+
18+
if (app.get('env') !== 'testing') {
19+
app.use(logger('dev'));
20+
}
21+
22+
app.use(bodyParser.json({limit: '10mb'}));
23+
app.use(bodyParser.urlencoded({ extended: true }));
24+
app.use(cookieParser());
25+
26+
27+
//Bot routes
28+
const botRoutes = require('./routes');
29+
30+
app.get('/bot', botRoutes.get);
31+
app.post('/bot', botRoutes.receive);
32+
33+
34+
const server = app.listen(app.get('port'), function () {
35+
console.log('express server listening on port ' + server.address().port);
36+
});
37+

config/config.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
3+
module.exports = {
4+
shared: {
5+
},
6+
7+
development: {
8+
redis: {
9+
port: 6379,
10+
host: 'localhost',
11+
pass: '',
12+
db: 1
13+
},
14+
db: 'mongodb://localhost/chatbotdb',
15+
16+
fbPageToken: '',
17+
fbPageID: '',
18+
fbWebhookVerifyToken: '',
19+
witToken: ''
20+
},
21+
22+
production: {
23+
24+
}
25+
}

config/index.js

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
'use strict';
2+
3+
/**
4+
* The Module Dynamically loads the configurations for
5+
* the heroku deployed project. This way of managing the configuration
6+
* is done because of the heroku suggestion for
7+
* Multiple Environments for the App article.
8+
*/
9+
10+
const url = require('url');
11+
12+
13+
/**
14+
* Returns the Redis config object for the staging,
15+
* testing and production servers
16+
* @returns {{port: *, host: (*|string), pass: *}}
17+
* @private
18+
*/
19+
function redisConfig() {
20+
if (!process.env.REDISCLOUD_URL || process.env.REDISCLOUD_URL === 'undefined') {
21+
return null;
22+
}
23+
var redisURL = url.parse(process.env.REDISCLOUD_URL);
24+
return {
25+
port: redisURL.port,
26+
host: redisURL.hostname,
27+
pass: redisURL.auth.split(':')[1]
28+
};
29+
}
30+
31+
/**
32+
* Returns the mongo db config for the staging,
33+
* testing and production servers
34+
* @returns {*}
35+
* @private
36+
*/
37+
function mongoConfig() {
38+
return process.env.MONGOHQ_URL !== 'undefined' && process.env.MONGOHQ_URL ||
39+
process.env.MONGOLAB_URI !== 'undefined' && process.env.MONGOLAB_URI;
40+
}
41+
42+
43+
function mergeSharedConfigs(shared, config) {
44+
for (var key in shared) {
45+
config[key] = config[key] || shared[key];
46+
}
47+
48+
return config
49+
}
50+
51+
52+
/**
53+
* Creates a config object dynamically for the application.
54+
* @returns {*}
55+
* @private
56+
*/
57+
function createConfig() {
58+
const env = process.env.NODE_ENV || 'development';
59+
var config = require('./config');
60+
61+
config = mergeSharedConfigs(config.shared, config[env]);
62+
63+
config.fbPageToken = process.env.FB_VERIFY_TOKEN || config.fbPageToken;
64+
config.fbPageID = process.env.FB_PAGE_ID || config.fbPageID;
65+
config.fbWebhookVerifyToken = process.env.FB_WEBHOOK_VERIFY_TOKEN || config.fbWebhookVerifyToken;
66+
config.witToken = process.env.WIT_TOKEN || config.witToken;
67+
68+
config.redis = redisConfig() || config.redis;
69+
config.db = mongoConfig() || config.db;
70+
71+
return config;
72+
}
73+
74+
module.exports = createConfig();

graphAPI.js

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
'use strict';
2+
3+
const request = require('request-promise');
4+
const config = require('./config')
5+
const FB_PAGE_TOKEN = config.fbPageToken;
6+
const Q = require('q');
7+
const _ = require('lodash');
8+
9+
class GraphAPI {
10+
constructor() {
11+
this.api = request.defaults({
12+
uri: 'https://graph.facebook.com/v2.6/me/messages',
13+
method: 'POST',
14+
json: true,
15+
qs: { access_token: FB_PAGE_TOKEN },
16+
headers: {'Content-Type': 'application/json'},
17+
});
18+
}
19+
20+
sendTemplateMessage(recipientId, data) {
21+
const opts = {
22+
form: {
23+
recipient: {
24+
id: recipientId,
25+
},
26+
message: data,
27+
}
28+
};
29+
return this.api(opts);
30+
}
31+
32+
sendPlainMessage(recipientId, msg) {
33+
return this.sendTemplateMessage(recipientId, {text: msg});
34+
}
35+
36+
sendBulkMessages(recipientId, messages) {
37+
return messages.reduce((p, message) => {
38+
return p.then(() => {
39+
let delay = message.text && message.text.length * 10;
40+
return Q.delay(delay || 500)
41+
.then(() => {
42+
if (_.isString(message)) {
43+
return this.sendPlainMessage(recipientId, message);
44+
} else {
45+
return this.sendTemplateMessage(recipientId, message);
46+
}
47+
});
48+
});
49+
}, Q());
50+
}
51+
52+
sendTypingOn(recipientId) {
53+
return this._sendTyping(recipientId, 'typing_on');
54+
}
55+
56+
sendTypingOff(recipientId) {
57+
return this._sendTyping(recipientId, 'typing_off');
58+
}
59+
60+
_sendTyping(recipientId, action) {
61+
const opts = {
62+
form: {
63+
recipient: {
64+
id: recipientId,
65+
},
66+
sender_action: action
67+
}
68+
};
69+
return this.api(opts);
70+
}
71+
72+
getUserProfile(recipientId) {
73+
return request({
74+
method:'GET',
75+
url: 'https://graph.facebook.com/v2.6/' + recipientId,
76+
json: true,
77+
qs: {
78+
fields: 'first_name,last_name,locale,timezone,gender',
79+
access_token: FB_PAGE_TOKEN
80+
}
81+
})
82+
}
83+
}
84+
85+
86+
module.exports = new GraphAPI();

handlers/attachment.js

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use strict';
2+
3+
const sessionStore = require('../sessionStore');
4+
const GraphAPI = require('../graphAPI');
5+
const userService = require('../services/user');
6+
7+
const _ = require('lodash');
8+
const Q = require('q');
9+
10+
module.exports = function handler(sender, sessionId, context, atts) {
11+
12+
let promises = _.map(atts, att => {
13+
switch(att.type){
14+
case 'location':
15+
return processLocationData(sender, att);
16+
default:
17+
return GraphAPI.sendPlainMessage(sender,'Wow, cool pic! I\'ll keep this one ;)');
18+
}
19+
});
20+
21+
return Q.all(promises)
22+
.then(function() {
23+
return sessionStore.save(sessionId, context);
24+
});
25+
};
26+
27+
28+
function processLocationData(sender, attachment) {
29+
let location = {
30+
title: attachment.title,
31+
lat: attachment.payload.coordinates.lat,
32+
lon: attachment.payload.coordinates.long
33+
};
34+
35+
return userService.updateUserLocation(sender, location)
36+
.then(user => {
37+
return GraphAPI.sendPlainMessage(sender, 'Hey, thanks for sharing your location')
38+
});
39+
}

handlers/message.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
3+
const sessionStore = require('../sessionStore');
4+
const wit = require('../wit');
5+
6+
module.exports = function handleTextMessage (sessionId, context, msg) {
7+
context.message = msg;
8+
//console.log(context)
9+
wit.runActions(sessionId, msg, context, (error, context) => {
10+
if (error) {
11+
console.log('Oops! Got an error from Wit:', error);
12+
return;
13+
}
14+
15+
console.log('Waiting for futher messages.');
16+
17+
if (context['done']) {
18+
sessionStore.destroy(sessionId);
19+
}
20+
});
21+
};
22+
23+

handlers/postback.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use strict';
2+
3+
const GraphAPI = require('../graphAPI');
4+
const platformHelpers = require('../platformHelpers');
5+
6+
7+
module.exports = function handlePostback(sender, sessionId, context, payload) {
8+
let payloadTokens = payload.split(':');
9+
const action = payloadTokens[0];
10+
const data = payloadTokens[1];
11+
12+
switch(action) {
13+
case 'showSamples':
14+
return sendSamplesQuickReplies(sender);
15+
break;
16+
case 'somepostback':
17+
break;
18+
case 'getstarted':
19+
break;
20+
21+
}
22+
};
23+
24+
25+
function sendSamplesQuickReplies(sender) {
26+
const replies = {
27+
'sampleLocation': 'Send location',
28+
'sampleList': 'List',
29+
'sampleGenericCards': 'Generic cards'
30+
};
31+
let data = platformHelpers.generateQuickReplies('Explore Messenger Platform features', replies);
32+
return GraphAPI.sendTemplateMessage(sender, data);
33+
}

0 commit comments

Comments
 (0)