-
Notifications
You must be signed in to change notification settings - Fork 4
/
index.js
165 lines (142 loc) · 4.26 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
var util = require('util')
, events = require('events')
, bole = require('bole')
, merge = require('deeply')
, agnostic = require('agnostic')
// , cache = require('async-cache')
// , stringify = require('fast-safe-stringify')
, verify = require('./lib/verify_endpoint.js')
, send = require('./lib/send.js')
, middleware = require('./lib/middleware.js')
, inMiddleware = require('./incoming/index.js')
, outMiddleware = require('./outgoing/index.js')
, Traverse = require('./traverse/index.js')
, inTraverse = require('./traverse/incoming.js')
, outTraverse = require('./traverse/outgoing.js')
;
module.exports = Fbbot;
util.inherits(Fbbot, events.EventEmitter);
// defaults
Fbbot.defaults = {
bodyMaxLength: '1mb',
bodyEncoding : 'utf8',
timeout : 5000,
apiUrl : 'https://graph.facebook.com/v2.6/me/messages?access_token='
};
// expose logger
Fbbot.logger = bole;
// -- public methods
// registers middleware
Fbbot.prototype.use = middleware.use;
// send message to a user
Fbbot.prototype.send = send;
// add message types to the top level
// shouldn't be conflicts since types are all uppercase
util._extend(Fbbot.prototype, send.types);
// -- private methods
// no need to expose middleware handler as public api
Fbbot.prototype._run = middleware.run;
// verifies endpoint to facebook
Fbbot.prototype._verifyEndpoint = verify;
/**
* Fbbot instance constructor
*
* @this Fbbot#
* @param {object} options - list of customization parameters
* @constructor
*/
function Fbbot(options)
{
if (!(this instanceof Fbbot)) return new Fbbot(options);
/**
* Custom options per instance
* @type {object}
*/
this.options = merge(Fbbot.defaults, options || {});
/**
* Store credentials
* @type {object}
*/
this.credentials =
{
// keep simple naming for internal reference
token : this.options.pageAccessToken || this.options.token,
secret: this.options.verifyToken || this.options.secret
};
if (!this.credentials.token || !this.credentials.secret)
{
throw new Error('Both `token` (pageAccessToken) and `secret` (verifyToken) are required');
}
// compose apiUrl
this.options.apiUrl += this.credentials.token;
/**
* expose logger
* @type {object}
*/
this.logger = options.logger || bole(options.name || 'fbbot');
/**
* middleware storage (per event)
* @type {object}
* @private
*/
this._stack = {};
/**
* lock-in public methods
* wrap `_handler` with agnostic to accommodate different http servers
* @type {function}
*/
this.requestHandler = agnostic(this._handler.bind(this));
// attach lifecycle filters
inMiddleware(this);
outMiddleware(this);
/**
* create incoming traverse paths
* @type {Traverse}
* @private
*/
this._incoming = new Traverse(inTraverse.steps, {
entry : middleware.entryPoint,
middleware: inTraverse.middleware.bind(this),
emitter : inTraverse.emitter.bind(this),
prefix : inTraverse.prefix
});
// wrap linkParent method
this._incoming.linkParent = inTraverse.linkParent.bind(null, this._incoming.linkParent);
/**
* create outgoing traverse paths
* @type {Traverse}
* @private
*/
this._outgoing = new Traverse(outTraverse.steps, {
middleware: outTraverse.middleware.bind(this),
emitter : outTraverse.emitter.bind(this),
prefix : outTraverse.prefix
});
// wrap linkParent method
this._outgoing.linkParent = outTraverse.linkParent.bind(null, this._outgoing.linkParent);
}
/**
* HTTP requests handler, could be used as middleware
*
* @private
* @this Fbbot#
* @param {EventEmitter} request - incoming http request object
* @param {function} respond - http response function
*/
Fbbot.prototype._handler = function(request, respond)
{
this.logger.info(request);
// GET request handling
if (request.method == 'GET')
{
this._verifyEndpoint(request, respond);
return;
}
// as per facebook doc – respond as soon as non-humanly possible, always respond with 200 OK
// https://developers.facebook.com/docs/messenger-platform/webhook-reference#response
respond(200);
this._incoming.traverse(request.body, function(err, payload)
{
this.emit('end', err, payload);
}.bind(this));
};