@resultcrafter/aimanager-instagram-connector 0.2.1 → 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.
@@ -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.data);
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) + "-fbm-" + channel.page_id + "-" + channel.sender_id;
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
- //messaging_product: TiledeskInstagramTranslator.INSTAGRAM_MESSAGING_PRODUCT,
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: "template",
85
+ type: "video",
86
86
  payload: {
87
- template_type: "open_graph",
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
- instagram_message.type = 'document'
115
- instagram_message.document = {
116
- link: tiledeskChannelMessage.metadata.src,
117
- caption: tiledeskChannelMessage.metadata.name || tiledeskChannelMessage.text
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("(fbm) Message received from AI Manager");
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,176 +391,154 @@ 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("(fbm) Skip same sender");
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("(fbm) Skip subtype (info)");
358
- return res.sendStatus(200);
359
- }
360
-
361
- if (attributes && attributes.subtype === "private") {
362
- winston.verbose("(wab) Skip subtype (private)");
399
+ if (attributes && (attributes.subtype === "info" || attributes.subtype === "private" || attributes.subtype === 'info/support')) {
400
+ winston.debug("(ig) Skip subtype: " + attributes.subtype);
363
401
  return res.sendStatus(200);
364
402
  }
365
403
 
366
- if (attributes && attributes.subtype === 'info/support') {
367
- winston.debug("(fbm) Skip subtype: " + attributes.subtype);
368
- return res.sendStatus(200);
369
- }
404
+ var parsed = parseRecipient(tiledeskChannelMessage.recipient);
405
+ var entry_id = parsed.entry_id;
406
+ var ig_receiver = parsed.ig_receiver;
370
407
 
371
- let recipient_id = tiledeskChannelMessage.recipient;
372
- let sender = tiledeskChannelMessage.sender;
373
- let page_id = recipient_id.substring(recipient_id.lastIndexOf("fbm-") + 4, recipient_id.lastIndexOf("-"));
374
- let instagram_receiver = recipient_id.substring(recipient_id.lastIndexOf("-") + 1);
375
-
376
- winston.verbose("(fbm) sender: " + sender);
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
- settings.expired === true) {
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 exoìpiration message" });
429
+ return res.status(500).send({ success: false, error: "Error sending expiration message" });
414
430
  }
415
431
  }
416
432
 
417
- const page_detail = settings.pages.find(p => p.id === page_id);
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
- let i = 0;
424
- async function execute(command) {
425
- // message
454
+ var cmdIdx = 0;
455
+ function executeCommand(command) {
426
456
  if (command.type === "message") {
427
- let tiledeskCommandMessage = await messageHandler.generateMessageObject(command);
428
- winston.debug("(fbm) message generated from command: ", tiledeskCommandMessage)
429
-
430
-
431
- let messagesList = await messageHandler.splitMessageFromTiledesk(tiledeskCommandMessage);
432
-
433
- let j = 0;
434
- async function send(message) {
435
- let instagramJsonMessage = await tlr.toInstagram(message, instagram_receiver)
436
- winston.verbose("(fbm) instagramJsonMessage" + JSON.stringify(instagramJsonMessage));
437
-
438
-
439
- if (instagramJsonMessage) {
440
- fbClient.send(instagramJsonMessage, page_detail.access_token).then((response) => {
441
- winston.debug("(fbm) Message sent to Instagram!" + response.status + response.statusText);
442
- j += 1;
443
- if (j < messagesList.length) {
444
- send(messagesList[j])
445
- } else {
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
- winston.debug("(fbm) End of commands")
477
+ cmdIdx++;
478
+ if (cmdIdx < commands.length) executeCommand(commands[cmdIdx]);
452
479
  }
453
- }
454
- }).catch((err) => {
455
- winston.error("(fbm) error send message: ", err?.response?.data || err.message || err);
456
- });
457
- } else {
458
- winston.error("(fbm) instagramJsonMessage is undefined!")
459
- }
460
- }
461
- send(messagesList[0]);
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);
462
490
  }
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")
491
+ }
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
+ });
472
503
  }
473
- }, command.time)
504
+ });
474
505
  }
475
- }
476
- execute(commands[0]);
506
+ sendNext(messagesList[0]);
507
+ });
477
508
  }
478
509
 
479
- else if (tiledeskChannelMessage.text || tiledeskChannelMessage.metadata) {
480
-
481
- let messagesList = await messageHandler.splitMessageFromTiledesk(tiledeskChannelMessage);
482
- winston.debug("(fbm) message splitted in " + messagesList.length + " messages");
510
+ return res.status(200).send({ message: "sent" });
511
+ })
483
512
 
