-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.js
255 lines (229 loc) · 7.89 KB
/
app.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/**
* Copyright 2021-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* Messenger For Original Coast Clothing
* https://developers.facebook.com/docs/messenger-platform/getting-started/sample-apps/original-coast-clothing
*/
"use strict";
// Import dependencies and set up http server
const express = require("express"),
{ urlencoded, json } = require("body-parser"),
crypto = require("crypto"),
path = require("path"),
Receive = require("./services/receive"),
GraphApi = require("./services/graph-api"),
User = require("./services/user"),
config = require("./services/config"),
i18n = require("./i18n.config"),
app = express();
//const language = require('@google-cloud/language');
var users = {};
// Parse application/x-www-form-urlencoded
app.use(
urlencoded({
extended: true
})
);
// Parse application/json. Verify that callback came from Facebook
app.use(json({ verify: verifyRequestSignature }));
// Serving static files in Express
app.use(express.static(path.join(path.resolve(), "public")));
// Set template engine in Express
app.set("view engine", "ejs");
// Respond with index file when a GET request is made to the homepage
app.get("/", function(_req, res) {
res.render("index");
});
// Add support for GET requests to our webhook
app.get("/webhook", (req, res) => {
// Parse the query params
let mode = req.query["hub.mode"];
let token = req.query["hub.verify_token"];
let challenge = req.query["hub.challenge"];
// Check if a token and mode is in the query string of the request
if (mode && token) {
// Check the mode and token sent is correct
if (mode === "subscribe" && token === config.verifyToken) {
// Respond with the challenge token from the request
console.log("WEBHOOK_VERIFIED");
res.status(200).send(challenge);
} else {
// Respond with '403 Forbidden' if verify tokens do not match
res.sendStatus(403);
}
}
});
// Create the endpoint for your webhook
app.post("/webhook", (req, res) => {
let body = req.body;
console.log(`\u{1F7EA} Received webhook:`);
console.dir(body, { depth: null });
// Check if this is an event from a page subscription
if (body.object === "page") {
// Returns a '200 OK' response to all requests
res.status(200).send("EVENT_RECEIVED");
// Iterate over each entry - there may be multiple if batched
body.entry.forEach(async function(entry) {
if ("changes" in entry) {
// Handle Page Changes event
let receiveMessage = new Receive();
if (entry.changes[0].field === "feed") {
let change = entry.changes[0].value;
switch (change.item) {
case "post":
return receiveMessage.handlePrivateReply(
"post_id",
change.post_id
);
case "comment":
return receiveMessage.handlePrivateReply(
"comment_id",
change.comment_id
);
default:
console.warn("Unsupported feed change type.");
return;
}
}
}
// Iterate over webhook events - there may be multiple
entry.messaging.forEach(async function(webhookEvent) {
// Discard uninteresting events
if ("read" in webhookEvent) {
console.log("Got a read event");
return;
} else if ("delivery" in webhookEvent) {
console.log("Got a delivery event");
return;
}
// Get the sender PSID
let senderPsid = webhookEvent.sender.id;
if (!(senderPsid in users)) {
// First time seeing this user
let user = new User(senderPsid);
let userProfile = await GraphApi.getUserProfile(senderPsid);
if (userProfile) {
user.setProfile(userProfile);
users[senderPsid] = user;
console.log(`Created new user profile:`);
console.log({ user });
}
}
i18n.setLocale(users[senderPsid].locale);
let receiveMessage = new Receive(users[senderPsid], webhookEvent);
return receiveMessage.handleMessage();
});
});
} else {
// Return a '404 Not Found' if event is not from a page subscription
res.sendStatus(404);
}
});
// Set up your App's Messenger Profile
app.get("/profile", (req, res) => {
let token = req.query["verify_token"];
let mode = req.query["mode"];
if (!config.webhookUrl.startsWith("https://")) {
res.status(200).send("ERROR - Need a proper API_URL in the .env file");
}
var Profile = require("./services/profile.js");
Profile = new Profile();
// Check if a token and mode is in the query string of the request
if (mode && token) {
if (token === config.verifyToken) {
if (mode == "webhook" || mode == "all") {
Profile.setWebhook();
res.write(
`<p>✅ Set app ${config.appId} call to ${config.webhookUrl}</p>`
);
}
if (mode == "profile" || mode == "all") {
Profile.setThread();
res.write(
`<p>✅ Set Messenger Profile of Page ${config.pageId}</p>`
);
}
if (mode == "personas" || mode == "all") {
Profile.setPersonas();
res.write(`<p>✅ Set Personas for ${config.appId}</p>`);
res.write(
"<p>Note: To persist the personas, add the following variables \
to your environment variables:</p>"
);
res.write("<ul>");
res.write(`<li>PERSONA_BILLING = ${config.personaBilling.id}</li>`);
res.write(`<li>PERSONA_CARE = ${config.personaCare.id}</li>`);
res.write(`<li>PERSONA_ORDER = ${config.personaOrder.id}</li>`);
res.write(`<li>PERSONA_SALES = ${config.personaSales.id}</li>`);
res.write("</ul>");
}
if (mode == "nlp" || mode == "all") {
GraphApi.callNLPConfigsAPI();
res.write(
`<p>✅ Enabled Built-in NLP for Page ${config.pageId}</p>`
);
}
if (mode == "domains" || mode == "all") {
Profile.setWhitelistedDomains();
res.write(
`<p>✅ Whitelisted domains: ${config.whitelistedDomains}</p>`
);
}
if (mode == "private-reply") {
Profile.setPageFeedWebhook();
res.write(`<p>✅ Set Page Feed Webhook for Private Replies.</p>`);
}
res.status(200).end();
} else {
// Responds with '403 Forbidden' if verify tokens do not match
res.sendStatus(403);
}
} else {
// Returns a '404 Not Found' if mode or token are missing
res.sendStatus(404);
}
});
// Verify that the callback came from Facebook.
function verifyRequestSignature(req, res, buf) {
var signature = req.headers["x-hub-signature"];
if (!signature) {
console.warn(`Couldn't find "x-hub-signature" in headers.`);
} else {
var elements = signature.split("=");
var signatureHash = elements[1];
var expectedHash = crypto
.createHmac("sha1", config.appSecret)
.update(buf)
.digest("hex");
if (signatureHash != expectedHash) {
throw new Error("Couldn't validate the request signature.");
}
}
}
// Check if all environment variables are set
config.checkEnvVariables();
// Listen for requests :)
var listener = app.listen(config.port, function() {
console.log(`The app is listening on port ${listener.address().port}`);
if (
Object.keys(config.personas).length == 0 &&
config.appUrl &&
config.verifyToken
) {
console.log(
"Is this the first time running?\n" +
"Make sure to set the both the Messenger profile, persona " +
"and webhook by visiting:\n" +
config.appUrl +
"/profile?mode=all&verify_token=" +
config.verifyToken
);
}
if (config.pageId) {
console.log("Test your app by messaging:");
console.log(`https://m.me/${config.pageId}`);
}
});