@mookielianhd/n8n-nodes-instagram 2.2.0 → 2.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.
@@ -38,9 +38,50 @@ class Instagram {
38
38
  name: 'resource',
39
39
  type: 'options',
40
40
  noDataExpression: true,
41
- options: [...resources_1.instagramResourceOptions],
42
- default: '',
43
- description: 'Select the Instagram media type to publish',
41
+ options: [
42
+ {
43
+ name: 'Auth',
44
+ value: 'auth',
45
+ description: 'Exchange and refresh Instagram access tokens; call /me',
46
+ },
47
+ {
48
+ name: 'Comment',
49
+ value: 'comments',
50
+ description: 'Moderate comments on Instagram media',
51
+ },
52
+ {
53
+ name: 'IG Hashtag',
54
+ value: 'igHashtag',
55
+ description: 'Search hashtags and fetch top or recent media for a hashtag',
56
+ },
57
+ {
58
+ name: 'IG User',
59
+ value: 'igUser',
60
+ description: 'Read profile and media for an Instagram Business or Creator account',
61
+ },
62
+ {
63
+ name: 'Image',
64
+ value: 'image',
65
+ description: 'Publish image posts to Instagram',
66
+ },
67
+ {
68
+ name: 'Messaging',
69
+ value: 'messaging',
70
+ description: 'Send direct messages via the Instagram Messaging API',
71
+ },
72
+ {
73
+ name: 'Reel',
74
+ value: 'reels',
75
+ description: 'Publish Reels videos to Instagram',
76
+ },
77
+ {
78
+ name: 'Story',
79
+ value: 'stories',
80
+ description: 'Publish story videos to Instagram',
81
+ },
82
+ ],
83
+ default: 'image',
84
+ description: 'Select what you want to do with the Instagram API',
44
85
  required: true,
45
86
  },
