@memberjunction/actions-bizapps-social 2.111.0 → 2.112.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +6 -6
  3. package/dist/base/base-social.action.d.ts.map +1 -1
  4. package/dist/base/base-social.action.js +18 -24
  5. package/dist/base/base-social.action.js.map +1 -1
  6. package/dist/providers/buffer/buffer-base.action.d.ts.map +1 -1
  7. package/dist/providers/buffer/buffer-base.action.js +35 -34
  8. package/dist/providers/buffer/buffer-base.action.js.map +1 -1
  9. package/dist/providers/facebook/actions/boost-post.action.d.ts.map +1 -1
  10. package/dist/providers/facebook/actions/boost-post.action.js +33 -33
  11. package/dist/providers/facebook/actions/boost-post.action.js.map +1 -1
  12. package/dist/providers/facebook/actions/create-album.action.d.ts.map +1 -1
  13. package/dist/providers/facebook/actions/create-album.action.js +34 -36
  14. package/dist/providers/facebook/actions/create-album.action.js.map +1 -1
  15. package/dist/providers/facebook/actions/create-post.action.d.ts.map +1 -1
  16. package/dist/providers/facebook/actions/create-post.action.js +20 -20
  17. package/dist/providers/facebook/actions/create-post.action.js.map +1 -1
  18. package/dist/providers/facebook/actions/get-page-insights.action.d.ts.map +1 -1
  19. package/dist/providers/facebook/actions/get-page-insights.action.js +25 -27
  20. package/dist/providers/facebook/actions/get-page-insights.action.js.map +1 -1
  21. package/dist/providers/facebook/actions/get-page-posts.action.d.ts.map +1 -1
  22. package/dist/providers/facebook/actions/get-page-posts.action.js +19 -23
  23. package/dist/providers/facebook/actions/get-page-posts.action.js.map +1 -1
  24. package/dist/providers/facebook/actions/get-post-insights.action.d.ts.map +1 -1
  25. package/dist/providers/facebook/actions/get-post-insights.action.js +28 -32
  26. package/dist/providers/facebook/actions/get-post-insights.action.js.map +1 -1
  27. package/dist/providers/facebook/actions/respond-to-comments.action.d.ts.map +1 -1
  28. package/dist/providers/facebook/actions/respond-to-comments.action.js +42 -44
  29. package/dist/providers/facebook/actions/respond-to-comments.action.js.map +1 -1
  30. package/dist/providers/facebook/actions/schedule-post.action.d.ts.map +1 -1
  31. package/dist/providers/facebook/actions/schedule-post.action.js +29 -29
  32. package/dist/providers/facebook/actions/schedule-post.action.js.map +1 -1
  33. package/dist/providers/facebook/actions/search-posts.action.d.ts.map +1 -1
  34. package/dist/providers/facebook/actions/search-posts.action.js +37 -39
  35. package/dist/providers/facebook/actions/search-posts.action.js.map +1 -1
  36. package/dist/providers/facebook/facebook-base.action.d.ts.map +1 -1
  37. package/dist/providers/facebook/facebook-base.action.js +44 -59
  38. package/dist/providers/facebook/facebook-base.action.js.map +1 -1
  39. package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.d.ts.map +1 -1
  40. package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.js +33 -31
  41. package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.js.map +1 -1
  42. package/dist/providers/hootsuite/actions/create-scheduled-post.action.d.ts.map +1 -1
  43. package/dist/providers/hootsuite/actions/create-scheduled-post.action.js +28 -32
  44. package/dist/providers/hootsuite/actions/create-scheduled-post.action.js.map +1 -1
  45. package/dist/providers/hootsuite/actions/delete-scheduled-post.action.d.ts.map +1 -1
  46. package/dist/providers/hootsuite/actions/delete-scheduled-post.action.js +19 -19
  47. package/dist/providers/hootsuite/actions/delete-scheduled-post.action.js.map +1 -1
  48. package/dist/providers/hootsuite/actions/get-analytics.action.d.ts.map +1 -1
  49. package/dist/providers/hootsuite/actions/get-analytics.action.js +24 -26
  50. package/dist/providers/hootsuite/actions/get-analytics.action.js.map +1 -1
  51. package/dist/providers/hootsuite/actions/get-scheduled-posts.action.d.ts.map +1 -1
  52. package/dist/providers/hootsuite/actions/get-scheduled-posts.action.js +22 -22
  53. package/dist/providers/hootsuite/actions/get-scheduled-posts.action.js.map +1 -1
  54. package/dist/providers/hootsuite/actions/get-social-profiles.action.d.ts.map +1 -1
  55. package/dist/providers/hootsuite/actions/get-social-profiles.action.js +32 -34
  56. package/dist/providers/hootsuite/actions/get-social-profiles.action.js.map +1 -1
  57. package/dist/providers/hootsuite/actions/search-posts.action.d.ts.map +1 -1
  58. package/dist/providers/hootsuite/actions/search-posts.action.js +43 -52
  59. package/dist/providers/hootsuite/actions/search-posts.action.js.map +1 -1
  60. package/dist/providers/hootsuite/actions/update-scheduled-post.action.d.ts.map +1 -1
  61. package/dist/providers/hootsuite/actions/update-scheduled-post.action.js +30 -28
  62. package/dist/providers/hootsuite/actions/update-scheduled-post.action.js.map +1 -1
  63. package/dist/providers/hootsuite/hootsuite-base.action.d.ts.map +1 -1
  64. package/dist/providers/hootsuite/hootsuite-base.action.js +18 -20
  65. package/dist/providers/hootsuite/hootsuite-base.action.js.map +1 -1
  66. package/dist/providers/instagram/actions/create-post.action.d.ts.map +1 -1
  67. package/dist/providers/instagram/actions/create-post.action.js +27 -26
  68. package/dist/providers/instagram/actions/create-post.action.js.map +1 -1
  69. package/dist/providers/instagram/actions/create-story.action.d.ts.map +1 -1
  70. package/dist/providers/instagram/actions/create-story.action.js +35 -35
  71. package/dist/providers/instagram/actions/create-story.action.js.map +1 -1
  72. package/dist/providers/instagram/actions/get-account-insights.action.d.ts.map +1 -1
  73. package/dist/providers/instagram/actions/get-account-insights.action.js +38 -59
  74. package/dist/providers/instagram/actions/get-account-insights.action.js.map +1 -1
  75. package/dist/providers/instagram/actions/get-business-posts.action.d.ts.map +1 -1
  76. package/dist/providers/instagram/actions/get-business-posts.action.js +29 -29
  77. package/dist/providers/instagram/actions/get-business-posts.action.js.map +1 -1
  78. package/dist/providers/instagram/actions/get-comments.action.d.ts.map +1 -1
  79. package/dist/providers/instagram/actions/get-comments.action.js +36 -36
  80. package/dist/providers/instagram/actions/get-comments.action.js.map +1 -1
  81. package/dist/providers/instagram/actions/get-post-insights.action.d.ts.map +1 -1
  82. package/dist/providers/instagram/actions/get-post-insights.action.js +23 -25
  83. package/dist/providers/instagram/actions/get-post-insights.action.js.map +1 -1
  84. package/dist/providers/instagram/actions/schedule-post.action.d.ts.map +1 -1
  85. package/dist/providers/instagram/actions/schedule-post.action.js +25 -25
  86. package/dist/providers/instagram/actions/schedule-post.action.js.map +1 -1
  87. package/dist/providers/instagram/actions/search-posts.action.d.ts.map +1 -1
  88. package/dist/providers/instagram/actions/search-posts.action.js +56 -60
  89. package/dist/providers/instagram/actions/search-posts.action.js.map +1 -1
  90. package/dist/providers/instagram/instagram-base.action.d.ts.map +1 -1
  91. package/dist/providers/instagram/instagram-base.action.js +25 -27
  92. package/dist/providers/instagram/instagram-base.action.js.map +1 -1
  93. package/dist/providers/linkedin/actions/create-article.action.d.ts.map +1 -1
  94. package/dist/providers/linkedin/actions/create-article.action.js +55 -45
  95. package/dist/providers/linkedin/actions/create-article.action.js.map +1 -1
  96. package/dist/providers/linkedin/actions/create-post.action.d.ts.map +1 -1
  97. package/dist/providers/linkedin/actions/create-post.action.js +31 -29
  98. package/dist/providers/linkedin/actions/create-post.action.js.map +1 -1
  99. package/dist/providers/linkedin/actions/get-followers.action.d.ts.map +1 -1
  100. package/dist/providers/linkedin/actions/get-followers.action.js +28 -28
  101. package/dist/providers/linkedin/actions/get-followers.action.js.map +1 -1
  102. package/dist/providers/linkedin/actions/get-organization-posts.action.d.ts.map +1 -1
  103. package/dist/providers/linkedin/actions/get-organization-posts.action.js +20 -20
  104. package/dist/providers/linkedin/actions/get-organization-posts.action.js.map +1 -1
  105. package/dist/providers/linkedin/actions/get-personal-posts.action.d.ts.map +1 -1
  106. package/dist/providers/linkedin/actions/get-personal-posts.action.js +19 -19
  107. package/dist/providers/linkedin/actions/get-personal-posts.action.js.map +1 -1
  108. package/dist/providers/linkedin/actions/get-post-analytics.action.d.ts.map +1 -1
  109. package/dist/providers/linkedin/actions/get-post-analytics.action.js +25 -23
  110. package/dist/providers/linkedin/actions/get-post-analytics.action.js.map +1 -1
  111. package/dist/providers/linkedin/actions/schedule-post.action.d.ts.map +1 -1
  112. package/dist/providers/linkedin/actions/schedule-post.action.js +32 -30
  113. package/dist/providers/linkedin/actions/schedule-post.action.js.map +1 -1
  114. package/dist/providers/linkedin/actions/search-posts.action.d.ts.map +1 -1
  115. package/dist/providers/linkedin/actions/search-posts.action.js +28 -30
  116. package/dist/providers/linkedin/actions/search-posts.action.js.map +1 -1
  117. package/dist/providers/linkedin/linkedin-base.action.d.ts.map +1 -1
  118. package/dist/providers/linkedin/linkedin-base.action.js +33 -38
  119. package/dist/providers/linkedin/linkedin-base.action.js.map +1 -1
  120. package/dist/providers/tiktok/tiktok-base.action.d.ts.map +1 -1
  121. package/dist/providers/tiktok/tiktok-base.action.js +25 -26
  122. package/dist/providers/tiktok/tiktok-base.action.js.map +1 -1
  123. package/dist/providers/twitter/actions/create-thread.action.d.ts.map +1 -1
  124. package/dist/providers/twitter/actions/create-thread.action.js +25 -29
  125. package/dist/providers/twitter/actions/create-thread.action.js.map +1 -1
  126. package/dist/providers/twitter/actions/create-tweet.action.d.ts.map +1 -1
  127. package/dist/providers/twitter/actions/create-tweet.action.js +23 -23
  128. package/dist/providers/twitter/actions/create-tweet.action.js.map +1 -1
  129. package/dist/providers/twitter/actions/delete-tweet.action.d.ts.map +1 -1
  130. package/dist/providers/twitter/actions/delete-tweet.action.js +19 -19
  131. package/dist/providers/twitter/actions/delete-tweet.action.js.map +1 -1
  132. package/dist/providers/twitter/actions/get-analytics.action.d.ts.map +1 -1
  133. package/dist/providers/twitter/actions/get-analytics.action.js +40 -47
  134. package/dist/providers/twitter/actions/get-analytics.action.js.map +1 -1
  135. package/dist/providers/twitter/actions/get-mentions.action.d.ts.map +1 -1
  136. package/dist/providers/twitter/actions/get-mentions.action.js +30 -31
  137. package/dist/providers/twitter/actions/get-mentions.action.js.map +1 -1
  138. package/dist/providers/twitter/actions/get-timeline.action.d.ts.map +1 -1
  139. package/dist/providers/twitter/actions/get-timeline.action.js +29 -29
  140. package/dist/providers/twitter/actions/get-timeline.action.js.map +1 -1
  141. package/dist/providers/twitter/actions/schedule-tweet.action.d.ts.map +1 -1
  142. package/dist/providers/twitter/actions/schedule-tweet.action.js +26 -26
  143. package/dist/providers/twitter/actions/schedule-tweet.action.js.map +1 -1
  144. package/dist/providers/twitter/actions/search-tweets.action.d.ts.map +1 -1
  145. package/dist/providers/twitter/actions/search-tweets.action.js +56 -58
  146. package/dist/providers/twitter/actions/search-tweets.action.js.map +1 -1
  147. package/dist/providers/twitter/twitter-base.action.d.ts.map +1 -1
  148. package/dist/providers/twitter/twitter-base.action.js +58 -68
  149. package/dist/providers/twitter/twitter-base.action.js.map +1 -1
  150. package/dist/providers/youtube/youtube-base.action.d.ts +1 -1
  151. package/dist/providers/youtube/youtube-base.action.d.ts.map +1 -1
  152. package/dist/providers/youtube/youtube-base.action.js +22 -25
  153. package/dist/providers/youtube/youtube-base.action.js.map +1 -1
  154. package/package.json +5 -6
  155. package/src/base/base-social.action.ts +217 -224
  156. package/src/providers/buffer/buffer-base.action.ts +435 -441
  157. package/src/providers/facebook/actions/boost-post.action.ts +350 -386
  158. package/src/providers/facebook/actions/create-album.action.ts +291 -307
  159. package/src/providers/facebook/actions/create-post.action.ts +224 -227
  160. package/src/providers/facebook/actions/get-page-insights.action.ts +383 -403
  161. package/src/providers/facebook/actions/get-page-posts.action.ts +214 -225
  162. package/src/providers/facebook/actions/get-post-insights.action.ts +300 -316
  163. package/src/providers/facebook/actions/respond-to-comments.action.ts +319 -336
  164. package/src/providers/facebook/actions/schedule-post.action.ts +289 -292
  165. package/src/providers/facebook/actions/search-posts.action.ts +399 -413
  166. package/src/providers/facebook/facebook-base.action.ts +653 -670
  167. package/src/providers/hootsuite/actions/bulk-schedule-posts.action.ts +257 -257
  168. package/src/providers/hootsuite/actions/create-scheduled-post.action.ts +184 -189
  169. package/src/providers/hootsuite/actions/delete-scheduled-post.action.ts +160 -161
  170. package/src/providers/hootsuite/actions/get-analytics.action.ts +249 -254
  171. package/src/providers/hootsuite/actions/get-scheduled-posts.action.ts +206 -207
  172. package/src/providers/hootsuite/actions/get-social-profiles.action.ts +206 -205
  173. package/src/providers/hootsuite/actions/search-posts.action.ts +351 -369
  174. package/src/providers/hootsuite/actions/update-scheduled-post.action.ts +211 -209
  175. package/src/providers/hootsuite/hootsuite-base.action.ts +301 -307
  176. package/src/providers/instagram/actions/create-post.action.ts +276 -296
  177. package/src/providers/instagram/actions/create-story.action.ts +378 -394
  178. package/src/providers/instagram/actions/get-account-insights.action.ts +384 -420
  179. package/src/providers/instagram/actions/get-business-posts.action.ts +233 -242
  180. package/src/providers/instagram/actions/get-comments.action.ts +365 -377
  181. package/src/providers/instagram/actions/get-post-insights.action.ts +265 -273
  182. package/src/providers/instagram/actions/schedule-post.action.ts +233 -235
  183. package/src/providers/instagram/actions/search-posts.action.ts +512 -538
  184. package/src/providers/instagram/instagram-base.action.ts +368 -393
  185. package/src/providers/linkedin/actions/create-article.action.ts +275 -266
  186. package/src/providers/linkedin/actions/create-post.action.ts +179 -177
  187. package/src/providers/linkedin/actions/get-followers.action.ts +211 -211
  188. package/src/providers/linkedin/actions/get-organization-posts.action.ts +146 -147
  189. package/src/providers/linkedin/actions/get-personal-posts.action.ts +138 -139
  190. package/src/providers/linkedin/actions/get-post-analytics.action.ts +190 -189
  191. package/src/providers/linkedin/actions/schedule-post.action.ts +191 -189
  192. package/src/providers/linkedin/actions/search-posts.action.ts +275 -283
  193. package/src/providers/linkedin/linkedin-base.action.ts +407 -421
  194. package/src/providers/tiktok/tiktok-base.action.ts +305 -320
  195. package/src/providers/twitter/actions/create-thread.action.ts +203 -207
  196. package/src/providers/twitter/actions/create-tweet.action.ts +187 -188
  197. package/src/providers/twitter/actions/delete-tweet.action.ts +128 -129
  198. package/src/providers/twitter/actions/get-analytics.action.ts +402 -411
  199. package/src/providers/twitter/actions/get-mentions.action.ts +218 -219
  200. package/src/providers/twitter/actions/get-timeline.action.ts +232 -233
  201. package/src/providers/twitter/actions/schedule-tweet.action.ts +221 -222
  202. package/src/providers/twitter/actions/search-tweets.action.ts +540 -543
  203. package/src/providers/twitter/twitter-base.action.ts +541 -560
  204. package/src/providers/youtube/youtube-base.action.ts +320 -333
