@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
@@ -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/core';
5
+ import { LogStatus, LogError } from '@memberjunction/global';
6
6
  import axios from 'axios';
7
7
  import { BaseAction } from '@memberjunction/actions';
8
8
 
@@ -12,439 +12,425 @@ 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
- }
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
+ }
85
103
 
86
- /**
87
- * Execute the action
88
- */
89
- protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> {
90
- const { Params, ContextUser } = params;
91
-
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) {
92
149
  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
- }
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
-
150
+ const posts = await this.searchPagePosts(pageId, searchParams, postTypes, includeMetrics);
151
+ allPosts.push(...posts);
152
+ LogStatus(`Found ${posts.length} posts from page ${pageId}`);
199
153
  } catch (error) {
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
- };
154
+ LogError(`Failed to search page ${pageId}: ${error}`);
155
+ // Continue with other pages
211
156
  }
212
- }
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
+ }
213
168
 
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
- };
169
+ // Sort posts
170
+ const sortedPosts = this.sortPosts(filteredPosts, sortBy, sortOrder);
236
171
 
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
- }
172
+ // Limit results
173
+ const limitedPosts = sortedPosts.slice(0, limit);
244
174
 
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
- }
175
+ // Normalize posts to common format
176
+ const normalizedPosts = limitedPosts.map((post) => this.normalizePost(post));
264
177
 
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
- }
178
+ // Calculate search summary
179
+ const summary = this.calculateSearchSummary(normalizedPosts, searchParams);
276
180
 
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
- }
181
+ LogStatus(`Search completed. Found ${normalizedPosts.length} matching posts`);
284
182
 
285
- return filteredPosts;
286
- }
183
+ // Update output parameters
184
+ const outputParams = [...Params];
185
+ // TODO: Set output parameters based on result
287
186
 
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');
295
- }
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'}`);
296
195
 
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';
305
- }
196
+ if (this.isAuthError(error)) {
197
+ return this.handleOAuthError(error);
198
+ }
306
199
 
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;
200
+ return {
201
+ Success: false,
202
+ Message: error instanceof Error ? error.message : 'Unknown error occurred',
203
+ ResultCode: 'ERROR',
204
+ };
316
205
  }
317
-
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
- });
344
-
345
- return sorted;
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);
346
232
  }
347
-
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;
233
+ if (searchParams.endDate) {
234
+ apiParams.until = Math.floor(searchParams.endDate.getTime() / 1000);
359
235
  }
360
236
 
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
- };
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
+ });
422
255
  }
423
256
 
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
- };
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()));
436
260
 
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
- });
261
+ filteredPosts = filteredPosts.filter((post) => {
262
+ const content = (post.message || '').toLowerCase();
263
+ return hashtagsToFind.some((hashtag) => content.includes(hashtag));
264
+ });
265
+ }
445
266
 
446
- return distribution;
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
+ });
447
273
  }
448
274
 
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;
345
+ }
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
+ };
361
+ }
449
362
 
450
- }
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
+ }