@memberjunction/actions-bizapps-social 2.111.1 → 2.112.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 (204) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +6 -6
  3. package/dist/base/base-social.action.d.ts.map +1 -1
  4. package/dist/base/base-social.action.js +18 -24
  5. package/dist/base/base-social.action.js.map +1 -1
  6. package/dist/providers/buffer/buffer-base.action.d.ts.map +1 -1
  7. package/dist/providers/buffer/buffer-base.action.js +35 -34
  8. package/dist/providers/buffer/buffer-base.action.js.map +1 -1
  9. package/dist/providers/facebook/actions/boost-post.action.d.ts.map +1 -1
  10. package/dist/providers/facebook/actions/boost-post.action.js +33 -33
  11. package/dist/providers/facebook/actions/boost-post.action.js.map +1 -1
  12. package/dist/providers/facebook/actions/create-album.action.d.ts.map +1 -1
  13. package/dist/providers/facebook/actions/create-album.action.js +34 -36
  14. package/dist/providers/facebook/actions/create-album.action.js.map +1 -1
  15. package/dist/providers/facebook/actions/create-post.action.d.ts.map +1 -1
  16. package/dist/providers/facebook/actions/create-post.action.js +20 -20
  17. package/dist/providers/facebook/actions/create-post.action.js.map +1 -1
  18. package/dist/providers/facebook/actions/get-page-insights.action.d.ts.map +1 -1
  19. package/dist/providers/facebook/actions/get-page-insights.action.js +25 -27
  20. package/dist/providers/facebook/actions/get-page-insights.action.js.map +1 -1
  21. package/dist/providers/facebook/actions/get-page-posts.action.d.ts.map +1 -1
  22. package/dist/providers/facebook/actions/get-page-posts.action.js +19 -23
  23. package/dist/providers/facebook/actions/get-page-posts.action.js.map +1 -1
  24. package/dist/providers/facebook/actions/get-post-insights.action.d.ts.map +1 -1
  25. package/dist/providers/facebook/actions/get-post-insights.action.js +28 -32
  26. package/dist/providers/facebook/actions/get-post-insights.action.js.map +1 -1
  27. package/dist/providers/facebook/actions/respond-to-comments.action.d.ts.map +1 -1
  28. package/dist/providers/facebook/actions/respond-to-comments.action.js +42 -44
  29. package/dist/providers/facebook/actions/respond-to-comments.action.js.map +1 -1
  30. package/dist/providers/facebook/actions/schedule-post.action.d.ts.map +1 -1
  31. package/dist/providers/facebook/actions/schedule-post.action.js +29 -29
  32. package/dist/providers/facebook/actions/schedule-post.action.js.map +1 -1
  33. package/dist/providers/facebook/actions/search-posts.action.d.ts.map +1 -1
  34. package/dist/providers/facebook/actions/search-posts.action.js +37 -39
  35. package/dist/providers/facebook/actions/search-posts.action.js.map +1 -1
  36. package/dist/providers/facebook/facebook-base.action.d.ts.map +1 -1
  37. package/dist/providers/facebook/facebook-base.action.js +44 -59
  38. package/dist/providers/facebook/facebook-base.action.js.map +1 -1
  39. package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.d.ts.map +1 -1
  40. package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.js +33 -31
  41. package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.js.map +1 -1
  42. package/dist/providers/hootsuite/actions/create-scheduled-post.action.d.ts.map +1 -1
  43. package/dist/providers/hootsuite/actions/create-scheduled-post.action.js +28 -32
  44. package/dist/providers/hootsuite/actions/create-scheduled-post.action.js.map +1 -1
  45. package/dist/providers/hootsuite/actions/delete-scheduled-post.action.d.ts.map +1 -1
  46. package/dist/providers/hootsuite/actions/delete-scheduled-post.action.js +19 -19
  47. package/dist/providers/hootsuite/actions/delete-scheduled-post.action.js.map +1 -1
  48. package/dist/providers/hootsuite/actions/get-analytics.action.d.ts.map +1 -1
  49. package/dist/providers/hootsuite/actions/get-analytics.action.js +24 -26
  50. package/dist/providers/hootsuite/actions/get-analytics.action.js.map +1 -1
  51. package/dist/providers/hootsuite/actions/get-scheduled-posts.action.d.ts.map +1 -1
  52. package/dist/providers/hootsuite/actions/get-scheduled-posts.action.js +22 -22
  53. package/dist/providers/hootsuite/actions/get-scheduled-posts.action.js.map +1 -1
  54. package/dist/providers/hootsuite/actions/get-social-profiles.action.d.ts.map +1 -1
  55. package/dist/providers/hootsuite/actions/get-social-profiles.action.js +32 -34
  56. package/dist/providers/hootsuite/actions/get-social-profiles.action.js.map +1 -1
  57. package/dist/providers/hootsuite/actions/search-posts.action.d.ts.map +1 -1
  58. package/dist/providers/hootsuite/actions/search-posts.action.js +43 -52
  59. package/dist/providers/hootsuite/actions/search-posts.action.js.map +1 -1
  60. package/dist/providers/hootsuite/actions/update-scheduled-post.action.d.ts.map +1 -1
  61. package/dist/providers/hootsuite/actions/update-scheduled-post.action.js +30 -28
  62. package/dist/providers/hootsuite/actions/update-scheduled-post.action.js.map +1 -1
  63. package/dist/providers/hootsuite/hootsuite-base.action.d.ts.map +1 -1
  64. package/dist/providers/hootsuite/hootsuite-base.action.js +18 -20
  65. package/dist/providers/hootsuite/hootsuite-base.action.js.map +1 -1
  66. package/dist/providers/instagram/actions/create-post.action.d.ts.map +1 -1
  67. package/dist/providers/instagram/actions/create-post.action.js +27 -26
  68. package/dist/providers/instagram/actions/create-post.action.js.map +1 -1
  69. package/dist/providers/instagram/actions/create-story.action.d.ts.map +1 -1
  70. package/dist/providers/instagram/actions/create-story.action.js +35 -35
  71. package/dist/providers/instagram/actions/create-story.action.js.map +1 -1
  72. package/dist/providers/instagram/actions/get-account-insights.action.d.ts.map +1 -1
  73. package/dist/providers/instagram/actions/get-account-insights.action.js +38 -59
  74. package/dist/providers/instagram/actions/get-account-insights.action.js.map +1 -1
  75. package/dist/providers/instagram/actions/get-business-posts.action.d.ts.map +1 -1
  76. package/dist/providers/instagram/actions/get-business-posts.action.js +29 -29
  77. package/dist/providers/instagram/actions/get-business-posts.action.js.map +1 -1
  78. package/dist/providers/instagram/actions/get-comments.action.d.ts.map +1 -1
  79. package/dist/providers/instagram/actions/get-comments.action.js +36 -36
  80. package/dist/providers/instagram/actions/get-comments.action.js.map +1 -1
  81. package/dist/providers/instagram/actions/get-post-insights.action.d.ts.map +1 -1
  82. package/dist/providers/instagram/actions/get-post-insights.action.js +23 -25
  83. package/dist/providers/instagram/actions/get-post-insights.action.js.map +1 -1
  84. package/dist/providers/instagram/actions/schedule-post.action.d.ts.map +1 -1
  85. package/dist/providers/instagram/actions/schedule-post.action.js +25 -25
  86. package/dist/providers/instagram/actions/schedule-post.action.js.map +1 -1
  87. package/dist/providers/instagram/actions/search-posts.action.d.ts.map +1 -1
  88. package/dist/providers/instagram/actions/search-posts.action.js +56 -60
  89. package/dist/providers/instagram/actions/search-posts.action.js.map +1 -1
  90. package/dist/providers/instagram/instagram-base.action.d.ts.map +1 -1
  91. package/dist/providers/instagram/instagram-base.action.js +25 -27
  92. package/dist/providers/instagram/instagram-base.action.js.map +1 -1
  93. package/dist/providers/linkedin/actions/create-article.action.d.ts.map +1 -1
  94. package/dist/providers/linkedin/actions/create-article.action.js +55 -45
  95. package/dist/providers/linkedin/actions/create-article.action.js.map +1 -1
  96. package/dist/providers/linkedin/actions/create-post.action.d.ts.map +1 -1
  97. package/dist/providers/linkedin/actions/create-post.action.js +31 -29
  98. package/dist/providers/linkedin/actions/create-post.action.js.map +1 -1
  99. package/dist/providers/linkedin/actions/get-followers.action.d.ts.map +1 -1
  100. package/dist/providers/linkedin/actions/get-followers.action.js +28 -28
  101. package/dist/providers/linkedin/actions/get-followers.action.js.map +1 -1
  102. package/dist/providers/linkedin/actions/get-organization-posts.action.d.ts.map +1 -1
  103. package/dist/providers/linkedin/actions/get-organization-posts.action.js +20 -20
  104. package/dist/providers/linkedin/actions/get-organization-posts.action.js.map +1 -1
  105. package/dist/providers/linkedin/actions/get-personal-posts.action.d.ts.map +1 -1
  106. package/dist/providers/linkedin/actions/get-personal-posts.action.js +19 -19
  107. package/dist/providers/linkedin/actions/get-personal-posts.action.js.map +1 -1
  108. package/dist/providers/linkedin/actions/get-post-analytics.action.d.ts.map +1 -1
  109. package/dist/providers/linkedin/actions/get-post-analytics.action.js +25 -23
  110. package/dist/providers/linkedin/actions/get-post-analytics.action.js.map +1 -1
  111. package/dist/providers/linkedin/actions/schedule-post.action.d.ts.map +1 -1
  112. package/dist/providers/linkedin/actions/schedule-post.action.js +32 -30
  113. package/dist/providers/linkedin/actions/schedule-post.action.js.map +1 -1
  114. package/dist/providers/linkedin/actions/search-posts.action.d.ts.map +1 -1
  115. package/dist/providers/linkedin/actions/search-posts.action.js +28 -30
  116. package/dist/providers/linkedin/actions/search-posts.action.js.map +1 -1
  117. package/dist/providers/linkedin/linkedin-base.action.d.ts.map +1 -1
  118. package/dist/providers/linkedin/linkedin-base.action.js +33 -38
  119. package/dist/providers/linkedin/linkedin-base.action.js.map +1 -1
  120. package/dist/providers/tiktok/tiktok-base.action.d.ts.map +1 -1
  121. package/dist/providers/tiktok/tiktok-base.action.js +25 -26
  122. package/dist/providers/tiktok/tiktok-base.action.js.map +1 -1
  123. package/dist/providers/twitter/actions/create-thread.action.d.ts.map +1 -1
  124. package/dist/providers/twitter/actions/create-thread.action.js +25 -29
  125. package/dist/providers/twitter/actions/create-thread.action.js.map +1 -1
  126. package/dist/providers/twitter/actions/create-tweet.action.d.ts.map +1 -1
  127. package/dist/providers/twitter/actions/create-tweet.action.js +23 -23
  128. package/dist/providers/twitter/actions/create-tweet.action.js.map +1 -1
  129. package/dist/providers/twitter/actions/delete-tweet.action.d.ts.map +1 -1
  130. package/dist/providers/twitter/actions/delete-tweet.action.js +19 -19
  131. package/dist/providers/twitter/actions/delete-tweet.action.js.map +1 -1
  132. package/dist/providers/twitter/actions/get-analytics.action.d.ts.map +1 -1
  133. package/dist/providers/twitter/actions/get-analytics.action.js +40 -47
  134. package/dist/providers/twitter/actions/get-analytics.action.js.map +1 -1
  135. package/dist/providers/twitter/actions/get-mentions.action.d.ts.map +1 -1
  136. package/dist/providers/twitter/actions/get-mentions.action.js +30 -31
  137. package/dist/providers/twitter/actions/get-mentions.action.js.map +1 -1
  138. package/dist/providers/twitter/actions/get-timeline.action.d.ts.map +1 -1
  139. package/dist/providers/twitter/actions/get-timeline.action.js +29 -29
  140. package/dist/providers/twitter/actions/get-timeline.action.js.map +1 -1
  141. package/dist/providers/twitter/actions/schedule-tweet.action.d.ts.map +1 -1
  142. package/dist/providers/twitter/actions/schedule-tweet.action.js +26 -26
  143. package/dist/providers/twitter/actions/schedule-tweet.action.js.map +1 -1
  144. package/dist/providers/twitter/actions/search-tweets.action.d.ts.map +1 -1
  145. package/dist/providers/twitter/actions/search-tweets.action.js +56 -58
  146. package/dist/providers/twitter/actions/search-tweets.action.js.map +1 -1
  147. package/dist/providers/twitter/twitter-base.action.d.ts.map +1 -1
  148. package/dist/providers/twitter/twitter-base.action.js +58 -68
  149. package/dist/providers/twitter/twitter-base.action.js.map +1 -1
  150. package/dist/providers/youtube/youtube-base.action.d.ts +1 -1
  151. package/dist/providers/youtube/youtube-base.action.d.ts.map +1 -1
  152. package/dist/providers/youtube/youtube-base.action.js +22 -25
  153. package/dist/providers/youtube/youtube-base.action.js.map +1 -1
  154. package/package.json +5 -6
  155. package/src/base/base-social.action.ts +217 -224
  156. package/src/providers/buffer/buffer-base.action.ts +435 -441
  157. package/src/providers/facebook/actions/boost-post.action.ts +350 -386
  158. package/src/providers/facebook/actions/create-album.action.ts +291 -307
  159. package/src/providers/facebook/actions/create-post.action.ts +224 -227
  160. package/src/providers/facebook/actions/get-page-insights.action.ts +383 -403
  161. package/src/providers/facebook/actions/get-page-posts.action.ts +214 -225
  162. package/src/providers/facebook/actions/get-post-insights.action.ts +300 -316
  163. package/src/providers/facebook/actions/respond-to-comments.action.ts +319 -336
  164. package/src/providers/facebook/actions/schedule-post.action.ts +289 -292
  165. package/src/providers/facebook/actions/search-posts.action.ts +399 -413
  166. package/src/providers/facebook/facebook-base.action.ts +653 -670
  167. package/src/providers/hootsuite/actions/bulk-schedule-posts.action.ts +257 -257
  168. package/src/providers/hootsuite/actions/create-scheduled-post.action.ts +184 -189
  169. package/src/providers/hootsuite/actions/delete-scheduled-post.action.ts +160 -161
  170. package/src/providers/hootsuite/actions/get-analytics.action.ts +249 -254
  171. package/src/providers/hootsuite/actions/get-scheduled-posts.action.ts +206 -207
  172. package/src/providers/hootsuite/actions/get-social-profiles.action.ts +206 -205
  173. package/src/providers/hootsuite/actions/search-posts.action.ts +351 -369
  174. package/src/providers/hootsuite/actions/update-scheduled-post.action.ts +211 -209
  175. package/src/providers/hootsuite/hootsuite-base.action.ts +301 -307
  176. package/src/providers/instagram/actions/create-post.action.ts +276 -296
  177. package/src/providers/instagram/actions/create-story.action.ts +378 -394
  178. package/src/providers/instagram/actions/get-account-insights.action.ts +384 -420
  179. package/src/providers/instagram/actions/get-business-posts.action.ts +233 -242
  180. package/src/providers/instagram/actions/get-comments.action.ts +365 -377
  181. package/src/providers/instagram/actions/get-post-insights.action.ts +265 -273
  182. package/src/providers/instagram/actions/schedule-post.action.ts +233 -235
  183. package/src/providers/instagram/actions/search-posts.action.ts +512 -538
  184. package/src/providers/instagram/instagram-base.action.ts +368 -393
  185. package/src/providers/linkedin/actions/create-article.action.ts +275 -266
  186. package/src/providers/linkedin/actions/create-post.action.ts +179 -177
  187. package/src/providers/linkedin/actions/get-followers.action.ts +211 -211
  188. package/src/providers/linkedin/actions/get-organization-posts.action.ts +146 -147
  189. package/src/providers/linkedin/actions/get-personal-posts.action.ts +138 -139
  190. package/src/providers/linkedin/actions/get-post-analytics.action.ts +190 -189
  191. package/src/providers/linkedin/actions/schedule-post.action.ts +191 -189
  192. package/src/providers/linkedin/actions/search-posts.action.ts +275 -283
  193. package/src/providers/linkedin/linkedin-base.action.ts +407 -421
  194. package/src/providers/tiktok/tiktok-base.action.ts +305 -320
  195. package/src/providers/twitter/actions/create-thread.action.ts +203 -207
  196. package/src/providers/twitter/actions/create-tweet.action.ts +187 -188
  197. package/src/providers/twitter/actions/delete-tweet.action.ts +128 -129
  198. package/src/providers/twitter/actions/get-analytics.action.ts +402 -411
  199. package/src/providers/twitter/actions/get-mentions.action.ts +218 -219
  200. package/src/providers/twitter/actions/get-timeline.action.ts +232 -233
  201. package/src/providers/twitter/actions/schedule-tweet.action.ts +221 -222
  202. package/src/providers/twitter/actions/search-tweets.action.ts +540 -543
  203. package/src/providers/twitter/twitter-base.action.ts +541 -560
  204. package/src/providers/youtube/youtube-base.action.ts +320 -333