484
- let j = 0;
485
- async function send(message) {
486
- let instagramJsonMessage = await tlr.toInstagram(message, instagram_receiver);
487
- winston.verbose("(fbm) instagramJsonMessage", instagramJsonMessage)
513
+ function isValidIGSID(id) {
514
+ return /^\d{10,20}$/.test(String(id));
515
+ }
488
516
 
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")
497
- }
498
- }).catch((err) => {
499
- winston.error("(fbm) error send message: ", err)
500
- })
501
- } else {
502
- winston.error("(fbm) instagramJsonMessage is undefined!")
503
- }
517
+ function convertTestWebhook(body) {
518
+ if (body.field && body.value) {
519
+ winston.info("(ig) Converting test webhook from Meta dashboard");
520
+ var messaging = [];
521
+ if (body.value && body.value.messages && body.value.messages.data) {
522
+ body.value.messages.data.forEach(function(msg) {
523
+ messaging.push({
524
+ sender: { id: msg.from ? msg.from.id : 'test_sender' },
525
+ message: { text: msg.text || '[Test message from Meta]' }
526
+ });
527
+ });
504
528
  }
505
- send(messagesList[0]);
506
-
529
+ return {
530
+ object: 'instagram',
531
+ entry: [{ id: 'test_entry', messaging: messaging }]
532
+ };
507
533
  }
508
-
509
- else {
510
- winston.debug("(fbm) no command, no text --> skip")
511
- }
512
-
513
- return res.status(200).send({ message: "sent" });
514
- })
534
+ return null;
535
+ }
515
536
 
