@memberjunction/actions-bizapps-social 2.112.0 → 2.113.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 +13 -0
  3. package/dist/base/base-social.action.d.ts.map +1 -1
  4. package/dist/base/base-social.action.js +24 -18
  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 +34 -35
  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 +36 -34
  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 +27 -25
  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 +23 -19
  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 +32 -28
  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 +44 -42
  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 +39 -37
  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 +59 -44
  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 +31 -33
  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 +32 -28
  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 +26 -24
  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 +34 -32
  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 +52 -43
  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 +28 -30
  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 +20 -18
  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 +26 -27
  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 +59 -38
  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 +25 -23
  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 +60 -56
  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 +27 -25
  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 +45 -55
  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 +29 -31
  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 +23 -25
  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 +30 -32
  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 +30 -28
  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 +38 -33
  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 +26 -25
  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 +29 -25
  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 +47 -40
  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 +31 -30
  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 +58 -56
  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 +68 -58
  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 +25 -22
  153. package/dist/providers/youtube/youtube-base.action.js.map +1 -1
  154. package/package.json +6 -5
  155. package/src/base/base-social.action.ts +224 -217
  156. package/src/providers/buffer/buffer-base.action.ts +441 -435
  157. package/src/providers/facebook/actions/boost-post.action.ts +386 -350
  158. package/src/providers/facebook/actions/create-album.action.ts +307 -291
  159. package/src/providers/facebook/actions/create-post.action.ts +227 -224
  160. package/src/providers/facebook/actions/get-page-insights.action.ts +403 -383
  161. package/src/providers/facebook/actions/get-page-posts.action.ts +225 -214
  162. package/src/providers/facebook/actions/get-post-insights.action.ts +316 -300
  163. package/src/providers/facebook/actions/respond-to-comments.action.ts +336 -319
  164. package/src/providers/facebook/actions/schedule-post.action.ts +292 -289
  165. package/src/providers/facebook/actions/search-posts.action.ts +413 -399
  166. package/src/providers/facebook/facebook-base.action.ts +670 -653
  167. package/src/providers/hootsuite/actions/bulk-schedule-posts.action.ts +257 -257
  168. package/src/providers/hootsuite/actions/create-scheduled-post.action.ts +189 -184
  169. package/src/providers/hootsuite/actions/delete-scheduled-post.action.ts +161 -160
  170. package/src/providers/hootsuite/actions/get-analytics.action.ts +254 -249
  171. package/src/providers/hootsuite/actions/get-scheduled-posts.action.ts +207 -206
  172. package/src/providers/hootsuite/actions/get-social-profiles.action.ts +205 -206
  173. package/src/providers/hootsuite/actions/search-posts.action.ts +369 -351
  174. package/src/providers/hootsuite/actions/update-scheduled-post.action.ts +209 -211
  175. package/src/providers/hootsuite/hootsuite-base.action.ts +307 -301
  176. package/src/providers/instagram/actions/create-post.action.ts +296 -276
  177. package/src/providers/instagram/actions/create-story.action.ts +394 -378
  178. package/src/providers/instagram/actions/get-account-insights.action.ts +420 -384
  179. package/src/providers/instagram/actions/get-business-posts.action.ts +242 -233
  180. package/src/providers/instagram/actions/get-comments.action.ts +377 -365
  181. package/src/providers/instagram/actions/get-post-insights.action.ts +273 -265
  182. package/src/providers/instagram/actions/schedule-post.action.ts +235 -233
  183. package/src/providers/instagram/actions/search-posts.action.ts +538 -512
  184. package/src/providers/instagram/instagram-base.action.ts +393 -368
  185. package/src/providers/linkedin/actions/create-article.action.ts +266 -275
  186. package/src/providers/linkedin/actions/create-post.action.ts +177 -179
  187. package/src/providers/linkedin/actions/get-followers.action.ts +211 -211
  188. package/src/providers/linkedin/actions/get-organization-posts.action.ts +147 -146
  189. package/src/providers/linkedin/actions/get-personal-posts.action.ts +139 -138
  190. package/src/providers/linkedin/actions/get-post-analytics.action.ts +189 -190
  191. package/src/providers/linkedin/actions/schedule-post.action.ts +189 -191
  192. package/src/providers/linkedin/actions/search-posts.action.ts +283 -275
  193. package/src/providers/linkedin/linkedin-base.action.ts +421 -407
  194. package/src/providers/tiktok/tiktok-base.action.ts +320 -305
  195. package/src/providers/twitter/actions/create-thread.action.ts +207 -203
  196. package/src/providers/twitter/actions/create-tweet.action.ts +188 -187
  197. package/src/providers/twitter/actions/delete-tweet.action.ts +129 -128
  198. package/src/providers/twitter/actions/get-analytics.action.ts +411 -402
  199. package/src/providers/twitter/actions/get-mentions.action.ts +219 -218
  200. package/src/providers/twitter/actions/get-timeline.action.ts +233 -232
  201. package/src/providers/twitter/actions/schedule-tweet.action.ts +222 -221
  202. package/src/providers/twitter/actions/search-tweets.action.ts +543 -540
  203. package/src/providers/twitter/twitter-base.action.ts +560 -541
  204. package/src/providers/youtube/youtube-base.action.ts +333 -320