46
87
  {
@@ -64,18 +105,239 @@ class Instagram {
64
105
  default: 'publish',
65
106
  required: true,
66
107
  },
108
+ {
109
+ displayName: 'Operation',
110
+ name: 'operation',
111
+ type: 'options',
112
+ noDataExpression: true,
113
+ displayOptions: {
114
+ show: {
115
+ resource: ['comments'],
116
+ },
117
+ },
118
+ options: [
119
+ {
120
+ name: 'Delete',
121
+ value: 'deleteComment',
122
+ action: 'Delete a comment',
123
+ description: 'Delete a comment from an Instagram media object',
124
+ },
125
+ {
126
+ name: 'Disable Comments',
127
+ value: 'disableComments',
128
+ action: 'Disable comments on media',
129
+ description: 'Disable comments on an Instagram media object',
130
+ },
131
+ {
132
+ name: 'Enable Comments',
133
+ value: 'enableComments',
134
+ action: 'Enable comments on media',
135
+ description: 'Enable comments on an Instagram media object',
136
+ },
137
+ {
138
+ name: 'Hide',
139
+ value: 'hideComment',
140
+ action: 'Hide a comment',
141
+ description: 'Hide a comment from an Instagram media object',
142
+ },
143
+ {
144
+ name: 'List',
145
+ value: 'list',
146
+ action: 'List comments',
147
+ description: 'List comments on an Instagram media object',
148
+ },
149
+ {
150
+ name: 'Send Private Reply',
151
+ value: 'sendPrivateReply',
152
+ action: 'Send a private reply',
153
+ description: 'Send a private reply message to a commenter using the comment ID',
154
+ },
155
+ {
156
+ name: 'Unhide',
157
+ value: 'unhideComment',
158
+ action: 'Unhide a comment',
159
+ description: 'Unhide a previously hidden comment',
160
+ },
161
+ ],
162
+ default: 'list',
163
+ required: true,
164
+ },
165
+ {
166
+ displayName: 'Operation',
167
+ name: 'operation',
168
+ type: 'options',
169
+ noDataExpression: true,
170
+ displayOptions: {
171
+ show: {
172
+ resource: ['igUser'],
173
+ },
174
+ },
175
+ options: [
176
+ {
177
+ name: 'Get',
178
+ value: 'get',
179
+ action: 'Get IG user',
180
+ description: 'Get basic profile information for an Instagram Business or Creator account',
181
+ },
182
+ {
183
+ name: 'Get Media',
184
+ value: 'getMedia',
185
+ action: 'Get media',
186
+ description: 'List media objects owned by an Instagram Business or Creator account',
187
+ },
188
+ ],
189
+ default: 'get',
190
+ required: true,
191
+ },
192
+ {
193
+ displayName: 'Operation',
194
+ name: 'operation',
195
+ type: 'options',
196
+ noDataExpression: true,
197
+ displayOptions: {
198
+ show: {
199
+ resource: ['auth'],
200
+ },
201
+ },
202
+ options: [
203
+ {
204
+ name: 'Exchange Access Token',
205
+ value: 'exchangeAccessToken',
206
+ action: 'Exchange short lived token',
207
+ description: 'Exchange a short-lived Instagram User access token for a long-lived token using the Instagram Platform access token endpoint',
208
+ },
209
+ {
210
+ name: 'Get Me',
211
+ value: 'getMe',
212
+ action: 'Get me profile',
213
+ description: 'Call the Graph API /me endpoint using the access token from the Instagram API credential',
214
+ },
215
+ {
216
+ name: 'Refresh Access Token',
217
+ value: 'refreshAccessToken',
218
+ action: 'Refresh access token',
219
+ description: 'Refresh a long-lived Instagram User access token using the Instagram Platform refresh endpoint',
220
+ },
221
+ ],
222
+ default: 'refreshAccessToken',
223
+ required: true,
224
+ },
225
+ {
226
+ displayName: 'Operation',
227
+ name: 'operation',
228
+ type: 'options',
229
+ noDataExpression: true,
230
+ displayOptions: {
231
+ show: {
232
+ resource: ['igHashtag'],
233
+ },
234
+ },
235
+ options: [
236
+ {
237
+ name: 'Search',
238
+ value: 'search',
239
+ action: 'Search hashtags',
240
+ description: 'Search for a hashtag by name and return its ID',
241
+ },
242
+ {
243
+ name: 'Get Recent Media',
244
+ value: 'getRecentMedia',
245
+ action: 'Get recent media for hashtag',
246
+ description: 'Get the most recent media tagged with a hashtag',
247
+ },
248
+ {
249
+ name: 'Get Top Media',
250
+ value: 'getTopMedia',
251
+ action: 'Get top media for hashtag',
252
+ description: 'Get the most popular media tagged with a hashtag',
253
+ },
254
+ ],
255
+ default: 'search',
256
+ required: true,
257
+ },
258
+ {
259
+ displayName: 'Operation',
260
+ name: 'operation',
261
+ type: 'options',
262
+ noDataExpression: true,
263
+ displayOptions: {
264
+ show: {
265
+ resource: ['messaging'],
266
+ },
267
+ },
268
+ options: [
269
+ {
270
+ name: 'Send Message',
271
+ value: 'sendMessage',
272
+ action: 'Send direct message',
273
+ description: 'Send a text message to an Instagram user via the Messaging API',
274
+ },
275
+ ],
276
+ default: 'sendMessage',
277
+ required: true,
278
+ },
67
279
  {
68
280
  displayName: 'Node',
69
281
  name: 'node',
70
282
  type: 'string',
71
283
  default: '',
72
- description: 'The Instagram Business Account ID or User ID on which to publish the media',
284
+ description: 'The Instagram Business Account ID or User ID on which to publish the media, or the professional account that owns the commented media, or the IG User to read data for, or the IG User performing a hashtag or messaging query',
73
285
  placeholder: 'me',
74
286
  required: true,
75
287
  displayOptions: {
76
288
  show: {
77
- resource: ['image', 'reels', 'stories'],
78
- operation: ['publish'],
289
+ resource: ['image', 'reels', 'stories', 'comments', 'igUser', 'igHashtag', 'messaging'],
290
+ operation: ['publish', 'sendPrivateReply', 'get', 'getMedia', 'search', 'getRecentMedia', 'getTopMedia', 'sendMessage'],
291
+ },
292
+ },
293
+ },
294
+ {
295
+ displayName: 'Access Token',
296
+ name: 'accessToken',
297
+ type: 'string',
298
+ typeOptions: {
299
+ password: true,
300
+ },
301
+ default: '',
302
+ description: 'The long-lived Instagram User access token to refresh. Leave empty to use the access token from the Instagram API credential.',
303
+ displayOptions: {
304
+ show: {
305
+ resource: ['auth'],
306
+ operation: ['refreshAccessToken'],
307
+ },
308
+ },
309
+ },
310
+ {
311
+ displayName: 'Short-Lived Access Token',
312
+ name: 'shortLivedAccessToken',
313
+ type: 'string',
314
+ typeOptions: {
315
+ password: true,
316
+ },
317
+ default: '',
318
+ description: 'The short-lived Instagram User access token to exchange for a long-lived token. This is usually obtained from the login flow.',
319
+ required: true,
320
+ displayOptions: {
321
+ show: {
322
+ resource: ['auth'],
323
+ operation: ['exchangeAccessToken'],
324
+ },
325
+ },
326
+ },
327
+ {
328
+ displayName: 'App Secret',
329
+ name: 'appSecret',
330
+ type: 'string',
331
+ typeOptions: {
332
+ password: true,
333
+ },
334
+ default: '',
335
+ description: 'Instagram App Secret from the Meta App Dashboard. Required to securely exchange a short-lived access token for a long-lived token.',
336
+ required: true,
337
+ displayOptions: {
338
+ show: {
339
+ resource: ['auth'],
340
+ operation: ['exchangeAccessToken'],
79
341
  },
80
342
  },
81
343
  },
@@ -88,8 +350,159 @@ class Instagram {
88
350
  required: true,
89
351
  displayOptions: {
90
352
  show: {
91
- resource: ['image', 'reels', 'stories'],
92
- operation: ['publish'],
353
+ resource: ['image', 'reels', 'stories', 'comments', 'igUser', 'igHashtag', 'auth', 'messaging'],
354
+ operation: [
355
+ 'publish',
356
+ 'list',
357
+ 'hideComment',
358
+ 'unhideComment',
359
+ 'deleteComment',
360
+ 'disableComments',
361
+ 'enableComments',
362
+ 'get',
363
+ 'getMedia',
364
+ 'search',
365
+ 'getRecentMedia',
366
+ 'getTopMedia',
367
+ 'getMe',
368
+ 'sendMessage',
369
+ 'sendPrivateReply',
370
+ ],
371
+ },
372
+ },
373
+ },
374
+ {
375
+ displayName: 'Return All',
376
+ name: 'returnAll',
377
+ type: 'boolean',
378
+ default: false,
379
+ description: 'Whether to return all results or only up to a given limit',
380
+ displayOptions: {
381
+ show: {
382
+ resource: ['igUser', 'igHashtag'],
383
+ operation: ['getMedia', 'getRecentMedia', 'getTopMedia'],
384
+ },
385
+ },
386
+ },
387
+ {
388
+ displayName: 'Limit',
389
+ name: 'limit',
390
+ type: 'number',
391
+ typeOptions: {
392
+ minValue: 1,
393
+ maxValue: 500,
394
+ },
395
+ default: 50,
396
+ description: 'Max number of results to return',
397
+ displayOptions: {
398
+ show: {
399
+ resource: ['igUser', 'igHashtag'],
400
+ operation: ['getMedia', 'getRecentMedia', 'getTopMedia'],
401
+ returnAll: [false],
402
+ },
403
+ },
404
+ },
405
+ {
406
+ displayName: 'Recipient IG User ID',
407
+ name: 'recipientId',
408
+ type: 'string',
409
+ default: '',
410
+ description: 'The Instagram-scoped user ID (IGSID) of the recipient. This is provided in webhook events or other Messaging API responses.',
411
+ required: true,
412
+ displayOptions: {
413
+ show: {
414
+ resource: ['messaging'],
415
+ operation: ['sendMessage'],
416
+ },
417
+ },
418
+ },
419
+ {
420
+ displayName: 'Message Text',
421
+ name: 'messageText',
422
+ type: 'string',
423
+ typeOptions: {
424
+ rows: 4,
425
+ },
426
+ default: '',
427
+ description: 'The text content of the direct message to send',
428
+ required: true,
429
+ displayOptions: {
430
+ show: {
431
+ resource: ['messaging'],
432
+ operation: ['sendMessage'],
433
+ },
434
+ },
435
+ },
436
+ {
437
+ displayName: 'Hashtag Name',
438
+ name: 'hashtagName',
439
+ type: 'string',
440
+ default: '',
441
+ description: 'The hashtag name to search for, without the # symbol',
442
+ required: true,
443
+ displayOptions: {
444
+ show: {
445
+ resource: ['igHashtag'],
446
+ operation: ['search'],
447
+ },
448
+ },
449
+ },
450
+ {
451
+ displayName: 'Hashtag ID',
452
+ name: 'hashtagId',
453
+ type: 'string',
454
+ default: '',
455
+ description: 'The IG Hashtag ID returned by the hashtag search',
456
+ required: true,
457
+ displayOptions: {
458
+ show: {
459
+ resource: ['igHashtag'],
460
+ operation: ['getRecentMedia', 'getTopMedia'],
461
+ },
462
+ },
463
+ },
464
+ {
465
+ displayName: 'Media ID',
466
+ name: 'mediaId',
467
+ type: 'string',
468
+ default: '',
469
+ description: 'The IG Media ID whose comments you want to manage',
470
+ required: true,
471
+ displayOptions: {
472
+ show: {
473
+ resource: ['comments'],
474
+ operation: ['list', 'disableComments', 'enableComments'],
475
+ },
476
+ },
477
+ },
478
+ {
479
+ displayName: 'Comment ID',
480
+ name: 'commentId',
481
+ type: 'string',
482
+ default: '',
483
+ description: 'The IG Comment ID you want to moderate',
484
+ required: true,
485
+ displayOptions: {
486
+ show: {
487
+ resource: ['comments'],
488
+ operation: ['hideComment', 'unhideComment', 'deleteComment', 'sendPrivateReply'],
489
+ },
490
+ },
491
+ },
492
+ {
493
+ displayName: 'Message',
494
+ name: 'privateReplyText',
495
+ type: 'string',
496
+ typeOptions: {
497
+ rows: 4,
498
+ },
499
+ default: '',
500
+ description: 'The text of the private reply message',
501
+ required: true,
502
+ displayOptions: {
503
+ show: {
504
+ resource: ['comments'],
505
+ operation: ['sendPrivateReply'],
93
506
  },
94
507
  },
95
508
  },
@@ -243,9 +656,10 @@ class Instagram {
243
656
  };
244
657
  }
