@resultcrafter/aimanager-instagram-connector 0.3.0 → 0.4.1

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
@@ -113,7 +113,7 @@ router.post('/install', async (req, res) => {
113
113
  "project_id": project_id,
114
114
  "app_id": app_id,
115
115
  "token": token,
116
- "oauth_url": BASE_URL + '/auth/instagram/start'
116
+ "oauth_url": BASE_URL + '/auth/instagram/start?project_id=' + project_id + '&token=' + token + '&app_id=' + app_id
117
117
  }
118
118
  }));
119
119
 
@@ -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,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("(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]);
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
- execute(commands[0]);
477
- }
478
-
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");
483
-
484
- let j = 0;
485
- async function send(message) {
486
- let instagramJsonMessage = await tlr.toInstagram(message, instagram_receiver);
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
- }).catch((err) => {
499
- winston.error("(fbm) error send message: ", err)
500
- })
501
- } else {
502
- winston.error("(fbm) instagramJsonMessage is undefined!")
504
+ });
503
505
  }
504
- }
505
- send(messagesList[0]);
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");
@@ -727,6 +728,25 @@ router.get('/auth/instagram/start', async (req, res) => {
727
728
  }
728
729
  });
729
730
 
731
+ function oauthCallbackResponse(res, success, data) {
732
+ var username = data && data.username ? data.username : '';
733
+ var error = data && data.error ? data.error : '';
734
+ var redirectUri = data && data.redirect_uri ? data.redirect_uri : '';
735
+ res.send('<!DOCTYPE html><html><body><script>' +
736
+ 'if (window.opener) {' +
737
+ ' window.opener.postMessage({' +
738
+ ' type: "oauth-complete",' +
739
+ ' success: ' + (success ? 'true' : 'false') + ',' +
740
+ (username ? ' username: "' + username.replace(/"/g, '\\"') + '",' : '') +
741
+ (error ? ' error: "' + error.replace(/"/g, '\\"') + '"' : '') +
742
+ ' }, "*");' +
743
+ ' window.close();' +
744
+ '} else {' +
745
+ ' window.location.href = "' + redirectUri.replace(/"/g, '') + '";' +
746
+ '}' +
747
+ '</script></body></html>');
748
+ }
749
+
730
750
  router.get('/oauth-callback', async (req, res) => {
731
751
  winston.verbose("(ig) /oauth-callback");
732
752
 
@@ -734,16 +754,17 @@ router.get('/oauth-callback', async (req, res) => {
734
754
  var stateStr = req.query.state;
735
755
  if (!code || !stateStr) {
736
756
  winston.error("(ig) Missing code or state parameter");
737
- return res.status(400).send("Missing authorization code or state");
757
+ return oauthCallbackResponse(res, false, { error: 'Missing authorization code or state' });
738
758
  }
739
759
 
740
760
  var state = instagramOAuth.verifyState(stateStr);
741
761
  if (!state) {
742
762
  winston.error("(ig) Invalid or expired state");
743
- return res.status(400).send("Invalid or expired state parameter. Please try connecting again.");
763
+ return oauthCallbackResponse(res, false, { error: 'Invalid or expired state parameter' });
744
764
  }
745
765
 
746
766
  var projectId = state.project_id;
767
+ var redirect_uri = DASHBOARD_BASE_URL + "/#/project/" + projectId + "/integrations?name=instagram";
747
768
 
748
769
  try {
749
770
  var tokenData = await instagramOAuth.exchangeCodeForToken(code);
@@ -757,11 +778,10 @@ router.get('/oauth-callback', async (req, res) => {
757
778
  settings.connected = true;
758
779
  await db.set(CONTENT_KEY, settings);
759
780
 
760
- var redirect_uri = DASHBOARD_BASE_URL + "/#/project/" + projectId + "/integrations?name=instagram";
761
- res.redirect(redirect_uri);
781
+ oauthCallbackResponse(res, true, { username: tokenData.username, redirect_uri: redirect_uri });
762
782
  } catch (err) {
763
783
  winston.error("(ig) OAuth callback error: ", err?.response?.data || err.message || err);
764
- res.status(500).send("Authentication failed: " + (err.message || 'Unknown error'));
784
+ oauthCallbackResponse(res, false, { error: err.message || 'Authentication failed', redirect_uri: redirect_uri });
765
785
  }
766
786
  })
767
787
 
@@ -820,7 +840,8 @@ router.post('/enablePage', async (req, res) => {
820
840
  brand_name: BRAND_NAME,
821
841
  show_info_message: settings.show_info_message !== undefined ? settings.show_info_message : true,
822
842
  instagram_username: settings.instagram_username || settings.user_info?.username || 'Instagram Account',
823
- subscription_id: settings.subscription_id
843
+ subscription_id: settings.subscription_id,
844
+ oauth_url: BASE_URL + '/auth/instagram/start?project_id=' + project_id + '&token=' + token + '&app_id=' + app_id
824
845
  }
825
846
  var html = template(replacements)
826
847
  return res.send(html);
@@ -845,7 +866,8 @@ router.post('/enablePage', async (req, res) => {
845
866
  show_alert_modal: true,
846
867
  show_info_message: settings.show_info_message !== undefined ? settings.show_info_message : true,
847
868
  instagram_username: settings.instagram_username || settings.user_info?.username || 'Instagram Account',
848
- subscription_id: settings.subscription_id
869
+ subscription_id: settings.subscription_id,
870
+ oauth_url: BASE_URL + '/auth/instagram/start?project_id=' + project_id + '&token=' + token + '&app_id=' + app_id
849
871
  }
850
872
  var html = template(replacements)
851
873
  return res.send(html);
@@ -893,7 +915,8 @@ router.post('/disablePage', async (req, res) => {
893
915
  brand_name: BRAND_NAME,
894
916
  show_info_message: settings.show_info_message !== undefined ? settings.show_info_message : true,
895
917
  instagram_username: settings.instagram_username || settings.user_info?.username || 'Instagram Account',
896
- subscription_id: settings.subscription_id
918
+ subscription_id: settings.subscription_id,
919
+ oauth_url: BASE_URL + '/auth/instagram/start?project_id=' + project_id + '&token=' + token + '&app_id=' + app_id
897
920
  }
898
921
  var html = template(replacements)
899
922
  return res.send(html);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resultcrafter/aimanager-instagram-connector",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "AI Manager Instagram DM connector",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -242,6 +242,18 @@
242
242
 
243
243
  <script>
244
244
 
245
+ var oauthPopup = null;
246
+
247
+ window.addEventListener('message', function(event) {
248
+ if (event.data && event.data.type === 'oauth-complete') {
249
+ if (event.data.success) {
250
+ window.location.reload();
251
+ } else {
252
+ alert('Instagram connection failed: ' + (event.data.error || 'Unknown error'));
253
+ }
254
+ }
255
+ });
256
+
245
257
  function connectInstagram() {
246
258
  var width = 600;
247
259
  var height = 700;
@@ -259,7 +271,7 @@
259
271
  } catch (e) {
260
272
 
261
273
  }
262
- }, 1000);
274
+ }, 2000);
263
275
  }
264
276
 
265
277
  function update_advanced() {