@@ -1,7 +1,7 @@
1
1
  import { RegisterClass } from '@memberjunction/global';
2
2
  import { HootSuiteBaseAction, HootSuitePost } from '../hootsuite-base.action';
3
3
  import { ActionParam, ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
4
- import { LogStatus, LogError } from '@memberjunction/global';
4
+ import { LogStatus, LogError } from '@memberjunction/core';
5
5
  import { SearchParams, SocialPost } from '../../../base/base-social.action';
6
6
  import { BaseAction } from '@memberjunction/actions';
7
7
 
@@ -10,369 +10,387 @@ import { BaseAction } from '@memberjunction/actions';
10
10
  */
11
11
  @RegisterClass(BaseAction, 'HootSuiteSearchPostsAction')
12
12
  export class HootSuiteSearchPostsAction extends HootSuiteBaseAction {
13
- /**
14
- * Search for posts in HootSuite
15
- */
16
- protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> {
17
- const { Params, ContextUser } = params;
18
-
19
- try {
20
- // Initialize OAuth
21
- const companyIntegrationId = this.getParamValue(Params, 'CompanyIntegrationID');
22
- if (!(await this.initializeOAuth(companyIntegrationId))) {
23
- throw new Error('Failed to initialize OAuth connection');
24
- }
25
-
26
- // Extract search parameters
27
- const query = this.getParamValue(Params, 'Query');
28
- const hashtags = this.getParamValue(Params, 'Hashtags');
29
- const startDate = this.getParamValue(Params, 'StartDate');
30
- const endDate = this.getParamValue(Params, 'EndDate');
31
- const profileId = this.getParamValue(Params, 'ProfileID');
32
- const postState = this.getParamValue(Params, 'PostState') || 'PUBLISHED';
33
- const limit = this.getParamValue(Params, 'Limit') || 100;
34
- const includeAnalytics = this.getParamValue(Params, 'IncludeAnalytics') || false;
35
- const sortBy = this.getParamValue(Params, 'SortBy') || 'publishedTime';
36
- const sortOrder = this.getParamValue(Params, 'SortOrder') || 'DESC';
37
-
38
- // Build search parameters
39
- const searchParams: SearchParams = {
40
- query: query,
41
- hashtags: hashtags,
42
- startDate: startDate ? new Date(startDate) : undefined,
43
- endDate: endDate ? new Date(endDate) : undefined,
44
- limit: limit,
45
- };
46
-
47
- // Perform the search
48
- LogStatus('Searching for posts...');
49
- const posts = await this.searchPosts(searchParams);
50
-
51
- // Filter by profile if specified
52
- let filteredPosts = posts;
53
- if (profileId) {
54
- filteredPosts = posts.filter(
55
- (p) => p.profileId.includes(profileId) || p.platformSpecificData?.socialProfileIds?.includes(profileId)
56
- );
57
- }
58
-
59
- // Sort results
60
- filteredPosts = this.sortPosts(filteredPosts, sortBy, sortOrder);
61
-
62
- // Limit results
63
- if (limit && filteredPosts.length > limit) {
64
- filteredPosts = filteredPosts.slice(0, limit);
65
- }
66
-
67
- // Optionally include analytics
68
- if (includeAnalytics) {
69
- LogStatus('Fetching analytics for posts...');
70
- filteredPosts = await this.enrichPostsWithAnalytics(filteredPosts);
71
- }
72
-
73
- // Analyze content patterns
74
- const contentAnalysis = this.analyzePostContent(filteredPosts);
75
-
76
- // Create summary
77
- const summary = {
78
- totalResults: filteredPosts.length,
79
- dateRange: {
80
- start: startDate || 'Not specified',
81
- end: endDate || 'Not specified',
82
- actualStart: filteredPosts.length > 0 ? filteredPosts[filteredPosts.length - 1].publishedAt.toISOString() : null,
83
- actualEnd: filteredPosts.length > 0 ? filteredPosts[0].publishedAt.toISOString() : null,
84
- },
85
- byProfile: this.groupByProfile(filteredPosts),
86
- byMonth: this.groupByMonth(filteredPosts),
87
- contentAnalysis: contentAnalysis,
88
- topHashtags: this.extractTopHashtags(filteredPosts),
89
- performanceStats: includeAnalytics ? this.calculatePerformanceStats(filteredPosts) : null,
90
- };
91
-
92
- // Update output parameters
93
- const outputParams = [...Params];
94
- const postsParam = outputParams.find((p) => p.Name === 'Posts');
95
- if (postsParam) postsParam.Value = filteredPosts;
96
- const summaryParam = outputParams.find((p) => p.Name === 'Summary');
97
- if (summaryParam) summaryParam.Value = summary;
98
-
99
- return {
100
- Success: true,
101
- ResultCode: 'SUCCESS',
102
- Message: `Found ${filteredPosts.length} posts matching search criteria`,
103
- Params: outputParams,
104
- };
105
- } catch (error) {
106
- const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
107
-
108
- return {
109
- Success: false,
110
- ResultCode: 'ERROR',
111
- Message: `Failed to search posts: ${errorMessage}`,
112
- Params,
113
- };
13
+ /**
14
+ * Search for posts in HootSuite
15
+ */
16
+ protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> {
17
+ const { Params, ContextUser } = params;
18
+
19
+ try {
20
+ // Initialize OAuth
21
+ const companyIntegrationId = this.getParamValue(Params, 'CompanyIntegrationID');
22
+ if (!await this.initializeOAuth(companyIntegrationId)) {
23
+ throw new Error('Failed to initialize OAuth connection');
24
+ }
25
+
26
+ // Extract search parameters
27
+ const query = this.getParamValue(Params, 'Query');
28
+ const hashtags = this.getParamValue(Params, 'Hashtags');
29
+ const startDate = this.getParamValue(Params, 'StartDate');
30
+ const endDate = this.getParamValue(Params, 'EndDate');
31
+ const profileId = this.getParamValue(Params, 'ProfileID');
32
+ const postState = this.getParamValue(Params, 'PostState') || 'PUBLISHED';
33
+ const limit = this.getParamValue(Params, 'Limit') || 100;
34
+ const includeAnalytics = this.getParamValue(Params, 'IncludeAnalytics') || false;
35
+ const sortBy = this.getParamValue(Params, 'SortBy') || 'publishedTime';
36
+ const sortOrder = this.getParamValue(Params, 'SortOrder') || 'DESC';
37
+
38
+ // Build search parameters
39
+ const searchParams: SearchParams = {
40
+ query: query,
41
+ hashtags: hashtags,
42
+ startDate: startDate ? new Date(startDate) : undefined,
43
+ endDate: endDate ? new Date(endDate) : undefined,
44
+ limit: limit
45
+ };
46
+
47
+ // Perform the search
48
+ LogStatus('Searching for posts...');
49
+ const posts = await this.searchPosts(searchParams);
50
+
51
+ // Filter by profile if specified
52
+ let filteredPosts = posts;
53
+ if (profileId) {
54
+ filteredPosts = posts.filter(p =>
55
+ p.profileId.includes(profileId) ||
56
+ (p.platformSpecificData?.socialProfileIds?.includes(profileId))
57
+ );
58
+ }
59
+
60
+ // Sort results
61
+ filteredPosts = this.sortPosts(filteredPosts, sortBy, sortOrder);
62
+
63
+ // Limit results
64
+ if (limit && filteredPosts.length > limit) {
65
+ filteredPosts = filteredPosts.slice(0, limit);
66
+ }
67
+
68
+ // Optionally include analytics
69
+ if (includeAnalytics) {
70
+ LogStatus('Fetching analytics for posts...');
71
+ filteredPosts = await this.enrichPostsWithAnalytics(filteredPosts);
72
+ }
73
+
74
+ // Analyze content patterns
75
+ const contentAnalysis = this.analyzePostContent(filteredPosts);
76
+
77
+ // Create summary
78
+ const summary = {
79
+ totalResults: filteredPosts.length,
80
+ dateRange: {
81
+ start: startDate || 'Not specified',
82
+ end: endDate || 'Not specified',
83
+ actualStart: filteredPosts.length > 0
84
+ ? filteredPosts[filteredPosts.length - 1].publishedAt.toISOString()
85
+ : null,
86
+ actualEnd: filteredPosts.length > 0
87
+ ? filteredPosts[0].publishedAt.toISOString()
88
+ : null
89
+ },
90
+ byProfile: this.groupByProfile(filteredPosts),
91
+ byMonth: this.groupByMonth(filteredPosts),
92
+ contentAnalysis: contentAnalysis,
93
+ topHashtags: this.extractTopHashtags(filteredPosts),
94
+ performanceStats: includeAnalytics ? this.calculatePerformanceStats(filteredPosts) : null
95
+ };
96
+
97
+ // Update output parameters
98
+ const outputParams = [...Params];
99
+ const postsParam = outputParams.find(p => p.Name === 'Posts');
100
+ if (postsParam) postsParam.Value = filteredPosts;
101
+ const summaryParam = outputParams.find(p => p.Name === 'Summary');
102
+ if (summaryParam) summaryParam.Value = summary;
103
+
104
+ return {
105
+ Success: true,
106
+ ResultCode: 'SUCCESS',
107
+ Message: `Found ${filteredPosts.length} posts matching search criteria`,
108
+ Params: outputParams
109
+ };
110
+
111
+ } catch (error) {
112
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
113
+
114
+ return {
115
+ Success: false,
116
+ ResultCode: 'ERROR',
117
+ Message: `Failed to search posts: ${errorMessage}`,
118
+ Params
119
+ };
120
+ }
114
121
  }
115
- }
116
-
117
- /**
118
- * Search for posts implementation
119
- */
120
- protected async searchPosts(params: SearchParams): Promise<SocialPost[]> {
121
- const queryParams: any = {
122
- limit: Math.min(params.limit || 100, 100),
123
- maxResults: params.limit,
124
- };
125
-
126
- // Add state filter - default to published for historical search
127
- queryParams.state = 'PUBLISHED';
128
-
129
- // Add date filters
130
- if (params.startDate) {
131
- queryParams.publishedAfter = this.formatHootSuiteDate(params.startDate);
122
+
123
+ /**
124
+ * Search for posts implementation
125
+ */
126
+ protected async searchPosts(params: SearchParams): Promise<SocialPost[]> {
127
+ const queryParams: any = {
128
+ limit: Math.min(params.limit || 100, 100),
129
+ maxResults: params.limit
130
+ };
131
+
132
+ // Add state filter - default to published for historical search
133
+ queryParams.state = 'PUBLISHED';
134
+
135
+ // Add date filters
136
+ if (params.startDate) {
137
+ queryParams.publishedAfter = this.formatHootSuiteDate(params.startDate);
138
+ }
139
+ if (params.endDate) {
140
+ queryParams.publishedBefore = this.formatHootSuiteDate(params.endDate);
141
+ }
142
+
143
+ // Add text search
144
+ if (params.query) {
145
+ queryParams.text = params.query;
146
+ }
147
+
148
+ // Add hashtag search
149
+ if (params.hashtags && params.hashtags.length > 0) {
150
+ // HootSuite API may require hashtags in the text query
151
+ const hashtagQuery = params.hashtags.map(tag =>
152
+ tag.startsWith('#') ? tag : `#${tag}`
153
+ ).join(' ');
154
+
155
+ queryParams.text = params.query
156
+ ? `${params.query} ${hashtagQuery}`
157
+ : hashtagQuery;
158
+ }
159
+
160
+ // Make paginated request
161
+ const hootSuitePosts = await this.makePaginatedRequest<HootSuitePost>('/messages', queryParams);
162
+
163
+ // Convert to common format
164
+ return hootSuitePosts.map(post => this.normalizePost(post));
132
165
  }
133
- if (params.endDate) {
134
- queryParams.publishedBefore = this.formatHootSuiteDate(params.endDate);
166
+
167
+ /**
168
+ * Enrich posts with analytics data
169
+ */
170
+ private async enrichPostsWithAnalytics(posts: SocialPost[]): Promise<SocialPost[]> {
171
+ const enrichedPosts: SocialPost[] = [];
172
+
173
+ for (const post of posts) {
174
+ try {
175
+ const response = await this.axiosInstance.get(`/analytics/posts/${post.id}`);
176
+ const analytics = response.data;
177
+
178
+ enrichedPosts.push({
179
+ ...post,
180
+ analytics: this.normalizeAnalytics(analytics.metrics)
181
+ });
182
+ } catch (error) {
183
+ // If analytics fail, include post without analytics
184
+ LogStatus(`Could not get analytics for post ${post.id}`);
185
+ enrichedPosts.push(post);
186
+ }
187
+ }
188
+
189
+ return enrichedPosts;
135
190
  }
136
191
 
137
- // Add text search
138
- if (params.query) {
139
- queryParams.text = params.query;
192
+ /**
193
+ * Sort posts by specified criteria
194
+ */
195
+ private sortPosts(posts: SocialPost[], sortBy: string, sortOrder: string): SocialPost[] {
196
+ return posts.sort((a, b) => {
197
+ let compareValue = 0;
198
+
199
+ switch (sortBy) {
200
+ case 'publishedTime':
201
+ compareValue = a.publishedAt.getTime() - b.publishedAt.getTime();
202
+ break;
203
+ case 'engagements':
204
+ compareValue = (a.analytics?.engagements || 0) - (b.analytics?.engagements || 0);
205
+ break;
206
+ case 'impressions':
207
+ compareValue = (a.analytics?.impressions || 0) - (b.analytics?.impressions || 0);
208
+ break;
209
+ default:
210
+ compareValue = a.publishedAt.getTime() - b.publishedAt.getTime();
211
+ }
212
+
213
+ return sortOrder === 'DESC' ? -compareValue : compareValue;
214
+ });
140
215
  }
141
216
 
142
- // Add hashtag search
143
- if (params.hashtags && params.hashtags.length > 0) {
144
- // HootSuite API may require hashtags in the text query
145
- const hashtagQuery = params.hashtags.map((tag) => (tag.startsWith('#') ? tag : `#${tag}`)).join(' ');
217
+ /**
218
+ * Analyze post content patterns
219
+ */
220
+ private analyzePostContent(posts: SocialPost[]): any {
221
+ const totalPosts = posts.length;
222
+ if (totalPosts === 0) return null;
223
+
224
+ const contentLengths = posts.map(p => p.content.length);
225
+ const avgLength = contentLengths.reduce((a, b) => a + b, 0) / totalPosts;
226
+
227
+ const withMedia = posts.filter(p => p.mediaUrls && p.mediaUrls.length > 0).length;
228
+ const withHashtags = posts.filter(p => p.content.includes('#')).length;
229
+ const withLinks = posts.filter(p =>
230
+ p.content.includes('http://') || p.content.includes('https://')
231
+ ).length;
232
+
233
+ return {
234
+ averageLength: Math.round(avgLength),
235
+ minLength: Math.min(...contentLengths),
236
+ maxLength: Math.max(...contentLengths),
237
+ withMedia: withMedia,
238
+ withMediaPercentage: (withMedia / totalPosts) * 100,
239
+ withHashtags: withHashtags,
240
+ withHashtagsPercentage: (withHashtags / totalPosts) * 100,
241
+ withLinks: withLinks,
242
+ withLinksPercentage: (withLinks / totalPosts) * 100
243
+ };
244
+ }
146
245
 
147
- queryParams.text = params.query ? `${params.query} ${hashtagQuery}` : hashtagQuery;
246
+ /**
247
+ * Extract top hashtags from posts
248
+ */
249
+ private extractTopHashtags(posts: SocialPost[]): Array<{ hashtag: string; count: number }> {
250
+ const hashtagCounts: Record<string, number> = {};
251
+
252
+ posts.forEach(post => {
253
+ const hashtags = post.content.match(/#\w+/g) || [];
254
+ hashtags.forEach(tag => {
255
+ const normalized = tag.toLowerCase();
256
+ hashtagCounts[normalized] = (hashtagCounts[normalized] || 0) + 1;
257
+ });
258
+ });
259
+
260
+ return Object.entries(hashtagCounts)
261
+ .map(([hashtag, count]) => ({ hashtag, count }))
262
+ .sort((a, b) => b.count - a.count)
263
+ .slice(0, 10);
148
264
  }
149
265
 
150
- // Make paginated request
151
- const hootSuitePosts = await this.makePaginatedRequest<HootSuitePost>('/messages', queryParams);
266
+ /**
267
+ * Calculate performance statistics
268
+ */
269
+ private calculatePerformanceStats(posts: SocialPost[]): any {
270
+ const postsWithAnalytics = posts.filter(p => p.analytics);
271
+ if (postsWithAnalytics.length === 0) return null;
152
272
 
153
- // Convert to common format
154
- return hootSuitePosts.map((post) => this.normalizePost(post));
155
- }
273
+ const totalEngagements = postsWithAnalytics.reduce((sum, p) =>
274
+ sum + (p.analytics?.engagements || 0), 0
275
+ );
276
+ const totalImpressions = postsWithAnalytics.reduce((sum, p) =>
277
+ sum + (p.analytics?.impressions || 0), 0
278
+ );
156
279
 
157
- /**
158
- * Enrich posts with analytics data
159
- */
160
- private async enrichPostsWithAnalytics(posts: SocialPost[]): Promise<SocialPost[]> {
161
- const enrichedPosts: SocialPost[] = [];
280
+ const avgEngagements = totalEngagements / postsWithAnalytics.length;
281
+ const avgImpressions = totalImpressions / postsWithAnalytics.length;
282
+ const engagementRate = totalImpressions > 0
283
+ ? (totalEngagements / totalImpressions) * 100
284
+ : 0;
285
+
286
+ return {
287
+ postsAnalyzed: postsWithAnalytics.length,
288
+ averageEngagements: Math.round(avgEngagements),
289
+ averageImpressions: Math.round(avgImpressions),
290
+ totalEngagements: totalEngagements,
291
+ totalImpressions: totalImpressions,
292
+ engagementRate: engagementRate.toFixed(2)
293
+ };
294
+ }
295
+
296
+ /**
297
+ * Group posts by profile
298
+ */
299
+ private groupByProfile(posts: SocialPost[]): Record<string, number> {
300
+ const groups: Record<string, number> = {};
301
+
302
+ posts.forEach(post => {
303
+ const profiles = post.platformSpecificData?.socialProfileIds || [post.profileId];
304
+ profiles.forEach((profileId: string) => {
305
+ groups[profileId] = (groups[profileId] || 0) + 1;
306
+ });
307
+ });
162
308
 
163
- for (const post of posts) {
164
- try {
165
- const response = await this.axiosInstance.get(`/analytics/posts/${post.id}`);
166
- const analytics = response.data;
309
+ return groups;
310
+ }
167
311
 
168
- enrichedPosts.push({
169
- ...post,
170
- analytics: this.normalizeAnalytics(analytics.metrics),
312
+ /**
313
+ * Group posts by month
314
+ */
315
+ private groupByMonth(posts: SocialPost[]): Record<string, number> {
316
+ const groups: Record<string, number> = {};
317
+
318
+ posts.forEach(post => {
319
+ const monthKey = post.publishedAt.toISOString().substring(0, 7);
320
+ groups[monthKey] = (groups[monthKey] || 0) + 1;
171
321
  });
172
- } catch (error) {
173
- // If analytics fail, include post without analytics
174
- LogStatus(`Could not get analytics for post ${post.id}`);
175
- enrichedPosts.push(post);
176
- }
322
+
323
+ return groups;
324
+ }
325
+
326
+ /**
327
+ * Define the parameters this action expects
328
+ */
329
+ public get Params(): ActionParam[] {
330
+ return [
331
+ ...this.commonSocialParams,
332
+ {
333
+ Name: 'Query',
334
+ Type: 'Input',
335
+ Value: null
336
+ },
337
+ {
338
+ Name: 'Hashtags',
339
+ Type: 'Input',
340
+ Value: null
341
+ },
342
+ {
343
+ Name: 'StartDate',
344
+ Type: 'Input',
345
+ Value: null
346
+ },
347
+ {
348
+ Name: 'EndDate',
349
+ Type: 'Input',
350
+ Value: null
351
+ },
352
+ {
353
+ Name: 'PostState',
354
+ Type: 'Input',
355
+ Value: null
356
+ },
357
+ {
358
+ Name: 'Limit',
359
+ Type: 'Input',
360
+ Value: null
361
+ },
362
+ {
363
+ Name: 'IncludeAnalytics',
364
+ Type: 'Input',
365
+ Value: null
366
+ },
367
+ {
368
+ Name: 'SortBy',
369
+ Type: 'Input',
370
+ Value: null
371
+ },
372
+ {
373
+ Name: 'SortOrder',
374
+ Type: 'Input',
375
+ Value: null
376
+ },
377
+ {
378
+ Name: 'Posts',
379
+ Type: 'Output',
380
+ Value: null
381
+ },
382
+ {
383
+ Name: 'Summary',
384
+ Type: 'Output',
385
+ Value: null
386
+ }
387
+ ];
177
388
  }
178
389
 
179
- return enrichedPosts;
180
- }
181
-
182
- /**
183
- * Sort posts by specified criteria
184
- */
185
- private sortPosts(posts: SocialPost[], sortBy: string, sortOrder: string): SocialPost[] {
186
- return posts.sort((a, b) => {
187
- let compareValue = 0;
188
-
189
- switch (sortBy) {
190
- case 'publishedTime':
191
- compareValue = a.publishedAt.getTime() - b.publishedAt.getTime();
192
- break;
193
- case 'engagements':
194
- compareValue = (a.analytics?.engagements || 0) - (b.analytics?.engagements || 0);
195
- break;
196
- case 'impressions':
197
- compareValue = (a.analytics?.impressions || 0) - (b.analytics?.impressions || 0);
198
- break;
199
- default:
200
- compareValue = a.publishedAt.getTime() - b.publishedAt.getTime();
201
- }
202
-
203
- return sortOrder === 'DESC' ? -compareValue : compareValue;
204
- });
205
- }
206
-
207
- /**
208
- * Analyze post content patterns
209
- */
210
- private analyzePostContent(posts: SocialPost[]): any {
211
- const totalPosts = posts.length;
212
- if (totalPosts === 0) return null;
213
-
214
- const contentLengths = posts.map((p) => p.content.length);
215
- const avgLength = contentLengths.reduce((a, b) => a + b, 0) / totalPosts;
216
-
217
- const withMedia = posts.filter((p) => p.mediaUrls && p.mediaUrls.length > 0).length;
218
- const withHashtags = posts.filter((p) => p.content.includes('#')).length;
219
- const withLinks = posts.filter((p) => p.content.includes('http://') || p.content.includes('https://')).length;
220
-
221
- return {
222
- averageLength: Math.round(avgLength),
223
- minLength: Math.min(...contentLengths),
224
- maxLength: Math.max(...contentLengths),
225
- withMedia: withMedia,
226
- withMediaPercentage: (withMedia / totalPosts) * 100,
227
- withHashtags: withHashtags,
228
- withHashtagsPercentage: (withHashtags / totalPosts) * 100,
229
- withLinks: withLinks,
230
- withLinksPercentage: (withLinks / totalPosts) * 100,
231
- };
232
- }
233
-
234
- /**
235
- * Extract top hashtags from posts
236
- */
237
- private extractTopHashtags(posts: SocialPost[]): Array<{ hashtag: string; count: number }> {
238
- const hashtagCounts: Record<string, number> = {};
239
-
240
- posts.forEach((post) => {
241
- const hashtags = post.content.match(/#\w+/g) || [];
242
- hashtags.forEach((tag) => {
243
- const normalized = tag.toLowerCase();
244
- hashtagCounts[normalized] = (hashtagCounts[normalized] || 0) + 1;
245
- });
246
- });
247
-
248
- return Object.entries(hashtagCounts)
249
- .map(([hashtag, count]) => ({ hashtag, count }))
250
- .sort((a, b) => b.count - a.count)
251
- .slice(0, 10);
252
- }
253
-
254
- /**
255
- * Calculate performance statistics
256
- */
257
- private calculatePerformanceStats(posts: SocialPost[]): any {
258
- const postsWithAnalytics = posts.filter((p) => p.analytics);
259
- if (postsWithAnalytics.length === 0) return null;
260
-
261
- const totalEngagements = postsWithAnalytics.reduce((sum, p) => sum + (p.analytics?.engagements || 0), 0);
262
- const totalImpressions = postsWithAnalytics.reduce((sum, p) => sum + (p.analytics?.impressions || 0), 0);
263
-
264
- const avgEngagements = totalEngagements / postsWithAnalytics.length;
265
- const avgImpressions = totalImpressions / postsWithAnalytics.length;
266
- const engagementRate = totalImpressions > 0 ? (totalEngagements / totalImpressions) * 100 : 0;
267
-
268
- return {
269
- postsAnalyzed: postsWithAnalytics.length,
270
- averageEngagements: Math.round(avgEngagements),
271
- averageImpressions: Math.round(avgImpressions),
272
- totalEngagements: totalEngagements,
273
- totalImpressions: totalImpressions,
274
- engagementRate: engagementRate.toFixed(2),
275
- };
276
- }
277
-
278
- /**
279
- * Group posts by profile
280
- */
281
- private groupByProfile(posts: SocialPost[]): Record<string, number> {
282
- const groups: Record<string, number> = {};
283
-
284
- posts.forEach((post) => {
285
- const profiles = post.platformSpecificData?.socialProfileIds || [post.profileId];
286
- profiles.forEach((profileId: string) => {
287
- groups[profileId] = (groups[profileId] || 0) + 1;
288
- });
289
- });
290
-
291
- return groups;
292
- }
293
-
294
- /**
295
- * Group posts by month
296
- */
297
- private groupByMonth(posts: SocialPost[]): Record<string, number> {
298
- const groups: Record<string, number> = {};
299
-
300
- posts.forEach((post) => {
301
- const monthKey = post.publishedAt.toISOString().substring(0, 7);
302
- groups[monthKey] = (groups[monthKey] || 0) + 1;
303
- });
304
-
305
- return groups;
306
- }
307
-
308
- /**
309
- * Define the parameters this action expects
310
- */
311
- public get Params(): ActionParam[] {
312
- return [
313
- ...this.commonSocialParams,
314
- {
315
- Name: 'Query',
316
- Type: 'Input',
317
- Value: null,
318
- },
319
- {
320
- Name: 'Hashtags',
321
- Type: 'Input',
322
- Value: null,
323
- },
324
- {
325
- Name: 'StartDate',
326
- Type: 'Input',
327
- Value: null,
328
- },
329
- {
330
- Name: 'EndDate',
331
- Type: 'Input',
332
- Value: null,
333
- },
334
- {
335
- Name: 'PostState',
336
- Type: 'Input',
337
- Value: null,
338
- },
339
- {
340
- Name: 'Limit',
341
- Type: 'Input',
342
- Value: null,
343
- },
344
- {
345
- Name: 'IncludeAnalytics',
346
- Type: 'Input',
347
- Value: null,
348
- },
349
- {
350
- Name: 'SortBy',
351
- Type: 'Input',
352
- Value: null,
353
- },
354
- {
355
- Name: 'SortOrder',
356
- Type: 'Input',
357
- Value: null,
358
- },
359
- {
360
- Name: 'Posts',
361
- Type: 'Output',
362
- Value: null,
363
- },
364
- {
365
- Name: 'Summary',
366
- Type: 'Output',
367
- Value: null,
368
- },
369
- ];
370
- }
371
-
372
- /**
373
- * Get action description
374
- */
375
- public get Description(): string {
376
- return 'Searches historical posts in HootSuite with support for text queries, hashtags, date ranges, and content analysis';
377
- }
378
- }
390
+ /**
391
+ * Get action description
392
+ */
393
+ public get Description(): string {
394
+ return 'Searches historical posts in HootSuite with support for text queries, hashtags, date ranges, and content analysis';
395
+ }
396
+ }