@pakoor/n8n-nodes-instagram 0.1.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.
Files changed (51) hide show
  1. package/README.md +271 -0
  2. package/dist/__tests__/helpers/index.d.ts +2 -0
  3. package/dist/__tests__/helpers/index.d.ts.map +1 -0
  4. package/dist/__tests__/helpers/index.js +18 -0
  5. package/dist/__tests__/helpers/index.js.map +1 -0
  6. package/dist/__tests__/helpers/testUtils.d.ts +104 -0
  7. package/dist/__tests__/helpers/testUtils.d.ts.map +1 -0
  8. package/dist/__tests__/helpers/testUtils.js +175 -0
  9. package/dist/__tests__/helpers/testUtils.js.map +1 -0
  10. package/dist/credentials/InstagramAccessTokenApi.credentials.d.ts +19 -0
  11. package/dist/credentials/InstagramAccessTokenApi.credentials.d.ts.map +1 -0
  12. package/dist/credentials/InstagramAccessTokenApi.credentials.js +130 -0
  13. package/dist/credentials/InstagramAccessTokenApi.credentials.js.map +1 -0
  14. package/dist/credentials/InstagramOAuth2Api.credentials.d.ts +20 -0
  15. package/dist/credentials/InstagramOAuth2Api.credentials.d.ts.map +1 -0
  16. package/dist/credentials/InstagramOAuth2Api.credentials.js +177 -0
  17. package/dist/credentials/InstagramOAuth2Api.credentials.js.map +1 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +23 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/nodes/Instagram/Instagram.node.d.ts +10 -0
  23. package/dist/nodes/Instagram/Instagram.node.d.ts.map +1 -0
  24. package/dist/nodes/Instagram/Instagram.node.js +1152 -0
  25. package/dist/nodes/Instagram/Instagram.node.js.map +1 -0
  26. package/dist/nodes/Instagram/InstagramTrigger.node.d.ts +61 -0
  27. package/dist/nodes/Instagram/InstagramTrigger.node.d.ts.map +1 -0
  28. package/dist/nodes/Instagram/InstagramTrigger.node.js +315 -0
  29. package/dist/nodes/Instagram/InstagramTrigger.node.js.map +1 -0
  30. package/dist/nodes/Instagram/instagram.svg +15 -0
  31. package/dist/services/InstagramApiClient.d.ts +261 -0
  32. package/dist/services/InstagramApiClient.d.ts.map +1 -0
  33. package/dist/services/InstagramApiClient.js +548 -0
  34. package/dist/services/InstagramApiClient.js.map +1 -0
  35. package/dist/services/TokenManager.d.ts +73 -0
  36. package/dist/services/TokenManager.d.ts.map +1 -0
  37. package/dist/services/TokenManager.js +150 -0
  38. package/dist/services/TokenManager.js.map +1 -0
  39. package/dist/services/ValidationUtils.d.ts +44 -0
  40. package/dist/services/ValidationUtils.d.ts.map +1 -0
  41. package/dist/services/ValidationUtils.js +203 -0
  42. package/dist/services/ValidationUtils.js.map +1 -0
  43. package/dist/services/WebhookHandler.d.ts +206 -0
  44. package/dist/services/WebhookHandler.d.ts.map +1 -0
  45. package/dist/services/WebhookHandler.js +261 -0
  46. package/dist/services/WebhookHandler.js.map +1 -0
  47. package/dist/services/index.d.ts +5 -0
  48. package/dist/services/index.d.ts.map +1 -0
  49. package/dist/services/index.js +21 -0
  50. package/dist/services/index.js.map +1 -0
  51. package/package.json +69 -0
