@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.
Files changed (204) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +13 -0
  3. package/dist/base/base-social.action.d.ts.map +1 -1
  4. package/dist/base/base-social.action.js +24 -18
  5. package/dist/base/base-social.action.js.map +1 -1
  6. package/dist/providers/buffer/buffer-base.action.d.ts.map +1 -1
  7. package/dist/providers/buffer/buffer-base.action.js +34 -35
  8. package/dist/providers/buffer/buffer-base.action.js.map +1 -1
  9. package/dist/providers/facebook/actions/boost-post.action.d.ts.map +1 -1
  10. package/dist/providers/facebook/actions/boost-post.action.js +33 -33
  11. package/dist/providers/facebook/actions/boost-post.action.js.map +1 -1
  12. package/dist/providers/facebook/actions/create-album.action.d.ts.map +1 -1
  13. package/dist/providers/facebook/actions/create-album.action.js +36 -34
  14. package/dist/providers/facebook/actions/create-album.action.js.map +1 -1
  15. package/dist/providers/facebook/actions/create-post.action.d.ts.map +1 -1
  16. package/dist/providers/facebook/actions/create-post.action.js +20 -20
  17. package/dist/providers/facebook/actions/create-post.action.js.map +1 -1
  18. package/dist/providers/facebook/actions/get-page-insights.action.d.ts.map +1 -1
  19. package/dist/providers/facebook/actions/get-page-insights.action.js +27 -25
  20. package/dist/providers/facebook/actions/get-page-insights.action.js.map +1 -1
  21. package/dist/providers/facebook/actions/get-page-posts.action.d.ts.map +1 -1
  22. package/dist/providers/facebook/actions/get-page-posts.action.js +23 -19
  23. package/dist/providers/facebook/actions/get-page-posts.action.js.map +1 -1
  24. package/dist/providers/facebook/actions/get-post-insights.action.d.ts.map +1 -1
  25. package/dist/providers/facebook/actions/get-post-insights.action.js +32 -28
  26. package/dist/providers/facebook/actions/get-post-insights.action.js.map +1 -1
  27. package/dist/providers/facebook/actions/respond-to-comments.action.d.ts.map +1 -1
  28. package/dist/providers/facebook/actions/respond-to-comments.action.js +44 -42
  29. package/dist/providers/facebook/actions/respond-to-comments.action.js.map +1 -1
  30. package/dist/providers/facebook/actions/schedule-post.action.d.ts.map +1 -1
  31. package/dist/providers/facebook/actions/schedule-post.action.js +29 -29
  32. package/dist/providers/facebook/actions/schedule-post.action.js.map +1 -1
  33. package/dist/providers/facebook/actions/search-posts.action.d.ts.map +1 -1
  34. package/dist/providers/facebook/actions/search-posts.action.js +39 -37
  35. package/dist/providers/facebook/actions/search-posts.action.js.map +1 -1
  36. package/dist/providers/facebook/facebook-base.action.d.ts.map +1 -1
  37. package/dist/providers/facebook/facebook-base.action.js +59 -44
  38. package/dist/providers/facebook/facebook-base.action.js.map +1 -1
  39. package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.d.ts.map +1 -1
  40. package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.js +31 -33
  41. package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.js.map +1 -1
  42. package/dist/providers/hootsuite/actions/create-scheduled-post.action.d.ts.map +1 -1
  43. package/dist/providers/hootsuite/actions/create-scheduled-post.action.js +32 -28
  44. package/dist/providers/hootsuite/actions/create-scheduled-post.action.js.map +1 -1
  45. package/dist/providers/hootsuite/actions/delete-scheduled-post.action.d.ts.map +1 -1
  46. package/dist/providers/hootsuite/actions/delete-scheduled-post.action.js +19 -19
  47. package/dist/providers/hootsuite/actions/delete-scheduled-post.action.js.map +1 -1
  48. package/dist/providers/hootsuite/actions/get-analytics.action.d.ts.map +1 -1
  49. package/dist/providers/hootsuite/actions/get-analytics.action.js +26 -24
  50. package/dist/providers/hootsuite/actions/get-analytics.action.js.map +1 -1
  51. package/dist/providers/hootsuite/actions/get-scheduled-posts.action.d.ts.map +1 -1
  52. package/dist/providers/hootsuite/actions/get-scheduled-posts.action.js +22 -22
  53. package/dist/providers/hootsuite/actions/get-scheduled-posts.action.js.map +1 -1
  54. package/dist/providers/hootsuite/actions/get-social-profiles.action.d.ts.map +1 -1
  55. package/dist/providers/hootsuite/actions/get-social-profiles.action.js +34 -32
  56. package/dist/providers/hootsuite/actions/get-social-profiles.action.js.map +1 -1
  57. package/dist/providers/hootsuite/actions/search-posts.action.d.ts.map +1 -1
  58. package/dist/providers/hootsuite/actions/search-posts.action.js +52 -43
  59. package/dist/providers/hootsuite/actions/search-posts.action.js.map +1 -1
  60. package/dist/providers/hootsuite/actions/update-scheduled-post.action.d.ts.map +1 -1
  61. package/dist/providers/hootsuite/actions/update-scheduled-post.action.js +28 -30
  62. package/dist/providers/hootsuite/actions/update-scheduled-post.action.js.map +1 -1
  63. package/dist/providers/hootsuite/hootsuite-base.action.d.ts.map +1 -1
  64. package/dist/providers/hootsuite/hootsuite-base.action.js +20 -18
  65. package/dist/providers/hootsuite/hootsuite-base.action.js.map +1 -1
  66. package/dist/providers/instagram/actions/create-post.action.d.ts.map +1 -1
  67. package/dist/providers/instagram/actions/create-post.action.js +26 -27
  68. package/dist/providers/instagram/actions/create-post.action.js.map +1 -1
  69. package/dist/providers/instagram/actions/create-story.action.d.ts.map +1 -1
  70. package/dist/providers/instagram/actions/create-story.action.js +35 -35
  71. package/dist/providers/instagram/actions/create-story.action.js.map +1 -1
  72. package/dist/providers/instagram/actions/get-account-insights.action.d.ts.map +1 -1
  73. package/dist/providers/instagram/actions/get-account-insights.action.js +59 -38
  74. package/dist/providers/instagram/actions/get-account-insights.action.js.map +1 -1
  75. package/dist/providers/instagram/actions/get-business-posts.action.d.ts.map +1 -1
  76. package/dist/providers/instagram/actions/get-business-posts.action.js +29 -29
  77. package/dist/providers/instagram/actions/get-business-posts.action.js.map +1 -1
  78. package/dist/providers/instagram/actions/get-comments.action.d.ts.map +1 -1
  79. package/dist/providers/instagram/actions/get-comments.action.js +36 -36
  80. package/dist/providers/instagram/actions/get-comments.action.js.map +1 -1
  81. package/dist/providers/instagram/actions/get-post-insights.action.d.ts.map +1 -1
  82. package/dist/providers/instagram/actions/get-post-insights.action.js +25 -23
  83. package/dist/providers/instagram/actions/get-post-insights.action.js.map +1 -1
  84. package/dist/providers/instagram/actions/schedule-post.action.d.ts.map +1 -1
  85. package/dist/providers/instagram/actions/schedule-post.action.js +25 -25
  86. package/dist/providers/instagram/actions/schedule-post.action.js.map +1 -1
  87. package/dist/providers/instagram/actions/search-posts.action.d.ts.map +1 -1
  88. package/dist/providers/instagram/actions/search-posts.action.js +60 -56
  89. package/dist/providers/instagram/actions/search-posts.action.js.map +1 -1
  90. package/dist/providers/instagram/instagram-base.action.d.ts.map +1 -1
  91. package/dist/providers/instagram/instagram-base.action.js +27 -25
  92. package/dist/providers/instagram/instagram-base.action.js.map +1 -1
  93. package/dist/providers/linkedin/actions/create-article.action.d.ts.map +1 -1
  94. package/dist/providers/linkedin/actions/create-article.action.js +45 -55
  95. package/dist/providers/linkedin/actions/create-article.action.js.map +1 -1
  96. package/dist/providers/linkedin/actions/create-post.action.d.ts.map +1 -1
  97. package/dist/providers/linkedin/actions/create-post.action.js +29 -31
  98. package/dist/providers/linkedin/actions/create-post.action.js.map +1 -1
  99. package/dist/providers/linkedin/actions/get-followers.action.d.ts.map +1 -1
  100. package/dist/providers/linkedin/actions/get-followers.action.js +28 -28
  101. package/dist/providers/linkedin/actions/get-followers.action.js.map +1 -1
  102. package/dist/providers/linkedin/actions/get-organization-posts.action.d.ts.map +1 -1
  103. package/dist/providers/linkedin/actions/get-organization-posts.action.js +20 -20
  104. package/dist/providers/linkedin/actions/get-organization-posts.action.js.map +1 -1
  105. package/dist/providers/linkedin/actions/get-personal-posts.action.d.ts.map +1 -1
  106. package/dist/providers/linkedin/actions/get-personal-posts.action.js +19 -19
  107. package/dist/providers/linkedin/actions/get-personal-posts.action.js.map +1 -1
  108. package/dist/providers/linkedin/actions/get-post-analytics.action.d.ts.map +1 -1
  109. package/dist/providers/linkedin/actions/get-post-analytics.action.js +23 -25
  110. package/dist/providers/linkedin/actions/get-post-analytics.action.js.map +1 -1
  111. package/dist/providers/linkedin/actions/schedule-post.action.d.ts.map +1 -1
  112. package/dist/providers/linkedin/actions/schedule-post.action.js +30 -32
  113. package/dist/providers/linkedin/actions/schedule-post.action.js.map +1 -1
  114. package/dist/providers/linkedin/actions/search-posts.action.d.ts.map +1 -1
  115. package/dist/providers/linkedin/actions/search-posts.action.js +30 -28
  116. package/dist/providers/linkedin/actions/search-posts.action.js.map +1 -1
  117. package/dist/providers/linkedin/linkedin-base.action.d.ts.map +1 -1
  118. package/dist/providers/linkedin/linkedin-base.action.js +38 -33
  119. package/dist/providers/linkedin/linkedin-base.action.js.map +1 -1
  120. package/dist/providers/tiktok/tiktok-base.action.d.ts.map +1 -1
  121. package/dist/providers/tiktok/tiktok-base.action.js +26 -25
  122. package/dist/providers/tiktok/tiktok-base.action.js.map +1 -1
  123. package/dist/providers/twitter/actions/create-thread.action.d.ts.map +1 -1
  124. package/dist/providers/twitter/actions/create-thread.action.js +29 -25
  125. package/dist/providers/twitter/actions/create-thread.action.js.map +1 -1
  126. package/dist/providers/twitter/actions/create-tweet.action.d.ts.map +1 -1
  127. package/dist/providers/twitter/actions/create-tweet.action.js +23 -23
  128. package/dist/providers/twitter/actions/create-tweet.action.js.map +1 -1
  129. package/dist/providers/twitter/actions/delete-tweet.action.d.ts.map +1 -1
  130. package/dist/providers/twitter/actions/delete-tweet.action.js +19 -19
  131. package/dist/providers/twitter/actions/delete-tweet.action.js.map +1 -1
  132. package/dist/providers/twitter/actions/get-analytics.action.d.ts.map +1 -1
  133. package/dist/providers/twitter/actions/get-analytics.action.js +47 -40
  134. package/dist/providers/twitter/actions/get-analytics.action.js.map +1 -1
  135. package/dist/providers/twitter/actions/get-mentions.action.d.ts.map +1 -1
  136. package/dist/providers/twitter/actions/get-mentions.action.js +31 -30
  137. package/dist/providers/twitter/actions/get-mentions.action.js.map +1 -1
  138. package/dist/providers/twitter/actions/get-timeline.action.d.ts.map +1 -1
  139. package/dist/providers/twitter/actions/get-timeline.action.js +29 -29
  140. package/dist/providers/twitter/actions/get-timeline.action.js.map +1 -1
  141. package/dist/providers/twitter/actions/schedule-tweet.action.d.ts.map +1 -1
  142. package/dist/providers/twitter/actions/schedule-tweet.action.js +26 -26
  143. package/dist/providers/twitter/actions/schedule-tweet.action.js.map +1 -1
  144. package/dist/providers/twitter/actions/search-tweets.action.d.ts.map +1 -1
  145. package/dist/providers/twitter/actions/search-tweets.action.js +58 -56
  146. package/dist/providers/twitter/actions/search-tweets.action.js.map +1 -1
  147. package/dist/providers/twitter/twitter-base.action.d.ts.map +1 -1
  148. package/dist/providers/twitter/twitter-base.action.js +68 -58
  149. package/dist/providers/twitter/twitter-base.action.js.map +1 -1
  150. package/dist/providers/youtube/youtube-base.action.d.ts +1 -1
  151. package/dist/providers/youtube/youtube-base.action.d.ts.map +1 -1
  152. package/dist/providers/youtube/youtube-base.action.js +25 -22
  153. package/dist/providers/youtube/youtube-base.action.js.map +1 -1
  154. package/package.json +6 -5
  155. package/src/base/base-social.action.ts +224 -217
  156. package/src/providers/buffer/buffer-base.action.ts +441 -435
  157. package/src/providers/facebook/actions/boost-post.action.ts +386 -350
  158. package/src/providers/facebook/actions/create-album.action.ts +307 -291
  159. package/src/providers/facebook/actions/create-post.action.ts +227 -224
  160. package/src/providers/facebook/actions/get-page-insights.action.ts +403 -383
  161. package/src/providers/facebook/actions/get-page-posts.action.ts +225 -214
  162. package/src/providers/facebook/actions/get-post-insights.action.ts +316 -300
  163. package/src/providers/facebook/actions/respond-to-comments.action.ts +336 -319
  164. package/src/providers/facebook/actions/schedule-post.action.ts +292 -289
  165. package/src/providers/facebook/actions/search-posts.action.ts +413 -399
  166. package/src/providers/facebook/facebook-base.action.ts +670 -653
  167. package/src/providers/hootsuite/actions/bulk-schedule-posts.action.ts +257 -257
  168. package/src/providers/hootsuite/actions/create-scheduled-post.action.ts +189 -184
  169. package/src/providers/hootsuite/actions/delete-scheduled-post.action.ts +161 -160
  170. package/src/providers/hootsuite/actions/get-analytics.action.ts +254 -249
  171. package/src/providers/hootsuite/actions/get-scheduled-posts.action.ts +207 -206
  172. package/src/providers/hootsuite/actions/get-social-profiles.action.ts +205 -206
  173. package/src/providers/hootsuite/actions/search-posts.action.ts +369 -351
  174. package/src/providers/hootsuite/actions/update-scheduled-post.action.ts +209 -211
  175. package/src/providers/hootsuite/hootsuite-base.action.ts +307 -301
  176. package/src/providers/instagram/actions/create-post.action.ts +296 -276
  177. package/src/providers/instagram/actions/create-story.action.ts +394 -378
  178. package/src/providers/instagram/actions/get-account-insights.action.ts +420 -384
  179. package/src/providers/instagram/actions/get-business-posts.action.ts +242 -233
  180. package/src/providers/instagram/actions/get-comments.action.ts +377 -365
  181. package/src/providers/instagram/actions/get-post-insights.action.ts +273 -265
  182. package/src/providers/instagram/actions/schedule-post.action.ts +235 -233
  183. package/src/providers/instagram/actions/search-posts.action.ts +538 -512
  184. package/src/providers/instagram/instagram-base.action.ts +393 -368
  185. package/src/providers/linkedin/actions/create-article.action.ts +266 -275
  186. package/src/providers/linkedin/actions/create-post.action.ts +177 -179
  187. package/src/providers/linkedin/actions/get-followers.action.ts +211 -211
  188. package/src/providers/linkedin/actions/get-organization-posts.action.ts +147 -146
  189. package/src/providers/linkedin/actions/get-personal-posts.action.ts +139 -138
  190. package/src/providers/linkedin/actions/get-post-analytics.action.ts +189 -190
  191. package/src/providers/linkedin/actions/schedule-post.action.ts +189 -191
  192. package/src/providers/linkedin/actions/search-posts.action.ts +283 -275
  193. package/src/providers/linkedin/linkedin-base.action.ts +421 -407
  194. package/src/providers/tiktok/tiktok-base.action.ts +320 -305
  195. package/src/providers/twitter/actions/create-thread.action.ts +207 -203
  196. package/src/providers/twitter/actions/create-tweet.action.ts +188 -187
  197. package/src/providers/twitter/actions/delete-tweet.action.ts +129 -128
  198. package/src/providers/twitter/actions/get-analytics.action.ts +411 -402
  199. package/src/providers/twitter/actions/get-mentions.action.ts +219 -218
  200. package/src/providers/twitter/actions/get-timeline.action.ts +233 -232
  201. package/src/providers/twitter/actions/schedule-tweet.action.ts +222 -221
  202. package/src/providers/twitter/actions/search-tweets.action.ts +543 -540
  203. package/src/providers/twitter/twitter-base.action.ts +560 -541
  204. package/src/providers/youtube/youtube-base.action.ts +333 -320
