@resultcrafter/aimanager-instagram-connector 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/aimanager/FacebookClient.js +18 -6
- package/aimanager/TiledeskChannel.js +1 -1
- package/aimanager/TiledeskInstagramTranslator.js +7 -27
- package/index.js +126 -125
- package/package.json +1 -1
|
@@ -45,21 +45,33 @@ class FacebookClient {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
async send(message, page_access_token) {
|
|
48
|
-
|
|
49
48
|
winston.debug("(fbm) [FacebookClient] Sending message...");
|
|
50
|
-
|
|
51
49
|
return await axios({
|
|
52
50
|
url: this.graph_url + "me/messages?access_token=" + page_access_token,
|
|
53
|
-
header: {
|
|
54
|
-
'Content-Type': 'application/json'
|
|
55
|
-
},
|
|
51
|
+
header: { 'Content-Type': 'application/json' },
|
|
56
52
|
method: "POST",
|
|
57
53
|
data: message
|
|
58
54
|
}).then((response) => {
|
|
59
55
|
winston.debug("(fbm) [FacebookClient] Message sent!");
|
|
60
56
|
return response
|
|
61
57
|
}).catch((err) => {
|
|
62
|
-
winston.error("(fbm) [FacebookClient] error send message: ", err.response.
|
|
58
|
+
winston.error("(fbm) [FacebookClient] error send message: ", err.response?.data || err.message);
|
|
59
|
+
throw err;
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async sendToInstagram(message, accessToken) {
|
|
64
|
+
winston.debug("(ig) [FacebookClient] Sending to Instagram Graph API...");
|
|
65
|
+
return await axios({
|
|
66
|
+
url: "https://graph.instagram.com/v23.0/me/messages?access_token=" + accessToken,
|
|
67
|
+
header: { 'Content-Type': 'application/json' },
|
|
68
|
+
method: "POST",
|
|
69
|
+
data: message
|
|
70
|
+
}).then((response) => {
|
|
71
|
+
winston.debug("(ig) [FacebookClient] Instagram message sent!");
|
|
72
|
+
return response
|
|
73
|
+
}).catch((err) => {
|
|
74
|
+
winston.error("(ig) [FacebookClient] Instagram send error: ", err.response?.data || err.message);
|
|
63
75
|
throw err;
|
|
64
76
|
})
|
|
65
77
|
}
|
|
@@ -58,7 +58,7 @@ class TiledeskChannel {
|
|
|
58
58
|
|
|
59
59
|
} else if (messageInfo.channel == "instagram") {
|
|
60
60
|
channel = messageInfo.instagram;
|
|
61
|
-
new_request_id = "support-group-" + this.settings.project_id + "-" + uuidv4().substring(0, 8) + "-
|
|
61
|
+
new_request_id = "support-group-" + this.settings.project_id + "-" + uuidv4().substring(0, 8) + "-ig-" + channel.page_id + "-" + channel.sender_id;
|
|
62
62
|
|
|
63
63
|
} else {
|
|
64
64
|
winston.verbose("(fbm) [TiledeskChannel] Channel not supported");
|
|
@@ -50,7 +50,7 @@ const path = require('path');
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
let instagram_message = {
|
|
53
|
-
|
|
53
|
+
messaging_product: TiledeskInstagramTranslator.INSTAGRAM_MESSAGING_PRODUCT,
|
|
54
54
|
recipient: { id: instagram_receiver }
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -82,40 +82,20 @@ const path = require('path');
|
|
|
82
82
|
|
|
83
83
|
instagram_message.message = {
|
|
84
84
|
attachment: {
|
|
85
|
-
type: "
|
|
85
|
+
type: "video",
|
|
86
86
|
payload: {
|
|
87
|
-
|
|
88
|
-
elements: [
|
|
89
|
-
{
|
|
90
|
-
//media_type: "video",
|
|
91
|
-
url: tiledeskChannelMessage.metadata.src
|
|
92
|
-
//url: tiledeskChannelMessage.metadata.src
|
|
93
|
-
//url: // facebook url:
|
|
94
|
-
// https://developers.facebook.com/docs/instagram-platform/send-messages/template/media
|
|
95
|
-
// https://developers.facebook.com/docs/instagram-platform/reference/attachment-upload-api
|
|
96
|
-
}
|
|
97
|
-
]
|
|
87
|
+
url: tiledeskChannelMessage.metadata.src
|
|
98
88
|
}
|
|
99
89
|
}
|
|
100
90
|
}
|
|
101
|
-
|
|
102
|
-
/*
|
|
103
|
-
instagram_message.type = 'video'
|
|
104
|
-
instagram_message.video = {
|
|
105
|
-
link: tiledeskChannelMessage.metadata.src,
|
|
106
|
-
//caption: tiledeskChannelMessage.metadata.name || tiledeskChannelMessage.text
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
*/
|
|
110
91
|
}
|
|
111
92
|
|
|
112
93
|
else if (tiledeskChannelMessage.metadata.type && tiledeskChannelMessage.metadata.type.startsWith('application')) {
|
|
113
94
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
95
|
+
var docText = tiledeskChannelMessage.text || '';
|
|
96
|
+
if (tiledeskChannelMessage.metadata.name) docText += '\n' + tiledeskChannelMessage.metadata.name;
|
|
97
|
+
docText += '\n' + tiledeskChannelMessage.metadata.src;
|
|
98
|
+
instagram_message.message = { text: docText };
|
|
119
99
|
}
|
|
120
100
|
|
|
121
101
|
else {
|
package/index.js
CHANGED
|
@@ -328,17 +328,60 @@ router.post('/update_advanced', async (req, res) => {
|
|
|
328
328
|
})
|
|
329
329
|
})
|
|
330
330
|
|
|
331
|
+
function getOutboundToken(settings, entryId) {
|
|
332
|
+
if (settings && settings.pages && settings.pages.length > 0) {
|
|
333
|
+
var page = settings.pages.find(function(p) { return p.id === entryId; });
|
|
334
|
+
if (page && page.access_token) {
|
|
335
|
+
return { token: page.access_token, type: 'facebook_page' };
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (settings && settings.ig_oauth_token && settings.ig_oauth_token.access_token) {
|
|
339
|
+
return { token: settings.ig_oauth_token.access_token, type: 'instagram_oauth' };
|
|
340
|
+
}
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function parseRecipient(recipient_id) {
|
|
345
|
+
var result = { entry_id: null, ig_receiver: null, prefix: 'ig' };
|
|
346
|
+
if (!recipient_id) return result;
|
|
347
|
+
|
|
348
|
+
if (recipient_id.indexOf('ig-') > -1) {
|
|
349
|
+
result.entry_id = recipient_id.substring(recipient_id.lastIndexOf('ig-') + 3, recipient_id.lastIndexOf('-'));
|
|
350
|
+
result.ig_receiver = recipient_id.substring(recipient_id.lastIndexOf('-') + 1);
|
|
351
|
+
result.prefix = 'ig';
|
|
352
|
+
} else if (recipient_id.indexOf('fbm-') > -1) {
|
|
353
|
+
result.entry_id = recipient_id.substring(recipient_id.lastIndexOf('fbm-') + 4, recipient_id.lastIndexOf('-'));
|
|
354
|
+
result.ig_receiver = recipient_id.substring(recipient_id.lastIndexOf('-') + 1);
|
|
355
|
+
result.prefix = 'fbm';
|
|
356
|
+
}
|
|
357
|
+
return result;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async function sendInstagramMessage(instagramJsonMessage, accessToken, authType, fbClient) {
|
|
361
|
+
if (!isValidIGSID(instagramJsonMessage.recipient.id)) {
|
|
362
|
+
winston.error("(ig) Invalid IGSID format: " + instagramJsonMessage.recipient.id);
|
|
363
|
+
return { success: false, error: "Invalid IGSID format" };
|
|
364
|
+
}
|
|
365
|
+
if (authType === 'instagram_oauth') {
|
|
366
|
+
return await fbClient.sendToInstagram(instagramJsonMessage, accessToken);
|
|
367
|
+
}
|
|
368
|
+
return await fbClient.send(instagramJsonMessage, accessToken);
|
|
369
|
+
}
|
|
370
|
+
|
|
331
371
|
router.post('/tiledesk', async (req, res) => {
|
|
332
|
-
winston.verbose("(
|
|
333
|
-
winston.debug("(fbm) Message received from AI Manager body: ", req.body);
|
|
372
|
+
winston.verbose("(ig) Message received from AI Manager");
|
|
334
373
|
|
|
335
374
|
let tiledeskChannelMessage = req.body.payload;
|
|
336
375
|
let project_id = req.body.payload.id_project;
|
|
337
376
|
|
|
338
|
-
// get settings from mongo
|
|
339
377
|
let CONTENT_KEY = "instagram-" + project_id;
|
|
340
378
|
let settings = await db.get(CONTENT_KEY);
|
|
341
379
|
|
|
380
|
+
if (!settings) {
|
|
381
|
+
winston.warn("(ig) No settings found for project " + project_id);
|
|
382
|
+
return res.sendStatus(200);
|
|
383
|
+
}
|
|
384
|
+
|
|
342
385
|
var text = req.body.payload.text;
|
|
343
386
|
let sender_id = req.body.payload.sender;
|
|
344
387
|
let attributes = req.body.payload.attributes;
|
|
@@ -348,171 +391,129 @@ router.post('/tiledesk', async (req, res) => {
|
|
|
348
391
|
commands = attributes.commands;
|
|
349
392
|
}
|
|
350
393
|
|
|
351
|
-
if (sender_id.indexOf("fbm") > -1) {
|
|
352
|
-
winston.debug("(
|
|
394
|
+
if (sender_id && (sender_id.indexOf("fbm") > -1 || sender_id.indexOf("ig") > -1)) {
|
|
395
|
+
winston.debug("(ig) Skip same sender");
|
|
353
396
|
return res.sendStatus(200);
|
|
354
397
|
}
|
|
355
398
|
|
|
356
|
-
if (attributes && attributes.subtype === "info") {
|
|
357
|
-
winston.debug("(
|
|
358
|
-
return res.sendStatus(200);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
if (attributes && attributes.subtype === "private") {
|
|
362
|
-
winston.verbose("(wab) Skip subtype (private)");
|
|
363
|
-
return res.sendStatus(200);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
if (attributes && attributes.subtype === 'info/support') {
|
|
367
|
-
winston.debug("(fbm) Skip subtype: " + attributes.subtype);
|
|
399
|
+
if (attributes && (attributes.subtype === "info" || attributes.subtype === "private" || attributes.subtype === 'info/support')) {
|
|
400
|
+
winston.debug("(ig) Skip subtype: " + attributes.subtype);
|
|
368
401
|
return res.sendStatus(200);
|
|
369
402
|
}
|
|
370
403
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
let instagram_receiver = recipient_id.substring(recipient_id.lastIndexOf("-") + 1);
|
|
404
|
+
var parsed = parseRecipient(tiledeskChannelMessage.recipient);
|
|
405
|
+
var entry_id = parsed.entry_id;
|
|
406
|
+
var ig_receiver = parsed.ig_receiver;
|
|
375
407
|
|
|
376
|
-
winston.verbose("(
|
|
377
|
-
winston.verbose("(fbm) text: " + text);
|
|
378
|
-
winston.verbose("(fbm) attributes: ", attributes);
|
|
379
|
-
winston.verbose("(fbm) tiledesk sender_id: " + sender_id);
|
|
380
|
-
winston.verbose("(fbm) recipient_id: " + recipient_id);
|
|
381
|
-
winston.verbose("(fbm) page_id: " + page_id);
|
|
382
|
-
winston.verbose("(fbm) instagram_receiver: " + instagram_receiver);
|
|
408
|
+
winston.verbose("(ig) entry_id: " + entry_id + ", ig_receiver: " + ig_receiver + ", prefix: " + parsed.prefix);
|
|
383
409
|
|
|
384
410
|
// Return an info message option
|
|
385
|
-
if (settings.expired
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
winston.verbose("settings expired: " + settings.expired);
|
|
411
|
+
if (settings.expired === true) {
|
|
412
|
+
winston.verbose("(ig) Settings expired");
|
|
389
413
|
let tiledeskJsonMessage = {
|
|
390
414
|
text: 'Expired. Upgrade Plan.',
|
|
391
415
|
sender: "system",
|
|
392
416
|
senderFullname: "System",
|
|
393
|
-
attributes: {
|
|
394
|
-
subtype: 'info',
|
|
395
|
-
messagelabel: { key: 'PLAN_EXPIRED' } // for translation
|
|
396
|
-
},
|
|
417
|
+
attributes: { subtype: 'info', messagelabel: { key: 'PLAN_EXPIRED' } },
|
|
397
418
|
channel: { name: 'instagram' }
|
|
398
419
|
}
|
|
399
420
|
let message_info = {
|
|
400
421
|
channel: "instagram",
|
|
401
|
-
instagram: {
|
|
402
|
-
page_id: page_id,
|
|
403
|
-
sender_id: instagram_receiver
|
|
404
|
-
}
|
|
422
|
+
instagram: { page_id: entry_id, sender_id: ig_receiver }
|
|
405
423
|
}
|
|
406
|
-
|
|
407
424
|
const tdChannel = new TiledeskChannel({ settings: settings, API_URL: API_URL })
|
|
408
425
|
try {
|
|
409
426
|
await tdChannel.send(tiledeskJsonMessage, message_info, settings.department_id);
|
|
410
|
-
winston.verbose("(wab) Expiration message sent to AI Manager")
|
|
411
427
|
return res.sendStatus(200);
|
|
412
428
|
} catch (err) {
|
|
413
|
-
return res.status(500).send({ success: false, error: "Error sending
|
|
429
|
+
return res.status(500).send({ success: false, error: "Error sending expiration message" });
|
|
414
430
|
}
|
|
415
431
|
}
|
|
416
432
|
|
|
417
|
-
|
|
433
|
+
var tokenInfo = getOutboundToken(settings, entry_id);
|
|
434
|
+
if (!tokenInfo) {
|
|
435
|
+
winston.error("(ig) No access token available for outbound message (no pages, no OAuth)");
|
|
436
|
+
return res.status(200).send({ message: "skipped - no token" });
|
|
437
|
+
}
|
|
438
|
+
|
|
418
439
|
const fbClient = new FacebookClient({ GRAPH_URL: GRAPH_URL, FB_APP_ID: FB_APP_ID, APP_SECRET: APP_SECRET, BASE_URL: BASE_URL });
|
|
419
440
|
const messageHandler = new MessageHandler({ tiledeskChannelMessage: tiledeskChannelMessage });
|
|
420
441
|
const tlr = new TiledeskInstagramTranslator();
|
|
421
442
|
|
|
443
|
+
function sendMessageViaInstagram(jsonMsg, cb) {
|
|
444
|
+
sendInstagramMessage(jsonMsg, tokenInfo.token, tokenInfo.type, fbClient).then(function(response) {
|
|
445
|
+
winston.debug("(ig) Message sent via " + tokenInfo.type);
|
|
446
|
+
if (cb) cb(null, response);
|
|
447
|
+
}).catch(function(err) {
|
|
448
|
+
winston.error("(ig) Send error: ", err?.response?.data || err.message);
|
|
449
|
+
if (cb) cb(err);
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
422
453
|
if (commands) {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
// message
|
|
454
|
+
var cmdIdx = 0;
|
|
455
|
+
function executeCommand(command) {
|
|
426
456
|
if (command.type === "message") {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
winston.debug("(fbm) End of splitted message -> go to next command")
|
|
447
|
-
i += 1;
|
|
448
|
-
if (i < commands.length) {
|
|
449
|
-
execute(commands[i]);
|
|
457
|
+
messageHandler.generateMessageObject(command).then(function(tiledeskCommandMessage) {
|
|
458
|
+
messageHandler.splitMessageFromTiledesk(tiledeskCommandMessage).then(function(messagesList) {
|
|
459
|
+
var msgIdx = 0;
|
|
460
|
+
function sendNextMessage(msg) {
|
|
461
|
+
tlr.toInstagram(msg, ig_receiver).then(function(instagramJsonMessage) {
|
|
462
|
+
if (instagramJsonMessage) {
|
|
463
|
+
sendMessageViaInstagram(instagramJsonMessage, function(err) {
|
|
464
|
+
if (!err) {
|
|
465
|
+
msgIdx++;
|
|
466
|
+
if (msgIdx < messagesList.length) {
|
|
467
|
+
sendNextMessage(messagesList[msgIdx]);
|
|
468
|
+
} else {
|
|
469
|
+
cmdIdx++;
|
|
470
|
+
if (cmdIdx < commands.length) {
|
|
471
|
+
executeCommand(commands[cmdIdx]);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
});
|
|
450
476
|
} else {
|
|
451
|
-
|
|
477
|
+
cmdIdx++;
|
|
478
|
+
if (cmdIdx < commands.length) executeCommand(commands[cmdIdx]);
|
|
452
479
|
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
// wait
|
|
465
|
-
if (command.type === "wait") {
|
|
466
|
-
setTimeout(() => {
|
|
467
|
-
i += 1;
|
|
468
|
-
if (i < commands.length) {
|
|
469
|
-
execute(commands[i]);
|
|
470
|
-
} else {
|
|
471
|
-
winston.debug("(fbm) End of commands")
|
|
472
|
-
}
|
|
473
|
-
}, command.time)
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
sendNextMessage(messagesList[0]);
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
} else if (command.type === "wait") {
|
|
486
|
+
setTimeout(function() {
|
|
487
|
+
cmdIdx++;
|
|
488
|
+
if (cmdIdx < commands.length) executeCommand(commands[cmdIdx]);
|
|
489
|
+
}, command.time);
|
|
474
490
|
}
|
|
475
491
|
}
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
winston.verbose("(fbm) instagramJsonMessage", instagramJsonMessage)
|
|
488
|
-
|
|
489
|
-
if (instagramJsonMessage) {
|
|
490
|
-
fbClient.send(instagramJsonMessage, page_detail.access_token).then((response) => {
|
|
491
|
-
winston.verbose("(fbm) Message sent to Instagram!" + response.status + " " + response.statusText);
|
|
492
|
-
j += 1;
|
|
493
|
-
if (j < messagesList.length) {
|
|
494
|
-
send(messagesList[j])
|
|
495
|
-
} else {
|
|
496
|
-
winston.debug("(fbm) End of splitted message")
|
|
492
|
+
executeCommand(commands[0]);
|
|
493
|
+
} else if (tiledeskChannelMessage.text || tiledeskChannelMessage.metadata) {
|
|
494
|
+
messageHandler.splitMessageFromTiledesk(tiledeskChannelMessage).then(function(messagesList) {
|
|
495
|
+
var msgIdx = 0;
|
|
496
|
+
function sendNext(msg) {
|
|
497
|
+
tlr.toInstagram(msg, ig_receiver).then(function(instagramJsonMessage) {
|
|
498
|
+
if (instagramJsonMessage) {
|
|
499
|
+
sendMessageViaInstagram(instagramJsonMessage, function() {
|
|
500
|
+
msgIdx++;
|
|
501
|
+
if (msgIdx < messagesList.length) sendNext(messagesList[msgIdx]);
|
|
502
|
+
});
|
|
497
503
|
}
|
|
498
|
-
})
|
|
499
|
-
winston.error("(fbm) error send message: ", err)
|
|
500
|
-
})
|
|
501
|
-
} else {
|
|
502
|
-
winston.error("(fbm) instagramJsonMessage is undefined!")
|
|
504
|
+
});
|
|
503
505
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
else {
|
|
510
|
-
winston.debug("(fbm) no command, no text --> skip")
|
|
506
|
+
sendNext(messagesList[0]);
|
|
507
|
+
});
|
|
511
508
|
}
|
|
512
509
|
|
|
513
510
|
return res.status(200).send({ message: "sent" });
|
|
514
511
|
})
|
|
515
512
|
|
|
513
|
+
function isValidIGSID(id) {
|
|
514
|
+
return /^\d{10,20}$/.test(String(id));
|
|
515
|
+
}
|
|
516
|
+
|
|
516
517
|
function convertTestWebhook(body) {
|
|
517
518
|
if (body.field && body.value) {
|
|
518
519
|
winston.info("(ig) Converting test webhook from Meta dashboard");
|