@@ -0,0 +1,1152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Instagram = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const InstagramApiClient_1 = require("../../services/InstagramApiClient");
6
+ /**
7
+ * Instagram Node
8
+ *
9
+ * Main n8n node for Instagram Messaging and Content API operations.
10
+ * Supports messaging, user profiles, comments, posts, stories, reels, and media management.
11
+ *
12
+ * Requirements: All messaging, user, comment, post, media operations
13
+ */
14
+ // Resource options
15
+ const resourceOptions = [
16
+ { name: 'Message', value: 'message', description: 'Send messages to Instagram users' },
17
+ { name: 'User', value: 'user', description: 'Get user profile information' },
18
+ { name: 'Comment', value: 'comment', description: 'Manage comments on posts' },
19
+ { name: 'Post', value: 'post', description: 'Create and publish posts' },
20
+ { name: 'Story', value: 'story', description: 'Create and publish stories' },
21
+ { name: 'Reel', value: 'reel', description: 'Create and publish reels' },
22
+ { name: 'Media', value: 'media', description: 'Manage existing media' },
23
+ ];
24
+ // Message operations
25
+ const messageOperations = [
26
+ { name: 'Send Text', value: 'sendText', description: 'Send a text message' },
27
+ { name: 'Send Image', value: 'sendImage', description: 'Send an image message' },
28
+ { name: 'Send Audio', value: 'sendAudio', description: 'Send an audio message' },
29
+ { name: 'Send Video', value: 'sendVideo', description: 'Send a video message' },
30
+ { name: 'Send Button Template', value: 'sendButtonTemplate', description: 'Send interactive buttons' },
31
+ { name: 'Send Generic Template', value: 'sendGenericTemplate', description: 'Send a carousel of cards' },
32
+ { name: 'Send Quick Replies', value: 'sendQuickReplies', description: 'Send quick reply options' },
33
+ ];
34
+ // User operations
35
+ const userOperations = [
36
+ { name: 'Get Profile', value: 'getProfile', description: 'Get user profile by IGSID' },
37
+ { name: 'Get My Profile', value: 'getMyProfile', description: 'Get authenticated account profile' },
38
+ ];
39
+ // Comment operations
40
+ const commentOperations = [
41
+ { name: 'Get Comments', value: 'getComments', description: 'Get comments on a media' },
42
+ { name: 'Get Replies', value: 'getReplies', description: 'Get replies to a comment' },
43
+ { name: 'Reply', value: 'reply', description: 'Reply to a comment publicly' },
44
+ { name: 'Send Private Reply', value: 'sendPrivateReply', description: 'Send a private DM reply' },
45
+ { name: 'Delete', value: 'delete', description: 'Delete a comment' },
46
+ { name: 'Toggle Visibility', value: 'toggleVisibility', description: 'Hide or unhide a comment' },
47
+ ];
48
+ // Post operations
49
+ const postOperations = [
50
+ { name: 'Create Single', value: 'createSingle', description: 'Create a single image or video post' },
51
+ { name: 'Create Carousel', value: 'createCarousel', description: 'Create a carousel post' },
52
+ { name: 'Publish', value: 'publish', description: 'Publish a media container' },
53
+ ];
54
+ // Story operations
55
+ const storyOperations = [
56
+ { name: 'Create', value: 'create', description: 'Create and publish a story' },
57
+ ];
58
+ // Reel operations
59
+ const reelOperations = [
60
+ { name: 'Create', value: 'create', description: 'Create and publish a reel' },
61
+ ];
62
+ // Media operations
63
+ const mediaOperations = [
64
+ { name: 'List', value: 'list', description: 'List all media' },
65
+ { name: 'Get', value: 'get', description: 'Get media details by ID' },
66
+ { name: 'Get Children', value: 'getChildren', description: 'Get carousel children' },
67
+ ];
68
+ /**
69
+ * Format error for output
70
+ */
71
+ function formatError(error) {
72
+ if (error instanceof InstagramApiClient_1.InstagramApiError) {
73
+ return {
74
+ error: true,
75
+ code: error.code,
76
+ message: error.message,
77
+ subcode: error.subcode,
78
+ type: error.type,
79
+ suggestion: error.suggestion,
80
+ retryable: error.retryable,
81
+ retryAfter: error.retryAfter,
82
+ };
83
+ }
84
+ return {
85
+ error: true,
86
+ message: error.message,
87
+ };
88
+ }
89
+ /**
90
+ * Execute message operations
91
+ * Requirements: 3.1-3.5, 4.1-4.5, 5.1-5.6
92
+ */
93
+ async function executeMessageOperation(context, client, operation, itemIndex) {
94
+ const recipientId = context.getNodeParameter('recipientId', itemIndex);
95
+ switch (operation) {
96
+ case 'sendText': {
97
+ const text = context.getNodeParameter('messageText', itemIndex);
98
+ return client.sendTextMessage(recipientId, text);
99
+ }
100
+ case 'sendImage': {
101
+ const mediaUrl = context.getNodeParameter('mediaUrl', itemIndex);
102
+ return client.sendMediaMessage(recipientId, 'image', mediaUrl);
103
+ }
104
+ case 'sendAudio': {
105
+ const mediaUrl = context.getNodeParameter('mediaUrl', itemIndex);
106
+ return client.sendMediaMessage(recipientId, 'audio', mediaUrl);
107
+ }
108
+ case 'sendVideo': {
109
+ const mediaUrl = context.getNodeParameter('mediaUrl', itemIndex);
110
+ return client.sendMediaMessage(recipientId, 'video', mediaUrl);
111
+ }
112
+ case 'sendButtonTemplate': {
113
+ const text = context.getNodeParameter('messageText', itemIndex);
114
+ const buttonsData = context.getNodeParameter('buttons.buttonValues', itemIndex, []);
115
+ const buttons = buttonsData.map((b) => ({
116
+ type: b.type,
117
+ title: b.title,
118
+ url: b.url,
119
+ payload: b.payload,
120
+ }));
121
+ return client.sendButtonTemplate(recipientId, text, buttons);
122
+ }
123
+ case 'sendGenericTemplate': {
124
+ const elementsData = context.getNodeParameter('carouselElements.elementValues', itemIndex, []);
125
+ const elements = elementsData.map((e) => ({
126
+ title: e.title,
127
+ subtitle: e.subtitle,
128
+ image_url: e.image_url,
129
+ }));
130
+ return client.sendGenericTemplate(recipientId, elements);
131
+ }
132
+ case 'sendQuickReplies': {
133
+ const text = context.getNodeParameter('messageText', itemIndex);
134
+ const repliesData = context.getNodeParameter('quickReplies.replyValues', itemIndex, []);
135
+ const quickReplies = repliesData.map((r) => ({
136
+ content_type: 'text',
137
+ title: r.title,
138
+ payload: r.payload,
139
+ image_url: r.image_url,
140
+ }));
141
+ return client.sendQuickReplies(recipientId, text, quickReplies);
142
+ }
143
+ default:
144
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Unknown message operation: ${operation}`);
145
+ }
146
+ }
147
+ /**
148
+ * Execute user operations
149
+ * Requirements: 6.1-6.3
150
+ */
151
+ async function executeUserOperation(context, client, operation, itemIndex) {
152
+ switch (operation) {
153
+ case 'getProfile': {
154
+ const userId = context.getNodeParameter('userId', itemIndex);
155
+ return client.getUserProfile(userId);
156
+ }
157
+ case 'getMyProfile': {
158
+ return client.getMyProfile();
159
+ }
160
+ default:
161
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Unknown user operation: ${operation}`);
162
+ }
163
+ }
164
+ /**
165
+ * Execute comment operations
166
+ * Requirements: 7.1-7.7
167
+ */
168
+ async function executeCommentOperation(context, client, operation, itemIndex) {
169
+ switch (operation) {
170
+ case 'getComments': {
171
+ const mediaId = context.getNodeParameter('mediaId', itemIndex);
172
+ const options = context.getNodeParameter('commentOptions', itemIndex, {});
173
+ return client.getComments(mediaId, {
174
+ limit: options.limit,
175
+ after: options.after,
176
+ });
177
+ }
178
+ case 'getReplies': {
179
+ const commentId = context.getNodeParameter('commentId', itemIndex);
180
+ return client.getReplies(commentId);
181
+ }
182
+ case 'reply': {
183
+ const commentId = context.getNodeParameter('commentId', itemIndex);
184
+ const message = context.getNodeParameter('replyMessage', itemIndex);
185
+ return client.replyToComment(commentId, message);
186
+ }
187
+ case 'sendPrivateReply': {
188
+ const commentId = context.getNodeParameter('commentId', itemIndex);
189
+ const message = context.getNodeParameter('replyMessage', itemIndex);
190
+ return client.sendPrivateReply(commentId, message);
191
+ }
192
+ case 'delete': {
193
+ const commentId = context.getNodeParameter('commentId', itemIndex);
194
+ return client.deleteComment(commentId);
195
+ }
196
+ case 'toggleVisibility': {
197
+ const commentId = context.getNodeParameter('commentId', itemIndex);
198
+ const hide = context.getNodeParameter('hideComment', itemIndex);
199
+ return client.toggleCommentVisibility(commentId, hide);
200
+ }
201
+ default:
202
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Unknown comment operation: ${operation}`);
203
+ }
204
+ }
205
+ /**
206
+ * Execute post operations
207
+ * Requirements: 8.1-8.5, 9.1-9.5
208
+ */
209
+ async function executePostOperation(context, client, operation, itemIndex) {
210
+ switch (operation) {
211
+ case 'createSingle': {
212
+ const mediaType = context.getNodeParameter('postMediaType', itemIndex);
213
+ const mediaUrl = context.getNodeParameter('postMediaUrl', itemIndex);
214
+ const caption = context.getNodeParameter('caption', itemIndex, '');
215
+ const options = context.getNodeParameter('postOptions', itemIndex, {});
216
+ const userTagsData = context.getNodeParameter('userTags.tagValues', itemIndex, []);
217
+ const userTags = userTagsData.map((t) => ({
218
+ username: t.username,
219
+ x: t.x,
220
+ y: t.y,
221
+ }));
222
+ return client.createMediaContainer(mediaType, mediaUrl, {
223
+ caption: caption || undefined,
224
+ location_id: options.location_id,
225
+ thumb_offset: options.thumb_offset,
226
+ user_tags: userTags.length > 0 ? userTags : undefined,
227
+ });
228
+ }
229
+ case 'createCarousel': {
230
+ const inputMode = context.getNodeParameter('carouselInputMode', itemIndex, 'commaSeparated');
231
+ let childrenIds;
232
+ if (inputMode === 'json') {
233
+ // JSON/Expression mode - parse JSON array
234
+ const jsonInput = context.getNodeParameter('childrenIdsJson', itemIndex);
235
+ if (Array.isArray(jsonInput)) {
236
+ // Already parsed by n8n (from expression)
237
+ childrenIds = jsonInput.map(id => String(id).trim()).filter(Boolean);
238
+ }
239
+ else if (typeof jsonInput === 'string') {
240
+ // Parse JSON string
241
+ try {
242
+ const parsed = JSON.parse(jsonInput);
243
+ if (!Array.isArray(parsed)) {
244
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Children IDs must be a JSON array');
245
+ }
246
+ childrenIds = parsed.map((id) => String(id).trim()).filter(Boolean);
247
+ }
248
+ catch (parseError) {
249
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Invalid JSON for carousel children IDs: ${parseError.message}`);
250
+ }
251
+ }
252
+ else {
253
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Invalid input for carousel children IDs');
254
+ }
255
+ }
256
+ else {
257
+ // Comma-separated mode
258
+ const childrenIdsStr = context.getNodeParameter('childrenIds', itemIndex);
259
+ childrenIds = childrenIdsStr.split(',').map((id) => id.trim()).filter(Boolean);
260
+ }
261
+ if (childrenIds.length < 2 || childrenIds.length > 10) {
262
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Carousel must have 2-10 items, got ${childrenIds.length}`);
263
+ }
264
+ const caption = context.getNodeParameter('caption', itemIndex, '');
265
+ return client.createCarouselContainer(childrenIds, caption || undefined);
266
+ }
267
+ case 'publish': {
268
+ const containerId = context.getNodeParameter('containerId', itemIndex);
269
+ return client.publishMedia(containerId);
270
+ }
271
+ default:
272
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Unknown post operation: ${operation}`);
273
+ }
274
+ }
275
+ /**
276
+ * Execute story operations
277
+ * Requirements: 10.2
278
+ */
279
+ async function executeStoryOperation(context, client, itemIndex) {
280
+ const mediaUrl = context.getNodeParameter('storyMediaUrl', itemIndex);
281
+ // Create story container
282
+ const container = await client.createMediaContainer('STORIES', mediaUrl);
283
+ // Auto-publish the story
284
+ return client.publishMedia(container.id);
285
+ }
286
+ /**
287
+ * Execute reel operations
288
+ * Requirements: 10.1, 10.4, 10.5
289
+ */
290
+ async function executeReelOperation(context, client, itemIndex) {
291
+ const videoUrl = context.getNodeParameter('reelVideoUrl', itemIndex);
292
+ const caption = context.getNodeParameter('reelCaption', itemIndex, '');
293
+ const options = context.getNodeParameter('reelOptions', itemIndex, {});
294
+ // Create reel container
295
+ const container = await client.createMediaContainer('REELS', videoUrl, {
296
+ caption: caption || undefined,
297
+ audio_name: options.audio_name,
298
+ thumb_offset: options.thumb_offset,
299
+ });
300
+ // Auto-publish the reel
301
+ return client.publishMedia(container.id);
302
+ }
303
+ /**
304
+ * Execute media operations
305
+ * Requirements: 11.1-11.3
306
+ */
307
+ async function executeMediaOperation(context, client, operation, itemIndex) {
308
+ switch (operation) {
309
+ case 'list': {
310
+ const options = context.getNodeParameter('mediaOptions', itemIndex, {});
311
+ return client.listMedia({
312
+ limit: options.limit,
313
+ after: options.after,
314
+ });
315
+ }
316
+ case 'get': {
317
+ const mediaId = context.getNodeParameter('mediaIdForGet', itemIndex);
318
+ return client.getMedia(mediaId);
319
+ }
320
+ case 'getChildren': {
321
+ const mediaId = context.getNodeParameter('mediaIdForGet', itemIndex);
322
+ return client.getMediaChildren(mediaId);
323
+ }
324
+ default:
325
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Unknown media operation: ${operation}`);
326
+ }
327
+ }
328
+ class Instagram {
329
+ constructor() {
330
+ this.description = {
331
+ displayName: 'Instagram',
332
+ name: 'instagram',
333
+ icon: 'file:instagram.svg',
334
+ group: ['transform'],
335
+ version: 1,
336
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
337
+ description: 'Instagram Messaging and Content API',
338
+ defaults: {
339
+ name: 'Instagram',
340
+ },
341
+ inputs: ['main'],
342
+ outputs: ['main'],
343
+ credentials: [
344
+ {
345
+ name: 'instagramOAuth2Api',
346
+ required: true,
347
+ displayOptions: {
348
+ show: {
349
+ authentication: ['oAuth2'],
350
+ },
351
+ },
352
+ },
353
+ {
354
+ name: 'instagramAccessTokenApi',
355
+ required: true,
356
+ displayOptions: {
357
+ show: {
358
+ authentication: ['accessToken'],
359
+ },
360
+ },
361
+ },
362
+ ],
363
+ properties: [
364
+ // Authentication selector
365
+ {
366
+ displayName: 'Authentication',
367
+ name: 'authentication',
368
+ type: 'options',
369
+ options: [
370
+ { name: 'OAuth2', value: 'oAuth2' },
371
+ { name: 'Access Token', value: 'accessToken' },
372
+ ],
373
+ default: 'oAuth2',
374
+ },
375
+ // Resource selector
376
+ {
377
+ displayName: 'Resource',
378
+ name: 'resource',
379
+ type: 'options',
380
+ noDataExpression: true,
381
+ options: resourceOptions,
382
+ default: 'message',
383
+ },
384
+ // Operation selectors per resource
385
+ {
386
+ displayName: 'Operation',
387
+ name: 'operation',
388
+ type: 'options',
389
+ noDataExpression: true,
390
+ displayOptions: { show: { resource: ['message'] } },
391
+ options: messageOperations,
392
+ default: 'sendText',
393
+ },
394
+ {
395
+ displayName: 'Operation',
396
+ name: 'operation',
397
+ type: 'options',
398
+ noDataExpression: true,
399
+ displayOptions: { show: { resource: ['user'] } },
400
+ options: userOperations,
401
+ default: 'getProfile',
402
+ },
403
+ {
404
+ displayName: 'Operation',
405
+ name: 'operation',
406
+ type: 'options',
407
+ noDataExpression: true,
408
+ displayOptions: { show: { resource: ['comment'] } },
409
+ options: commentOperations,
410
+ default: 'getComments',
411
+ },
412
+ {
413
+ displayName: 'Operation',
414
+ name: 'operation',
415
+ type: 'options',
416
+ noDataExpression: true,
417
+ displayOptions: { show: { resource: ['post'] } },
418
+ options: postOperations,
419
+ default: 'createSingle',
420
+ },
421
+ {
422
+ displayName: 'Operation',
423
+ name: 'operation',
424
+ type: 'options',
425
+ noDataExpression: true,
426
+ displayOptions: { show: { resource: ['story'] } },
427
+ options: storyOperations,
428
+ default: 'create',
429
+ },
430
+ {
431
+ displayName: 'Operation',
432
+ name: 'operation',
433
+ type: 'options',
434
+ noDataExpression: true,
435
+ displayOptions: { show: { resource: ['reel'] } },
436
+ options: reelOperations,
437
+ default: 'create',
438
+ },
439
+ {
440
+ displayName: 'Operation',
441
+ name: 'operation',
442
+ type: 'options',
443
+ noDataExpression: true,
444
+ displayOptions: { show: { resource: ['media'] } },
445
+ options: mediaOperations,
446
+ default: 'list',
447
+ },
448
+ // ============================================
449
+ // MESSAGE RESOURCE FIELDS
450
+ // ============================================
451
+ // Recipient ID - common for all message operations
452
+ {
453
+ displayName: 'Recipient ID',
454
+ name: 'recipientId',
455
+ type: 'string',
456
+ required: true,
457
+ default: '',
458
+ description: 'Instagram-scoped User ID (IGSID) of the recipient',
459
+ displayOptions: {
460
+ show: {
461
+ resource: ['message'],
462
+ },
463
+ },
464
+ },
465
+ // Text message field
466
+ {
467
+ displayName: 'Message Text',
468
+ name: 'messageText',
469
+ type: 'string',
470
+ typeOptions: { rows: 4 },
471
+ required: true,
472
+ default: '',
473
+ description: 'Text message to send (max 1000 characters)',
474
+ displayOptions: {
475
+ show: {
476
+ resource: ['message'],
477
+ operation: ['sendText', 'sendButtonTemplate', 'sendQuickReplies'],
478
+ },
479
+ },
480
+ },
481
+ // Media URL for image/audio/video
482
+ {
483
+ displayName: 'Media URL',
484
+ name: 'mediaUrl',
485
+ type: 'string',
486
+ required: true,
487
+ default: '',
488
+ description: 'Public HTTPS URL of the media file',
489
+ displayOptions: {
490
+ show: {
491
+ resource: ['message'],
492
+ operation: ['sendImage', 'sendAudio', 'sendVideo'],
493
+ },
494
+ },
495
+ },
496
+ // Buttons for button template
497
+ {
498
+ displayName: 'Buttons',
499
+ name: 'buttons',
500
+ type: 'fixedCollection',
501
+ typeOptions: { multipleValues: true },
502
+ default: {},
503
+ description: 'Buttons to include (max 3)',
504
+ displayOptions: {
505
+ show: {
506
+ resource: ['message'],
507
+ operation: ['sendButtonTemplate'],
508
+ },
509
+ },
510
+ options: [
511
+ {
512
+ name: 'buttonValues',
513
+ displayName: 'Button',
514
+ values: [
515
+ {
516
+ displayName: 'Type',
517
+ name: 'type',
518
+ type: 'options',
519
+ options: [
520
+ { name: 'Web URL', value: 'web_url' },
521
+ { name: 'Postback', value: 'postback' },
522
+ ],
523
+ default: 'web_url',
524
+ },
525
+ {
526
+ displayName: 'Title',
527
+ name: 'title',
528
+ type: 'string',
529
+ default: '',
530
+ description: 'Button title (max 20 characters)',
531
+ },
532
+ {
533
+ displayName: 'URL',
534
+ name: 'url',
535
+ type: 'string',
536
+ default: '',
537
+ description: 'URL to open (for web_url type)',
538
+ displayOptions: { show: { type: ['web_url'] } },
539
+ },
540
+ {
541
+ displayName: 'Payload',
542
+ name: 'payload',
543
+ type: 'string',
544
+ default: '',
545
+ description: 'Postback payload (for postback type)',
546
+ displayOptions: { show: { type: ['postback'] } },
547
+ },
548
+ ],
549
+ },
550
+ ],
551
+ },
552
+ // Carousel elements for generic template
553
+ {
554
+ displayName: 'Carousel Elements',
555
+ name: 'carouselElements',
556
+ type: 'fixedCollection',
557
+ typeOptions: { multipleValues: true },
558
+ default: {},
559
+ description: 'Carousel cards to include',
560
+ displayOptions: {
561
+ show: {
562
+ resource: ['message'],
563
+ operation: ['sendGenericTemplate'],
564
+ },
565
+ },
566
+ options: [
567
+ {
568
+ name: 'elementValues',
569
+ displayName: 'Element',
570
+ values: [
571
+ {
572
+ displayName: 'Title',
573
+ name: 'title',
574
+ type: 'string',
575
+ default: '',
576
+ required: true,
577
+ },
578
+ {
579
+ displayName: 'Subtitle',
580
+ name: 'subtitle',
581
+ type: 'string',
582
+ default: '',
583
+ },
584
+ {
585
+ displayName: 'Image URL',
586
+ name: 'image_url',
587
+ type: 'string',
588
+ default: '',
589
+ description: 'HTTPS URL of the image',
590
+ },
591
+ ],
592
+ },
593
+ ],
594
+ },
595
+ // Quick replies
596
+ {
597
+ displayName: 'Quick Replies',
598
+ name: 'quickReplies',
599
+ type: 'fixedCollection',
600
+ typeOptions: { multipleValues: true },
601
+ default: {},
602
+ description: 'Quick reply options (max 13)',
603
+ displayOptions: {
604
+ show: {
605
+ resource: ['message'],
606
+ operation: ['sendQuickReplies'],
607
+ },
608
+ },
609
+ options: [
610
+ {
611
+ name: 'replyValues',
612
+ displayName: 'Quick Reply',
613
+ values: [
614
+ {
615
+ displayName: 'Title',
616
+ name: 'title',
617
+ type: 'string',
618
+ default: '',
619
+ required: true,
620
+ },
621
+ {
622
+ displayName: 'Payload',
623
+ name: 'payload',
624
+ type: 'string',
625
+ default: '',
626
+ required: true,
627
+ },
628
+ {
629
+ displayName: 'Image URL',
630
+ name: 'image_url',
631
+ type: 'string',
632
+ default: '',
633
+ description: 'Optional icon image URL',
634
+ },
635
+ ],
636
+ },
637
+ ],
638
+ },
639
+ // ============================================
640
+ // USER RESOURCE FIELDS
641
+ // ============================================
642
+ // User ID for getProfile
643
+ {
644
+ displayName: 'User ID',
645
+ name: 'userId',
646
+ type: 'string',
647
+ required: true,
648
+ default: '',
649
+ description: 'Instagram-scoped User ID (IGSID)',
650
+ displayOptions: {
651
+ show: {
652
+ resource: ['user'],
653
+ operation: ['getProfile'],
654
+ },
655
+ },
656
+ },
657
+ // ============================================
658
+ // COMMENT RESOURCE FIELDS
659
+ // ============================================
660
+ // Media ID for getComments
661
+ {
662
+ displayName: 'Media ID',
663
+ name: 'mediaId',
664
+ type: 'string',
665
+ required: true,
666
+ default: '',
667
+ description: 'ID of the media to get comments from',
668
+ displayOptions: {
669
+ show: {
670
+ resource: ['comment'],
671
+ operation: ['getComments'],
672
+ },
673
+ },
674
+ },
675
+ // Comment ID for various operations
676
+ {
677
+ displayName: 'Comment ID',
678
+ name: 'commentId',
679
+ type: 'string',
680
+ required: true,
681
+ default: '',
682
+ description: 'ID of the comment',
683
+ displayOptions: {
684
+ show: {
685
+ resource: ['comment'],
686
+ operation: ['getReplies', 'reply', 'sendPrivateReply', 'delete', 'toggleVisibility'],
687
+ },
688
+ },
689
+ },
690
+ // Reply message
691
+ {
692
+ displayName: 'Reply Message',
693
+ name: 'replyMessage',
694
+ type: 'string',
695
+ typeOptions: { rows: 3 },
696
+ required: true,
697
+ default: '',
698
+ description: 'Message to reply with',
699
+ displayOptions: {
700
+ show: {
701
+ resource: ['comment'],
702
+ operation: ['reply', 'sendPrivateReply'],
703
+ },
704
+ },
705
+ },
706
+ // Hide/unhide toggle
707
+ {
708
+ displayName: 'Hide Comment',
709
+ name: 'hideComment',
710
+ type: 'boolean',
711
+ default: true,
712
+ description: 'Whether to hide (true) or unhide (false) the comment',
713
+ displayOptions: {
714
+ show: {
715
+ resource: ['comment'],
716
+ operation: ['toggleVisibility'],
717
+ },
718
+ },
719
+ },
720
+ // Pagination options for comments
721
+ {
722
+ displayName: 'Options',
723
+ name: 'commentOptions',
724
+ type: 'collection',
725
+ placeholder: 'Add Option',
726
+ default: {},
727
+ displayOptions: {
728
+ show: {
729
+ resource: ['comment'],
730
+ operation: ['getComments'],
731
+ },
732
+ },
733
+ options: [
734
+ {
735
+ displayName: 'Limit',
736
+ name: 'limit',
737
+ type: 'number',
738
+ default: 25,
739
+ description: 'Number of comments to return',
740
+ },
741
+ {
742
+ displayName: 'After Cursor',
743
+ name: 'after',
744
+ type: 'string',
745
+ default: '',
746
+ description: 'Pagination cursor for next page',
747
+ },
748
+ ],
749
+ },
750
+ // ============================================
751
+ // POST RESOURCE FIELDS
752
+ // ============================================
753
+ // Media type for single post
754
+ {
755
+ displayName: 'Media Type',
756
+ name: 'postMediaType',
757
+ type: 'options',
758
+ options: [
759
+ { name: 'Image', value: 'IMAGE' },
760
+ { name: 'Video', value: 'VIDEO' },
761
+ ],
762
+ default: 'IMAGE',
763
+ displayOptions: {
764
+ show: {
765
+ resource: ['post'],
766
+ operation: ['createSingle'],
767
+ },
768
+ },
769
+ },
770
+ // Media URL for post
771
+ {
772
+ displayName: 'Media URL',
773
+ name: 'postMediaUrl',
774
+ type: 'string',
775
+ required: true,
776
+ default: '',
777
+ description: 'Public HTTPS URL of the image or video',
778
+ displayOptions: {
779
+ show: {
780
+ resource: ['post'],
781
+ operation: ['createSingle'],
782
+ },
783
+ },
784
+ },
785
+ // Caption for post
786
+ {
787
+ displayName: 'Caption',
788
+ name: 'caption',
789
+ type: 'string',
790
+ typeOptions: { rows: 4 },
791
+ default: '',
792
+ description: 'Post caption',
793
+ displayOptions: {
794
+ show: {
795
+ resource: ['post'],
796
+ operation: ['createSingle', 'createCarousel'],
797
+ },
798
+ },
799
+ },
800
+ // Carousel children IDs - Input Mode selector
801
+ {
802
+ displayName: 'Input Mode',
803
+ name: 'carouselInputMode',
804
+ type: 'options',
805
+ options: [
806
+ { name: 'Comma-Separated IDs', value: 'commaSeparated', description: 'Enter container IDs separated by commas' },
807
+ { name: 'JSON Array / Expression', value: 'json', description: 'Enter JSON array or use expression from previous node' },
808
+ ],
809
+ default: 'commaSeparated',
810
+ description: 'How to provide carousel item container IDs',
811
+ displayOptions: {
812
+ show: {
813
+ resource: ['post'],
814
+ operation: ['createCarousel'],
815
+ },
816
+ },
817
+ },
818
+ // Carousel children IDs - Comma-separated mode
819
+ {
820
+ displayName: 'Children Container IDs',
821
+ name: 'childrenIds',
822
+ type: 'string',
823
+ required: true,
824
+ default: '',
825
+ description: 'Comma-separated list of media container IDs (2-10 items)',
826
+ displayOptions: {
827
+ show: {
828
+ resource: ['post'],
829
+ operation: ['createCarousel'],
830
+ carouselInputMode: ['commaSeparated'],
831
+ },
832
+ },
833
+ },
834
+ // Carousel children IDs - JSON/Expression mode
835
+ {
836
+ displayName: 'Children Container IDs (JSON)',
837
+ name: 'childrenIdsJson',
838
+ type: 'json',
839
+ required: true,
840
+ default: '[]',
841
+ description: 'JSON array of container IDs. Supports expressions like {{ $json.containerIds }}. Example: ["123456", "789012"]',
842
+ displayOptions: {
843
+ show: {
844
+ resource: ['post'],
845
+ operation: ['createCarousel'],
846
+ carouselInputMode: ['json'],
847
+ },
848
+ },
849
+ },
850
+ // Container ID for publish
851
+ {
852
+ displayName: 'Container ID',
853
+ name: 'containerId',
854
+ type: 'string',
855
+ required: true,
856
+ default: '',
857
+ description: 'ID of the media container to publish',
858
+ displayOptions: {
859
+ show: {
860
+ resource: ['post'],
861
+ operation: ['publish'],
862
+ },
863
+ },
864
+ },
865
+ // Post options
866
+ {
867
+ displayName: 'Options',
868
+ name: 'postOptions',
869
+ type: 'collection',
870
+ placeholder: 'Add Option',
871
+ default: {},
872
+ displayOptions: {
873
+ show: {
874
+ resource: ['post'],
875
+ operation: ['createSingle'],
876
+ },
877
+ },
878
+ options: [
879
+ {
880
+ displayName: 'Location ID',
881
+ name: 'location_id',
882
+ type: 'string',
883
+ default: '',
884
+ description: 'Facebook Page ID of a location',
885
+ },
886
+ {
887
+ displayName: 'Thumbnail Offset (ms)',
888
+ name: 'thumb_offset',
889
+ type: 'number',
890
+ default: 0,
891
+ description: 'Video thumbnail position in milliseconds',
892
+ },
893
+ ],
894
+ },
895
+ // User tags for post
896
+ {
897
+ displayName: 'User Tags',
898
+ name: 'userTags',
899
+ type: 'fixedCollection',
900
+ typeOptions: { multipleValues: true },
901
+ default: {},
902
+ description: 'Tag users in the post (max 20)',
903
+ displayOptions: {
904
+ show: {
905
+ resource: ['post'],
906
+ operation: ['createSingle'],
907
+ },
908
+ },
909
+ options: [
910
+ {
911
+ name: 'tagValues',
912
+ displayName: 'User Tag',
913
+ values: [
914
+ {
915
+ displayName: 'Username',
916
+ name: 'username',
917
+ type: 'string',
918
+ default: '',
919
+ required: true,
920
+ },
921
+ {
922
+ displayName: 'X Position',
923
+ name: 'x',
924
+ type: 'number',
925
+ default: 0.5,
926
+ description: 'Horizontal position (0-1)',
927
+ },
928
+ {
929
+ displayName: 'Y Position',
930
+ name: 'y',
931
+ type: 'number',
932
+ default: 0.5,
933
+ description: 'Vertical position (0-1)',
934
+ },
935
+ ],
936
+ },
937
+ ],
938
+ },
939
+ // ============================================
940
+ // STORY RESOURCE FIELDS
941
+ // ============================================
942
+ // Media URL for story
943
+ {
944
+ displayName: 'Media URL',
945
+ name: 'storyMediaUrl',
946
+ type: 'string',
947
+ required: true,
948
+ default: '',
949
+ description: 'Public HTTPS URL of the image or video for the story',
950
+ displayOptions: {
951
+ show: {
952
+ resource: ['story'],
953
+ operation: ['create'],
954
+ },
955
+ },
956
+ },
957
+ // ============================================
958
+ // REEL RESOURCE FIELDS
959
+ // ============================================
960
+ // Video URL for reel
961
+ {
962
+ displayName: 'Video URL',
963
+ name: 'reelVideoUrl',
964
+ type: 'string',
965
+ required: true,
966
+ default: '',
967
+ description: 'Public HTTPS URL of the video (max 60 seconds)',
968
+ displayOptions: {
969
+ show: {
970
+ resource: ['reel'],
971
+ operation: ['create'],
972
+ },
973
+ },
974
+ },
975
+ // Reel caption
976
+ {
977
+ displayName: 'Caption',
978
+ name: 'reelCaption',
979
+ type: 'string',
980
+ typeOptions: { rows: 4 },
981
+ default: '',
982
+ description: 'Reel caption',
983
+ displayOptions: {
984
+ show: {
985
+ resource: ['reel'],
986
+ operation: ['create'],
987
+ },
988
+ },
989
+ },
990
+ // Reel options
991
+ {
992
+ displayName: 'Options',
993
+ name: 'reelOptions',
994
+ type: 'collection',
995
+ placeholder: 'Add Option',
996
+ default: {},
997
+ displayOptions: {
998
+ show: {
999
+ resource: ['reel'],
1000
+ operation: ['create'],
1001
+ },
1002
+ },
1003
+ options: [
1004
+ {
1005
+ displayName: 'Audio Name',
1006
+ name: 'audio_name',
1007
+ type: 'string',
1008
+ default: '',
1009
+ description: 'Name for audio attribution',
1010
+ },
1011
+ {
1012
+ displayName: 'Thumbnail Offset (ms)',
1013
+ name: 'thumb_offset',
1014
+ type: 'number',
1015
+ default: 0,
1016
+ description: 'Video thumbnail position in milliseconds',
1017
+ },
1018
+ ],
1019
+ },
1020
+ // ============================================
1021
+ // MEDIA RESOURCE FIELDS
1022
+ // ============================================
1023
+ // Media ID for get/getChildren
1024
+ {
1025
+ displayName: 'Media ID',
1026
+ name: 'mediaIdForGet',
1027
+ type: 'string',
1028
+ required: true,
1029
+ default: '',
1030
+ description: 'ID of the media',
1031
+ displayOptions: {
1032
+ show: {
1033
+ resource: ['media'],
1034
+ operation: ['get', 'getChildren'],
1035
+ },
1036
+ },
1037
+ },
1038
+ // Pagination options for media list
1039
+ {
1040
+ displayName: 'Options',
1041
+ name: 'mediaOptions',
1042
+ type: 'collection',
1043
+ placeholder: 'Add Option',
1044
+ default: {},
1045
+ displayOptions: {
1046
+ show: {
1047
+ resource: ['media'],
1048
+ operation: ['list'],
1049
+ },
1050
+ },
1051
+ options: [
1052
+ {
1053
+ displayName: 'Limit',
1054
+ name: 'limit',
1055
+ type: 'number',
1056
+ default: 25,
1057
+ description: 'Number of media items to return',
1058
+ },
1059
+ {
1060
+ displayName: 'After Cursor',
1061
+ name: 'after',
1062
+ type: 'string',
1063
+ default: '',
1064
+ description: 'Pagination cursor for next page',
1065
+ },
1066
+ ],
1067
+ },
1068
+ ],
1069
+ };
1070
+ }
1071
+ /**
1072
+ * Execute method - wires up all operations to InstagramApiClient
1073
+ * Requirements: All node operations, 14.4 (continueOnFail support)
1074
+ */
1075
+ async execute() {
1076
+ const items = this.getInputData();
1077
+ const returnData = [];
1078
+ // Get credentials
1079
+ const authentication = this.getNodeParameter('authentication', 0);
1080
+ let accessToken;
1081
+ let accountId;
1082
+ if (authentication === 'oAuth2') {
1083
+ const credentials = await this.getCredentials('instagramOAuth2Api');
1084
+ accessToken = credentials.longLivedToken || credentials.accessToken;
1085
+ accountId = credentials.instagramAccountId;
1086
+ }
1087
+ else {
1088
+ const credentials = await this.getCredentials('instagramAccessTokenApi');
1089
+ accessToken = credentials.accessToken;
1090
+ accountId = credentials.instagramAccountId;
1091
+ }
1092
+ if (!accessToken) {
1093
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No access token available. Please reconnect your credentials.');
1094
+ }
1095
+ if (!accountId) {
1096
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Instagram Account ID not found. Please reconnect your credentials.');
1097
+ }
1098
+ const client = new InstagramApiClient_1.InstagramApiClient(accessToken, accountId);
1099
+ for (let i = 0; i < items.length; i++) {
1100
+ try {
1101
+ const resource = this.getNodeParameter('resource', i);
1102
+ const operation = this.getNodeParameter('operation', i);
1103
+ let responseData;
1104
+ // Route to appropriate handler
1105
+ switch (resource) {
1106
+ case 'message':
1107
+ responseData = await executeMessageOperation(this, client, operation, i);
1108
+ break;
1109
+ case 'user':
1110
+ responseData = await executeUserOperation(this, client, operation, i);
1111
+ break;
1112
+ case 'comment':
1113
+ responseData = await executeCommentOperation(this, client, operation, i);
1114
+ break;
1115
+ case 'post':
1116
+ responseData = await executePostOperation(this, client, operation, i);
1117
+ break;
1118
+ case 'story':
1119
+ responseData = await executeStoryOperation(this, client, i);
1120
+ break;
1121
+ case 'reel':
1122
+ responseData = await executeReelOperation(this, client, i);
1123
+ break;
1124
+ case 'media':
1125
+ responseData = await executeMediaOperation(this, client, operation, i);
1126
+ break;
1127
+ default:
1128
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown resource: ${resource}`);
1129
+ }
1130
+ returnData.push({
1131
+ json: responseData,
1132
+ pairedItem: { item: i },
1133
+ });
1134
+ }
1135
+ catch (error) {
1136
+ // Requirement 14.4: continueOnFail support
1137
+ if (this.continueOnFail()) {
1138
+ const errorData = formatError(error);
1139
+ returnData.push({
1140
+ json: errorData,
1141
+ pairedItem: { item: i },
1142
+ });
1143
+ continue;
1144
+ }
1145
+ throw error;
1146
+ }
1147
+ }
1148
+ return [returnData];
1149
+ }
1150
+ }
1151
+ exports.Instagram = Instagram;
1152
+ //# sourceMappingURL=Instagram.node.js.map