@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
@@ -2,7 +2,7 @@ import { RegisterClass } from '@memberjunction/global';
2
2
  import { FacebookBaseAction, FacebookPost } from '../facebook-base.action';
3
3
  import { ActionParam, ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
4
4
  import { SocialPost, SearchParams, SocialMediaErrorCode } from '../../../base/base-social.action';
5
- import { LogStatus, LogError } from '@memberjunction/global';
5
+ import { LogStatus, LogError } from '@memberjunction/core';
6
6
  import axios from 'axios';
7
7
  import { BaseAction } from '@memberjunction/actions';
8
8
 
@@ -12,425 +12,439 @@ import { BaseAction } from '@memberjunction/actions';
12
12
  */
13
13
  @RegisterClass(BaseAction, 'FacebookSearchPostsAction')
14
14
  export class FacebookSearchPostsAction extends FacebookBaseAction {
15
- /**
16
- * Get action description
17
- */
18
- public get Description(): string {
19
- return 'Searches for historical posts on Facebook pages with filters for date ranges, keywords, hashtags, and content types';
20
- }
21
-
22
- /**
23
- * Define the parameters for this action
24
- */
25
- public get Params(): ActionParam[] {
26
- return [
27
- ...this.commonSocialParams,
28
- {
29
- Name: 'PageIDs',
30
- Type: 'Input',
31
- Value: null,
32
- },
33
- {
34
- Name: 'Query',
35
- Type: 'Input',
36
- Value: null,
37
- },
38
- {
39
- Name: 'Hashtags',
40
- Type: 'Input',
41
- Value: null,
42
- },
43
- {
44
- Name: 'StartDate',
45
- Type: 'Input',
46
- Value: null,
47
- },
48
- {
49
- Name: 'EndDate',
50
- Type: 'Input',
51
- Value: null,
52
- },
53
- {
54
- Name: 'PostTypes',
55
- Type: 'Input',
56
- Value: null,
57
- },
58
- {
59
- Name: 'MinEngagements',
60
- Type: 'Input',
61
- Value: null,
62
- },
63
- {
64
- Name: 'IncludeMetrics',
65
- Type: 'Input',
66
- Value: true,
67
- },
68
- {
69
- Name: 'Limit',
70
- Type: 'Input',
71
- Value: 100,
72
- },
73
- {
74
- Name: 'SortBy',
75
- Type: 'Input',
76
- Value: 'created_time',
77
- },
78
- {
79
- Name: 'SortOrder',
80
- Type: 'Input',
81
- Value: 'DESC',
82
- },
83
- ];
84
- }
85
-
86
- /**
87
- * Execute the action
88
- */
89
- protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> {
90
- const { Params, ContextUser } = params;
91
-
92
- try {
93
- // Validate required parameters
94
- const companyIntegrationId = this.getParamValue(Params, 'CompanyIntegrationID');
95
-
96
- if (!companyIntegrationId) {
97
- return {
98
- Success: false,
99
- Message: 'CompanyIntegrationID is required',
100
- ResultCode: 'INVALID_TOKEN',
101
- };
102
- }
15
+ /**
16
+ * Get action description
17
+ */
18
+ public get Description(): string {
19
+ return 'Searches for historical posts on Facebook pages with filters for date ranges, keywords, hashtags, and content types';
20
+ }
103
21
 
104
- // Initialize OAuth
105
- if (!(await this.initializeOAuth(companyIntegrationId))) {
106
- return {
107
- Success: false,
108
- Message: 'Failed to initialize Facebook OAuth connection',
109
- ResultCode: 'INVALID_TOKEN',
110
- };
111
- }
112
-
113
- // Get parameters
114
- const pageIds = this.getParamValue(Params, 'PageIDs') as string[];
115
- const query = this.getParamValue(Params, 'Query') as string;
116
- const hashtags = this.getParamValue(Params, 'Hashtags') as string[];
117
- const startDate = this.getParamValue(Params, 'StartDate') as string;
118
- const endDate = this.getParamValue(Params, 'EndDate') as string;
119
- const postTypes = this.getParamValue(Params, 'PostTypes') as string[];
120
- const minEngagements = this.getParamValue(Params, 'MinEngagements') as number;
121
- const includeMetrics = this.getParamValue(Params, 'IncludeMetrics') !== false;
122
- const limit = (this.getParamValue(Params, 'Limit') as number) || 100;
123
- const sortBy = (this.getParamValue(Params, 'SortBy') as string) || 'created_time';
124
- const sortOrder = (this.getParamValue(Params, 'SortOrder') as string) || 'DESC';
125
-
126
- // Build search parameters
127
- const searchParams: SearchParams = {
128
- query,
129
- hashtags,
130
- startDate: startDate ? new Date(startDate) : undefined,
131
- endDate: endDate ? new Date(endDate) : undefined,
132
- limit,
133
- };
134
-
135
- LogStatus('Starting Facebook post search...');
136
-
137
- // Get pages to search
138
- let pagesToSearch = pageIds;
139
- if (!pagesToSearch || pagesToSearch.length === 0) {
140
- // Get all accessible pages
141
- const userPages = await this.getUserPages();
142
- pagesToSearch = userPages.map((p) => p.id);
143
- LogStatus(`Searching across ${pagesToSearch.length} accessible pages`);
144
- }
145
-
146
- // Search posts from each page
147
- const allPosts: FacebookPost[] = [];
148
- for (const pageId of pagesToSearch) {
22
+ /**
23
+ * Define the parameters for this action
24
+ */
25
+ public get Params(): ActionParam[] {
26
+ return [
27
+ ...this.commonSocialParams,
28
+ {
29
+ Name: 'PageIDs',
30
+ Type: 'Input',
31
+ Value: null,
32
+ },
33
+ {
34
+ Name: 'Query',
35
+ Type: 'Input',
36
+ Value: null,
37
+ },
38
+ {
39
+ Name: 'Hashtags',
40
+ Type: 'Input',
41
+ Value: null,
42
+ },
43
+ {
44
+ Name: 'StartDate',
45
+ Type: 'Input',
46
+ Value: null,
47
+ },
48
+ {
49
+ Name: 'EndDate',
50
+ Type: 'Input',
51
+ Value: null,
52
+ },
53
+ {
54
+ Name: 'PostTypes',
55
+ Type: 'Input',
56
+ Value: null,
57
+ },
58
+ {
59
+ Name: 'MinEngagements',
60
+ Type: 'Input',
61
+ Value: null,
62
+ },
63
+ {
64
+ Name: 'IncludeMetrics',
65
+ Type: 'Input',
66
+ Value: true,
67
+ },
68
+ {
69
+ Name: 'Limit',
70
+ Type: 'Input',
71
+ Value: 100,
72
+ },
73
+ {
74
+ Name: 'SortBy',
75
+ Type: 'Input',
76
+ Value: 'created_time',
77
+ },
78
+ {
79
+ Name: 'SortOrder',
80
+ Type: 'Input',
81
+ Value: 'DESC',
82
+ }
83
+ ];
84
+ }
85
+
86
+ /**
87
+ * Execute the action
88
+ */
89
+ protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> {
90
+ const { Params, ContextUser } = params;
91
+
149
92
  try {
150
- const posts = await this.searchPagePosts(pageId, searchParams, postTypes, includeMetrics);
151
- allPosts.push(...posts);
152
- LogStatus(`Found ${posts.length} posts from page ${pageId}`);
93
+ // Validate required parameters
94
+ const companyIntegrationId = this.getParamValue(Params, 'CompanyIntegrationID');
95
+
96
+ if (!companyIntegrationId) {
97
+ return {
98
+ Success: false,
99
+ Message: 'CompanyIntegrationID is required',
100
+ ResultCode: 'INVALID_TOKEN'
101
+ };
102
+ }
103
+
104
+ // Initialize OAuth
105
+ if (!await this.initializeOAuth(companyIntegrationId)) {
106
+ return {
107
+ Success: false,
108
+ Message: 'Failed to initialize Facebook OAuth connection',
109
+ ResultCode: 'INVALID_TOKEN'
110
+ };
111
+ }
112
+
113
+ // Get parameters
114
+ const pageIds = this.getParamValue(Params, 'PageIDs') as string[];
115
+ const query = this.getParamValue(Params, 'Query') as string;
116
+ const hashtags = this.getParamValue(Params, 'Hashtags') as string[];
117
+ const startDate = this.getParamValue(Params, 'StartDate') as string;
118
+ const endDate = this.getParamValue(Params, 'EndDate') as string;
119
+ const postTypes = this.getParamValue(Params, 'PostTypes') as string[];
120
+ const minEngagements = this.getParamValue(Params, 'MinEngagements') as number;
121
+ const includeMetrics = this.getParamValue(Params, 'IncludeMetrics') !== false;
122
+ const limit = this.getParamValue(Params, 'Limit') as number || 100;
123
+ const sortBy = this.getParamValue(Params, 'SortBy') as string || 'created_time';
124
+ const sortOrder = this.getParamValue(Params, 'SortOrder') as string || 'DESC';
125
+
126
+ // Build search parameters
127
+ const searchParams: SearchParams = {
128
+ query,
129
+ hashtags,
130
+ startDate: startDate ? new Date(startDate) : undefined,
131
+ endDate: endDate ? new Date(endDate) : undefined,
132
+ limit
133
+ };
134
+
135
+ LogStatus('Starting Facebook post search...');
136
+
137
+ // Get pages to search
138
+ let pagesToSearch = pageIds;
139
+ if (!pagesToSearch || pagesToSearch.length === 0) {
140
+ // Get all accessible pages
141
+ const userPages = await this.getUserPages();
142
+ pagesToSearch = userPages.map(p => p.id);
143
+ LogStatus(`Searching across ${pagesToSearch.length} accessible pages`);
144
+ }
145
+
146
+ // Search posts from each page
147
+ const allPosts: FacebookPost[] = [];
148
+ for (const pageId of pagesToSearch) {
149
+ try {
150
+ const posts = await this.searchPagePosts(
151
+ pageId,
152
+ searchParams,
153
+ postTypes,
154
+ includeMetrics
155
+ );
156
+ allPosts.push(...posts);
157
+ LogStatus(`Found ${posts.length} posts from page ${pageId}`);
158
+ } catch (error) {
159
+ LogError(`Failed to search page ${pageId}: ${error}`);
160
+ // Continue with other pages
161
+ }
162
+ }
163
+
164
+ // Filter by minimum engagements if specified
165
+ let filteredPosts = allPosts;
166
+ if (minEngagements && minEngagements > 0) {
167
+ filteredPosts = allPosts.filter(post => {
168
+ const engagements = this.calculateEngagements(post);
169
+ return engagements >= minEngagements;
170
+ });
171
+ LogStatus(`Filtered to ${filteredPosts.length} posts with at least ${minEngagements} engagements`);
172
+ }
173
+
174
+ // Sort posts
175
+ const sortedPosts = this.sortPosts(filteredPosts, sortBy, sortOrder);
176
+
177
+ // Limit results
178
+ const limitedPosts = sortedPosts.slice(0, limit);
179
+
180
+ // Normalize posts to common format
181
+ const normalizedPosts = limitedPosts.map(post => this.normalizePost(post));
182
+
183
+ // Calculate search summary
184
+ const summary = this.calculateSearchSummary(normalizedPosts, searchParams);
185
+
186
+ LogStatus(`Search completed. Found ${normalizedPosts.length} matching posts`);
187
+
188
+ // Update output parameters
189
+ const outputParams = [...Params];
190
+ // TODO: Set output parameters based on result
191
+
192
+ return {
193
+ Success: true,
194
+ Message: `Found ${normalizedPosts.length} posts matching search criteria`,
195
+ ResultCode: 'SUCCESS',
196
+ Params: outputParams
197
+ };
198
+
153
199
  } catch (error) {
154
- LogError(`Failed to search page ${pageId}: ${error}`);
155
- // Continue with other pages
200
+ LogError(`Failed to search Facebook posts: ${error instanceof Error ? error.message : 'Unknown error'}`);
201
+
202
+ if (this.isAuthError(error)) {
203
+ return this.handleOAuthError(error);
204
+ }
205
+
206
+ return {
207
+ Success: false,
208
+ Message: error instanceof Error ? error.message : 'Unknown error occurred',
209
+ ResultCode: 'ERROR'
210
+ };
156
211
  }
157
- }
158
-
159
- // Filter by minimum engagements if specified
160
- let filteredPosts = allPosts;
161
- if (minEngagements && minEngagements > 0) {
162
- filteredPosts = allPosts.filter((post) => {
163
- const engagements = this.calculateEngagements(post);
164
- return engagements >= minEngagements;
165
- });
166
- LogStatus(`Filtered to ${filteredPosts.length} posts with at least ${minEngagements} engagements`);
167
- }
168
-
169
- // Sort posts
170
- const sortedPosts = this.sortPosts(filteredPosts, sortBy, sortOrder);
171
-
172
- // Limit results
173
- const limitedPosts = sortedPosts.slice(0, limit);
174
-
175
- // Normalize posts to common format
176
- const normalizedPosts = limitedPosts.map((post) => this.normalizePost(post));
212
+ }
177
213
 
178
- // Calculate search summary
179
- const summary = this.calculateSearchSummary(normalizedPosts, searchParams);
214
+ /**
215
+ * Search posts from a specific page
216
+ */
217
+ private async searchPagePosts(
218
+ pageId: string,
219
+ searchParams: SearchParams,
220
+ postTypes: string[] | null,
221
+ includeMetrics: boolean
222
+ ): Promise<FacebookPost[]> {
223
+ const pageToken = await this.getPageAccessToken(pageId);
224
+
225
+ // Build fields parameter
226
+ const fields = includeMetrics
227
+ ? this.getPostFields()
228
+ : 'id,message,created_time,updated_time,from,story,permalink_url,attachments';
229
+
230
+ // Build API parameters
231
+ const apiParams: any = {
232
+ access_token: pageToken,
233
+ fields,
234
+ limit: 100 // Get max per request, we'll filter later
235
+ };
180
236
 
181
- LogStatus(`Search completed. Found ${normalizedPosts.length} matching posts`);
237
+ // Add date range
238
+ if (searchParams.startDate) {
239
+ apiParams.since = Math.floor(searchParams.startDate.getTime() / 1000);
240
+ }
241
+ if (searchParams.endDate) {
242
+ apiParams.until = Math.floor(searchParams.endDate.getTime() / 1000);
243
+ }
182
244
 
183
- // Update output parameters
184
- const outputParams = [...Params];
185
- // TODO: Set output parameters based on result
245
+ // Get all posts in date range
246
+ const allPosts = await this.getPaginatedResults<FacebookPost>(
247
+ `${this.apiBaseUrl}/${pageId}/posts`,
248
+ apiParams,
249
+ searchParams.limit ? searchParams.limit * 2 : undefined // Get extra to account for filtering
250
+ );
251
+
252
+ // Filter posts based on search criteria
253
+ let filteredPosts = allPosts;
254
+
255
+ // Filter by content/query
256
+ if (searchParams.query) {
257
+ const queryLower = searchParams.query.toLowerCase();
258
+ filteredPosts = filteredPosts.filter(post => {
259
+ const message = (post.message || '').toLowerCase();
260
+ const story = (post.story || '').toLowerCase();
261
+ return message.includes(queryLower) || story.includes(queryLower);
262
+ });
263
+ }
186
264
 
187
- return {
188
- Success: true,
189
- Message: `Found ${normalizedPosts.length} posts matching search criteria`,
190
- ResultCode: 'SUCCESS',
191
- Params: outputParams,
192
- };
193
- } catch (error) {
194
- LogError(`Failed to search Facebook posts: ${error instanceof Error ? error.message : 'Unknown error'}`);
265
+ // Filter by hashtags
266
+ if (searchParams.hashtags && searchParams.hashtags.length > 0) {
267
+ const hashtagsToFind = searchParams.hashtags.map(tag =>
268
+ tag.startsWith('#') ? tag.toLowerCase() : `#${tag}`.toLowerCase()
269
+ );
270
+
271
+ filteredPosts = filteredPosts.filter(post => {
272
+ const content = (post.message || '').toLowerCase();
273
+ return hashtagsToFind.some(hashtag => content.includes(hashtag));
274
+ });
275
+ }
195
276
 
196
- if (this.isAuthError(error)) {
197
- return this.handleOAuthError(error);
198
- }
277
+ // Filter by post types
278
+ if (postTypes && postTypes.length > 0) {
279
+ filteredPosts = filteredPosts.filter(post => {
280
+ const postType = this.getPostType(post);
281
+ return postTypes.includes(postType);
282
+ });
283
+ }
199
284
 
200
- return {
201
- Success: false,
202
- Message: error instanceof Error ? error.message : 'Unknown error occurred',
203
- ResultCode: 'ERROR',
204
- };
285
+ return filteredPosts;
205
286
  }
206
- }
207
-
208
- /**
209
- * Search posts from a specific page
210
- */
211
- private async searchPagePosts(
212
- pageId: string,
213
- searchParams: SearchParams,
214
- postTypes: string[] | null,
215
- includeMetrics: boolean
216
- ): Promise<FacebookPost[]> {
217
- const pageToken = await this.getPageAccessToken(pageId);
218
-
219
- // Build fields parameter
220
- const fields = includeMetrics ? this.getPostFields() : 'id,message,created_time,updated_time,from,story,permalink_url,attachments';
221
-
222
- // Build API parameters
223
- const apiParams: any = {
224
- access_token: pageToken,
225
- fields,
226
- limit: 100, // Get max per request, we'll filter later
227
- };
228
-
229
- // Add date range
230
- if (searchParams.startDate) {
231
- apiParams.since = Math.floor(searchParams.startDate.getTime() / 1000);
287
+
288
+ /**
289
+ * Implement the abstract searchPosts method
290
+ */
291
+ protected async searchPosts(params: SearchParams): Promise<SocialPost[]> {
292
+ // This is called by the base class, but we implement our own search logic
293
+ // in RunAction, so we'll just throw an error here
294
+ throw new Error('Use FacebookSearchPostsAction.RunAction instead');
232
295
  }
233
- if (searchParams.endDate) {
234
- apiParams.until = Math.floor(searchParams.endDate.getTime() / 1000);
296
+
297
+ /**
298
+ * Get post type from Facebook post
299
+ */
300
+ private getPostType(post: FacebookPost): string {
301
+ if (post.attachments?.data?.[0]?.type) {
302
+ return post.attachments.data[0].type.toLowerCase();
303
+ }
304
+ return 'status';
235
305
  }
236
306
 
237
- // Get all posts in date range
238
- const allPosts = await this.getPaginatedResults<FacebookPost>(
239
- `${this.apiBaseUrl}/${pageId}/posts`,
240
- apiParams,
241
- searchParams.limit ? searchParams.limit * 2 : undefined // Get extra to account for filtering
242
- );
243
-
244
- // Filter posts based on search criteria
245
- let filteredPosts = allPosts;
246
-
247
- // Filter by content/query
248
- if (searchParams.query) {
249
- const queryLower = searchParams.query.toLowerCase();
250
- filteredPosts = filteredPosts.filter((post) => {
251
- const message = (post.message || '').toLowerCase();
252
- const story = (post.story || '').toLowerCase();
253
- return message.includes(queryLower) || story.includes(queryLower);
254
- });
307
+ /**
308
+ * Calculate total engagements for a post
309
+ */
310
+ private calculateEngagements(post: FacebookPost): number {
311
+ const reactions = post.reactions?.summary?.total_count || 0;
312
+ const comments = post.comments?.summary?.total_count || 0;
313
+ const shares = post.shares?.count || 0;
314
+
315
+ return reactions + comments + shares;
255
316
  }
256
317
 
257
- // Filter by hashtags
258
- if (searchParams.hashtags && searchParams.hashtags.length > 0) {
259
- const hashtagsToFind = searchParams.hashtags.map((tag) => (tag.startsWith('#') ? tag.toLowerCase() : `#${tag}`.toLowerCase()));
318
+ /**
319
+ * Sort posts by specified criteria
320
+ */
321
+ private sortPosts(posts: FacebookPost[], sortBy: string, sortOrder: string): FacebookPost[] {
322
+ const sorted = [...posts].sort((a, b) => {
323
+ let aValue: number;
324
+ let bValue: number;
325
+
326
+ switch (sortBy) {
327
+ case 'engagement':
328
+ aValue = this.calculateEngagements(a);
329
+ bValue = this.calculateEngagements(b);
330
+ break;
331
+ case 'reach':
332
+ aValue = this.getPostReach(a);
333
+ bValue = this.getPostReach(b);
334
+ break;
335
+ case 'created_time':
336
+ default:
337
+ aValue = new Date(a.created_time).getTime();
338
+ bValue = new Date(b.created_time).getTime();
339
+ break;
340
+ }
341
+
342
+ return sortOrder === 'DESC' ? bValue - aValue : aValue - bValue;
343
+ });
260
344
 
261
- filteredPosts = filteredPosts.filter((post) => {
262
- const content = (post.message || '').toLowerCase();
263
- return hashtagsToFind.some((hashtag) => content.includes(hashtag));
264
- });
345
+ return sorted;
265
346
  }
266
347
 
267
- // Filter by post types
268
- if (postTypes && postTypes.length > 0) {
269
- filteredPosts = filteredPosts.filter((post) => {
270
- const postType = this.getPostType(post);
271
- return postTypes.includes(postType);
272
- });
348
+ /**
349
+ * Get post reach from insights
350
+ */
351
+ private getPostReach(post: FacebookPost): number {
352
+ if (post.insights?.data) {
353
+ const reachInsight = post.insights.data.find(i =>
354
+ i.name === 'post_impressions_unique' || i.name === 'post_reach'
355
+ );
356
+ return reachInsight?.values?.[0]?.value || 0;
357
+ }
358
+ return 0;
273
359
  }
274
360
 
275
- return filteredPosts;
276
- }
277
-
278
- /**
279
- * Implement the abstract searchPosts method
280
- */
281
- protected async searchPosts(params: SearchParams): Promise<SocialPost[]> {
282
- // This is called by the base class, but we implement our own search logic
283
- // in RunAction, so we'll just throw an error here
284
- throw new Error('Use FacebookSearchPostsAction.RunAction instead');
285
- }
286
-
287
- /**
288
- * Get post type from Facebook post
289
- */
290
- private getPostType(post: FacebookPost): string {
291
- if (post.attachments?.data?.[0]?.type) {
292
- return post.attachments.data[0].type.toLowerCase();
293
- }
294
- return 'status';
295
- }
296
-
297
- /**
298
- * Calculate total engagements for a post
299
- */
300
- private calculateEngagements(post: FacebookPost): number {
301
- const reactions = post.reactions?.summary?.total_count || 0;
302
- const comments = post.comments?.summary?.total_count || 0;
303
- const shares = post.shares?.count || 0;
304
-
305
- return reactions + comments + shares;
306
- }
307
-
308
- /**
309
- * Sort posts by specified criteria
310
- */
311
- private sortPosts(posts: FacebookPost[], sortBy: string, sortOrder: string): FacebookPost[] {
312
- const sorted = [...posts].sort((a, b) => {
313
- let aValue: number;
314
- let bValue: number;
315
-
316
- switch (sortBy) {
317
- case 'engagement':
318
- aValue = this.calculateEngagements(a);
319
- bValue = this.calculateEngagements(b);
320
- break;
321
- case 'reach':
322
- aValue = this.getPostReach(a);
323
- bValue = this.getPostReach(b);
324
- break;
325
- case 'created_time':
326
- default:
327
- aValue = new Date(a.created_time).getTime();
328
- bValue = new Date(b.created_time).getTime();
329
- break;
330
- }
331
-
332
- return sortOrder === 'DESC' ? bValue - aValue : aValue - bValue;
333
- });
334
-
335
- return sorted;
336
- }
337
-
338
- /**
339
- * Get post reach from insights
340
- */
341
- private getPostReach(post: FacebookPost): number {
342
- if (post.insights?.data) {
343
- const reachInsight = post.insights.data.find((i) => i.name === 'post_impressions_unique' || i.name === 'post_reach');
344
- return reachInsight?.values?.[0]?.value || 0;
361
+ /**
362
+ * Calculate search summary statistics
363
+ */
364
+ private calculateSearchSummary(posts: SocialPost[], searchParams: SearchParams): any {
365
+ if (posts.length === 0) {
366
+ return {
367
+ totalPosts: 0,
368
+ dateRange: null,
369
+ topHashtags: [],
370
+ postTypes: {},
371
+ engagementStats: null
372
+ };
373
+ }
374
+
375
+ // Date range
376
+ const dates = posts.map(p => p.publishedAt.getTime());
377
+ const dateRange = {
378
+ earliest: new Date(Math.min(...dates)),
379
+ latest: new Date(Math.max(...dates))
380
+ };
381
+
382
+ // Extract hashtags
383
+ const hashtagCounts: Record<string, number> = {};
384
+ posts.forEach(post => {
385
+ const hashtags = post.content.match(/#\w+/g) || [];
386
+ hashtags.forEach(tag => {
387
+ hashtagCounts[tag] = (hashtagCounts[tag] || 0) + 1;
388
+ });
389
+ });
390
+
391
+ // Top hashtags
392
+ const topHashtags = Object.entries(hashtagCounts)
393
+ .sort(([, a], [, b]) => b - a)
394
+ .slice(0, 10)
395
+ .map(([tag, count]) => ({ tag, count }));
396
+
397
+ // Post types
398
+ const postTypes: Record<string, number> = {};
399
+ posts.forEach(post => {
400
+ const type = post.platformSpecificData.postType || 'status';
401
+ postTypes[type] = (postTypes[type] || 0) + 1;
402
+ });
403
+
404
+ // Engagement statistics
405
+ const engagements = posts.map(p => p.analytics?.engagements || 0);
406
+ const totalEngagements = engagements.reduce((sum, e) => sum + e, 0);
407
+ const avgEngagements = totalEngagements / posts.length;
408
+ const maxEngagements = Math.max(...engagements);
409
+
410
+ return {
411
+ totalPosts: posts.length,
412
+ dateRange,
413
+ topHashtags,
414
+ postTypes,
415
+ engagementStats: {
416
+ total: totalEngagements,
417
+ average: Math.round(avgEngagements),
418
+ max: maxEngagements,
419
+ distribution: this.getEngagementDistribution(engagements)
420
+ }
421
+ };
345
422
  }
346
- return 0;
347
- }
348
-
349
- /**
350
- * Calculate search summary statistics
351
- */
352
- private calculateSearchSummary(posts: SocialPost[], searchParams: SearchParams): any {
353
- if (posts.length === 0) {
354
- return {
355
- totalPosts: 0,
356
- dateRange: null,
357
- topHashtags: [],
358
- postTypes: {},
359
- engagementStats: null,
360
- };
423
+
424
+ /**
425
+ * Get engagement distribution
426
+ */
427
+ private getEngagementDistribution(engagements: number[]): Record<string, number> {
428
+ const distribution = {
429
+ '0-10': 0,
430
+ '11-50': 0,
431
+ '51-100': 0,
432
+ '101-500': 0,
433
+ '501-1000': 0,
434
+ '1000+': 0
435
+ };
436
+
437
+ engagements.forEach(e => {
438
+ if (e <= 10) distribution['0-10']++;
439
+ else if (e <= 50) distribution['11-50']++;
440
+ else if (e <= 100) distribution['51-100']++;
441
+ else if (e <= 500) distribution['101-500']++;
442
+ else if (e <= 1000) distribution['501-1000']++;
443
+ else distribution['1000+']++;
444
+ });
445
+
446
+ return distribution;
361
447
  }
362
448
 
363
- // Date range
364
- const dates = posts.map((p) => p.publishedAt.getTime());
365
- const dateRange = {
366
- earliest: new Date(Math.min(...dates)),
367
- latest: new Date(Math.max(...dates)),
368
- };
369
-
370
- // Extract hashtags
371
- const hashtagCounts: Record<string, number> = {};
372
- posts.forEach((post) => {
373
- const hashtags = post.content.match(/#\w+/g) || [];
374
- hashtags.forEach((tag) => {
375
- hashtagCounts[tag] = (hashtagCounts[tag] || 0) + 1;
376
- });
377
- });
378
-
379
- // Top hashtags
380
- const topHashtags = Object.entries(hashtagCounts)
381
- .sort(([, a], [, b]) => b - a)
382
- .slice(0, 10)
383
- .map(([tag, count]) => ({ tag, count }));
384
-
385
- // Post types
386
- const postTypes: Record<string, number> = {};
387
- posts.forEach((post) => {
388
- const type = post.platformSpecificData.postType || 'status';
389
- postTypes[type] = (postTypes[type] || 0) + 1;
390
- });
391
-
392
- // Engagement statistics
393
- const engagements = posts.map((p) => p.analytics?.engagements || 0);
394
- const totalEngagements = engagements.reduce((sum, e) => sum + e, 0);
395
- const avgEngagements = totalEngagements / posts.length;
396
- const maxEngagements = Math.max(...engagements);
397
-
398
- return {
399
- totalPosts: posts.length,
400
- dateRange,
401
- topHashtags,
402
- postTypes,
403
- engagementStats: {
404
- total: totalEngagements,
405
- average: Math.round(avgEngagements),
406
- max: maxEngagements,
407
- distribution: this.getEngagementDistribution(engagements),
408
- },
409
- };
410
- }
411
-
412
- /**
413
- * Get engagement distribution
414
- */
415
- private getEngagementDistribution(engagements: number[]): Record<string, number> {
416
- const distribution = {
417
- '0-10': 0,
418
- '11-50': 0,
419
- '51-100': 0,
420
- '101-500': 0,
421
- '501-1000': 0,
422
- '1000+': 0,
423
- };
424
-
425
- engagements.forEach((e) => {
426
- if (e <= 10) distribution['0-10']++;
427
- else if (e <= 50) distribution['11-50']++;
428
- else if (e <= 100) distribution['51-100']++;
429
- else if (e <= 500) distribution['101-500']++;
430
- else if (e <= 1000) distribution['501-1000']++;
431
- else distribution['1000+']++;
432
- });
433
-
434
- return distribution;
435
- }
436
- }
449
+
450
+ }