@@ -1,7 +1,7 @@
1
1
  import { RegisterClass } from '@memberjunction/global';
2
2
  import { InstagramBaseAction } from '../instagram-base.action';
3
3
  import { ActionParam, ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
4
- import { LogError } from '@memberjunction/core';
4
+ import { LogError } from '@memberjunction/global';
5
5
  import { BaseAction } from '@memberjunction/actions';
6
6
 
7
7
  /**
@@ -10,387 +10,375 @@ import { BaseAction } from '@memberjunction/actions';
10
10
  */
11
11
  @RegisterClass(BaseAction, 'Instagram - Get Comments')
12
12
  export class InstagramGetCommentsAction extends InstagramBaseAction {
13
-
14
- protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> {
15
- try {
16
- const companyIntegrationId = this.getParamValue(params.Params, 'CompanyIntegrationID');
17
- const postId = this.getParamValue(params.Params, 'PostID');
18
- const includeReplies = this.getParamValue(params.Params, 'IncludeReplies') !== false;
19
- const includeHidden = this.getParamValue(params.Params, 'IncludeHidden') || false;
20
- const limit = this.getParamValue(params.Params, 'Limit') || 50;
21
- const afterCursor = this.getParamValue(params.Params, 'AfterCursor');
22
-
23
- // Initialize OAuth
24
- if (!await this.initializeOAuth(companyIntegrationId)) {
25
- return {
26
- Success: false,
27
- Message: 'Failed to initialize Instagram authentication',
28
- ResultCode: 'AUTH_FAILED'
29
- };
30
- }
31
-
32
- // Validate inputs
33
- if (!postId) {
34
- return {
35
- Success: false,
36
- Message: 'PostID is required',
37
- ResultCode: 'MISSING_PARAMS'
38
- };
39
- }
40
-
41
- // Build fields for comment data
42
- const fields = 'id,text,username,timestamp,like_count,replies{id,text,username,timestamp,like_count}';
43
-
44
- // Build query parameters
45
- const queryParams: any = {
46
- fields,
47
- access_token: this.getAccessToken(),
48
- limit: Math.min(limit, 100)
49
- };
50
-
51
- if (afterCursor) {
52
- queryParams.after = afterCursor;
53
- }
54
-
55
- // Get comments
56
- const response = await this.makeInstagramRequest<{
57
- data: any[];
58
- paging?: {
59
- cursors: {
60
- before: string;
61
- after: string;
62
- };
63
- next?: string;
64
- };
65
- }>(
66
- `${postId}/comments`,
67
- 'GET',
68
- null,
69
- queryParams
70
- );
71
-
72
- const comments = response.data || [];
73
-
74
- // Process comments
75
- const processedComments = await this.processComments(comments, includeReplies);
76
-
77
- // Get hidden comments if requested
78
- let hiddenComments: any[] = [];
79
- if (includeHidden) {
80
- hiddenComments = await this.getHiddenComments(postId);
81
- }
82
-
83
- // Calculate engagement metrics
84
- const metrics = this.calculateCommentMetrics(processedComments);
85
-
86
- // Analyze sentiment patterns
87
- const sentimentAnalysis = this.analyzeSentiment(processedComments);
88
-
89
- // Store result in output params
90
- const outputParams = [...params.Params];
91
- outputParams.push({
92
- Name: 'ResultData',
93
- Type: 'Output',
94
- Value: JSON.stringify({
95
- postId,
96
- comments: processedComments,
97
- hiddenComments,
98
- metrics,
99
- sentimentAnalysis,
100
- paging: {
101
- hasNext: !!response.paging?.next,
102
- afterCursor: response.paging?.cursors?.after
103
- }
104
- })
105
- });
106
-
107
- return {
108
- Success: true,
109
- Message: `Retrieved ${processedComments.length} comments`,
110
- ResultCode: 'SUCCESS',
111
- Params: outputParams
112
- };
113
-
114
- } catch (error: any) {
115
- LogError('Failed to retrieve Instagram comments', error);
116
-
117
- if (error.code === 'RATE_LIMIT') {
118
- return {
119
- Success: false,
120
- Message: 'Instagram API rate limit exceeded. Please try again later.',
121
- ResultCode: 'RATE_LIMIT'
122
- };
123
- }
124
-
125
- if (error.code === 'POST_NOT_FOUND') {
126
- return {
127
- Success: false,
128
- Message: 'Instagram post not found or access denied',
129
- ResultCode: 'POST_NOT_FOUND'
130
- };
131
- }
132
-
133
- return {
134
- Success: false,
135
- Message: `Failed to retrieve comments: ${error.message}`,
136
- ResultCode: 'ERROR'
137
- };
138
- }
139
- }
140
-
141
- /**
142
- * Process comments and fetch replies if needed
143
- */
144
- private async processComments(comments: any[], includeReplies: boolean): Promise<any[]> {
145
- const processed: any[] = [];
146
-
147
- for (const comment of comments) {
148
- const processedComment: any = {
149
- id: comment.id,
150
- text: comment.text,
151
- username: comment.username,
152
- timestamp: comment.timestamp,
153
- likeCount: comment.like_count || 0,
154
- replies: [],
155
- metrics: {
156
- wordCount: this.countWords(comment.text),
157
- hasEmojis: this.containsEmojis(comment.text),
158
- hasMentions: this.containsMentions(comment.text),
159
- hasHashtags: this.containsHashtags(comment.text)
160
- }
161
- };
162
-
163
- // Process replies if they exist and are requested
164
- if (includeReplies && comment.replies?.data) {
165
- processedComment.replies = comment.replies.data.map((reply: any) => ({
166
- id: reply.id,
167
- text: reply.text,
168
- username: reply.username,
169
- timestamp: reply.timestamp,
170
- likeCount: reply.like_count || 0,
171
- metrics: {
172
- wordCount: this.countWords(reply.text),
173
- hasEmojis: this.containsEmojis(reply.text),
174
- hasMentions: this.containsMentions(reply.text),
175
- hasHashtags: this.containsHashtags(reply.text)
176
- }
177
- }));
178
- }
179
-
180
- processed.push(processedComment);
181
- }
182
-
183
- return processed;
184
- }
185
-
186
- /**
187
- * Get hidden comments (comments hidden by the account)
188
- */
189
- private async getHiddenComments(postId: string): Promise<any[]> {
190
- try {
191
- const response = await this.makeInstagramRequest<{ data: any[] }>(
192
- `${postId}/comments`,
193
- 'GET',
194
- null,
195
- {
196
- fields: 'id,text,username,timestamp,hidden',
197
- access_token: this.getAccessToken(),
198
- filter: 'hidden'
199
- }
200
- );
201
-
202
- return response.data || [];
203
- } catch (error) {
204
- LogError('Failed to get hidden comments', error);
205
- return [];
206
- }
207
- }
208
-
209
- /**
210
- * Calculate comment metrics
211
- */
212
- private calculateCommentMetrics(comments: any[]): any {
213
- const metrics = {
214
- totalComments: comments.length,
215
- totalReplies: 0,
216
- avgCommentLength: 0,
217
- avgLikesPerComment: 0,
218
- topCommenters: [] as any[],
219
- engagementRate: 0,
220
- responseRate: 0
13
+ protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> {
14
+ try {
15
+ const companyIntegrationId = this.getParamValue(params.Params, 'CompanyIntegrationID');
16
+ const postId = this.getParamValue(params.Params, 'PostID');
17
+ const includeReplies = this.getParamValue(params.Params, 'IncludeReplies') !== false;
18
+ const includeHidden = this.getParamValue(params.Params, 'IncludeHidden') || false;
19
+ const limit = this.getParamValue(params.Params, 'Limit') || 50;
20
+ const afterCursor = this.getParamValue(params.Params, 'AfterCursor');
21
+
22
+ // Initialize OAuth
23
+ if (!(await this.initializeOAuth(companyIntegrationId))) {
24
+ return {
25
+ Success: false,
26
+ Message: 'Failed to initialize Instagram authentication',
27
+ ResultCode: 'AUTH_FAILED',
221
28
  };
222
-
223
- if (comments.length === 0) return metrics;
224
-
225
- let totalLength = 0;
226
- let totalLikes = 0;
227
- const commenterCounts: Record<string, number> = {};
228
-
229
- comments.forEach(comment => {
230
- totalLength += comment.metrics.wordCount;
231
- totalLikes += comment.likeCount;
232
-
233
- // Count commenters
234
- commenterCounts[comment.username] = (commenterCounts[comment.username] || 0) + 1;
235
-
236
- // Count replies
237
- metrics.totalReplies += comment.replies.length;
238
-
239
- // Add reply stats
240
- comment.replies.forEach((reply: any) => {
241
- totalLength += reply.metrics.wordCount;
242
- totalLikes += reply.likeCount;
243
- commenterCounts[reply.username] = (commenterCounts[reply.username] || 0) + 1;
244
- });
245
- });
246
-
247
- const totalInteractions = comments.length + metrics.totalReplies;
248
- metrics.avgCommentLength = Math.round(totalLength / totalInteractions);
249
- metrics.avgLikesPerComment = Math.round(totalLikes / totalInteractions);
250
-
251
- // Get top commenters
252
- metrics.topCommenters = Object.entries(commenterCounts)
253
- .sort(([, a], [, b]) => b - a)
254
- .slice(0, 10)
255
- .map(([username, count]) => ({ username, count }));
256
-
257
- // Calculate response rate (comments with replies)
258
- const commentsWithReplies = comments.filter(c => c.replies.length > 0).length;
259
- metrics.responseRate = (commentsWithReplies / comments.length) * 100;
260
-
261
- return metrics;
262
- }
263
-
264
- /**
265
- * Analyze sentiment patterns in comments
266
- */
267
- private analyzeSentiment(comments: any[]): any {
268
- const analysis = {
269
- positive: 0,
270
- negative: 0,
271
- neutral: 0,
272
- questions: 0,
273
- keywords: [] as any[],
274
- emojis: [] as any[]
29
+ }
30
+
31
+ // Validate inputs
32
+ if (!postId) {
33
+ return {
34
+ Success: false,
35
+ Message: 'PostID is required',
36
+ ResultCode: 'MISSING_PARAMS',
275
37
  };
38
+ }
39
+
40
+ // Build fields for comment data
41
+ const fields = 'id,text,username,timestamp,like_count,replies{id,text,username,timestamp,like_count}';
42
+
43
+ // Build query parameters
44
+ const queryParams: any = {
45
+ fields,
46
+ access_token: this.getAccessToken(),
47
+ limit: Math.min(limit, 100),
48
+ };
49
+
50
+ if (afterCursor) {
51
+ queryParams.after = afterCursor;
52
+ }
53
+
54
+ // Get comments
55
+ const response = await this.makeInstagramRequest<{
56
+ data: any[];
57
+ paging?: {
58
+ cursors: {
59
+ before: string;
60
+ after: string;
61
+ };
62
+ next?: string;
63
+ };
64
+ }>(`${postId}/comments`, 'GET', null, queryParams);
65
+
66
+ const comments = response.data || [];
67
+
68
+ // Process comments
69
+ const processedComments = await this.processComments(comments, includeReplies);
70
+
71
+ // Get hidden comments if requested
72
+ let hiddenComments: any[] = [];
73
+ if (includeHidden) {
74
+ hiddenComments = await this.getHiddenComments(postId);
75
+ }
76
+
77
+ // Calculate engagement metrics
78
+ const metrics = this.calculateCommentMetrics(processedComments);
79
+
80
+ // Analyze sentiment patterns
81
+ const sentimentAnalysis = this.analyzeSentiment(processedComments);
82
+
83
+ // Store result in output params
84
+ const outputParams = [...params.Params];
85
+ outputParams.push({
86
+ Name: 'ResultData',
87
+ Type: 'Output',
88
+ Value: JSON.stringify({
89
+ postId,
90
+ comments: processedComments,
91
+ hiddenComments,
92
+ metrics,
93
+ sentimentAnalysis,
94
+ paging: {
95
+ hasNext: !!response.paging?.next,
96
+ afterCursor: response.paging?.cursors?.after,
97
+ },
98
+ }),
99
+ });
100
+
101
+ return {
102
+ Success: true,
103
+ Message: `Retrieved ${processedComments.length} comments`,
104
+ ResultCode: 'SUCCESS',
105
+ Params: outputParams,
106
+ };
107
+ } catch (error: any) {
108
+ LogError('Failed to retrieve Instagram comments', error);
109
+
110
+ if (error.code === 'RATE_LIMIT') {
111
+ return {
112
+ Success: false,
113
+ Message: 'Instagram API rate limit exceeded. Please try again later.',
114
+ ResultCode: 'RATE_LIMIT',
115
+ };
116
+ }
276
117
 
277
- const keywordCounts: Record<string, number> = {};
278
- const emojiCounts: Record<string, number> = {};
279
-
280
- // Simple sentiment analysis based on keywords and patterns
281
- const positiveWords = ['love', 'amazing', 'beautiful', 'great', 'awesome', 'perfect', 'excellent', 'wonderful', '❤️', '😍', '🔥', '💯'];
282
- const negativeWords = ['hate', 'awful', 'terrible', 'bad', 'worst', 'ugly', 'disgusting', '😠', '😡', '👎'];
283
- const questionWords = ['?', 'what', 'where', 'when', 'how', 'why', 'who'];
284
-
285
- comments.forEach(comment => {
286
- const text = comment.text.toLowerCase();
287
-
288
- // Check sentiment
289
- const hasPositive = positiveWords.some(word => text.includes(word));
290
- const hasNegative = negativeWords.some(word => text.includes(word));
291
- const hasQuestion = questionWords.some(word => text.includes(word));
292
-
293
- if (hasQuestion) {
294
- analysis.questions++;
295
- }
296
-
297
- if (hasPositive && !hasNegative) {
298
- analysis.positive++;
299
- } else if (hasNegative && !hasPositive) {
300
- analysis.negative++;
301
- } else {
302
- analysis.neutral++;
303
- }
304
-
305
- // Extract keywords (simple word frequency)
306
- const words = text.split(/\s+/).filter(word => word.length > 3);
307
- words.forEach(word => {
308
- keywordCounts[word] = (keywordCounts[word] || 0) + 1;
309
- });
310
-
311
- // Extract emojis
312
- const emojis = text.match(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]/gu) || [];
313
- emojis.forEach(emoji => {
314
- emojiCounts[emoji] = (emojiCounts[emoji] || 0) + 1;
315
- });
316
-
317
- // Analyze replies too
318
- comment.replies.forEach((reply: any) => {
319
- // Similar analysis for replies...
320
- });
321
- });
322
-
323
- // Get top keywords and emojis
324
- analysis.keywords = Object.entries(keywordCounts)
325
- .sort(([, a], [, b]) => b - a)
326
- .slice(0, 20)
327
- .map(([keyword, count]) => ({ keyword, count }));
328
-
329
- analysis.emojis = Object.entries(emojiCounts)
330
- .sort(([, a], [, b]) => b - a)
331
- .slice(0, 10)
332
- .map(([emoji, count]) => ({ emoji, count }));
333
-
334
- return analysis;
335
- }
336
-
337
- /**
338
- * Helper methods for text analysis
339
- */
340
- private countWords(text: string): number {
341
- return text.trim().split(/\s+/).length;
342
- }
343
-
344
- private containsEmojis(text: string): boolean {
345
- return /[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]/u.test(text);
346
- }
347
-
348
- private containsMentions(text: string): boolean {
349
- return /@\w+/.test(text);
350
- }
118
+ if (error.code === 'POST_NOT_FOUND') {
119
+ return {
120
+ Success: false,
121
+ Message: 'Instagram post not found or access denied',
122
+ ResultCode: 'POST_NOT_FOUND',
123
+ };
124
+ }
351
125
 
352
- private containsHashtags(text: string): boolean {
353
- return /#\w+/.test(text);
126
+ return {
127
+ Success: false,
128
+ Message: `Failed to retrieve comments: ${error.message}`,
129
+ ResultCode: 'ERROR',
130
+ };
354
131
  }
355
-
356
- /**
357
- * Define the parameters for this action
358
- */
359
- public get Params(): ActionParam[] {
360
- return [
361
- ...this.commonSocialParams,
362
- {
363
- Name: 'PostID',
364
- Type: 'Input',
365
- Value: null
366
- },
367
- {
368
- Name: 'IncludeReplies',
369
- Type: 'Input',
370
- Value: true
371
- },
372
- {
373
- Name: 'IncludeHidden',
374
- Type: 'Input',
375
- Value: false
376
- },
377
- {
378
- Name: 'Limit',
379
- Type: 'Input',
380
- Value: 50
381
- },
382
- {
383
- Name: 'AfterCursor',
384
- Type: 'Input',
385
- Value: null
386
- }
387
- ];
132
+ }
133
+
134
+ /**
135
+ * Process comments and fetch replies if needed
136
+ */
137
+ private async processComments(comments: any[], includeReplies: boolean): Promise<any[]> {
138
+ const processed: any[] = [];
139
+
140
+ for (const comment of comments) {
141
+ const processedComment: any = {
142
+ id: comment.id,
143
+ text: comment.text,
144
+ username: comment.username,
145
+ timestamp: comment.timestamp,
146
+ likeCount: comment.like_count || 0,
147
+ replies: [],
148
+ metrics: {
149
+ wordCount: this.countWords(comment.text),
150
+ hasEmojis: this.containsEmojis(comment.text),
151
+ hasMentions: this.containsMentions(comment.text),
152
+ hasHashtags: this.containsHashtags(comment.text),
153
+ },
154
+ };
155
+
156
+ // Process replies if they exist and are requested
157
+ if (includeReplies && comment.replies?.data) {
158
+ processedComment.replies = comment.replies.data.map((reply: any) => ({
159
+ id: reply.id,
160
+ text: reply.text,
161
+ username: reply.username,
162
+ timestamp: reply.timestamp,
163
+ likeCount: reply.like_count || 0,
164
+ metrics: {
165
+ wordCount: this.countWords(reply.text),
166
+ hasEmojis: this.containsEmojis(reply.text),
167
+ hasMentions: this.containsMentions(reply.text),
168
+ hasHashtags: this.containsHashtags(reply.text),
169
+ },
170
+ }));
171
+ }
172
+
173
+ processed.push(processedComment);
388
174
  }
389
175
 
390
- /**
391
- * Get the description for this action
392
- */
393
- public get Description(): string {
394
- return 'Retrieves comments for an Instagram post including replies, metrics, and sentiment analysis.';
176
+ return processed;
177
+ }
178
+
179
+ /**
180
+ * Get hidden comments (comments hidden by the account)
181
+ */
182
+ private async getHiddenComments(postId: string): Promise<any[]> {
183
+ try {
184
+ const response = await this.makeInstagramRequest<{ data: any[] }>(`${postId}/comments`, 'GET', null, {
185
+ fields: 'id,text,username,timestamp,hidden',
186
+ access_token: this.getAccessToken(),
187
+ filter: 'hidden',
188
+ });
189
+
190
+ return response.data || [];
191
+ } catch (error) {
192
+ LogError('Failed to get hidden comments', error);
193
+ return [];
395
194
  }
396
- }
195
+ }
196
+
197
+ /**
198
+ * Calculate comment metrics
199
+ */
200
+ private calculateCommentMetrics(comments: any[]): any {
201
+ const metrics = {
202
+ totalComments: comments.length,
203
+ totalReplies: 0,
204
+ avgCommentLength: 0,
205
+ avgLikesPerComment: 0,
206
+ topCommenters: [] as any[],
207
+ engagementRate: 0,
208
+ responseRate: 0,
209
+ };
210
+
211
+ if (comments.length === 0) return metrics;
212
+
213
+ let totalLength = 0;
214
+ let totalLikes = 0;
215
+ const commenterCounts: Record<string, number> = {};
216
+
217
+ comments.forEach((comment) => {
218
+ totalLength += comment.metrics.wordCount;
219
+ totalLikes += comment.likeCount;
220
+
221
+ // Count commenters
222
+ commenterCounts[comment.username] = (commenterCounts[comment.username] || 0) + 1;
223
+
224
+ // Count replies
225
+ metrics.totalReplies += comment.replies.length;
226
+
227
+ // Add reply stats
228
+ comment.replies.forEach((reply: any) => {
229
+ totalLength += reply.metrics.wordCount;
230
+ totalLikes += reply.likeCount;
231
+ commenterCounts[reply.username] = (commenterCounts[reply.username] || 0) + 1;
232
+ });
233
+ });
234
+
235
+ const totalInteractions = comments.length + metrics.totalReplies;
236
+ metrics.avgCommentLength = Math.round(totalLength / totalInteractions);
237
+ metrics.avgLikesPerComment = Math.round(totalLikes / totalInteractions);
238
+
239
+ // Get top commenters
240
+ metrics.topCommenters = Object.entries(commenterCounts)
241
+ .sort(([, a], [, b]) => b - a)
242
+ .slice(0, 10)
243
+ .map(([username, count]) => ({ username, count }));
244
+
245
+ // Calculate response rate (comments with replies)
246
+ const commentsWithReplies = comments.filter((c) => c.replies.length > 0).length;
247
+ metrics.responseRate = (commentsWithReplies / comments.length) * 100;
248
+
249
+ return metrics;
250
+ }
251
+
252
+ /**
253
+ * Analyze sentiment patterns in comments
254
+ */
255
+ private analyzeSentiment(comments: any[]): any {
256
+ const analysis = {
257
+ positive: 0,
258
+ negative: 0,
259
+ neutral: 0,
260
+ questions: 0,
261
+ keywords: [] as any[],
262
+ emojis: [] as any[],
263
+ };
264
+
265
+ const keywordCounts: Record<string, number> = {};
266
+ const emojiCounts: Record<string, number> = {};
267
+
268
+ // Simple sentiment analysis based on keywords and patterns
269
+ const positiveWords = ['love', 'amazing', 'beautiful', 'great', 'awesome', 'perfect', 'excellent', 'wonderful', '❤️', '😍', '🔥', '💯'];
270
+ const negativeWords = ['hate', 'awful', 'terrible', 'bad', 'worst', 'ugly', 'disgusting', '😠', '😡', '👎'];
271
+ const questionWords = ['?', 'what', 'where', 'when', 'how', 'why', 'who'];
272
+
273
+ comments.forEach((comment) => {
274
+ const text = comment.text.toLowerCase();
275
+
276
+ // Check sentiment
277
+ const hasPositive = positiveWords.some((word) => text.includes(word));
278
+ const hasNegative = negativeWords.some((word) => text.includes(word));
279
+ const hasQuestion = questionWords.some((word) => text.includes(word));
280
+
281
+ if (hasQuestion) {
282
+ analysis.questions++;
283
+ }
284
+
285
+ if (hasPositive && !hasNegative) {
286
+ analysis.positive++;
287
+ } else if (hasNegative && !hasPositive) {
288
+ analysis.negative++;
289
+ } else {
290
+ analysis.neutral++;
291
+ }
292
+
293
+ // Extract keywords (simple word frequency)
294
+ const words = text.split(/\s+/).filter((word) => word.length > 3);
295
+ words.forEach((word) => {
296
+ keywordCounts[word] = (keywordCounts[word] || 0) + 1;
297
+ });
298
+
299
+ // Extract emojis
300
+ const emojis = text.match(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]/gu) || [];
301
+ emojis.forEach((emoji) => {
302
+ emojiCounts[emoji] = (emojiCounts[emoji] || 0) + 1;
303
+ });
304
+
305
+ // Analyze replies too
306
+ comment.replies.forEach((reply: any) => {
307
+ // Similar analysis for replies...
308
+ });
309
+ });
310
+
311
+ // Get top keywords and emojis
312
+ analysis.keywords = Object.entries(keywordCounts)
313
+ .sort(([, a], [, b]) => b - a)
314
+ .slice(0, 20)
315
+ .map(([keyword, count]) => ({ keyword, count }));
316
+
317
+ analysis.emojis = Object.entries(emojiCounts)
318
+ .sort(([, a], [, b]) => b - a)
319
+ .slice(0, 10)
320
+ .map(([emoji, count]) => ({ emoji, count }));
321
+
322
+ return analysis;
323
+ }
324
+
325
+ /**
326
+ * Helper methods for text analysis
327
+ */
328
+ private countWords(text: string): number {
329
+ return text.trim().split(/\s+/).length;
330
+ }
331
+
332
+ private containsEmojis(text: string): boolean {
333
+ return /[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]/u.test(text);
334
+ }
335
+
336
+ private containsMentions(text: string): boolean {
337
+ return /@\w+/.test(text);
338
+ }
339
+
340
+ private containsHashtags(text: string): boolean {
341
+ return /#\w+/.test(text);
342
+ }
343
+
344
+ /**
345
+ * Define the parameters for this action
346
+ */
347
+ public get Params(): ActionParam[] {
348
+ return [
349
+ ...this.commonSocialParams,
350
+ {
351
+ Name: 'PostID',
352
+ Type: 'Input',
353
+ Value: null,
354
+ },
355
+ {
356
+ Name: 'IncludeReplies',
357
+ Type: 'Input',
358
+ Value: true,
359
+ },
360
+ {
361
+ Name: 'IncludeHidden',
362
+ Type: 'Input',
363
+ Value: false,
364
+ },
365
+ {
366
+ Name: 'Limit',
367
+ Type: 'Input',
368
+ Value: 50,
369
+ },
370
+ {
371
+ Name: 'AfterCursor',
372
+ Type: 'Input',
373
+ Value: null,
374
+ },
375
+ ];
376
+ }
377
+
378
+ /**
379
+ * Get the description for this action
380
+ */
381
+ public get Description(): string {
382
+ return 'Retrieves comments for an Instagram post including replies, metrics, and sentiment analysis.';
383
+ }
384
+ }