516
537
  function verifyWebhookSignature(req) {
517
538
  var signature = req.headers['x-hub-signature-256'];
518
539
  if (!signature) {
519
- winston.error("(ig) Missing x-hub-signature-256 header");
520
- return false;
540
+ winston.warn("(ig) Missing x-hub-signature-256 header — processing anyway");
541
+ return true;
521
542
  }
522
543
  if (!APP_SECRET) {
523
544
  winston.warn("(ig) APP_SECRET not configured — skipping signature validation");
@@ -528,7 +549,7 @@ function verifyWebhookSignature(req) {
528
549
  var hmac = crypto.createHmac(algo, APP_SECRET);
529
550
  hmac.update(JSON.stringify(req.body));
530
551
  var computed = hmac.digest('hex');
531
- var valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(computed));
552
+ var valid = (expected === computed);
532
553
  if (!valid) {
533
554
  winston.error("(ig) Webhook signature mismatch — expected=" + expected + " computed=" + computed);
534
555
  if (process.env.DEBUG_SIGNATURES === 'true') {
@@ -538,12 +559,11 @@ function verifyWebhookSignature(req) {
538
559
  return valid;
539
560
  }
540
561
 
541
- router.post('/webhookFB', async (req, res) => {
542
-
543
- winston.verbose("(ig) Webhook received");
562
+ function processInstagramWebhook(req, res) {
563
+ winston.verbose("(ig) Processing Instagram webhook");
544
564
 
545
565
  if (!verifyWebhookSignature(req)) {
546
- return res.sendStatus(403);
566
+ winston.warn("(ig) Signature invalid — still responding 200 to keep webhook healthy");
547
567
  }
548
568
 
549
569
  res.status(200).send('EVENT_RECEIVED');
@@ -553,130 +573,139 @@ router.post('/webhookFB', async (req, res) => {
553
573
  let body = req.body;
554
574
  winston.verbose("(ig) Processing webhook asynchronously");
555
575
 
556
- let page_id = body.entry[0].id;
557
- let PAGE_KEY = "instagram-page-" + page_id;
558
- let info_settings = await db.get(PAGE_KEY);
559
-
560
- if (!info_settings) {
561
- winston.debug("(fbm) Facebook page not enabled --> Skip")
562
- return res.status(200).send('EVENT_RECEIVED');
563
- }
576
+ var testBody = convertTestWebhook(body);
577
+ if (testBody) {
578
+ winston.info("(ig) Using converted test webhook body");
579
+ body = testBody;
580
+ }
564
581
 
565
- let project_id = info_settings.project_id;
566
- winston.debug("(fbm) project_id: " + project_id);
582
+ if (body.object !== 'instagram' && body.object !== 'page') {
583
+ winston.debug("(ig) Unknown webhook object: " + body.object + " — skipping");
584
+ return;
585
+ }
567
586
 
568
- let CONTENT_KEY = "instagram-" + project_id;
569
- let settings = await db.get(CONTENT_KEY);
587
+ body.entry.forEach(async (entry) => {
588
+ if (entry.changes) {
589
+ winston.verbose("(ig) Non-message event (changes): " + JSON.stringify(entry.changes).substring(0, 200));
590
+ return;
591
+ }
570
592
 
571
- body.entry.forEach(async (entry) => {
593
+ if (!entry.messaging || !entry.messaging[0]) {
594
+ winston.verbose("(ig) Entry has no messaging events — skipping");
595
+ return;
596
+ }
572
597
 
573
- let instagramChannelMessage = entry.messaging[0];
574
- winston.debug("(fbm) webhook_event: ", instagramChannelMessage);
598
+ var entryId = entry.id;
599
+ winston.debug("(ig) Entry ID: " + entryId);
575
600
 
576
- const tlr = new TiledeskInstagramTranslator();
577
- const tdChannel = new TiledeskChannel({ settings: settings, API_URL: API_URL })
578
- const fbClient = new FacebookClient({ GRAPH_URL: GRAPH_URL, FB_APP_ID: FB_APP_ID, APP_SECRET: APP_SECRET, BASE_URL: BASE_URL });
601
+ var PAGE_KEY = "instagram-page-" + entryId;
602
+ var info_settings = await db.get(PAGE_KEY);
579
603
 
580
- const page = settings.pages.find(p => p.id === page_id);
604
+ if (!info_settings) {
605
+ winston.debug("(ig) No settings found for entry " + entryId + " — checking project by scanning");
606
+ return;
607
+ }
581
608
 
582
- let user_info = await fbClient.getUserInfo(page.access_token, instagramChannelMessage.sender.id).catch((err) => {
583
- winston.error("(fbm) getUserInfo error: ", err?.response?.data || err.message || err);
584
- return { first_name: "User", last_name: instagramChannelMessage.sender.id };
585
- });
609
+ var project_id = info_settings.project_id;
610
+ winston.debug("(ig) project_id: " + project_id);
586
611
 
587
- instagramChannelMessage.sender.fullname = user_info.first_name + " " + user_info.last_name;
612
+ let CONTENT_KEY = "instagram-" + project_id;
613
+ let settings = await db.get(CONTENT_KEY);
588
614
 
589
- winston.debug("(fbm) page: " + page);
590
- winston.debug("(fbm) user_info: ", user_info);
615
+ var instagramChannelMessage = entry.messaging[0];
616
+ winston.debug("(ig) webhook_event: ", instagramChannelMessage);
591
617
 
592
- let message_info = {
593
- channel: TiledeskInstagramTranslator.CHANNEL_NAME,
594
- instagram: {
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
- }
618
+ const tlr = new TiledeskInstagramTranslator();
619
+ const tdChannel = new TiledeskChannel({ settings: settings, API_URL: API_URL });
620
+ const fbClient = new FacebookClient({ GRAPH_URL: GRAPH_URL, FB_APP_ID: FB_APP_ID, APP_SECRET: APP_SECRET, BASE_URL: BASE_URL });
601
621
 
602
- if (instagramChannelMessage.message &&
603
- instagramChannelMessage.message.attachments &&
604
- instagramChannelMessage.message.attachments.length > 1) {
622
+ const page = settings && settings.pages ? settings.pages.find(p => p.id === entryId) : null;
623
+ var pageAccessToken = page ? page.access_token : null;
605
624
 
606
- const messageHandler = new MessageHandler();
607
- let messagesList = await messageHandler.splitMessageFromInstagram(instagramChannelMessage);
625
+ if (!pageAccessToken && settings && settings.ig_oauth_token) {
626
+ pageAccessToken = settings.ig_oauth_token.access_token;
627
+ }
608
628
 
609
- for (let message of messagesList) {
629
+ var user_info = { first_name: "User", last_name: instagramChannelMessage.sender.id };
630
+ if (pageAccessToken) {
631
+ user_info = await fbClient.getUserInfo(pageAccessToken, instagramChannelMessage.sender.id).catch(function(err) {
632
+ winston.error("(ig) getUserInfo error: ", err?.response?.data || err.message || err);
633
+ return { first_name: "User", last_name: instagramChannelMessage.sender.id };
634
+ });
635
+ }
610
636
 
611
- let tiledeskJsonMessage = await tlr.toTiledesk(message);
612
- winston.verbose("(fbm) tiledeskJsonMessage: ", tiledeskJsonMessage);
637
+ instagramChannelMessage.sender.fullname = user_info.first_name + " " + user_info.last_name;
613
638
 
639
+ var message_info = {
640
+ channel: TiledeskInstagramTranslator.CHANNEL_NAME,
641
+ instagram: {
642
+ page_id: entryId,
643
+ sender_id: instagramChannelMessage.sender.id,
644
+ firstname: user_info.first_name,
645
+ lastname: user_info.last_name
646
+ }
647
+ };
648
+
649
+ if (instagramChannelMessage.message &&
650
+ instagramChannelMessage.message.attachments &&
651
+ instagramChannelMessage.message.attachments.length > 1) {
652
+
653
+ const messageHandler = new MessageHandler();
654
+ let messagesList = await messageHandler.splitMessageFromInstagram(instagramChannelMessage);
655
+
656
+ for (let message of messagesList) {
657
+ let tiledeskJsonMessage = await tlr.toTiledesk(message);
658
+ if (tiledeskJsonMessage) {
659
+ try {
660
+ await tdChannel.send(tiledeskJsonMessage, message_info, settings.department_id);
661
+ } catch (err) {
662
+ winston.error("(ig) Error sending split message: ", err);
663
+ }
664
+ }
665
+ }
666
+ } else {
667
+ let tiledeskJsonMessage = await tlr.toTiledesk(instagramChannelMessage);
614
668
  if (tiledeskJsonMessage) {
615
669
  try {
616
670
  await tdChannel.send(tiledeskJsonMessage, message_info, settings.department_id);
617
- winston.verbose("(fbm) Message sent to AI Manager")
618
671
  } catch (err) {
619
- return res.status(500).send({ success: false, error: "Error sending exoìpiration message" });
672
+ winston.error("(ig) Error sending message: ", err);
620
673
  }
621
- } else {
622
- winston.verbose("(fbm) tiledeskJsonMessage is undefined!")
623
674
  }
624
675
  }
676
+ });
625
677
 
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
- }
637
- } else {
638
- winston.verbose("(fbm) tiledeskJsonMessage is undefined!")
639
- }
640
- }
641
-
642
- })
643
678
  } catch (err) {
644
679
  winston.error("(ig) Async webhook processing error: ", err?.response?.data || err.message || err);
645
680
  }
646
681
  });
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) {
682
+ }
665
683
 
666
- // Checks the mode and token sent is correct
667
- if (mode === 'subscribe' && token === VERIFY_TOKEN) {
684
+ router.post('/webhookIG', function(req, res) {
685
+ winston.verbose("(ig) POST /webhookIG");
686
+ processInstagramWebhook(req, res);
687
+ });
668
688
 
669
- // Responds with the challenge token from the request
670
- winston.verbose('(fbm) Webhook verified');
671
- return res.status(200).send(challenge);
689
+ router.post('/webhookFB', function(req, res) {
690
+ winston.verbose("(ig) POST /webhookFB");
691
+ processInstagramWebhook(req, res);
692
+ });
672
693
 
673
- } else {
674
- // Responds with '403 Forbidden' if verify tokens do not match
675
- return res.sendStatus(403);
676
- }
694
+ function handleWebhookVerify(req, res) {
695
+ winston.verbose("(ig) Verify webhook... ", req.query);
696
+ var mode = req.query['hub.mode'];
697
+ var token = req.query['hub.verify_token'];
698
+ var challenge = req.query['hub.challenge'];
699
+ if (mode && token && mode === 'subscribe' && token === VERIFY_TOKEN) {
700
+ winston.info("(ig) Webhook verified");
701
+ return res.status(200).send(challenge);
677
702
  }
703
+ winston.warn("(ig) Webhook verify failed — mode=" + mode + " token match=" + (token === VERIFY_TOKEN));
678
704
  return res.sendStatus(403);
679
- })
705
+ }
706
+
707
+ router.get('/webhookIG', function(req, res) { handleWebhookVerify(req, res); });
708
+ router.get('/webhookFB', function(req, res) { handleWebhookVerify(req, res); });
680
709
 
681
710
  router.get('/auth/instagram/start', async (req, res) => {
682
711
  winston.verbose("(ig) /auth/instagram/start");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resultcrafter/aimanager-instagram-connector",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "AI Manager Instagram DM connector",
5
5
  "main": "index.js",
6
6
  "scripts": {