245
658
  async execute() {
246
- var _a, _b, _c, _d, _e, _f, _g;
659
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
247
660
  const items = this.getInputData();
248
661
  const returnItems = [];
662
+ const hostUrl = 'graph.facebook.com';
249
663
  const waitForContainerReady = async ({ creationId, hostUrl, graphApiVersion, itemIndex, pollIntervalMs, maxPollAttempts, }) => {
250
664
  const statusUri = `https://${hostUrl}/${graphApiVersion}/${creationId}`;
251
665
  const statusFields = ['status_code', 'status'];
@@ -299,6 +713,468 @@ class Instagram {
299
713
  try {
300
714
  const resource = this.getNodeParameter('resource', itemIndex);
301
715
  const operation = this.getNodeParameter('operation', itemIndex);
716
+ if (resource === 'messaging') {
717
+ const graphApiVersion = this.getNodeParameter('graphApiVersion', itemIndex);
718
+ const accountId = this.getNodeParameter('node', itemIndex);
719
+ try {
720
+ if (operation === 'sendMessage') {
721
+ const recipientId = this.getNodeParameter('recipientId', itemIndex);
722
+ const text = this.getNodeParameter('messageText', itemIndex);
723
+ const url = `https://${hostUrl}/${graphApiVersion}/${accountId}/messages`;
724
+ const requestOptions = {
725
+ headers: {
726
+ accept: 'application/json,text/*;q=0.99',
727
+ 'Content-Type': 'application/json',
728
+ },
729
+ method: 'POST',
730
+ url,
731
+ body: {
732
+ recipient: {
733
+ id: recipientId,
734
+ },
735
+ message: {
736
+ text,
737
+ },
738
+ },
739
+ json: true,
740
+ };
741
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'instagramApi', requestOptions));
742
+ returnItems.push({ json: response, pairedItem: { item: itemIndex } });
743
+ continue;
744
+ }
745
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported messaging operation: ${operation}`, { itemIndex });
746
+ }
747
+ catch (error) {
748
+ if (!this.continueOnFail()) {
749
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
750
+ }
751
+ let errorItem;
752
+ const errorWithGraph = error;
753
+ if (errorWithGraph.response !== undefined) {
754
+ const graphApiErrors = (_b = (_a = errorWithGraph.response.body) === null || _a === void 0 ? void 0 : _a.error) !== null && _b !== void 0 ? _b : {};
755
+ errorItem = {
756
+ statusCode: errorWithGraph.statusCode,
757
+ ...graphApiErrors,
758
+ headers: errorWithGraph.response.headers,
759
+ };
760
+ }
761
+ else {
762
+ errorItem = error;
763
+ }
764
+ returnItems.push({ json: { ...errorItem }, pairedItem: { item: itemIndex } });
765
+ continue;
766
+ }
767
+ }
768
+ if (resource === 'auth') {
769
+ try {
770
+ if (operation === 'refreshAccessToken') {
771
+ let token = (_c = this.getNodeParameter('accessToken', itemIndex, '')) !== null && _c !== void 0 ? _c : '';
772
+ if (!token) {
773
+ const credentials = (await this.getCredentials('instagramApi'));
774
+ token = (_d = credentials === null || credentials === void 0 ? void 0 : credentials.accessToken) !== null && _d !== void 0 ? _d : '';
775
+ }
776
+ if (!token) {
777
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No access token provided and no access token found in the Instagram API credential.', { itemIndex });
778
+ }
779
+ const url = 'https://graph.instagram.com/refresh_access_token';
780
+ const requestOptions = {
781
+ headers: {
782
+ accept: 'application/json,text/*;q=0.99',
783
+ },
784
+ method: 'GET',
785
+ url,
786
+ qs: {
787
+ grant_type: 'ig_refresh_token',
788
+ access_token: token,
789
+ },
790
+ json: true,
791
+ };
792
+ const response = (await this.helpers.httpRequest.call(this, requestOptions));
793
+ returnItems.push({ json: response, pairedItem: { item: itemIndex } });
794
+ continue;
795
+ }
796
+ if (operation === 'exchangeAccessToken') {
797
+ const shortLivedToken = this.getNodeParameter('shortLivedAccessToken', itemIndex);
798
+ const appSecret = this.getNodeParameter('appSecret', itemIndex);
799
+ const url = 'https://graph.instagram.com/access_token';
800
+ const requestOptions = {
801
+ headers: {
802
+ accept: 'application/json,text/*;q=0.99',
803
+ },
804
+ method: 'GET',
805
+ url,
806
+ qs: {
807
+ grant_type: 'ig_exchange_token',
808
+ client_secret: appSecret,
809
+ access_token: shortLivedToken,
810
+ },
811
+ json: true,
812
+ };
813
+ const response = (await this.helpers.httpRequest.call(this, requestOptions));
814
+ returnItems.push({ json: response, pairedItem: { item: itemIndex } });
815
+ continue;
816
+ }
817
+ if (operation === 'getMe') {
818
+ const graphApiVersion = this.getNodeParameter('graphApiVersion', itemIndex);
819
+ const url = `https://${hostUrl}/${graphApiVersion}/me`;
820
+ const requestOptions = {
821
+ headers: {
822
+ accept: 'application/json,text/*;q=0.99',
823
+ },
824
+ method: 'GET',
825
+ url,
826
+ json: true,
827
+ };
828
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'instagramApi', requestOptions));
829
+ returnItems.push({ json: response, pairedItem: { item: itemIndex } });
830
+ continue;
831
+ }
832
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported auth operation: ${operation}`, { itemIndex });
833
+ }
834
+ catch (error) {
835
+ if (!this.continueOnFail()) {
836
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
837
+ }
838
+ let errorItem;
839
+ const errorWithGraph = error;
840
+ if (errorWithGraph.response !== undefined) {
841
+ const graphApiErrors = (_f = (_e = errorWithGraph.response.body) === null || _e === void 0 ? void 0 : _e.error) !== null && _f !== void 0 ? _f : {};
842
+ errorItem = {
843
+ statusCode: errorWithGraph.statusCode,
844
+ ...graphApiErrors,
845
+ headers: errorWithGraph.response.headers,
846
+ };
847
+ }
848
+ else {
849
+ errorItem = error;
850
+ }
851
+ returnItems.push({ json: { ...errorItem }, pairedItem: { item: itemIndex } });
852
+ continue;
853
+ }
854
+ }
855
+ if (resource === 'igHashtag') {
856
+ const graphApiVersion = this.getNodeParameter('graphApiVersion', itemIndex);
857
+ const accountId = this.getNodeParameter('node', itemIndex);
858
+ try {
859
+ if (operation === 'search') {
860
+ const hashtagName = this.getNodeParameter('hashtagName', itemIndex);
861
+ const url = `https://${hostUrl}/${graphApiVersion}/ig_hashtag_search`;
862
+ const requestOptions = {
863
+ headers: {
864
+ accept: 'application/json,text/*;q=0.99',
865
+ },
866
+ method: 'GET',
867
+ url,
868
+ qs: {
869
+ user_id: accountId,
870
+ q: hashtagName,
871
+ },
872
+ json: true,
873
+ };
874
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'instagramApi', requestOptions));
875
+ returnItems.push({ json: response, pairedItem: { item: itemIndex } });
876
+ continue;
877
+ }
878
+ if (operation === 'getRecentMedia' || operation === 'getTopMedia') {
879
+ const hashtagId = this.getNodeParameter('hashtagId', itemIndex);
880
+ const returnAll = this.getNodeParameter('returnAll', itemIndex, false);
881
+ const limit = this.getNodeParameter('limit', itemIndex, 0);
882
+ const edge = operation === 'getRecentMedia' ? 'recent_media' : 'top_media';
883
+ const baseUrl = `https://${hostUrl}/${graphApiVersion}/${hashtagId}/${edge}`;
884
+ const fields = [
885
+ 'id',
886
+ 'media_type',
887
+ 'media_url',
888
+ 'caption',
889
+ 'permalink',
890
+ 'timestamp',
891
+ 'comments_count',
892
+ 'like_count',
893
+ ].join(',');
894
+ const accumulated = [];
895
+ let after;
896
+ const hardCap = returnAll ? 5000 : limit;
897
+ let hasMore = true;
898
+ while (hasMore) {
899
+ const remaining = returnAll ? undefined : hardCap - accumulated.length;
900
+ const pageLimit = remaining !== undefined ? Math.min(remaining, 50) : 50;
901
+ const qs = {
902
+ user_id: accountId,
903
+ fields,
904
+ limit: pageLimit,
905
+ };
906
+ if (after) {
907
+ qs.after = after;
908
+ }
909
+ const requestOptions = {
910
+ headers: {
911
+ accept: 'application/json,text/*;q=0.99',
912
+ },
913
+ method: 'GET',
914
+ url: baseUrl,
915
+ qs,
916
+ json: true,
917
+ };
918
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'instagramApi', requestOptions));
919
+ const pageData = (_g = response.data) !== null && _g !== void 0 ? _g : [];
920
+ accumulated.push(...pageData);
921
+ const paging = response.paging;
922
+ after = (_h = paging === null || paging === void 0 ? void 0 : paging.cursors) === null || _h === void 0 ? void 0 : _h.after;
923
+ if ((!returnAll && accumulated.length >= hardCap) || !after) {
924
+ hasMore = false;
925
+ }
926
+ }
927
+ const finalData = !returnAll && hardCap > 0 ? accumulated.slice(0, hardCap) : accumulated;
928
+ returnItems.push({ json: { data: finalData }, pairedItem: { item: itemIndex } });
929
+ continue;
930
+ }
931
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported IG Hashtag operation: ${operation}`, { itemIndex });
932
+ }
933
+ catch (error) {
934
+ if (!this.continueOnFail()) {
935
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
936
+ }
937
+ let errorItem;
938
+ const errorWithGraph = error;
939
+ if (errorWithGraph.response !== undefined) {
940
+ const graphApiErrors = (_k = (_j = errorWithGraph.response.body) === null || _j === void 0 ? void 0 : _j.error) !== null && _k !== void 0 ? _k : {};
941
+ errorItem = {
942
+ statusCode: errorWithGraph.statusCode,
943
+ ...graphApiErrors,
944
+ headers: errorWithGraph.response.headers,
945
+ };
946
+ }
947
+ else {
948
+ errorItem = error;
949
+ }
950
+ returnItems.push({ json: { ...errorItem }, pairedItem: { item: itemIndex } });
951
+ continue;
952
+ }
953
+ }
954
+ if (resource === 'igUser') {
955
+ const graphApiVersion = this.getNodeParameter('graphApiVersion', itemIndex);
956
+ const accountId = this.getNodeParameter('node', itemIndex);
957
+ try {
958
+ if (operation === 'get') {
959
+ const url = `https://${hostUrl}/${graphApiVersion}/${accountId}`;
960
+ const requestOptions = {
961
+ headers: {
962
+ accept: 'application/json,text/*;q=0.99',
963
+ },
964
+ method: 'GET',
965
+ url,
966
+ qs: {
967
+ fields: [
968
+ 'id',
969
+ 'username',
970
+ 'name',
971
+ 'biography',
972
+ 'website',
973
+ 'media_count',
974
+ 'followers_count',
975
+ 'follows_count',
976
+ 'profile_picture_url',
977
+ ].join(','),
978
+ },
979
+ json: true,
980
+ };
981
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'instagramApi', requestOptions));
982
+ returnItems.push({ json: response, pairedItem: { item: itemIndex } });
983
+ continue;
984
+ }
985
+ if (operation === 'getMedia') {
986
+ const returnAll = this.getNodeParameter('returnAll', itemIndex, false);
987
+ const limit = this.getNodeParameter('limit', itemIndex, 0);
988
+ const baseUrl = `https://${hostUrl}/${graphApiVersion}/${accountId}/media`;
989
+ const fields = [
990
+ 'id',
991
+ 'media_type',
992
+ 'media_url',
993
+ 'thumbnail_url',
994
+ 'caption',
995
+ 'permalink',
996
+ 'timestamp',
997
+ 'username',
998
+ ].join(',');
999
+ const accumulated = [];
1000
+ let after;
1001
+ const hardCap = returnAll ? 5000 : limit;
1002
+ let hasMore = true;
1003
+ while (hasMore) {
1004
+ const remaining = returnAll ? undefined : hardCap - accumulated.length;
1005
+ const pageLimit = remaining !== undefined ? Math.min(remaining, 100) : 100;
1006
+ const qs = {
1007
+ fields,
1008
+ limit: pageLimit,
1009
+ };
1010
+ if (after) {
1011
+ qs.after = after;
1012
+ }
1013
+ const requestOptions = {
1014
+ headers: {
1015
+ accept: 'application/json,text/*;q=0.99',
1016
+ },
1017
+ method: 'GET',
1018
+ url: baseUrl,
1019
+ qs,
1020
+ json: true,
1021
+ };
1022
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'instagramApi', requestOptions));
1023
+ const pageData = (_l = response.data) !== null && _l !== void 0 ? _l : [];
1024
+ accumulated.push(...pageData);
1025
+ const paging = response.paging;
1026
+ after = (_m = paging === null || paging === void 0 ? void 0 : paging.cursors) === null || _m === void 0 ? void 0 : _m.after;
1027
+ if ((!returnAll && accumulated.length >= hardCap) || !after) {
1028
+ hasMore = false;
1029
+ }
1030
+ }
1031
+ const finalData = !returnAll && hardCap > 0 ? accumulated.slice(0, hardCap) : accumulated;
1032
+ returnItems.push({ json: { data: finalData }, pairedItem: { item: itemIndex } });
1033
+ continue;
1034
+ }
1035
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported IG User operation: ${operation}`, { itemIndex });
1036
+ }
1037
+ catch (error) {
1038
+ if (!this.continueOnFail()) {
1039
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
1040
+ }
1041
+ let errorItem;
1042
+ const errorWithGraph = error;
1043
+ if (errorWithGraph.response !== undefined) {
1044
+ const graphApiErrors = (_p = (_o = errorWithGraph.response.body) === null || _o === void 0 ? void 0 : _o.error) !== null && _p !== void 0 ? _p : {};
1045
+ errorItem = {
1046
+ statusCode: errorWithGraph.statusCode,
1047
+ ...graphApiErrors,
1048
+ headers: errorWithGraph.response.headers,
1049
+ };
1050
+ }
1051
+ else {
1052
+ errorItem = error;
1053
+ }
1054
+ returnItems.push({ json: { ...errorItem }, pairedItem: { item: itemIndex } });
1055
+ continue;
1056
+ }
1057
+ }
1058
+ if (resource === 'comments') {
1059
+ const graphApiVersion = this.getNodeParameter('graphApiVersion', itemIndex);
1060
+ try {
1061
+ if (operation === 'list') {
1062
+ const mediaId = this.getNodeParameter('mediaId', itemIndex);
1063
+ const url = `https://${hostUrl}/${graphApiVersion}/${mediaId}/comments`;
1064
+ const requestOptions = {
1065
+ headers: {
1066
+ accept: 'application/json,text/*;q=0.99',
1067
+ },
1068
+ method: 'GET',
1069
+ url,
1070
+ json: true,
1071
+ };
1072
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'instagramApi', requestOptions));
1073
+ returnItems.push({ json: response, pairedItem: { item: itemIndex } });
1074
+ continue;
1075
+ }
1076
+ if (operation === 'hideComment' || operation === 'unhideComment') {
1077
+ const commentId = this.getNodeParameter('commentId', itemIndex);
1078
+ const hideValue = operation === 'hideComment';
1079
+ const url = `https://${hostUrl}/${graphApiVersion}/${commentId}`;
1080
+ const requestOptions = {
1081
+ headers: {
1082
+ accept: 'application/json,text/*;q=0.99',
1083
+ },
1084
+ method: 'POST',
1085
+ url,
1086
+ qs: {
1087
+ hide: hideValue,
1088
+ },
1089
+ json: true,
1090
+ };
1091
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'instagramApi', requestOptions));
1092
+ returnItems.push({ json: response, pairedItem: { item: itemIndex } });
1093
+ continue;
1094
+ }
1095
+ if (operation === 'deleteComment') {
1096
+ const commentId = this.getNodeParameter('commentId', itemIndex);
1097
+ const url = `https://${hostUrl}/${graphApiVersion}/${commentId}`;
1098
+ const requestOptions = {
1099
+ headers: {
1100
+ accept: 'application/json,text/*;q=0.99',
1101
+ },
1102
+ method: 'DELETE',
1103
+ url,
1104
+ json: true,
1105
+ };
1106
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'instagramApi', requestOptions));
1107
+ returnItems.push({ json: response, pairedItem: { item: itemIndex } });
1108
+ continue;
1109
+ }
1110
+ if (operation === 'disableComments' || operation === 'enableComments') {
1111
+ const mediaId = this.getNodeParameter('mediaId', itemIndex);
1112
+ const commentEnabled = operation === 'enableComments';
1113
+ const url = `https://${hostUrl}/${graphApiVersion}/${mediaId}`;
1114
+ const requestOptions = {
1115
+ headers: {
1116
+ accept: 'application/json,text/*;q=0.99',
1117
+ },
1118
+ method: 'POST',
1119
+ url,
1120
+ qs: {
1121
+ comment_enabled: commentEnabled,
1122
+ },
1123
+ json: true,
1124
+ };
1125
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'instagramApi', requestOptions));
1126
+ returnItems.push({ json: response, pairedItem: { item: itemIndex } });
1127
+ continue;
1128
+ }
1129
+ if (operation === 'sendPrivateReply') {
1130
+ const accountId = this.getNodeParameter('node', itemIndex);
1131
+ const commentId = this.getNodeParameter('commentId', itemIndex);
1132
+ const text = this.getNodeParameter('privateReplyText', itemIndex);
1133
+ const url = `https://${hostUrl}/${graphApiVersion}/${accountId}/messages`;
1134
+ const requestOptions = {
1135
+ headers: {
1136
+ accept: 'application/json,text/*;q=0.99',
1137
+ 'Content-Type': 'application/json',
1138
+ },
1139
+ method: 'POST',
1140
+ url,
1141
+ body: {
1142
+ recipient: {
1143
+ comment_id: commentId,
1144
+ },
1145
+ message: {
1146
+ text,
1147
+ },
1148
+ },
1149
+ json: true,
1150
+ };
1151
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'instagramApi', requestOptions));
1152
+ returnItems.push({ json: response, pairedItem: { item: itemIndex } });
1153
+ continue;
1154
+ }
1155
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported comments operation: ${operation}`, { itemIndex });
1156
+ }
1157
+ catch (error) {
1158
+ if (!this.continueOnFail()) {
1159
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
1160
+ }
1161
+ let errorItem;
1162
+ const errorWithGraph = error;
1163
+ if (errorWithGraph.response !== undefined) {
1164
+ const graphApiErrors = (_r = (_q = errorWithGraph.response.body) === null || _q === void 0 ? void 0 : _q.error) !== null && _r !== void 0 ? _r : {};
1165
+ errorItem = {
1166
+ statusCode: errorWithGraph.statusCode,
1167
+ ...graphApiErrors,
1168
+ headers: errorWithGraph.response.headers,
1169
+ };
1170
+ }
1171
+ else {
1172
+ errorItem = error;
1173
+ }
1174
+ returnItems.push({ json: { ...errorItem }, pairedItem: { item: itemIndex } });
1175
+ continue;
1176
+ }
1177
+ }
302
1178
  if (operation !== 'publish') {
303
1179
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported operation: ${operation}`, {
304
1180
  itemIndex,
@@ -314,11 +1190,10 @@ class Instagram {
314
1190
  const graphApiVersion = this.getNodeParameter('graphApiVersion', itemIndex);
315
1191
  const caption = this.getNodeParameter('caption', itemIndex);
316
1192
  const additionalFields = this.getNodeParameter('additionalFields', itemIndex, {});
317
- const altText = (_a = additionalFields.altText) !== null && _a !== void 0 ? _a : '';
1193
+ const altText = (_s = additionalFields.altText) !== null && _s !== void 0 ? _s : '';
318
1194
  const rawLocationId = additionalFields.locationId;
319
1195
  const userTagsCollection = additionalFields.userTags;
320
1196
  const productTagsCollection = additionalFields.productTags;
321
- const hostUrl = 'graph.facebook.com';
322
1197
  const httpRequestMethod = 'POST';
323
1198
  const mediaUri = `https://${hostUrl}/${graphApiVersion}/${node}/media`;
324
1199
  const mediaPayload = handler.buildMediaPayload.call(this, itemIndex);
@@ -393,7 +1268,7 @@ class Instagram {
393
1268
  let errorItem;
394
1269
  const err = error;
395
1270
  if (err.response !== undefined) {
396
- const graphApiErrors = (_c = (_b = err.response.body) === null || _b === void 0 ? void 0 : _b.error) !== null && _c !== void 0 ? _c : {};
1271
+ const graphApiErrors = (_u = (_t = err.response.body) === null || _t === void 0 ? void 0 : _t.error) !== null && _u !== void 0 ? _u : {};
397
1272
  errorItem = {
398
1273
  statusCode: err.statusCode,
399
1274
  ...graphApiErrors,
@@ -466,7 +1341,7 @@ class Instagram {
466
1341
  let errorItem;
467
1342
  const err = error;
468
1343
  if (err.response !== undefined) {
469
- const graphApiErrors = (_e = (_d = err.response.body) === null || _d === void 0 ? void 0 : _d.error) !== null && _e !== void 0 ? _e : {};
1344
+ const graphApiErrors = (_w = (_v = err.response.body) === null || _v === void 0 ? void 0 : _v.error) !== null && _w !== void 0 ? _w : {};
470
1345
  errorItem = {
471
1346
  statusCode: err.statusCode,
472
1347
  ...graphApiErrors,
@@ -507,7 +1382,7 @@ class Instagram {
507
1382
  let errorItem;
508
1383
  const errorWithGraph = error;
509
1384
  if (errorWithGraph.response !== undefined) {
510
- const graphApiErrors = (_g = (_f = errorWithGraph.response.body) === null || _f === void 0 ? void 0 : _f.error) !== null && _g !== void 0 ? _g : {};
1385
+ const graphApiErrors = (_y = (_x = errorWithGraph.response.body) === null || _x === void 0 ? void 0 : _x.error) !== null && _y !== void 0 ? _y : {};
511
1386
  errorItem = {
512
1387
  statusCode: errorWithGraph.statusCode,
513
1388
  ...graphApiErrors,