@resultcrafter/aimanager-instagram-connector 0.2.1 → 0.3.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/index.js +127 -99
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -513,11 +513,31 @@ router.post('/tiledesk', async (req, res) => {
|
|
|
513
513
|
return res.status(200).send({ message: "sent" });
|
|
514
514
|
})
|
|
515
515
|
|
|
516
|
+
function convertTestWebhook(body) {
|
|
517
|
+
if (body.field && body.value) {
|
|
518
|
+
winston.info("(ig) Converting test webhook from Meta dashboard");
|
|
519
|
+
var messaging = [];
|
|
520
|
+
if (body.value && body.value.messages && body.value.messages.data) {
|
|
521
|
+
body.value.messages.data.forEach(function(msg) {
|
|
522
|
+
messaging.push({
|
|
523
|
+
sender: { id: msg.from ? msg.from.id : 'test_sender' },
|
|
524
|
+
message: { text: msg.text || '[Test message from Meta]' }
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
object: 'instagram',
|
|
530
|
+
entry: [{ id: 'test_entry', messaging: messaging }]
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
|
|
516
536
|
function verifyWebhookSignature(req) {
|
|
517
537
|
var signature = req.headers['x-hub-signature-256'];
|
|
518
538
|
if (!signature) {
|
|
519
|
-
winston.
|
|
520
|
-
return
|
|
539
|
+
winston.warn("(ig) Missing x-hub-signature-256 header — processing anyway");
|
|
540
|
+
return true;
|
|
521
541
|
}
|
|
522
542
|
if (!APP_SECRET) {
|
|
523
543
|
winston.warn("(ig) APP_SECRET not configured — skipping signature validation");
|
|
@@ -528,7 +548,7 @@ function verifyWebhookSignature(req) {
|
|
|
528
548
|
var hmac = crypto.createHmac(algo, APP_SECRET);
|
|
529
549
|
hmac.update(JSON.stringify(req.body));
|
|
530
550
|
var computed = hmac.digest('hex');
|
|
531
|
-
var valid =
|
|
551
|
+
var valid = (expected === computed);
|
|
532
552
|
if (!valid) {
|
|
533
553
|
winston.error("(ig) Webhook signature mismatch — expected=" + expected + " computed=" + computed);
|
|
534
554
|
if (process.env.DEBUG_SIGNATURES === 'true') {
|
|
@@ -538,12 +558,11 @@ function verifyWebhookSignature(req) {
|
|
|
538
558
|
return valid;
|
|
539
559
|
}
|
|
540
560
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
winston.verbose("(ig) Webhook received");
|
|
561
|
+
function processInstagramWebhook(req, res) {
|
|
562
|
+
winston.verbose("(ig) Processing Instagram webhook");
|
|
544
563
|
|
|
545
564
|
if (!verifyWebhookSignature(req)) {
|
|
546
|
-
|
|
565
|
+
winston.warn("(ig) Signature invalid — still responding 200 to keep webhook healthy");
|
|
547
566
|
}
|
|
548
567
|
|
|
549
568
|
res.status(200).send('EVENT_RECEIVED');
|
|
@@ -553,130 +572,139 @@ router.post('/webhookFB', async (req, res) => {
|
|
|
553
572
|
let body = req.body;
|
|
554
573
|
winston.verbose("(ig) Processing webhook asynchronously");
|
|
555
574
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
winston.debug("(fbm) Facebook page not enabled --> Skip")
|
|
562
|
-
return res.status(200).send('EVENT_RECEIVED');
|
|
563
|
-
}
|
|
575
|
+
var testBody = convertTestWebhook(body);
|
|
576
|
+
if (testBody) {
|
|
577
|
+
winston.info("(ig) Using converted test webhook body");
|
|
578
|
+
body = testBody;
|
|
579
|
+
}
|
|
564
580
|
|
|
565
|
-
|
|
566
|
-
|
|
581
|
+
if (body.object !== 'instagram' && body.object !== 'page') {
|
|
582
|
+
winston.debug("(ig) Unknown webhook object: " + body.object + " — skipping");
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
567
585
|
|
|
568
|
-
|
|
569
|
-
|
|
586
|
+
body.entry.forEach(async (entry) => {
|
|
587
|
+
if (entry.changes) {
|
|
588
|
+
winston.verbose("(ig) Non-message event (changes): " + JSON.stringify(entry.changes).substring(0, 200));
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
570
591
|
|
|
571
|
-
|
|
592
|
+
if (!entry.messaging || !entry.messaging[0]) {
|
|
593
|
+
winston.verbose("(ig) Entry has no messaging events — skipping");
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
572
596
|
|
|
573
|
-
|
|
574
|
-
|
|
597
|
+
var entryId = entry.id;
|
|
598
|
+
winston.debug("(ig) Entry ID: " + entryId);
|
|
575
599
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
const fbClient = new FacebookClient({ GRAPH_URL: GRAPH_URL, FB_APP_ID: FB_APP_ID, APP_SECRET: APP_SECRET, BASE_URL: BASE_URL });
|
|
600
|
+
var PAGE_KEY = "instagram-page-" + entryId;
|
|
601
|
+
var info_settings = await db.get(PAGE_KEY);
|
|
579
602
|
|
|
580
|
-
|
|
603
|
+
if (!info_settings) {
|
|
604
|
+
winston.debug("(ig) No settings found for entry " + entryId + " — checking project by scanning");
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
581
607
|
|
|
582
|
-
|
|
583
|
-
winston.
|
|
584
|
-
return { first_name: "User", last_name: instagramChannelMessage.sender.id };
|
|
585
|
-
});
|
|
608
|
+
var project_id = info_settings.project_id;
|
|
609
|
+
winston.debug("(ig) project_id: " + project_id);
|
|
586
610
|
|
|
587
|
-
|
|
611
|
+
let CONTENT_KEY = "instagram-" + project_id;
|
|
612
|
+
let settings = await db.get(CONTENT_KEY);
|
|
588
613
|
|
|
589
|
-
|
|
590
|
-
|
|
614
|
+
var instagramChannelMessage = entry.messaging[0];
|
|
615
|
+
winston.debug("(ig) webhook_event: ", instagramChannelMessage);
|
|
591
616
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
page_id: page_id,
|
|
596
|
-
sender_id: instagramChannelMessage.sender.id,
|
|
597
|
-
firstname: user_info.first_name,
|
|
598
|
-
lastname: user_info.last_name
|
|
599
|
-
}
|
|
600
|
-
}
|
|
617
|
+
const tlr = new TiledeskInstagramTranslator();
|
|
618
|
+
const tdChannel = new TiledeskChannel({ settings: settings, API_URL: API_URL });
|
|
619
|
+
const fbClient = new FacebookClient({ GRAPH_URL: GRAPH_URL, FB_APP_ID: FB_APP_ID, APP_SECRET: APP_SECRET, BASE_URL: BASE_URL });
|
|
601
620
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
instagramChannelMessage.message.attachments.length > 1) {
|
|
621
|
+
const page = settings && settings.pages ? settings.pages.find(p => p.id === entryId) : null;
|
|
622
|
+
var pageAccessToken = page ? page.access_token : null;
|
|
605
623
|
|
|
606
|
-
|
|
607
|
-
|
|
624
|
+
if (!pageAccessToken && settings && settings.ig_oauth_token) {
|
|
625
|
+
pageAccessToken = settings.ig_oauth_token.access_token;
|
|
626
|
+
}
|
|
608
627
|
|
|
609
|
-
|
|
628
|
+
var user_info = { first_name: "User", last_name: instagramChannelMessage.sender.id };
|
|
629
|
+
if (pageAccessToken) {
|
|
630
|
+
user_info = await fbClient.getUserInfo(pageAccessToken, instagramChannelMessage.sender.id).catch(function(err) {
|
|
631
|
+
winston.error("(ig) getUserInfo error: ", err?.response?.data || err.message || err);
|
|
632
|
+
return { first_name: "User", last_name: instagramChannelMessage.sender.id };
|
|
633
|
+
});
|
|
634
|
+
}
|
|
610
635
|
|
|
611
|
-
|
|
612
|
-
winston.verbose("(fbm) tiledeskJsonMessage: ", tiledeskJsonMessage);
|
|
636
|
+
instagramChannelMessage.sender.fullname = user_info.first_name + " " + user_info.last_name;
|
|
613
637
|
|
|
638
|
+
var message_info = {
|
|
639
|
+
channel: TiledeskInstagramTranslator.CHANNEL_NAME,
|
|
640
|
+
instagram: {
|
|
641
|
+
page_id: entryId,
|
|
642
|
+
sender_id: instagramChannelMessage.sender.id,
|
|
643
|
+
firstname: user_info.first_name,
|
|
644
|
+
lastname: user_info.last_name
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
if (instagramChannelMessage.message &&
|
|
649
|
+
instagramChannelMessage.message.attachments &&
|
|
650
|
+
instagramChannelMessage.message.attachments.length > 1) {
|
|
651
|
+
|
|
652
|
+
const messageHandler = new MessageHandler();
|
|
653
|
+
let messagesList = await messageHandler.splitMessageFromInstagram(instagramChannelMessage);
|
|
654
|
+
|
|
655
|
+
for (let message of messagesList) {
|
|
656
|
+
let tiledeskJsonMessage = await tlr.toTiledesk(message);
|
|
657
|
+
if (tiledeskJsonMessage) {
|
|
658
|
+
try {
|
|
659
|
+
await tdChannel.send(tiledeskJsonMessage, message_info, settings.department_id);
|
|
660
|
+
} catch (err) {
|
|
661
|
+
winston.error("(ig) Error sending split message: ", err);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
} else {
|
|
666
|
+
let tiledeskJsonMessage = await tlr.toTiledesk(instagramChannelMessage);
|
|
614
667
|
if (tiledeskJsonMessage) {
|
|
615
668
|
try {
|
|
616
669
|
await tdChannel.send(tiledeskJsonMessage, message_info, settings.department_id);
|
|
617
|
-
winston.verbose("(fbm) Message sent to AI Manager")
|
|
618
670
|
} catch (err) {
|
|
619
|
-
|
|
671
|
+
winston.error("(ig) Error sending message: ", err);
|
|
620
672
|
}
|
|
621
|
-
} else {
|
|
622
|
-
winston.verbose("(fbm) tiledeskJsonMessage is undefined!")
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
} else {
|
|
627
|
-
let tiledeskJsonMessage = await tlr.toTiledesk(instagramChannelMessage);
|
|
628
|
-
winston.verbose("(fbm) tiledeskJsonMessage: ", tiledeskJsonMessage);
|
|
629
|
-
|
|
630
|
-
if (tiledeskJsonMessage) {
|
|
631
|
-
try {
|
|
632
|
-
await tdChannel.send(tiledeskJsonMessage, message_info, settings.department_id);
|
|
633
|
-
winston.verbose("(fbm) Message sent to AI Manager")
|
|
634
|
-
} catch (err) {
|
|
635
|
-
return res.status(500).send({ success: false, error: "Error sending message" });
|
|
636
673
|
}
|
|
637
|
-
} else {
|
|
638
|
-
winston.verbose("(fbm) tiledeskJsonMessage is undefined!")
|
|
639
674
|
}
|
|
640
|
-
}
|
|
675
|
+
});
|
|
641
676
|
|
|
642
|
-
})
|
|
643
677
|
} catch (err) {
|
|
644
678
|
winston.error("(ig) Async webhook processing error: ", err?.response?.data || err.message || err);
|
|
645
679
|
}
|
|
646
680
|
});
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
router.get('/webhookFB', async (req, res) => {
|
|
650
|
-
|
|
651
|
-
winston.verbose("(fbm) Verify the webhook... ", req.query);
|
|
652
|
-
|
|
653
|
-
// Parse the query params
|
|
654
|
-
let mode = req.query['hub.mode'];
|
|
655
|
-
let token = req.query['hub.verify_token'];
|
|
656
|
-
let challenge = req.query['hub.challenge'];
|
|
657
|
-
|
|
658
|
-
winston.verbose("(fbm) token: " + token);
|
|
659
|
-
winston.verbose("(fbm) verify token: " + VERIFY_TOKEN);
|
|
660
|
-
winston.verbose("(fbm) mode: " + mode)
|
|
661
|
-
winston.verbose("(fbm) challenge: " + challenge)
|
|
662
|
-
|
|
663
|
-
// Checks if a token and mode is in the query string of the request
|
|
664
|
-
if (mode && token) {
|
|
681
|
+
}
|
|
665
682
|
|
|
666
|
-
|
|
667
|
-
|
|
683
|
+
router.post('/webhookIG', function(req, res) {
|
|
684
|
+
winston.verbose("(ig) POST /webhookIG");
|
|
685
|
+
processInstagramWebhook(req, res);
|
|
686
|
+
});
|
|
668
687
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
688
|
+
router.post('/webhookFB', function(req, res) {
|
|
689
|
+
winston.verbose("(ig) POST /webhookFB");
|
|
690
|
+
processInstagramWebhook(req, res);
|
|
691
|
+
});
|
|
672
692
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
693
|
+
function handleWebhookVerify(req, res) {
|
|
694
|
+
winston.verbose("(ig) Verify webhook... ", req.query);
|
|
695
|
+
var mode = req.query['hub.mode'];
|
|
696
|
+
var token = req.query['hub.verify_token'];
|
|
697
|
+
var challenge = req.query['hub.challenge'];
|
|
698
|
+
if (mode && token && mode === 'subscribe' && token === VERIFY_TOKEN) {
|
|
699
|
+
winston.info("(ig) Webhook verified");
|
|
700
|
+
return res.status(200).send(challenge);
|
|
677
701
|
}
|
|
702
|
+
winston.warn("(ig) Webhook verify failed — mode=" + mode + " token match=" + (token === VERIFY_TOKEN));
|
|
678
703
|
return res.sendStatus(403);
|
|
679
|
-
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
router.get('/webhookIG', function(req, res) { handleWebhookVerify(req, res); });
|
|
707
|
+
router.get('/webhookFB', function(req, res) { handleWebhookVerify(req, res); });
|
|
680
708
|
|
|
681
709
|
router.get('/auth/instagram/start', async (req, res) => {
|
|
682
710
|
winston.verbose("(ig) /auth/instagram/start");
|