@@ -1,7 +1,7 @@
1
1
  import { RegisterClass } from '@memberjunction/global';
2
2
  import { TwitterBaseAction, Tweet, TwitterMetrics } from '../twitter-base.action';
3
3
  import { ActionParam, ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
4
- import { LogStatus, LogError } from '@memberjunction/core';
4
+ import { LogStatus, LogError } from '@memberjunction/global';
5
5
  import { SocialAnalytics } from '../../../base/base-social.action';
6
6
  import { BaseAction } from '@memberjunction/actions';
7
7
 
@@ -10,432 +10,423 @@ import { BaseAction } from '@memberjunction/actions';
10
10
  */
11
11
  @RegisterClass(BaseAction, 'TwitterGetAnalyticsAction')
12
12
  export class TwitterGetAnalyticsAction extends TwitterBaseAction {
13
- /**
14
- * Get analytics from Twitter
15
- */
16
- protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> {
17
- const { Params, ContextUser } = params;
18
-
19
- try {
20
- // Initialize OAuth
21
- const companyIntegrationId = this.getParamValue(Params, 'CompanyIntegrationID');
22
- if (!await this.initializeOAuth(companyIntegrationId)) {
23
- throw new Error('Failed to initialize OAuth connection');
24
- }
25
-
26
- // Extract parameters
27
- const analyticsType = this.getParamValue(Params, 'AnalyticsType') || 'account'; // 'account' or 'tweets'
28
- const tweetIds = this.getParamValue(Params, 'TweetIDs');
29
- const startDate = this.getParamValue(Params, 'StartDate');
30
- const endDate = this.getParamValue(Params, 'EndDate');
31
- const granularity = this.getParamValue(Params, 'Granularity') || 'day'; // 'hour', 'day', 'total'
32
-
33
- if (analyticsType === 'tweets') {
34
- // Get analytics for specific tweets
35
- if (!tweetIds || !Array.isArray(tweetIds) || tweetIds.length === 0) {
36
- throw new Error('TweetIDs array is required for tweet analytics');
37
- }
38
-
39
- return await this.getTweetAnalytics(Params, tweetIds);
40
- } else {
41
- // Get account-level analytics
42
- return await this.getAccountAnalytics(Params, startDate, endDate, granularity);
43
- }
44
-
45
- } catch (error) {
46
- const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
47
-
48
- return {
49
- Success: false,
50
- ResultCode: this.getErrorCode(error),
51
- Message: `Failed to get analytics: ${errorMessage}`,
52
- Params
53
- };
13
+ /**
14
+ * Get analytics from Twitter
15
+ */
16
+ protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> {
17
+ const { Params, ContextUser } = params;
18
+
19
+ try {
20
+ // Initialize OAuth
21
+ const companyIntegrationId = this.getParamValue(Params, 'CompanyIntegrationID');
22
+ if (!(await this.initializeOAuth(companyIntegrationId))) {
23
+ throw new Error('Failed to initialize OAuth connection');
24
+ }
25
+
26
+ // Extract parameters
27
+ const analyticsType = this.getParamValue(Params, 'AnalyticsType') || 'account'; // 'account' or 'tweets'
28
+ const tweetIds = this.getParamValue(Params, 'TweetIDs');
29
+ const startDate = this.getParamValue(Params, 'StartDate');
30
+ const endDate = this.getParamValue(Params, 'EndDate');
31
+ const granularity = this.getParamValue(Params, 'Granularity') || 'day'; // 'hour', 'day', 'total'
32
+
33
+ if (analyticsType === 'tweets') {
34
+ // Get analytics for specific tweets
35
+ if (!tweetIds || !Array.isArray(tweetIds) || tweetIds.length === 0) {
36
+ throw new Error('TweetIDs array is required for tweet analytics');
54
37
  }
55
- }
56
38
 
57
- /**
58
- * Get analytics for specific tweets
59
- */
60
- private async getTweetAnalytics(params: ActionParam[], tweetIds: string[]): Promise<ActionResultSimple> {
61
- try {
62
- LogStatus(`Getting analytics for ${tweetIds.length} tweets...`);
63
-
64
- // Twitter API v2 requires organic metrics scope for detailed analytics
65
- // We'll get public metrics for each tweet
66
- const tweetAnalytics: any[] = [];
67
-
68
- // Process in batches of 100 (API limit)
69
- const batchSize = 100;
70
- for (let i = 0; i < tweetIds.length; i += batchSize) {
71
- const batch = tweetIds.slice(i, i + batchSize);
72
- const ids = batch.join(',');
73
-
74
- const response = await this.axiosInstance.get('/tweets', {
75
- params: {
76
- 'ids': ids,
77
- 'tweet.fields': 'id,text,created_at,public_metrics,organic_metrics,promoted_metrics',
78
- 'expansions': 'author_id',
79
- 'user.fields': 'id,username'
80
- }
81
- });
82
-
83
- if (response.data.data) {
84
- for (const tweet of response.data.data) {
85
- const metrics: TwitterMetrics = {
86
- impression_count: 0,
87
- engagement_count: 0,
88
- retweet_count: 0,
89
- reply_count: 0,
90
- like_count: 0,
91
- quote_count: 0,
92
- bookmark_count: 0,
93
- url_link_clicks: 0,
94
- user_profile_clicks: 0
95
- };
96
-
97
- // Combine public and organic metrics if available
98
- if (tweet.public_metrics) {
99
- Object.assign(metrics, tweet.public_metrics);
100
- }
101
- if (tweet.organic_metrics) {
102
- Object.assign(metrics, tweet.organic_metrics);
103
- }
104
-
105
- // Calculate engagement count
106
- metrics.engagement_count =
107
- metrics.retweet_count +
108
- metrics.reply_count +
109
- metrics.like_count +
110
- metrics.quote_count +
111
- metrics.url_link_clicks +
112
- metrics.user_profile_clicks;
113
-
114
- const normalizedAnalytics = this.normalizeAnalytics(metrics);
115
-
116
- tweetAnalytics.push({
117
- tweetId: tweet.id,
118
- text: tweet.text.substring(0, 100) + (tweet.text.length > 100 ? '...' : ''),
119
- createdAt: tweet.created_at,
120
- metrics: normalizedAnalytics,
121
- engagementRate: metrics.impression_count > 0
122
- ? ((metrics.engagement_count / metrics.impression_count) * 100).toFixed(2) + '%'
123
- : '0%'
124
- });
125
- }
126
- }
127
- }
128
-
129
- // Calculate aggregate metrics
130
- const aggregateMetrics = this.calculateAggregateMetrics(tweetAnalytics);
131
-
132
- // Update output parameters
133
- const outputParams = [...params];
134
- const analyticsParam = outputParams.find(p => p.Name === 'Analytics');
135
- if (analyticsParam) analyticsParam.Value = tweetAnalytics;
136
- const aggregateParam = outputParams.find(p => p.Name === 'AggregateMetrics');
137
- if (aggregateParam) aggregateParam.Value = aggregateMetrics;
138
-
139
- return {
140
- Success: true,
141
- ResultCode: 'SUCCESS',
142
- Message: `Successfully retrieved analytics for ${tweetAnalytics.length} tweets`,
143
- Params: outputParams
144
- };
145
-
146
- } catch (error) {
147
- throw error;
148
- }
39
+ return await this.getTweetAnalytics(Params, tweetIds);
40
+ } else {
41
+ // Get account-level analytics
42
+ return await this.getAccountAnalytics(Params, startDate, endDate, granularity);
43
+ }
44
+ } catch (error) {
45
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
46
+
47
+ return {
48
+ Success: false,
49
+ ResultCode: this.getErrorCode(error),
50
+ Message: `Failed to get analytics: ${errorMessage}`,
51
+ Params,
52
+ };
149
53
  }
54
+ }
55
+
56
+ /**
57
+ * Get analytics for specific tweets
58
+ */
59
+ private async getTweetAnalytics(params: ActionParam[], tweetIds: string[]): Promise<ActionResultSimple> {
60
+ try {
61
+ LogStatus(`Getting analytics for ${tweetIds.length} tweets...`);
62
+
63
+ // Twitter API v2 requires organic metrics scope for detailed analytics
64
+ // We'll get public metrics for each tweet
65
+ const tweetAnalytics: any[] = [];
66
+
67
+ // Process in batches of 100 (API limit)
68
+ const batchSize = 100;
69
+ for (let i = 0; i < tweetIds.length; i += batchSize) {
70
+ const batch = tweetIds.slice(i, i + batchSize);
71
+ const ids = batch.join(',');
72
+
73
+ const response = await this.axiosInstance.get('/tweets', {
74
+ params: {
75
+ ids: ids,
76
+ 'tweet.fields': 'id,text,created_at,public_metrics,organic_metrics,promoted_metrics',
77
+ expansions: 'author_id',
78
+ 'user.fields': 'id,username',
79
+ },
80
+ });
150
81
 
151
- /**
152
- * Get account-level analytics
153
- */
154
- private async getAccountAnalytics(
155
- params: ActionParam[],
156
- startDate?: string,
157
- endDate?: string,
158
- granularity?: string
159
- ): Promise<ActionResultSimple> {
160
- try {
161
- // Get current user
162
- const currentUser = await this.getCurrentUser();
163
-
164
- LogStatus(`Getting account analytics for @${currentUser.username}...`);
165
-
166
- // For account analytics, we'll analyze recent tweets performance
167
- const queryParams: Record<string, any> = {
168
- 'tweet.fields': 'id,text,created_at,public_metrics,organic_metrics',
169
- 'max_results': 100
82
+ if (response.data.data) {
83
+ for (const tweet of response.data.data) {
84
+ const metrics: TwitterMetrics = {
85
+ impression_count: 0,
86
+ engagement_count: 0,
87
+ retweet_count: 0,
88
+ reply_count: 0,
89
+ like_count: 0,
90
+ quote_count: 0,
91
+ bookmark_count: 0,
92
+ url_link_clicks: 0,
93
+ user_profile_clicks: 0,
170
94
  };
171
95
 
172
- if (startDate) {
173
- queryParams['start_time'] = this.formatTwitterDate(startDate);
96
+ // Combine public and organic metrics if available
97
+ if (tweet.public_metrics) {
98
+ Object.assign(metrics, tweet.public_metrics);
174
99
  }
175
- if (endDate) {
176
- queryParams['end_time'] = this.formatTwitterDate(endDate);
100
+ if (tweet.organic_metrics) {
101
+ Object.assign(metrics, tweet.organic_metrics);
177
102
  }
178
103
 
179
- // Get user's tweets
180
- const tweets = await this.getPaginatedTweets(
181
- `/users/${currentUser.id}/tweets`,
182
- queryParams,
183
- 200 // Get up to 200 tweets for analysis
184
- );
185
-
186
- // Calculate time-based analytics
187
- const timeBasedAnalytics = this.calculateTimeBasedAnalytics(tweets, granularity || 'day');
188
-
189
- // Calculate overall metrics
190
- const overallMetrics = {
191
- totalTweets: tweets.length,
192
- totalImpressions: 0,
193
- totalEngagements: 0,
194
- totalLikes: 0,
195
- totalRetweets: 0,
196
- totalReplies: 0,
197
- totalQuotes: 0,
198
- averageEngagementRate: 0,
199
- topPerformingTweets: [] as any[]
200
- };
201
-
202
- tweets.forEach(tweet => {
203
- if (tweet.public_metrics) {
204
- overallMetrics.totalImpressions += tweet.public_metrics.impression_count || 0;
205
- overallMetrics.totalLikes += tweet.public_metrics.like_count || 0;
206
- overallMetrics.totalRetweets += tweet.public_metrics.retweet_count || 0;
207
- overallMetrics.totalReplies += tweet.public_metrics.reply_count || 0;
208
- overallMetrics.totalQuotes += tweet.public_metrics.quote_count || 0;
209
-
210
- const engagement =
211
- (tweet.public_metrics.like_count || 0) +
212
- (tweet.public_metrics.retweet_count || 0) +
213
- (tweet.public_metrics.reply_count || 0) +
214
- (tweet.public_metrics.quote_count || 0);
215
-
216
- overallMetrics.totalEngagements += engagement;
217
- }
104
+ // Calculate engagement count
105
+ metrics.engagement_count =
106
+ metrics.retweet_count +
107
+ metrics.reply_count +
108
+ metrics.like_count +
109
+ metrics.quote_count +
110
+ metrics.url_link_clicks +
111
+ metrics.user_profile_clicks;
112
+
113
+ const normalizedAnalytics = this.normalizeAnalytics(metrics);
114
+
115
+ tweetAnalytics.push({
116
+ tweetId: tweet.id,
117
+ text: tweet.text.substring(0, 100) + (tweet.text.length > 100 ? '...' : ''),
118
+ createdAt: tweet.created_at,
119
+ metrics: normalizedAnalytics,
120
+ engagementRate:
121
+ metrics.impression_count > 0 ? ((metrics.engagement_count / metrics.impression_count) * 100).toFixed(2) + '%' : '0%',
218
122
  });
219
-
220
- // Calculate average engagement rate
221
- if (overallMetrics.totalImpressions > 0) {
222
- overallMetrics.averageEngagementRate =
223
- parseFloat(((overallMetrics.totalEngagements / overallMetrics.totalImpressions) * 100).toFixed(2));
224
- }
225
-
226
- // Get top performing tweets
227
- overallMetrics.topPerformingTweets = tweets
228
- .filter(t => t.public_metrics)
229
- .sort((a, b) => {
230
- const aEngagement = this.calculateTweetEngagement(a.public_metrics!);
231
- const bEngagement = this.calculateTweetEngagement(b.public_metrics!);
232
- return bEngagement - aEngagement;
233
- })
234
- .slice(0, 5)
235
- .map(tweet => ({
236
- id: tweet.id,
237
- text: tweet.text.substring(0, 100) + (tweet.text.length > 100 ? '...' : ''),
238
- createdAt: tweet.created_at,
239
- metrics: tweet.public_metrics,
240
- engagement: this.calculateTweetEngagement(tweet.public_metrics!)
241
- }));
242
-
243
- // Update output parameters
244
- const outputParams = [...params];
245
- const overallParam = outputParams.find(p => p.Name === 'OverallMetrics');
246
- if (overallParam) overallParam.Value = overallMetrics;
247
- const timeBasedParam = outputParams.find(p => p.Name === 'TimeBasedAnalytics');
248
- if (timeBasedParam) timeBasedParam.Value = timeBasedAnalytics;
249
-
250
- return {
251
- Success: true,
252
- ResultCode: 'SUCCESS',
253
- Message: `Successfully retrieved account analytics for ${tweets.length} tweets`,
254
- Params: outputParams
255
- };
256
-
257
- } catch (error) {
258
- throw error;
123
+ }
259
124
  }
125
+ }
126
+
127
+ // Calculate aggregate metrics
128
+ const aggregateMetrics = this.calculateAggregateMetrics(tweetAnalytics);
129
+
130
+ // Update output parameters
131
+ const outputParams = [...params];
132
+ const analyticsParam = outputParams.find((p) => p.Name === 'Analytics');
133
+ if (analyticsParam) analyticsParam.Value = tweetAnalytics;
134
+ const aggregateParam = outputParams.find((p) => p.Name === 'AggregateMetrics');
135
+ if (aggregateParam) aggregateParam.Value = aggregateMetrics;
136
+
137
+ return {
138
+ Success: true,
139
+ ResultCode: 'SUCCESS',
140
+ Message: `Successfully retrieved analytics for ${tweetAnalytics.length} tweets`,
141
+ Params: outputParams,
142
+ };
143
+ } catch (error) {
144
+ throw error;
260
145
  }
261
-
262
- /**
263
- * Calculate aggregate metrics from tweet analytics
264
- */
265
- private calculateAggregateMetrics(tweetAnalytics: any[]): any {
266
- const aggregate = {
267
- totalImpressions: 0,
268
- totalEngagements: 0,
269
- totalLikes: 0,
270
- totalRetweets: 0,
271
- totalReplies: 0,
272
- averageEngagementRate: 0,
273
- bestPerformingTweet: null as any,
274
- worstPerformingTweet: null as any
275
- };
276
-
277
- let bestEngagement = -1;
278
- let worstEngagement = Infinity;
279
-
280
- tweetAnalytics.forEach(analytics => {
281
- const metrics = analytics.metrics;
282
- aggregate.totalImpressions += metrics.impressions;
283
- aggregate.totalEngagements += metrics.engagements;
284
- aggregate.totalLikes += metrics.likes;
285
- aggregate.totalRetweets += metrics.shares;
286
- aggregate.totalReplies += metrics.comments;
287
-
288
- if (metrics.engagements > bestEngagement) {
289
- bestEngagement = metrics.engagements;
290
- aggregate.bestPerformingTweet = analytics;
291
- }
292
- if (metrics.engagements < worstEngagement) {
293
- worstEngagement = metrics.engagements;
294
- aggregate.worstPerformingTweet = analytics;
295
- }
296
- });
297
-
298
- if (aggregate.totalImpressions > 0) {
299
- aggregate.averageEngagementRate =
300
- parseFloat(((aggregate.totalEngagements / aggregate.totalImpressions) * 100).toFixed(2));
146
+ }
147
+
148
+ /**
149
+ * Get account-level analytics
150
+ */
151
+ private async getAccountAnalytics(
152
+ params: ActionParam[],
153
+ startDate?: string,
154
+ endDate?: string,
155
+ granularity?: string
156
+ ): Promise<ActionResultSimple> {
157
+ try {
158
+ // Get current user
159
+ const currentUser = await this.getCurrentUser();
160
+
161
+ LogStatus(`Getting account analytics for @${currentUser.username}...`);
162
+
163
+ // For account analytics, we'll analyze recent tweets performance
164
+ const queryParams: Record<string, any> = {
165
+ 'tweet.fields': 'id,text,created_at,public_metrics,organic_metrics',
166
+ max_results: 100,
167
+ };
168
+
169
+ if (startDate) {
170
+ queryParams['start_time'] = this.formatTwitterDate(startDate);
171
+ }
172
+ if (endDate) {
173
+ queryParams['end_time'] = this.formatTwitterDate(endDate);
174
+ }
175
+
176
+ // Get user's tweets
177
+ const tweets = await this.getPaginatedTweets(
178
+ `/users/${currentUser.id}/tweets`,
179
+ queryParams,
180
+ 200 // Get up to 200 tweets for analysis
181
+ );
182
+
183
+ // Calculate time-based analytics
184
+ const timeBasedAnalytics = this.calculateTimeBasedAnalytics(tweets, granularity || 'day');
185
+
186
+ // Calculate overall metrics
187
+ const overallMetrics = {
188
+ totalTweets: tweets.length,
189
+ totalImpressions: 0,
190
+ totalEngagements: 0,
191
+ totalLikes: 0,
192
+ totalRetweets: 0,
193
+ totalReplies: 0,
194
+ totalQuotes: 0,
195
+ averageEngagementRate: 0,
196
+ topPerformingTweets: [] as any[],
197
+ };
198
+
199
+ tweets.forEach((tweet) => {
200
+ if (tweet.public_metrics) {
201
+ overallMetrics.totalImpressions += tweet.public_metrics.impression_count || 0;
202
+ overallMetrics.totalLikes += tweet.public_metrics.like_count || 0;
203
+ overallMetrics.totalRetweets += tweet.public_metrics.retweet_count || 0;
204
+ overallMetrics.totalReplies += tweet.public_metrics.reply_count || 0;
205
+ overallMetrics.totalQuotes += tweet.public_metrics.quote_count || 0;
206
+
207
+ const engagement =
208
+ (tweet.public_metrics.like_count || 0) +
209
+ (tweet.public_metrics.retweet_count || 0) +
210
+ (tweet.public_metrics.reply_count || 0) +
211
+ (tweet.public_metrics.quote_count || 0);
212
+
213
+ overallMetrics.totalEngagements += engagement;
301
214
  }
215
+ });
302
216
 
303
- return aggregate;
304
- }
305
-
306
- /**
307
- * Calculate time-based analytics
308
- */
309
- private calculateTimeBasedAnalytics(tweets: Tweet[], granularity: string): any[] {
310
- const buckets: Map<string, any> = new Map();
311
-
312
- tweets.forEach(tweet => {
313
- const date = new Date(tweet.created_at);
314
- let bucketKey: string;
315
-
316
- switch (granularity) {
317
- case 'hour':
318
- bucketKey = `${date.toISOString().slice(0, 13)}:00:00Z`;
319
- break;
320
- case 'day':
321
- bucketKey = date.toISOString().slice(0, 10);
322
- break;
323
- case 'total':
324
- bucketKey = 'total';
325
- break;
326
- default:
327
- bucketKey = date.toISOString().slice(0, 10);
328
- }
329
-
330
- if (!buckets.has(bucketKey)) {
331
- buckets.set(bucketKey, {
332
- period: bucketKey,
333
- tweets: 0,
334
- impressions: 0,
335
- engagements: 0,
336
- likes: 0,
337
- retweets: 0,
338
- replies: 0
339
- });
340
- }
341
-
342
- const bucket = buckets.get(bucketKey)!;
343
- bucket.tweets++;
344
-
345
- if (tweet.public_metrics) {
346
- bucket.impressions += tweet.public_metrics.impression_count || 0;
347
- bucket.likes += tweet.public_metrics.like_count || 0;
348
- bucket.retweets += tweet.public_metrics.retweet_count || 0;
349
- bucket.replies += tweet.public_metrics.reply_count || 0;
350
- bucket.engagements += this.calculateTweetEngagement(tweet.public_metrics);
351
- }
352
- });
353
-
354
- return Array.from(buckets.values()).sort((a, b) =>
355
- a.period.localeCompare(b.period)
217
+ // Calculate average engagement rate
218
+ if (overallMetrics.totalImpressions > 0) {
219
+ overallMetrics.averageEngagementRate = parseFloat(
220
+ ((overallMetrics.totalEngagements / overallMetrics.totalImpressions) * 100).toFixed(2)
356
221
  );
222
+ }
223
+
224
+ // Get top performing tweets
225
+ overallMetrics.topPerformingTweets = tweets
226
+ .filter((t) => t.public_metrics)
227
+ .sort((a, b) => {
228
+ const aEngagement = this.calculateTweetEngagement(a.public_metrics!);
229
+ const bEngagement = this.calculateTweetEngagement(b.public_metrics!);
230
+ return bEngagement - aEngagement;
231
+ })
232
+ .slice(0, 5)
233
+ .map((tweet) => ({
234
+ id: tweet.id,
235
+ text: tweet.text.substring(0, 100) + (tweet.text.length > 100 ? '...' : ''),
236
+ createdAt: tweet.created_at,
237
+ metrics: tweet.public_metrics,
238
+ engagement: this.calculateTweetEngagement(tweet.public_metrics!),
239
+ }));
240
+
241
+ // Update output parameters
242
+ const outputParams = [...params];
243
+ const overallParam = outputParams.find((p) => p.Name === 'OverallMetrics');
244
+ if (overallParam) overallParam.Value = overallMetrics;
245
+ const timeBasedParam = outputParams.find((p) => p.Name === 'TimeBasedAnalytics');
246
+ if (timeBasedParam) timeBasedParam.Value = timeBasedAnalytics;
247
+
248
+ return {
249
+ Success: true,
250
+ ResultCode: 'SUCCESS',
251
+ Message: `Successfully retrieved account analytics for ${tweets.length} tweets`,
252
+ Params: outputParams,
253
+ };
254
+ } catch (error) {
255
+ throw error;
357
256
  }
358
-
359
- /**
360
- * Calculate tweet engagement from public metrics
361
- */
362
- private calculateTweetEngagement(metrics: any): number {
363
- return (metrics.like_count || 0) +
364
- (metrics.retweet_count || 0) +
365
- (metrics.reply_count || 0) +
366
- (metrics.quote_count || 0);
257
+ }
258
+
259
+ /**
260
+ * Calculate aggregate metrics from tweet analytics
261
+ */
262
+ private calculateAggregateMetrics(tweetAnalytics: any[]): any {
263
+ const aggregate = {
264
+ totalImpressions: 0,
265
+ totalEngagements: 0,
266
+ totalLikes: 0,
267
+ totalRetweets: 0,
268
+ totalReplies: 0,
269
+ averageEngagementRate: 0,
270
+ bestPerformingTweet: null as any,
271
+ worstPerformingTweet: null as any,
272
+ };
273
+
274
+ let bestEngagement = -1;
275
+ let worstEngagement = Infinity;
276
+
277
+ tweetAnalytics.forEach((analytics) => {
278
+ const metrics = analytics.metrics;
279
+ aggregate.totalImpressions += metrics.impressions;
280
+ aggregate.totalEngagements += metrics.engagements;
281
+ aggregate.totalLikes += metrics.likes;
282
+ aggregate.totalRetweets += metrics.shares;
283
+ aggregate.totalReplies += metrics.comments;
284
+
285
+ if (metrics.engagements > bestEngagement) {
286
+ bestEngagement = metrics.engagements;
287
+ aggregate.bestPerformingTweet = analytics;
288
+ }
289
+ if (metrics.engagements < worstEngagement) {
290
+ worstEngagement = metrics.engagements;
291
+ aggregate.worstPerformingTweet = analytics;
292
+ }
293
+ });
294
+
295
+ if (aggregate.totalImpressions > 0) {
296
+ aggregate.averageEngagementRate = parseFloat(((aggregate.totalEngagements / aggregate.totalImpressions) * 100).toFixed(2));
367
297
  }
368
298
 
369
- /**
370
- * Get error code based on error type
371
- */
372
- private getErrorCode(error: any): string {
373
- if (error instanceof Error) {
374
- if (error.message.includes('Rate Limit')) return 'RATE_LIMIT';
375
- if (error.message.includes('Unauthorized')) return 'INVALID_TOKEN';
376
- if (error.message.includes('Forbidden')) return 'INSUFFICIENT_PERMISSIONS';
377
- }
378
- return 'ERROR';
379
- }
380
-
381
- /**
382
- * Define the parameters this action expects
383
- */
384
- public get Params(): ActionParam[] {
385
- return [
386
- ...this.commonSocialParams,
387
- {
388
- Name: 'AnalyticsType',
389
- Type: 'Input',
390
- Value: 'account' // 'account' or 'tweets'
391
- },
392
- {
393
- Name: 'TweetIDs',
394
- Type: 'Input',
395
- Value: null
396
- },
397
- {
398
- Name: 'StartDate',
399
- Type: 'Input',
400
- Value: null
401
- },
402
- {
403
- Name: 'EndDate',
404
- Type: 'Input',
405
- Value: null
406
- },
407
- {
408
- Name: 'Granularity',
409
- Type: 'Input',
410
- Value: 'day' // 'hour', 'day', 'total'
411
- },
412
- {
413
- Name: 'Analytics',
414
- Type: 'Output',
415
- Value: null
416
- },
417
- {
418
- Name: 'AggregateMetrics',
419
- Type: 'Output',
420
- Value: null
421
- },
422
- {
423
- Name: 'OverallMetrics',
424
- Type: 'Output',
425
- Value: null
426
- },
427
- {
428
- Name: 'TimeBasedAnalytics',
429
- Type: 'Output',
430
- Value: null
431
- }
432
- ];
433
- }
434
-
435
- /**
436
- * Get action description
437
- */
438
- public get Description(): string {
439
- return 'Gets analytics data from Twitter/X for specific tweets or account-level metrics with time-based analysis';
299
+ return aggregate;
300
+ }
301
+
302
+ /**
303
+ * Calculate time-based analytics
304
+ */
305
+ private calculateTimeBasedAnalytics(tweets: Tweet[], granularity: string): any[] {
306
+ const buckets: Map<string, any> = new Map();
307
+
308
+ tweets.forEach((tweet) => {
309
+ const date = new Date(tweet.created_at);
310
+ let bucketKey: string;
311
+
312
+ switch (granularity) {
313
+ case 'hour':
314
+ bucketKey = `${date.toISOString().slice(0, 13)}:00:00Z`;
315
+ break;
316
+ case 'day':
317
+ bucketKey = date.toISOString().slice(0, 10);
318
+ break;
319
+ case 'total':
320
+ bucketKey = 'total';
321
+ break;
322
+ default:
323
+ bucketKey = date.toISOString().slice(0, 10);
324
+ }
325
+
326
+ if (!buckets.has(bucketKey)) {
327
+ buckets.set(bucketKey, {
328
+ period: bucketKey,
329
+ tweets: 0,
330
+ impressions: 0,
331
+ engagements: 0,
332
+ likes: 0,
333
+ retweets: 0,
334
+ replies: 0,
335
+ });
336
+ }
337
+
338
+ const bucket = buckets.get(bucketKey)!;
339
+ bucket.tweets++;
340
+
341
+ if (tweet.public_metrics) {
342
+ bucket.impressions += tweet.public_metrics.impression_count || 0;
343
+ bucket.likes += tweet.public_metrics.like_count || 0;
344
+ bucket.retweets += tweet.public_metrics.retweet_count || 0;
345
+ bucket.replies += tweet.public_metrics.reply_count || 0;
346
+ bucket.engagements += this.calculateTweetEngagement(tweet.public_metrics);
347
+ }
348
+ });
349
+
350
+ return Array.from(buckets.values()).sort((a, b) => a.period.localeCompare(b.period));
351
+ }
352
+
353
+ /**
354
+ * Calculate tweet engagement from public metrics
355
+ */
356
+ private calculateTweetEngagement(metrics: any): number {
357
+ return (metrics.like_count || 0) + (metrics.retweet_count || 0) + (metrics.reply_count || 0) + (metrics.quote_count || 0);
358
+ }
359
+
360
+ /**
361
+ * Get error code based on error type
362
+ */
363
+ private getErrorCode(error: any): string {
364
+ if (error instanceof Error) {
365
+ if (error.message.includes('Rate Limit')) return 'RATE_LIMIT';
366
+ if (error.message.includes('Unauthorized')) return 'INVALID_TOKEN';
367
+ if (error.message.includes('Forbidden')) return 'INSUFFICIENT_PERMISSIONS';
440
368
  }
441
- }
369
+ return 'ERROR';
370
+ }
371
+
372
+ /**
373
+ * Define the parameters this action expects
374
+ */
375
+ public get Params(): ActionParam[] {
376
+ return [
377
+ ...this.commonSocialParams,
378
+ {
379
+ Name: 'AnalyticsType',
380
+ Type: 'Input',
381
+ Value: 'account', // 'account' or 'tweets'
382
+ },
383
+ {
384
+ Name: 'TweetIDs',
385
+ Type: 'Input',
386
+ Value: null,
387
+ },
388
+ {
389
+ Name: 'StartDate',
390
+ Type: 'Input',
391
+ Value: null,
392
+ },
393
+ {
394
+ Name: 'EndDate',
395
+ Type: 'Input',
396
+ Value: null,
397
+ },
398
+ {
399
+ Name: 'Granularity',
400
+ Type: 'Input',
401
+ Value: 'day', // 'hour', 'day', 'total'
402
+ },
403
+ {
404
+ Name: 'Analytics',
405
+ Type: 'Output',
406
+ Value: null,
407
+ },
408
+ {
409
+ Name: 'AggregateMetrics',
410
+ Type: 'Output',
411
+ Value: null,
412
+ },
413
+ {
414
+ Name: 'OverallMetrics',
415
+ Type: 'Output',
416
+ Value: null,
417
+ },
418
+ {
419
+ Name: 'TimeBasedAnalytics',
420
+ Type: 'Output',
421
+ Value: null,
422
+ },
423
+ ];
424
+ }
425
+
426
+ /**
427
+ * Get action description
428
+ */
429
+ public get Description(): string {
430
+ return 'Gets analytics data from Twitter/X for specific tweets or account-level metrics with time-based analysis';
431
+ }
432
+ }