@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,150 @@
1
+ "use strict";
2
+ /**
3
+ * TokenManager Service
4
+ * Handles token exchange and refresh for Instagram OAuth2 authentication
5
+ *
6
+ * Requirements:
7
+ * - 1.3: Exchange short-lived token for long-lived token (60 days validity)
8
+ * - 1.4: Auto-refresh when token is older than 24h AND expiring within 7 days
9
+ * - 1.5: Fallback to OAuth token exchange when refresh fails
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TokenManager = void 0;
13
+ class TokenManager {
14
+ constructor(options) {
15
+ this.baseUrl = options?.baseUrl ?? 'https://graph.facebook.com';
16
+ }
17
+ /**
18
+ * Exchange short-lived token for long-lived token (60 days validity)
19
+ * Requirement 1.3: WHEN a short-lived token is received, THE OAuth2_Credential
20
+ * SHALL automatically exchange it for a long-lived token (60 days validity)
21
+ *
22
+ * @param shortLivedToken - The short-lived access token (1 hour validity)
23
+ * @param appSecret - The Facebook App Secret
24
+ * @returns Long-lived token response with 60 days validity
25
+ */
26
+ async exchangeForLongLivedToken(shortLivedToken, appSecret) {
27
+ const url = new URL(`${this.baseUrl}/${TokenManager.GRAPH_API_VERSION}/oauth/access_token`);
28
+ url.searchParams.set('grant_type', 'fb_exchange_token');
29
+ url.searchParams.set('client_secret', appSecret);
30
+ url.searchParams.set('fb_exchange_token', shortLivedToken);
31
+ const response = await fetch(url.toString(), {
32
+ method: 'GET',
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ },
36
+ });
37
+ if (!response.ok) {
38
+ const errorData = (await response.json().catch(() => ({})));
39
+ const errorMessage = errorData?.error?.message ?? 'Failed to exchange token';
40
+ throw new Error(`Token exchange failed: ${errorMessage}`);
41
+ }
42
+ const data = (await response.json());
43
+ return {
44
+ access_token: data.access_token,
45
+ token_type: data.token_type ?? 'bearer',
46
+ expires_in: data.expires_in ?? 5184000, // Default 60 days
47
+ };
48
+ }
49
+ /**
50
+ * Refresh long-lived token (must be at least 24h old)
51
+ * Requirement 1.4: WHEN a long-lived token is older than 24 hours AND expiring
52
+ * within 7 days, THE OAuth2_Credential SHALL automatically refresh the token
53
+ *
54
+ * @param longLivedToken - The current long-lived access token
55
+ * @returns New long-lived token response
56
+ */
57
+ async refreshLongLivedToken(longLivedToken) {
58
+ const url = new URL(`${this.baseUrl}/${TokenManager.GRAPH_API_VERSION}/oauth/access_token`);
59
+ url.searchParams.set('grant_type', 'fb_exchange_token');
60
+ url.searchParams.set('fb_exchange_token', longLivedToken);
61
+ const response = await fetch(url.toString(), {
62
+ method: 'GET',
63
+ headers: {
64
+ 'Content-Type': 'application/json',
65
+ },
66
+ });
67
+ if (!response.ok) {
68
+ const errorData = (await response.json().catch(() => ({})));
69
+ const errorMessage = errorData?.error?.message ?? 'Failed to refresh token';
70
+ throw new Error(`Token refresh failed: ${errorMessage}`);
71
+ }
72
+ const data = (await response.json());
73
+ return {
74
+ access_token: data.access_token,
75
+ token_type: data.token_type ?? 'bearer',
76
+ expires_in: data.expires_in ?? 5184000, // Default 60 days
77
+ };
78
+ }
79
+ /**
80
+ * Check if token needs refresh (expiring within 7 days AND older than 24 hours)
81
+ * Requirement 1.4: Token should be refreshed when it's older than 24 hours
82
+ * AND expiring within 7 days
83
+ *
84
+ * @param expiresAt - Token expiration timestamp in milliseconds
85
+ * @param createdAt - Token creation timestamp in milliseconds (optional)
86
+ * @returns true if token should be refreshed
87
+ */
88
+ shouldRefreshToken(expiresAt, createdAt) {
89
+ const now = Date.now();
90
+ const sevenDaysInMs = TokenManager.REFRESH_THRESHOLD_DAYS * 24 * 60 * 60 * 1000;
91
+ const twentyFourHoursInMs = TokenManager.MIN_TOKEN_AGE_HOURS * 60 * 60 * 1000;
92
+ // Check if token is expiring within 7 days
93
+ const isExpiringWithin7Days = expiresAt - now <= sevenDaysInMs;
94
+ // If createdAt is provided, check if token is older than 24 hours
95
+ if (createdAt !== undefined) {
96
+ const isOlderThan24Hours = now - createdAt >= twentyFourHoursInMs;
97
+ return isExpiringWithin7Days && isOlderThan24Hours;
98
+ }
99
+ // If no createdAt, only check expiry (assume token is old enough)
100
+ return isExpiringWithin7Days;
101
+ }
102
+ /**
103
+ * Get valid token (auto-refresh if needed)
104
+ * Requirement 1.4 & 1.5: Auto-refresh token when needed, with fallback
105
+ *
106
+ * @param credentials - The OAuth2 credentials object
107
+ * @returns Valid access token
108
+ */
109
+ async getValidToken(credentials) {
110
+ // If we have a long-lived token, check if it needs refresh
111
+ if (credentials.longLivedToken && credentials.longLivedTokenExpiresAt) {
112
+ const needsRefresh = this.shouldRefreshToken(credentials.longLivedTokenExpiresAt, credentials.longLivedTokenCreatedAt);
113
+ if (!needsRefresh) {
114
+ // Token is still valid, return it
115
+ return credentials.longLivedToken;
116
+ }
117
+ // Token needs refresh - try to refresh it
118
+ try {
119
+ const refreshedToken = await this.refreshLongLivedToken(credentials.longLivedToken);
120
+ return refreshedToken.access_token;
121
+ }
122
+ catch {
123
+ // Requirement 1.5: Fallback to exchanging current OAuth token
124
+ if (credentials.accessToken && credentials.clientSecret) {
125
+ try {
126
+ const exchangedToken = await this.exchangeForLongLivedToken(credentials.accessToken, credentials.clientSecret);
127
+ return exchangedToken.access_token;
128
+ }
129
+ catch {
130
+ // Both refresh and exchange failed
131
+ throw new Error('Token refresh failed and fallback exchange also failed. Please re-authenticate.');
132
+ }
133
+ }
134
+ throw new Error('Token refresh failed. Please re-authenticate.');
135
+ }
136
+ }
137
+ // No long-lived token - try to exchange short-lived token
138
+ if (credentials.accessToken && credentials.clientSecret) {
139
+ const exchangedToken = await this.exchangeForLongLivedToken(credentials.accessToken, credentials.clientSecret);
140
+ return exchangedToken.access_token;
141
+ }
142
+ // No valid tokens available
143
+ throw new Error('No valid token available. Please authenticate first.');
144
+ }
145
+ }
146
+ exports.TokenManager = TokenManager;
147
+ TokenManager.REFRESH_THRESHOLD_DAYS = 7;
148
+ TokenManager.MIN_TOKEN_AGE_HOURS = 24;
149
+ TokenManager.GRAPH_API_VERSION = 'v18.0';
150
+ //# sourceMappingURL=TokenManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenManager.js","sourceRoot":"","sources":["../../src/services/TokenManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Validation Utilities for Instagram API inputs
3
+ * Validates inputs according to Instagram Graph API constraints
4
+ */
5
+ import type { IButton, IQuickReply, IUserTag } from './InstagramApiClient';
6
+ export interface IValidationResult {
7
+ valid: boolean;
8
+ error?: string;
9
+ }
10
+ /**
11
+ * Validates text message length (max 1000 characters)
12
+ * Requirements: 3.2
13
+ */
14
+ export declare function validateTextMessage(text: string): IValidationResult;
15
+ /**
16
+ * Validates media URL is HTTPS
17
+ * Requirements: 4.4
18
+ */
19
+ export declare function validateMediaUrl(url: string): IValidationResult;
20
+ /**
21
+ * Validates button template constraints
22
+ * - Max 3 buttons
23
+ * - Max 20 characters per button title
24
+ * Requirements: 5.1, 5.2
25
+ */
26
+ export declare function validateButtons(buttons: IButton[]): IValidationResult;
27
+ /**
28
+ * Validates quick replies constraints (max 13 items)
29
+ * Requirements: 5.4, 5.5
30
+ */
31
+ export declare function validateQuickReplies(quickReplies: IQuickReply[]): IValidationResult;
32
+ /**
33
+ * Validates user tags constraints
34
+ * - Max 20 tags
35
+ * - x, y positions must be between 0 and 1
36
+ * Requirements: 8.4, 8.5
37
+ */
38
+ export declare function validateUserTags(tags: IUserTag[]): IValidationResult;
39
+ /**
40
+ * Validates carousel items count (2-10 items)
41
+ * Requirements: 9.1, 9.2, 9.3
42
+ */
43
+ export declare function validateCarouselItems<T>(items: T[]): IValidationResult;
44
+ //# sourceMappingURL=ValidationUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ValidationUtils.d.ts","sourceRoot":"","sources":["../../src/services/ValidationUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE3E,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAanE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAsB/D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAiDrE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,iBAAiB,CAuCnF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,iBAAiB,CA2CpE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,iBAAiB,CAoBtE"}
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ /**
3
+ * Validation Utilities for Instagram API inputs
4
+ * Validates inputs according to Instagram Graph API constraints
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.validateTextMessage = validateTextMessage;
8
+ exports.validateMediaUrl = validateMediaUrl;
9
+ exports.validateButtons = validateButtons;
10
+ exports.validateQuickReplies = validateQuickReplies;
11
+ exports.validateUserTags = validateUserTags;
12
+ exports.validateCarouselItems = validateCarouselItems;
13
+ /**
14
+ * Validates text message length (max 1000 characters)
15
+ * Requirements: 3.2
16
+ */
17
+ function validateTextMessage(text) {
18
+ if (typeof text !== 'string') {
19
+ return { valid: false, error: 'Message text must be a string' };
20
+ }
21
+ if (text.length > 1000) {
22
+ return {
23
+ valid: false,
24
+ error: `Message text exceeds 1000 character limit (${text.length} characters)`,
25
+ };
26
+ }
27
+ return { valid: true };
28
+ }
29
+ /**
30
+ * Validates media URL is HTTPS
31
+ * Requirements: 4.4
32
+ */
33
+ function validateMediaUrl(url) {
34
+ if (typeof url !== 'string') {
35
+ return { valid: false, error: 'Media URL must be a string' };
36
+ }
37
+ if (!url.trim()) {
38
+ return { valid: false, error: 'Media URL cannot be empty' };
39
+ }
40
+ try {
41
+ const parsedUrl = new URL(url);
42
+ if (parsedUrl.protocol !== 'https:') {
43
+ return {
44
+ valid: false,
45
+ error: `Media URL must use HTTPS protocol (got ${parsedUrl.protocol.replace(':', '')})`,
46
+ };
47
+ }
48
+ }
49
+ catch {
50
+ return { valid: false, error: 'Invalid URL format' };
51
+ }
52
+ return { valid: true };
53
+ }
54
+ /**
55
+ * Validates button template constraints
56
+ * - Max 3 buttons
57
+ * - Max 20 characters per button title
58
+ * Requirements: 5.1, 5.2
59
+ */
60
+ function validateButtons(buttons) {
61
+ if (!Array.isArray(buttons)) {
62
+ return { valid: false, error: 'Buttons must be an array' };
63
+ }
64
+ if (buttons.length === 0) {
65
+ return { valid: false, error: 'At least one button is required' };
66
+ }
67
+ if (buttons.length > 3) {
68
+ return {
69
+ valid: false,
70
+ error: `Button count exceeds maximum of 3 (got ${buttons.length})`,
71
+ };
72
+ }
73
+ for (let i = 0; i < buttons.length; i++) {
74
+ const button = buttons[i];
75
+ if (!button.title || typeof button.title !== 'string') {
76
+ return { valid: false, error: `Button ${i + 1}: title is required and must be a string` };
77
+ }
78
+ if (button.title.length > 20) {
79
+ return {
80
+ valid: false,
81
+ error: `Button ${i + 1}: title exceeds 20 character limit (${button.title.length} characters)`,
82
+ };
83
+ }
84
+ if (!['web_url', 'postback'].includes(button.type)) {
85
+ return {
86
+ valid: false,
87
+ error: `Button ${i + 1}: type must be 'web_url' or 'postback'`,
88
+ };
89
+ }
90
+ if (button.type === 'web_url' && button.url) {
91
+ const urlValidation = validateMediaUrl(button.url);
92
+ if (!urlValidation.valid) {
93
+ return {
94
+ valid: false,
95
+ error: `Button ${i + 1}: ${urlValidation.error}`,
96
+ };
97
+ }
98
+ }
99
+ }
100
+ return { valid: true };
101
+ }
102
+ /**
103
+ * Validates quick replies constraints (max 13 items)
104
+ * Requirements: 5.4, 5.5
105
+ */
106
+ function validateQuickReplies(quickReplies) {
107
+ if (!Array.isArray(quickReplies)) {
108
+ return { valid: false, error: 'Quick replies must be an array' };
109
+ }
110
+ if (quickReplies.length === 0) {
111
+ return { valid: false, error: 'At least one quick reply is required' };
112
+ }
113
+ if (quickReplies.length > 13) {
114
+ return {
115
+ valid: false,
116
+ error: `Quick reply count exceeds maximum of 13 (got ${quickReplies.length})`,
117
+ };
118
+ }
119
+ for (let i = 0; i < quickReplies.length; i++) {
120
+ const qr = quickReplies[i];
121
+ if (!qr.title || typeof qr.title !== 'string') {
122
+ return { valid: false, error: `Quick reply ${i + 1}: title is required and must be a string` };
123
+ }
124
+ if (!qr.payload || typeof qr.payload !== 'string') {
125
+ return { valid: false, error: `Quick reply ${i + 1}: payload is required and must be a string` };
126
+ }
127
+ if (qr.image_url) {
128
+ const urlValidation = validateMediaUrl(qr.image_url);
129
+ if (!urlValidation.valid) {
130
+ return {
131
+ valid: false,
132
+ error: `Quick reply ${i + 1}: ${urlValidation.error}`,
133
+ };
134
+ }
135
+ }
136
+ }
137
+ return { valid: true };
138
+ }
139
+ /**
140
+ * Validates user tags constraints
141
+ * - Max 20 tags
142
+ * - x, y positions must be between 0 and 1
143
+ * Requirements: 8.4, 8.5
144
+ */
145
+ function validateUserTags(tags) {
146
+ if (!Array.isArray(tags)) {
147
+ return { valid: false, error: 'User tags must be an array' };
148
+ }
149
+ if (tags.length > 20) {
150
+ return {
151
+ valid: false,
152
+ error: `User tag count exceeds maximum of 20 (got ${tags.length})`,
153
+ };
154
+ }
155
+ for (let i = 0; i < tags.length; i++) {
156
+ const tag = tags[i];
157
+ if (!tag.username || typeof tag.username !== 'string') {
158
+ return { valid: false, error: `User tag ${i + 1}: username is required and must be a string` };
159
+ }
160
+ if (typeof tag.x !== 'number' || isNaN(tag.x)) {
161
+ return { valid: false, error: `User tag ${i + 1}: x position must be a number` };
162
+ }
163
+ if (typeof tag.y !== 'number' || isNaN(tag.y)) {
164
+ return { valid: false, error: `User tag ${i + 1}: y position must be a number` };
165
+ }
166
+ if (tag.x < 0 || tag.x > 1) {
167
+ return {
168
+ valid: false,
169
+ error: `User tag ${i + 1}: x position must be between 0 and 1 (got ${tag.x})`,
170
+ };
171
+ }
172
+ if (tag.y < 0 || tag.y > 1) {
173
+ return {
174
+ valid: false,
175
+ error: `User tag ${i + 1}: y position must be between 0 and 1 (got ${tag.y})`,
176
+ };
177
+ }
178
+ }
179
+ return { valid: true };
180
+ }
181
+ /**
182
+ * Validates carousel items count (2-10 items)
183
+ * Requirements: 9.1, 9.2, 9.3
184
+ */
185
+ function validateCarouselItems(items) {
186
+ if (!Array.isArray(items)) {
187
+ return { valid: false, error: 'Carousel items must be an array' };
188
+ }
189
+ if (items.length < 2) {
190
+ return {
191
+ valid: false,
192
+ error: `Carousel requires at least 2 items (got ${items.length})`,
193
+ };
194
+ }
195
+ if (items.length > 10) {
196
+ return {
197
+ valid: false,
198
+ error: `Carousel exceeds maximum of 10 items (got ${items.length})`,
199
+ };
200
+ }
201
+ return { valid: true };
202
+ }
203
+ //# sourceMappingURL=ValidationUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ValidationUtils.js","sourceRoot":"","sources":["../../src/services/ValidationUtils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,206 @@
1
+ /**
2
+ * WebhookHandler Service
3
+ * Handles Instagram webhook verification, signature validation, and event processing
4
+ *
5
+ * Requirements covered:
6
+ * - 12.1: Webhook verification with hub.challenge response
7
+ * - 12.2: Incoming message event parsing (sender, text, attachments)
8
+ * - 12.3: Postback event parsing (title, payload)
9
+ * - 12.4: Opt-in event parsing (ref, user_ref)
10
+ * - 12.5: X-Hub-Signature-256 validation using HMAC SHA256
11
+ * - 12.6: Signature validation failure handling
12
+ * - 13.1: Comment event parsing (media_id, comment text, commenter info)
13
+ * - 13.2: Mention event parsing (media_id, mentioned by)
14
+ * - 13.3: Dual output system (Output 1: messaging, Output 2: content)
15
+ */
16
+ export interface IVerifyQuery {
17
+ 'hub.mode'?: string;
18
+ 'hub.verify_token'?: string;
19
+ 'hub.challenge'?: string;
20
+ }
21
+ export interface IAttachment {
22
+ type: 'image' | 'audio' | 'video' | 'file';
23
+ payload: {
24
+ url: string;
25
+ };
26
+ }
27
+ export interface IMessageContent {
28
+ mid: string;
29
+ text?: string;
30
+ attachments?: IAttachment[];
31
+ }
32
+ export interface IPostbackContent {
33
+ mid: string;
34
+ title: string;
35
+ payload: string;
36
+ }
37
+ export interface IOptinContent {
38
+ ref: string;
39
+ user_ref?: string;
40
+ }
41
+ export interface IMessagingEvent {
42
+ sender: {
43
+ id: string;
44
+ };
45
+ recipient: {
46
+ id: string;
47
+ };
48
+ timestamp: number;
49
+ message?: IMessageContent;
50
+ postback?: IPostbackContent;
51
+ optin?: IOptinContent;
52
+ }
53
+ export interface IChangeEvent {
54
+ field: 'comments' | 'mentions';
55
+ value: {
56
+ media_id: string;
57
+ id: string;
58
+ text?: string;
59
+ from: {
60
+ id: string;
61
+ username: string;
62
+ };
63
+ };
64
+ }
65
+ export interface IWebhookEntry {
66
+ id: string;
67
+ time: number;
68
+ messaging?: IMessagingEvent[];
69
+ changes?: IChangeEvent[];
70
+ }
71
+ export interface IWebhookBody {
72
+ object: 'instagram';
73
+ entry: IWebhookEntry[];
74
+ }
75
+ export interface IWebhookResult {
76
+ messagingEvents: IMessagingEvent[];
77
+ contentEvents: IChangeEvent[];
78
+ }
79
+ export interface IParsedMessageEvent {
80
+ type: 'message';
81
+ senderId: string;
82
+ recipientId: string;
83
+ timestamp: number;
84
+ messageId: string;
85
+ text?: string;
86
+ attachments?: IAttachment[];
87
+ }
88
+ export interface IParsedPostbackEvent {
89
+ type: 'postback';
90
+ senderId: string;
91
+ recipientId: string;
92
+ timestamp: number;
93
+ messageId: string;
94
+ title: string;
95
+ payload: string;
96
+ }
97
+ export interface IParsedOptinEvent {
98
+ type: 'optin';
99
+ senderId: string;
100
+ recipientId: string;
101
+ timestamp: number;
102
+ ref: string;
103
+ userRef?: string;
104
+ }
105
+ export interface IParsedCommentEvent {
106
+ type: 'comment';
107
+ mediaId: string;
108
+ commentId: string;
109
+ text?: string;
110
+ from: {
111
+ id: string;
112
+ username: string;
113
+ };
114
+ }
115
+ export interface IParsedMentionEvent {
116
+ type: 'mention';
117
+ mediaId: string;
118
+ mentionId: string;
119
+ from: {
120
+ id: string;
121
+ username: string;
122
+ };
123
+ }
124
+ export type ParsedMessagingEvent = IParsedMessageEvent | IParsedPostbackEvent | IParsedOptinEvent;
125
+ export type ParsedContentEvent = IParsedCommentEvent | IParsedMentionEvent;
126
+ export declare class WebhookHandler {
127
+ /**
128
+ * Verify webhook subscription request (Requirement 12.1)
129
+ * Returns challenge if verify token matches, null otherwise
130
+ *
131
+ * @param query - The query parameters from the verification request
132
+ * @param expectedVerifyToken - The expected verify token configured in the app
133
+ * @returns The challenge string if verification succeeds, null otherwise
134
+ */
135
+ verifyWebhook(query: IVerifyQuery, expectedVerifyToken: string): string | null;
136
+ /**
137
+ * Validate webhook signature using HMAC SHA256 (Requirements 12.5, 12.6)
138
+ * Uses timing-safe comparison to prevent timing attacks
139
+ *
140
+ * @param payload - The raw request body as a string
141
+ * @param signature - The X-Hub-Signature-256 header value
142
+ * @param appSecret - The app secret used for HMAC calculation
143
+ * @returns true if signature is valid, false otherwise
144
+ */
145
+ validateSignature(payload: string, signature: string, appSecret: string): boolean;
146
+ /**
147
+ * Process webhook payload and route events to appropriate outputs (Requirement 13.3)
148
+ * Output 1: Messaging events (messages, postbacks, optins)
149
+ * Output 2: Content events (comments, mentions)
150
+ *
151
+ * @param body - The webhook body from Instagram
152
+ * @returns Separated messaging and content events
153
+ */
154
+ processWebhook(body: IWebhookBody): IWebhookResult;
155
+ /**
156
+ * Parse a messaging event into a more consumable format (Requirements 12.2, 12.3, 12.4)
157
+ * Extracts sender ID, message ID, text/attachments for messages
158
+ * Extracts sender ID, title, payload for postbacks
159
+ * Extracts ref, user_ref for optins
160
+ *
161
+ * @param event - The raw messaging event
162
+ * @returns Parsed event with extracted fields, or null if invalid
163
+ */
164
+ parseMessagingEvent(event: IMessagingEvent): ParsedMessagingEvent | null;
165
+ /**
166
+ * Parse a content event into a more consumable format (Requirements 13.1, 13.2)
167
+ * Extracts media ID, comment ID, text, commenter info for comments
168
+ * Extracts media ID, mentioned by info for mentions
169
+ *
170
+ * @param event - The raw change event
171
+ * @returns Parsed event with extracted fields, or null if invalid
172
+ */
173
+ parseContentEvent(event: IChangeEvent): ParsedContentEvent | null;
174
+ /**
175
+ * Process and parse all events from a webhook body
176
+ * Returns fully parsed events ready for n8n output
177
+ *
178
+ * @param body - The webhook body from Instagram
179
+ * @returns Parsed messaging and content events
180
+ */
181
+ processAndParseWebhook(body: IWebhookBody): {
182
+ messagingEvents: ParsedMessagingEvent[];
183
+ contentEvents: ParsedContentEvent[];
184
+ };
185
+ /**
186
+ * Check if a messaging event is a message type
187
+ */
188
+ isMessageEvent(event: IMessagingEvent): boolean;
189
+ /**
190
+ * Check if a messaging event is a postback type
191
+ */
192
+ isPostbackEvent(event: IMessagingEvent): boolean;
193
+ /**
194
+ * Check if a messaging event is an opt-in type
195
+ */
196
+ isOptinEvent(event: IMessagingEvent): boolean;
197
+ /**
198
+ * Check if a content event is a comment type
199
+ */
200
+ isCommentEvent(event: IChangeEvent): boolean;
201
+ /**
202
+ * Check if a content event is a mention type
203
+ */
204
+ isMentionEvent(event: IChangeEvent): boolean;
205
+ }
206
+ //# sourceMappingURL=WebhookHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebhookHandler.d.ts","sourceRoot":"","sources":["../../src/services/WebhookHandler.ts"],"names":[],"mappings":"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"}