@@ -2,7 +2,7 @@ import { RegisterClass } from '@memberjunction/global';
2
2
  import { BaseSocialMediaAction, MediaFile, SocialPost, SearchParams } from '../../base/base-social.action';
3
3
  import axios, { AxiosInstance, AxiosError } from 'axios';
4
4
  import { ActionParam } from '@memberjunction/actions-base';
5
- import { LogStatus, LogError } from '@memberjunction/global';
5
+ import { LogStatus, LogError } from '@memberjunction/core';
6
6
  import { BaseAction } from '@memberjunction/actions';
7
7
 
8
8
  /**
@@ -11,331 +11,337 @@ import { BaseAction } from '@memberjunction/actions';
11
11
  */
12
12
  @RegisterClass(BaseAction, 'HootSuiteBaseAction')
13
13
  export abstract class HootSuiteBaseAction extends BaseSocialMediaAction {
14
- protected get platformName(): string {
15
- return 'HootSuite';
16
- }
17
-
18
- protected get apiBaseUrl(): string {
19
- return 'https://platform.hootsuite.com/v1';
20
- }
21
-
22
- /**
23
- * Axios instance for making HTTP requests
24
- */
25
- private _axiosInstance: AxiosInstance | null = null;
26
-
27
- /**
28
- * Get or create axios instance with interceptors
29
- */
30
- protected get axiosInstance(): AxiosInstance {
31
- if (!this._axiosInstance) {
32
- this._axiosInstance = axios.create({
33
- baseURL: this.apiBaseUrl,
34
- timeout: 30000,
35
- headers: {
36
- 'Content-Type': 'application/json',
37
- Accept: 'application/json',
38
- },
39
- });
40
-
41
- // Add request interceptor for auth
42
- this._axiosInstance.interceptors.request.use(
43
- (config) => {
44
- const token = this.getAccessToken();
45
- if (token) {
46
- config.headers.Authorization = `Bearer ${token}`;
47
- }
48
- return config;
49
- },
50
- (error) => Promise.reject(error)
51
- );
52
-
53
- // Add response interceptor for rate limit handling
54
- this._axiosInstance.interceptors.response.use(
55
- (response) => {
56
- // Log rate limit info
57
- const rateLimitInfo = this.parseRateLimitHeaders(response.headers);
58
- if (rateLimitInfo) {
59
- LogStatus(`HootSuite Rate Limit - Remaining: ${rateLimitInfo.remaining}/${rateLimitInfo.limit}, Reset: ${rateLimitInfo.reset}`);
60
- }
61
- return response;
62
- },
63
- async (error: AxiosError) => {
64
- if (error.response?.status === 429) {
65
- // Rate limit exceeded
66
- const retryAfter = error.response.headers['retry-after'];
67
- const waitTime = retryAfter ? parseInt(retryAfter) : 60;
68
- await this.handleRateLimit(waitTime);
69
-
70
- // Retry the request
71
- return this._axiosInstance!.request(error.config!);
72
- }
73
- return Promise.reject(error);
74
- }
75
- );
14
+ protected get platformName(): string {
15
+ return 'HootSuite';
76
16
  }
77
- return this._axiosInstance;
78
- }
79
-
80
- /**
81
- * Refresh the access token using the refresh token
82
- */
83
- protected async refreshAccessToken(): Promise<void> {
84
- const refreshToken = this.getRefreshToken();
85
- if (!refreshToken) {
86
- throw new Error('No refresh token available for HootSuite');
17
+
18
+ protected get apiBaseUrl(): string {
19
+ return 'https://platform.hootsuite.com/v1';
87
20
  }
88
21
 
89
- try {
90
- const response = await axios.post(
91
- 'https://platform.hootsuite.com/oauth2/token',
92
- {
93
- grant_type: 'refresh_token',
94
- refresh_token: refreshToken,
95
- client_id: this.getCustomAttribute(2), // Client ID stored in CustomAttribute2
96
- client_secret: this.getCustomAttribute(3), // Client Secret stored in CustomAttribute3
97
- },
98
- {
99
- headers: {
100
- 'Content-Type': 'application/x-www-form-urlencoded',
101
- },
22
+ /**
23
+ * Axios instance for making HTTP requests
24
+ */
25
+ private _axiosInstance: AxiosInstance | null = null;
26
+
27
+ /**
28
+ * Get or create axios instance with interceptors
29
+ */
30
+ protected get axiosInstance(): AxiosInstance {
31
+ if (!this._axiosInstance) {
32
+ this._axiosInstance = axios.create({
33
+ baseURL: this.apiBaseUrl,
34
+ timeout: 30000,
35
+ headers: {
36
+ 'Content-Type': 'application/json',
37
+ 'Accept': 'application/json'
38
+ }
39
+ });
40
+
41
+ // Add request interceptor for auth
42
+ this._axiosInstance.interceptors.request.use(
43
+ (config) => {
44
+ const token = this.getAccessToken();
45
+ if (token) {
46
+ config.headers.Authorization = `Bearer ${token}`;
47
+ }
48
+ return config;
49
+ },
50
+ (error) => Promise.reject(error)
51
+ );
52
+
53
+ // Add response interceptor for rate limit handling
54
+ this._axiosInstance.interceptors.response.use(
55
+ (response) => {
56
+ // Log rate limit info
57
+ const rateLimitInfo = this.parseRateLimitHeaders(response.headers);
58
+ if (rateLimitInfo) {
59
+ LogStatus(`HootSuite Rate Limit - Remaining: ${rateLimitInfo.remaining}/${rateLimitInfo.limit}, Reset: ${rateLimitInfo.reset}`);
60
+ }
61
+ return response;
62
+ },
63
+ async (error: AxiosError) => {
64
+ if (error.response?.status === 429) {
65
+ // Rate limit exceeded
66
+ const retryAfter = error.response.headers['retry-after'];
67
+ const waitTime = retryAfter ? parseInt(retryAfter) : 60;
68
+ await this.handleRateLimit(waitTime);
69
+
70
+ // Retry the request
71
+ return this._axiosInstance!.request(error.config!);
72
+ }
73
+ return Promise.reject(error);
74
+ }
75
+ );
102
76
  }
103
- );
77
+ return this._axiosInstance;
78
+ }
104
79
 
105
- const { access_token, refresh_token: newRefreshToken, expires_in } = response.data;
80
+ /**
81
+ * Refresh the access token using the refresh token
82
+ */
83
+ protected async refreshAccessToken(): Promise<void> {
84
+ const refreshToken = this.getRefreshToken();
85
+ if (!refreshToken) {
86
+ throw new Error('No refresh token available for HootSuite');
87
+ }
106
88
 
107
- // Update stored tokens
108
- await this.updateStoredTokens(access_token, newRefreshToken || refreshToken, expires_in);
89
+ try {
90
+ const response = await axios.post('https://platform.hootsuite.com/oauth2/token', {
91
+ grant_type: 'refresh_token',
92
+ refresh_token: refreshToken,
93
+ client_id: this.getCustomAttribute(2), // Client ID stored in CustomAttribute2
94
+ client_secret: this.getCustomAttribute(3) // Client Secret stored in CustomAttribute3
95
+ }, {
96
+ headers: {
97
+ 'Content-Type': 'application/x-www-form-urlencoded'
98
+ }
99
+ });
100
+
101
+ const { access_token, refresh_token: newRefreshToken, expires_in } = response.data;
102
+
103
+ // Update stored tokens
104
+ await this.updateStoredTokens(
105
+ access_token,
106
+ newRefreshToken || refreshToken,
107
+ expires_in
108
+ );
109
+
110
+ LogStatus('HootSuite access token refreshed successfully');
111
+ } catch (error) {
112
+ LogError(`Failed to refresh HootSuite access token: ${error instanceof Error ? error.message : 'Unknown error'}`);
113
+ throw error;
114
+ }
115
+ }
109
116
 
110
- LogStatus('HootSuite access token refreshed successfully');
111
- } catch (error) {
112
- LogError(`Failed to refresh HootSuite access token: ${error instanceof Error ? error.message : 'Unknown error'}`);
113
- throw error;
117
+ /**
118
+ * Upload media to HootSuite
119
+ */
120
+ protected async uploadSingleMedia(file: MediaFile): Promise<string> {
121
+ try {
122
+ // First, request an upload URL
123
+ const uploadRequest = await this.axiosInstance.post('/media', {
124
+ mimeType: file.mimeType,
125
+ sizeBytes: file.size
126
+ });
127
+
128
+ const { uploadUrl, mediaId } = uploadRequest.data;
129
+
130
+ // Upload the file to the provided URL
131
+ const fileData = typeof file.data === 'string'
132
+ ? Buffer.from(file.data, 'base64')
133
+ : file.data;
134
+
135
+ await axios.put(uploadUrl, fileData, {
136
+ headers: {
137
+ 'Content-Type': file.mimeType,
138
+ 'Content-Length': file.size.toString()
139
+ }
140
+ });
141
+
142
+ // Wait for processing
143
+ await this.waitForMediaProcessing(mediaId);
144
+
145
+ return mediaId;
146
+ } catch (error) {
147
+ LogError(`Failed to upload media to HootSuite: ${error instanceof Error ? error.message : 'Unknown error'}`);
148
+ throw error;
149
+ }
114
150
  }
115
- }
116
-
117
- /**
118
- * Upload media to HootSuite
119
- */
120
- protected async uploadSingleMedia(file: MediaFile): Promise<string> {
121
- try {
122
- // First, request an upload URL
123
- const uploadRequest = await this.axiosInstance.post('/media', {
124
- mimeType: file.mimeType,
125
- sizeBytes: file.size,
126
- });
127
-
128
- const { uploadUrl, mediaId } = uploadRequest.data;
129
-
130
- // Upload the file to the provided URL
131
- const fileData = typeof file.data === 'string' ? Buffer.from(file.data, 'base64') : file.data;
132
-
133
- await axios.put(uploadUrl, fileData, {
134
- headers: {
135
- 'Content-Type': file.mimeType,
136
- 'Content-Length': file.size.toString(),
137
- },
138
- });
139
-
140
- // Wait for processing
141
- await this.waitForMediaProcessing(mediaId);
142
-
143
- return mediaId;
144
- } catch (error) {
145
- LogError(`Failed to upload media to HootSuite: ${error instanceof Error ? error.message : 'Unknown error'}`);
146
- throw error;
151
+
152
+ /**
153
+ * Wait for media to finish processing
154
+ */
155
+ private async waitForMediaProcessing(mediaId: string, maxAttempts: number = 30): Promise<void> {
156
+ for (let i = 0; i < maxAttempts; i++) {
157
+ try {
158
+ const response = await this.axiosInstance.get(`/media/${mediaId}`);
159
+ const { state } = response.data;
160
+
161
+ if (state === 'READY') {
162
+ return;
163
+ } else if (state === 'FAILED') {
164
+ throw new Error('Media processing failed');
165
+ }
166
+
167
+ // Wait 2 seconds before next check
168
+ await new Promise(resolve => setTimeout(resolve, 2000));
169
+ } catch (error) {
170
+ if (i === maxAttempts - 1) {
171
+ throw new Error(`Media processing timeout for ${mediaId}`);
172
+ }
173
+ }
174
+ }
147
175
  }
148
- }
149
-
150
- /**
151
- * Wait for media to finish processing
152
- */
153
- private async waitForMediaProcessing(mediaId: string, maxAttempts: number = 30): Promise<void> {
154
- for (let i = 0; i < maxAttempts; i++) {
155
- try {
156
- const response = await this.axiosInstance.get(`/media/${mediaId}`);
157
- const { state } = response.data;
158
-
159
- if (state === 'READY') {
160
- return;
161
- } else if (state === 'FAILED') {
162
- throw new Error('Media processing failed');
176
+
177
+ /**
178
+ * Get social profiles for the authenticated user
179
+ */
180
+ protected async getSocialProfiles(): Promise<HootSuiteProfile[]> {
181
+ try {
182
+ const response = await this.axiosInstance.get('/socialProfiles');
183
+ return response.data.data || [];
184
+ } catch (error) {
185
+ LogError(`Failed to get social profiles: ${error instanceof Error ? error.message : 'Unknown error'}`);
186
+ throw error;
163
187
  }
188
+ }
189
+
190
+ /**
191
+ * Make a paginated request to HootSuite API
192
+ */
193
+ protected async makePaginatedRequest<T>(
194
+ endpoint: string,
195
+ params: Record<string, any> = {}
196
+ ): Promise<T[]> {
197
+ const results: T[] = [];
198
+ let cursor: string | null = null;
199
+ const limit = params.limit || 50;
200
+
201
+ do {
202
+ const queryParams: any = { ...params, limit };
203
+ if (cursor) {
204
+ queryParams.cursor = cursor;
205
+ }
206
+
207
+ const response = await this.axiosInstance.get(endpoint, { params: queryParams });
208
+ const data = response.data;
209
+
210
+ if (data.data && Array.isArray(data.data)) {
211
+ results.push(...data.data);
212
+ }
213
+
214
+ cursor = data.cursor || null;
164
215
 
165
- // Wait 2 seconds before next check
166
- await new Promise((resolve) => setTimeout(resolve, 2000));
167
- } catch (error) {
168
- if (i === maxAttempts - 1) {
169
- throw new Error(`Media processing timeout for ${mediaId}`);
216
+ // Check if we've reached the desired number of results
217
+ if (params.maxResults && results.length >= params.maxResults) {
218
+ return results.slice(0, params.maxResults);
219
+ }
220
+
221
+ } while (cursor);
222
+
223
+ return results;
224
+ }
225
+
226
+ /**
227
+ * Format date for HootSuite API (ISO 8601)
228
+ */
229
+ protected formatHootSuiteDate(date: Date | string): string {
230
+ if (typeof date === 'string') {
231
+ date = new Date(date);
170
232
  }
171
- }
233
+ return date.toISOString();
172
234
  }
173
- }
174
-
175
- /**
176
- * Get social profiles for the authenticated user
177
- */
178
- protected async getSocialProfiles(): Promise<HootSuiteProfile[]> {
179
- try {
180
- const response = await this.axiosInstance.get('/socialProfiles');
181
- return response.data.data || [];
182
- } catch (error) {
183
- LogError(`Failed to get social profiles: ${error instanceof Error ? error.message : 'Unknown error'}`);
184
- throw error;
235
+
236
+ /**
237
+ * Parse HootSuite date string
238
+ */
239
+ protected parseHootSuiteDate(dateString: string): Date {
240
+ return new Date(dateString);
185
241
  }
186
- }
187
-
188
- /**
189
- * Make a paginated request to HootSuite API
190
- */
191
- protected async makePaginatedRequest<T>(endpoint: string, params: Record<string, any> = {}): Promise<T[]> {
192
- const results: T[] = [];
193
- let cursor: string | null = null;
194
- const limit = params.limit || 50;
195
-
196
- do {
197
- const queryParams: any = { ...params, limit };
198
- if (cursor) {
199
- queryParams.cursor = cursor;
200
- }
201
-
202
- const response = await this.axiosInstance.get(endpoint, { params: queryParams });
203
- const data = response.data;
204
-
205
- if (data.data && Array.isArray(data.data)) {
206
- results.push(...data.data);
207
- }
208
-
209
- cursor = data.cursor || null;
210
-
211
- // Check if we've reached the desired number of results
212
- if (params.maxResults && results.length >= params.maxResults) {
213
- return results.slice(0, params.maxResults);
214
- }
215
- } while (cursor);
216
-
217
- return results;
218
- }
219
-
220
- /**
221
- * Format date for HootSuite API (ISO 8601)
222
- */
223
- protected formatHootSuiteDate(date: Date | string): string {
224
- if (typeof date === 'string') {
225
- date = new Date(date);
242
+
243
+ /**
244
+ * Convert HootSuite post to common format
245
+ */
246
+ protected normalizePost(hootsuitePost: HootSuitePost): SocialPost {
247
+ return {
248
+ id: hootsuitePost.id,
249
+ platform: 'HootSuite',
250
+ profileId: hootsuitePost.socialProfileIds.join(','), // Multiple profiles possible
251
+ content: hootsuitePost.text,
252
+ mediaUrls: hootsuitePost.mediaIds || [],
253
+ publishedAt: this.parseHootSuiteDate(hootsuitePost.createdTime),
254
+ scheduledFor: hootsuitePost.scheduledTime ? this.parseHootSuiteDate(hootsuitePost.scheduledTime) : undefined,
255
+ platformSpecificData: {
256
+ state: hootsuitePost.state,
257
+ tags: hootsuitePost.tags,
258
+ location: hootsuitePost.location,
259
+ socialProfileIds: hootsuitePost.socialProfileIds
260
+ }
261
+ };
226
262
  }
227
- return date.toISOString();
228
- }
229
-
230
- /**
231
- * Parse HootSuite date string
232
- */
233
- protected parseHootSuiteDate(dateString: string): Date {
234
- return new Date(dateString);
235
- }
236
-
237
- /**
238
- * Convert HootSuite post to common format
239
- */
240
- protected normalizePost(hootsuitePost: HootSuitePost): SocialPost {
241
- return {
242
- id: hootsuitePost.id,
243
- platform: 'HootSuite',
244
- profileId: hootsuitePost.socialProfileIds.join(','), // Multiple profiles possible
245
- content: hootsuitePost.text,
246
- mediaUrls: hootsuitePost.mediaIds || [],
247
- publishedAt: this.parseHootSuiteDate(hootsuitePost.createdTime),
248
- scheduledFor: hootsuitePost.scheduledTime ? this.parseHootSuiteDate(hootsuitePost.scheduledTime) : undefined,
249
- platformSpecificData: {
250
- state: hootsuitePost.state,
251
- tags: hootsuitePost.tags,
252
- location: hootsuitePost.location,
253
- socialProfileIds: hootsuitePost.socialProfileIds,
254
- },
255
- };
256
- }
257
-
258
- /**
259
- * Search for posts - implemented in search action
260
- */
261
- protected async searchPosts(params: SearchParams): Promise<SocialPost[]> {
262
- // This is implemented in the search-posts.action.ts
263
- throw new Error('Search posts is implemented in HootSuiteSearchPostsAction');
264
- }
265
-
266
- /**
267
- * Handle HootSuite-specific errors
268
- */
269
- protected handleHootSuiteError(error: AxiosError): never {
270
- if (error.response) {
271
- const { status, data } = error.response;
272
- const errorData = data as any;
273
-
274
- switch (status) {
275
- case 400:
276
- throw new Error(`Bad Request: ${errorData.message || 'Invalid request parameters'}`);
277
- case 401:
278
- throw new Error('Unauthorized: Invalid or expired access token');
279
- case 403:
280
- throw new Error('Forbidden: Insufficient permissions');
281
- case 404:
282
- throw new Error('Not Found: Resource does not exist');
283
- case 429:
284
- throw new Error('Rate Limit Exceeded: Too many requests');
285
- case 500:
286
- throw new Error('Internal Server Error: HootSuite service error');
287
- default:
288
- throw new Error(`HootSuite API Error (${status}): ${errorData.message || 'Unknown error'}`);
289
- }
290
- } else if (error.request) {
291
- throw new Error('Network Error: No response from HootSuite');
292
- } else {
293
- throw new Error(`Request Error: ${error.message}`);
263
+
264
+ /**
265
+ * Search for posts - implemented in search action
266
+ */
267
+ protected async searchPosts(params: SearchParams): Promise<SocialPost[]> {
268
+ // This is implemented in the search-posts.action.ts
269
+ throw new Error('Search posts is implemented in HootSuiteSearchPostsAction');
270
+ }
271
+
272
+ /**
273
+ * Handle HootSuite-specific errors
274
+ */
275
+ protected handleHootSuiteError(error: AxiosError): never {
276
+ if (error.response) {
277
+ const { status, data } = error.response;
278
+ const errorData = data as any;
279
+
280
+ switch (status) {
281
+ case 400:
282
+ throw new Error(`Bad Request: ${errorData.message || 'Invalid request parameters'}`);
283
+ case 401:
284
+ throw new Error('Unauthorized: Invalid or expired access token');
285
+ case 403:
286
+ throw new Error('Forbidden: Insufficient permissions');
287
+ case 404:
288
+ throw new Error('Not Found: Resource does not exist');
289
+ case 429:
290
+ throw new Error('Rate Limit Exceeded: Too many requests');
291
+ case 500:
292
+ throw new Error('Internal Server Error: HootSuite service error');
293
+ default:
294
+ throw new Error(`HootSuite API Error (${status}): ${errorData.message || 'Unknown error'}`);
295
+ }
296
+ } else if (error.request) {
297
+ throw new Error('Network Error: No response from HootSuite');
298
+ } else {
299
+ throw new Error(`Request Error: ${error.message}`);
300
+ }
294
301
  }
295
- }
296
302
  }
297
303
 
298
304
  /**
299
305
  * HootSuite-specific interfaces
300
306
  */
301
307
  export interface HootSuiteProfile {
302
- id: string;
303
- socialNetworkId: string;
304
- socialNetworkUserId: string;
305
- avatarUrl: string;
306
- displayName: string;
307
- type: string;
308
- ownerId: string;
308
+ id: string;
309
+ socialNetworkId: string;
310
+ socialNetworkUserId: string;
311
+ avatarUrl: string;
312
+ displayName: string;
313
+ type: string;
314
+ ownerId: string;
309
315
  }
310
316
 
311
317
  export interface HootSuitePost {
312
- id: string;
313
- socialProfileIds: string[];
314
- text: string;
315
- scheduledTime?: string;
316
- createdTime: string;
317
- state: 'SCHEDULED' | 'PUBLISHED' | 'FAILED' | 'DRAFT';
318
- mediaIds?: string[];
319
- tags?: string[];
320
- location?: {
321
- latitude: number;
322
- longitude: number;
323
- };
318
+ id: string;
319
+ socialProfileIds: string[];
320
+ text: string;
321
+ scheduledTime?: string;
322
+ createdTime: string;
323
+ state: 'SCHEDULED' | 'PUBLISHED' | 'FAILED' | 'DRAFT';
324
+ mediaIds?: string[];
325
+ tags?: string[];
326
+ location?: {
327
+ latitude: number;
328
+ longitude: number;
329
+ };
324
330
  }
325
331
 
326
332
  export interface HootSuiteAnalytics {
327
- postId: string;
328
- metrics: {
329
- likes: number;
330
- comments: number;
331
- shares: number;
332
- clicks: number;
333
- impressions: number;
334
- engagements: number;
335
- reach: number;
336
- };
337
- period: {
338
- start: string;
339
- end: string;
340
- };
341
- }
333
+ postId: string;
334
+ metrics: {
335
+ likes: number;
336
+ comments: number;
337
+ shares: number;
338
+ clicks: number;
339
+ impressions: number;
340
+ engagements: number;
341
+ reach: number;
342
+ };
343
+ period: {
344
+ start: string;
345
+ end: string;
346
+ };
347
+ }