@memberjunction/actions-bizapps-social 2.112.0 → 2.113.1
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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +13 -0
- package/dist/base/base-social.action.d.ts.map +1 -1
- package/dist/base/base-social.action.js +24 -18
- package/dist/base/base-social.action.js.map +1 -1
- package/dist/providers/buffer/buffer-base.action.d.ts.map +1 -1
- package/dist/providers/buffer/buffer-base.action.js +34 -35
- package/dist/providers/buffer/buffer-base.action.js.map +1 -1
- package/dist/providers/facebook/actions/boost-post.action.d.ts.map +1 -1
- package/dist/providers/facebook/actions/boost-post.action.js +33 -33
- package/dist/providers/facebook/actions/boost-post.action.js.map +1 -1
- package/dist/providers/facebook/actions/create-album.action.d.ts.map +1 -1
- package/dist/providers/facebook/actions/create-album.action.js +36 -34
- package/dist/providers/facebook/actions/create-album.action.js.map +1 -1
- package/dist/providers/facebook/actions/create-post.action.d.ts.map +1 -1
- package/dist/providers/facebook/actions/create-post.action.js +20 -20
- package/dist/providers/facebook/actions/create-post.action.js.map +1 -1
- package/dist/providers/facebook/actions/get-page-insights.action.d.ts.map +1 -1
- package/dist/providers/facebook/actions/get-page-insights.action.js +27 -25
- package/dist/providers/facebook/actions/get-page-insights.action.js.map +1 -1
- package/dist/providers/facebook/actions/get-page-posts.action.d.ts.map +1 -1
- package/dist/providers/facebook/actions/get-page-posts.action.js +23 -19
- package/dist/providers/facebook/actions/get-page-posts.action.js.map +1 -1
- package/dist/providers/facebook/actions/get-post-insights.action.d.ts.map +1 -1
- package/dist/providers/facebook/actions/get-post-insights.action.js +32 -28
- package/dist/providers/facebook/actions/get-post-insights.action.js.map +1 -1
- package/dist/providers/facebook/actions/respond-to-comments.action.d.ts.map +1 -1
- package/dist/providers/facebook/actions/respond-to-comments.action.js +44 -42
- package/dist/providers/facebook/actions/respond-to-comments.action.js.map +1 -1
- package/dist/providers/facebook/actions/schedule-post.action.d.ts.map +1 -1
- package/dist/providers/facebook/actions/schedule-post.action.js +29 -29
- package/dist/providers/facebook/actions/schedule-post.action.js.map +1 -1
- package/dist/providers/facebook/actions/search-posts.action.d.ts.map +1 -1
- package/dist/providers/facebook/actions/search-posts.action.js +39 -37
- package/dist/providers/facebook/actions/search-posts.action.js.map +1 -1
- package/dist/providers/facebook/facebook-base.action.d.ts.map +1 -1
- package/dist/providers/facebook/facebook-base.action.js +59 -44
- package/dist/providers/facebook/facebook-base.action.js.map +1 -1
- package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.d.ts.map +1 -1
- package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.js +31 -33
- package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.js.map +1 -1
- package/dist/providers/hootsuite/actions/create-scheduled-post.action.d.ts.map +1 -1
- package/dist/providers/hootsuite/actions/create-scheduled-post.action.js +32 -28
- package/dist/providers/hootsuite/actions/create-scheduled-post.action.js.map +1 -1
- package/dist/providers/hootsuite/actions/delete-scheduled-post.action.d.ts.map +1 -1
- package/dist/providers/hootsuite/actions/delete-scheduled-post.action.js +19 -19
- package/dist/providers/hootsuite/actions/delete-scheduled-post.action.js.map +1 -1
- package/dist/providers/hootsuite/actions/get-analytics.action.d.ts.map +1 -1
- package/dist/providers/hootsuite/actions/get-analytics.action.js +26 -24
- package/dist/providers/hootsuite/actions/get-analytics.action.js.map +1 -1
- package/dist/providers/hootsuite/actions/get-scheduled-posts.action.d.ts.map +1 -1
- package/dist/providers/hootsuite/actions/get-scheduled-posts.action.js +22 -22
- package/dist/providers/hootsuite/actions/get-scheduled-posts.action.js.map +1 -1
- package/dist/providers/hootsuite/actions/get-social-profiles.action.d.ts.map +1 -1
- package/dist/providers/hootsuite/actions/get-social-profiles.action.js +34 -32
- package/dist/providers/hootsuite/actions/get-social-profiles.action.js.map +1 -1
- package/dist/providers/hootsuite/actions/search-posts.action.d.ts.map +1 -1
- package/dist/providers/hootsuite/actions/search-posts.action.js +52 -43
- package/dist/providers/hootsuite/actions/search-posts.action.js.map +1 -1
- package/dist/providers/hootsuite/actions/update-scheduled-post.action.d.ts.map +1 -1
- package/dist/providers/hootsuite/actions/update-scheduled-post.action.js +28 -30
- package/dist/providers/hootsuite/actions/update-scheduled-post.action.js.map +1 -1
- package/dist/providers/hootsuite/hootsuite-base.action.d.ts.map +1 -1
- package/dist/providers/hootsuite/hootsuite-base.action.js +20 -18
- package/dist/providers/hootsuite/hootsuite-base.action.js.map +1 -1
- package/dist/providers/instagram/actions/create-post.action.d.ts.map +1 -1
- package/dist/providers/instagram/actions/create-post.action.js +26 -27
- package/dist/providers/instagram/actions/create-post.action.js.map +1 -1
- package/dist/providers/instagram/actions/create-story.action.d.ts.map +1 -1
- package/dist/providers/instagram/actions/create-story.action.js +35 -35
- package/dist/providers/instagram/actions/create-story.action.js.map +1 -1
- package/dist/providers/instagram/actions/get-account-insights.action.d.ts.map +1 -1
- package/dist/providers/instagram/actions/get-account-insights.action.js +59 -38
- package/dist/providers/instagram/actions/get-account-insights.action.js.map +1 -1
- package/dist/providers/instagram/actions/get-business-posts.action.d.ts.map +1 -1
- package/dist/providers/instagram/actions/get-business-posts.action.js +29 -29
- package/dist/providers/instagram/actions/get-business-posts.action.js.map +1 -1
- package/dist/providers/instagram/actions/get-comments.action.d.ts.map +1 -1
- package/dist/providers/instagram/actions/get-comments.action.js +36 -36
- package/dist/providers/instagram/actions/get-comments.action.js.map +1 -1
- package/dist/providers/instagram/actions/get-post-insights.action.d.ts.map +1 -1
- package/dist/providers/instagram/actions/get-post-insights.action.js +25 -23
- package/dist/providers/instagram/actions/get-post-insights.action.js.map +1 -1
- package/dist/providers/instagram/actions/schedule-post.action.d.ts.map +1 -1
- package/dist/providers/instagram/actions/schedule-post.action.js +25 -25
- package/dist/providers/instagram/actions/schedule-post.action.js.map +1 -1
- package/dist/providers/instagram/actions/search-posts.action.d.ts.map +1 -1
- package/dist/providers/instagram/actions/search-posts.action.js +60 -56
- package/dist/providers/instagram/actions/search-posts.action.js.map +1 -1
- package/dist/providers/instagram/instagram-base.action.d.ts.map +1 -1
- package/dist/providers/instagram/instagram-base.action.js +27 -25
- package/dist/providers/instagram/instagram-base.action.js.map +1 -1
- package/dist/providers/linkedin/actions/create-article.action.d.ts.map +1 -1
- package/dist/providers/linkedin/actions/create-article.action.js +45 -55
- package/dist/providers/linkedin/actions/create-article.action.js.map +1 -1
- package/dist/providers/linkedin/actions/create-post.action.d.ts.map +1 -1
- package/dist/providers/linkedin/actions/create-post.action.js +29 -31
- package/dist/providers/linkedin/actions/create-post.action.js.map +1 -1
- package/dist/providers/linkedin/actions/get-followers.action.d.ts.map +1 -1
- package/dist/providers/linkedin/actions/get-followers.action.js +28 -28
- package/dist/providers/linkedin/actions/get-followers.action.js.map +1 -1
- package/dist/providers/linkedin/actions/get-organization-posts.action.d.ts.map +1 -1
- package/dist/providers/linkedin/actions/get-organization-posts.action.js +20 -20
- package/dist/providers/linkedin/actions/get-organization-posts.action.js.map +1 -1
- package/dist/providers/linkedin/actions/get-personal-posts.action.d.ts.map +1 -1
- package/dist/providers/linkedin/actions/get-personal-posts.action.js +19 -19
- package/dist/providers/linkedin/actions/get-personal-posts.action.js.map +1 -1
- package/dist/providers/linkedin/actions/get-post-analytics.action.d.ts.map +1 -1
- package/dist/providers/linkedin/actions/get-post-analytics.action.js +23 -25
- package/dist/providers/linkedin/actions/get-post-analytics.action.js.map +1 -1
- package/dist/providers/linkedin/actions/schedule-post.action.d.ts.map +1 -1
- package/dist/providers/linkedin/actions/schedule-post.action.js +30 -32
- package/dist/providers/linkedin/actions/schedule-post.action.js.map +1 -1
- package/dist/providers/linkedin/actions/search-posts.action.d.ts.map +1 -1
- package/dist/providers/linkedin/actions/search-posts.action.js +30 -28
- package/dist/providers/linkedin/actions/search-posts.action.js.map +1 -1
- package/dist/providers/linkedin/linkedin-base.action.d.ts.map +1 -1
- package/dist/providers/linkedin/linkedin-base.action.js +38 -33
- package/dist/providers/linkedin/linkedin-base.action.js.map +1 -1
- package/dist/providers/tiktok/tiktok-base.action.d.ts.map +1 -1
- package/dist/providers/tiktok/tiktok-base.action.js +26 -25
- package/dist/providers/tiktok/tiktok-base.action.js.map +1 -1
- package/dist/providers/twitter/actions/create-thread.action.d.ts.map +1 -1
- package/dist/providers/twitter/actions/create-thread.action.js +29 -25
- package/dist/providers/twitter/actions/create-thread.action.js.map +1 -1
- package/dist/providers/twitter/actions/create-tweet.action.d.ts.map +1 -1
- package/dist/providers/twitter/actions/create-tweet.action.js +23 -23
- package/dist/providers/twitter/actions/create-tweet.action.js.map +1 -1
- package/dist/providers/twitter/actions/delete-tweet.action.d.ts.map +1 -1
- package/dist/providers/twitter/actions/delete-tweet.action.js +19 -19
- package/dist/providers/twitter/actions/delete-tweet.action.js.map +1 -1
- package/dist/providers/twitter/actions/get-analytics.action.d.ts.map +1 -1
- package/dist/providers/twitter/actions/get-analytics.action.js +47 -40
- package/dist/providers/twitter/actions/get-analytics.action.js.map +1 -1
- package/dist/providers/twitter/actions/get-mentions.action.d.ts.map +1 -1
- package/dist/providers/twitter/actions/get-mentions.action.js +31 -30
- package/dist/providers/twitter/actions/get-mentions.action.js.map +1 -1
- package/dist/providers/twitter/actions/get-timeline.action.d.ts.map +1 -1
- package/dist/providers/twitter/actions/get-timeline.action.js +29 -29
- package/dist/providers/twitter/actions/get-timeline.action.js.map +1 -1
- package/dist/providers/twitter/actions/schedule-tweet.action.d.ts.map +1 -1
- package/dist/providers/twitter/actions/schedule-tweet.action.js +26 -26
- package/dist/providers/twitter/actions/schedule-tweet.action.js.map +1 -1
- package/dist/providers/twitter/actions/search-tweets.action.d.ts.map +1 -1
- package/dist/providers/twitter/actions/search-tweets.action.js +58 -56
- package/dist/providers/twitter/actions/search-tweets.action.js.map +1 -1
- package/dist/providers/twitter/twitter-base.action.d.ts.map +1 -1
- package/dist/providers/twitter/twitter-base.action.js +68 -58
- package/dist/providers/twitter/twitter-base.action.js.map +1 -1
- package/dist/providers/youtube/youtube-base.action.d.ts +1 -1
- package/dist/providers/youtube/youtube-base.action.d.ts.map +1 -1
- package/dist/providers/youtube/youtube-base.action.js +25 -22
- package/dist/providers/youtube/youtube-base.action.js.map +1 -1
- package/package.json +6 -5
- package/src/base/base-social.action.ts +224 -217
- package/src/providers/buffer/buffer-base.action.ts +441 -435
- package/src/providers/facebook/actions/boost-post.action.ts +386 -350
- package/src/providers/facebook/actions/create-album.action.ts +307 -291
- package/src/providers/facebook/actions/create-post.action.ts +227 -224
- package/src/providers/facebook/actions/get-page-insights.action.ts +403 -383
- package/src/providers/facebook/actions/get-page-posts.action.ts +225 -214
- package/src/providers/facebook/actions/get-post-insights.action.ts +316 -300
- package/src/providers/facebook/actions/respond-to-comments.action.ts +336 -319
- package/src/providers/facebook/actions/schedule-post.action.ts +292 -289
- package/src/providers/facebook/actions/search-posts.action.ts +413 -399
- package/src/providers/facebook/facebook-base.action.ts +670 -653
- package/src/providers/hootsuite/actions/bulk-schedule-posts.action.ts +257 -257
- package/src/providers/hootsuite/actions/create-scheduled-post.action.ts +189 -184
- package/src/providers/hootsuite/actions/delete-scheduled-post.action.ts +161 -160
- package/src/providers/hootsuite/actions/get-analytics.action.ts +254 -249
- package/src/providers/hootsuite/actions/get-scheduled-posts.action.ts +207 -206
- package/src/providers/hootsuite/actions/get-social-profiles.action.ts +205 -206
- package/src/providers/hootsuite/actions/search-posts.action.ts +369 -351
- package/src/providers/hootsuite/actions/update-scheduled-post.action.ts +209 -211
- package/src/providers/hootsuite/hootsuite-base.action.ts +307 -301
- package/src/providers/instagram/actions/create-post.action.ts +296 -276
- package/src/providers/instagram/actions/create-story.action.ts +394 -378
- package/src/providers/instagram/actions/get-account-insights.action.ts +420 -384
- package/src/providers/instagram/actions/get-business-posts.action.ts +242 -233
- package/src/providers/instagram/actions/get-comments.action.ts +377 -365
- package/src/providers/instagram/actions/get-post-insights.action.ts +273 -265
- package/src/providers/instagram/actions/schedule-post.action.ts +235 -233
- package/src/providers/instagram/actions/search-posts.action.ts +538 -512
- package/src/providers/instagram/instagram-base.action.ts +393 -368
- package/src/providers/linkedin/actions/create-article.action.ts +266 -275
- package/src/providers/linkedin/actions/create-post.action.ts +177 -179
- package/src/providers/linkedin/actions/get-followers.action.ts +211 -211
- package/src/providers/linkedin/actions/get-organization-posts.action.ts +147 -146
- package/src/providers/linkedin/actions/get-personal-posts.action.ts +139 -138
- package/src/providers/linkedin/actions/get-post-analytics.action.ts +189 -190
- package/src/providers/linkedin/actions/schedule-post.action.ts +189 -191
- package/src/providers/linkedin/actions/search-posts.action.ts +283 -275
- package/src/providers/linkedin/linkedin-base.action.ts +421 -407
- package/src/providers/tiktok/tiktok-base.action.ts +320 -305
- package/src/providers/twitter/actions/create-thread.action.ts +207 -203
- package/src/providers/twitter/actions/create-tweet.action.ts +188 -187
- package/src/providers/twitter/actions/delete-tweet.action.ts +129 -128
- package/src/providers/twitter/actions/get-analytics.action.ts +411 -402
- package/src/providers/twitter/actions/get-mentions.action.ts +219 -218
- package/src/providers/twitter/actions/get-timeline.action.ts +233 -232
- package/src/providers/twitter/actions/schedule-tweet.action.ts +222 -221
- package/src/providers/twitter/actions/search-tweets.action.ts +543 -540
- package/src/providers/twitter/twitter-base.action.ts +560 -541
- 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/
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
155
|
-
|
|
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
|
-
|
|
179
|
-
|
|
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
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
197
|
-
|
|
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
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
234
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
262
|
-
const content = (post.message || '').toLowerCase();
|
|
263
|
-
return hashtagsToFind.some((hashtag) => content.includes(hashtag));
|
|
264
|
-
});
|
|
345
|
+
return sorted;
|
|
265
346
|
}
|
|
266
347
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
-
|
|
364
|
-
|
|
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
|
+
}
|