@memberjunction/actions-bizapps-social 2.112.0 → 2.113.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +13 -0
  3. package/dist/base/base-social.action.d.ts.map +1 -1
  4. package/dist/base/base-social.action.js +24 -18
  5. package/dist/base/base-social.action.js.map +1 -1
  6. package/dist/providers/buffer/buffer-base.action.d.ts.map +1 -1
  7. package/dist/providers/buffer/buffer-base.action.js +34 -35
  8. package/dist/providers/buffer/buffer-base.action.js.map +1 -1
  9. package/dist/providers/facebook/actions/boost-post.action.d.ts.map +1 -1
  10. package/dist/providers/facebook/actions/boost-post.action.js +33 -33
  11. package/dist/providers/facebook/actions/boost-post.action.js.map +1 -1
  12. package/dist/providers/facebook/actions/create-album.action.d.ts.map +1 -1
  13. package/dist/providers/facebook/actions/create-album.action.js +36 -34
  14. package/dist/providers/facebook/actions/create-album.action.js.map +1 -1
  15. package/dist/providers/facebook/actions/create-post.action.d.ts.map +1 -1
  16. package/dist/providers/facebook/actions/create-post.action.js +20 -20
  17. package/dist/providers/facebook/actions/create-post.action.js.map +1 -1
  18. package/dist/providers/facebook/actions/get-page-insights.action.d.ts.map +1 -1
  19. package/dist/providers/facebook/actions/get-page-insights.action.js +27 -25
  20. package/dist/providers/facebook/actions/get-page-insights.action.js.map +1 -1
  21. package/dist/providers/facebook/actions/get-page-posts.action.d.ts.map +1 -1
  22. package/dist/providers/facebook/actions/get-page-posts.action.js +23 -19
  23. package/dist/providers/facebook/actions/get-page-posts.action.js.map +1 -1
  24. package/dist/providers/facebook/actions/get-post-insights.action.d.ts.map +1 -1
  25. package/dist/providers/facebook/actions/get-post-insights.action.js +32 -28
  26. package/dist/providers/facebook/actions/get-post-insights.action.js.map +1 -1
  27. package/dist/providers/facebook/actions/respond-to-comments.action.d.ts.map +1 -1
  28. package/dist/providers/facebook/actions/respond-to-comments.action.js +44 -42
  29. package/dist/providers/facebook/actions/respond-to-comments.action.js.map +1 -1
  30. package/dist/providers/facebook/actions/schedule-post.action.d.ts.map +1 -1
  31. package/dist/providers/facebook/actions/schedule-post.action.js +29 -29
  32. package/dist/providers/facebook/actions/schedule-post.action.js.map +1 -1
  33. package/dist/providers/facebook/actions/search-posts.action.d.ts.map +1 -1
  34. package/dist/providers/facebook/actions/search-posts.action.js +39 -37
  35. package/dist/providers/facebook/actions/search-posts.action.js.map +1 -1
  36. package/dist/providers/facebook/facebook-base.action.d.ts.map +1 -1
  37. package/dist/providers/facebook/facebook-base.action.js +59 -44
  38. package/dist/providers/facebook/facebook-base.action.js.map +1 -1
  39. package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.d.ts.map +1 -1
  40. package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.js +31 -33
  41. package/dist/providers/hootsuite/actions/bulk-schedule-posts.action.js.map +1 -1
  42. package/dist/providers/hootsuite/actions/create-scheduled-post.action.d.ts.map +1 -1
  43. package/dist/providers/hootsuite/actions/create-scheduled-post.action.js +32 -28
  44. package/dist/providers/hootsuite/actions/create-scheduled-post.action.js.map +1 -1
  45. package/dist/providers/hootsuite/actions/delete-scheduled-post.action.d.ts.map +1 -1
  46. package/dist/providers/hootsuite/actions/delete-scheduled-post.action.js +19 -19
  47. package/dist/providers/hootsuite/actions/delete-scheduled-post.action.js.map +1 -1
  48. package/dist/providers/hootsuite/actions/get-analytics.action.d.ts.map +1 -1
  49. package/dist/providers/hootsuite/actions/get-analytics.action.js +26 -24
  50. package/dist/providers/hootsuite/actions/get-analytics.action.js.map +1 -1
  51. package/dist/providers/hootsuite/actions/get-scheduled-posts.action.d.ts.map +1 -1
  52. package/dist/providers/hootsuite/actions/get-scheduled-posts.action.js +22 -22
  53. package/dist/providers/hootsuite/actions/get-scheduled-posts.action.js.map +1 -1
  54. package/dist/providers/hootsuite/actions/get-social-profiles.action.d.ts.map +1 -1
  55. package/dist/providers/hootsuite/actions/get-social-profiles.action.js +34 -32
  56. package/dist/providers/hootsuite/actions/get-social-profiles.action.js.map +1 -1
  57. package/dist/providers/hootsuite/actions/search-posts.action.d.ts.map +1 -1
  58. package/dist/providers/hootsuite/actions/search-posts.action.js +52 -43
  59. package/dist/providers/hootsuite/actions/search-posts.action.js.map +1 -1
  60. package/dist/providers/hootsuite/actions/update-scheduled-post.action.d.ts.map +1 -1
  61. package/dist/providers/hootsuite/actions/update-scheduled-post.action.js +28 -30
  62. package/dist/providers/hootsuite/actions/update-scheduled-post.action.js.map +1 -1
  63. package/dist/providers/hootsuite/hootsuite-base.action.d.ts.map +1 -1
  64. package/dist/providers/hootsuite/hootsuite-base.action.js +20 -18
  65. package/dist/providers/hootsuite/hootsuite-base.action.js.map +1 -1
  66. package/dist/providers/instagram/actions/create-post.action.d.ts.map +1 -1
  67. package/dist/providers/instagram/actions/create-post.action.js +26 -27
  68. package/dist/providers/instagram/actions/create-post.action.js.map +1 -1
  69. package/dist/providers/instagram/actions/create-story.action.d.ts.map +1 -1
  70. package/dist/providers/instagram/actions/create-story.action.js +35 -35
  71. package/dist/providers/instagram/actions/create-story.action.js.map +1 -1
  72. package/dist/providers/instagram/actions/get-account-insights.action.d.ts.map +1 -1
  73. package/dist/providers/instagram/actions/get-account-insights.action.js +59 -38
  74. package/dist/providers/instagram/actions/get-account-insights.action.js.map +1 -1
  75. package/dist/providers/instagram/actions/get-business-posts.action.d.ts.map +1 -1
  76. package/dist/providers/instagram/actions/get-business-posts.action.js +29 -29
  77. package/dist/providers/instagram/actions/get-business-posts.action.js.map +1 -1
  78. package/dist/providers/instagram/actions/get-comments.action.d.ts.map +1 -1
  79. package/dist/providers/instagram/actions/get-comments.action.js +36 -36
  80. package/dist/providers/instagram/actions/get-comments.action.js.map +1 -1
  81. package/dist/providers/instagram/actions/get-post-insights.action.d.ts.map +1 -1
  82. package/dist/providers/instagram/actions/get-post-insights.action.js +25 -23
  83. package/dist/providers/instagram/actions/get-post-insights.action.js.map +1 -1
  84. package/dist/providers/instagram/actions/schedule-post.action.d.ts.map +1 -1
  85. package/dist/providers/instagram/actions/schedule-post.action.js +25 -25
  86. package/dist/providers/instagram/actions/schedule-post.action.js.map +1 -1
  87. package/dist/providers/instagram/actions/search-posts.action.d.ts.map +1 -1
  88. package/dist/providers/instagram/actions/search-posts.action.js +60 -56
  89. package/dist/providers/instagram/actions/search-posts.action.js.map +1 -1
  90. package/dist/providers/instagram/instagram-base.action.d.ts.map +1 -1
  91. package/dist/providers/instagram/instagram-base.action.js +27 -25
  92. package/dist/providers/instagram/instagram-base.action.js.map +1 -1
  93. package/dist/providers/linkedin/actions/create-article.action.d.ts.map +1 -1
  94. package/dist/providers/linkedin/actions/create-article.action.js +45 -55
  95. package/dist/providers/linkedin/actions/create-article.action.js.map +1 -1
  96. package/dist/providers/linkedin/actions/create-post.action.d.ts.map +1 -1
  97. package/dist/providers/linkedin/actions/create-post.action.js +29 -31
  98. package/dist/providers/linkedin/actions/create-post.action.js.map +1 -1
  99. package/dist/providers/linkedin/actions/get-followers.action.d.ts.map +1 -1
  100. package/dist/providers/linkedin/actions/get-followers.action.js +28 -28
  101. package/dist/providers/linkedin/actions/get-followers.action.js.map +1 -1
  102. package/dist/providers/linkedin/actions/get-organization-posts.action.d.ts.map +1 -1
  103. package/dist/providers/linkedin/actions/get-organization-posts.action.js +20 -20
  104. package/dist/providers/linkedin/actions/get-organization-posts.action.js.map +1 -1
  105. package/dist/providers/linkedin/actions/get-personal-posts.action.d.ts.map +1 -1
  106. package/dist/providers/linkedin/actions/get-personal-posts.action.js +19 -19
  107. package/dist/providers/linkedin/actions/get-personal-posts.action.js.map +1 -1
  108. package/dist/providers/linkedin/actions/get-post-analytics.action.d.ts.map +1 -1
  109. package/dist/providers/linkedin/actions/get-post-analytics.action.js +23 -25
  110. package/dist/providers/linkedin/actions/get-post-analytics.action.js.map +1 -1
  111. package/dist/providers/linkedin/actions/schedule-post.action.d.ts.map +1 -1
  112. package/dist/providers/linkedin/actions/schedule-post.action.js +30 -32
  113. package/dist/providers/linkedin/actions/schedule-post.action.js.map +1 -1
  114. package/dist/providers/linkedin/actions/search-posts.action.d.ts.map +1 -1
  115. package/dist/providers/linkedin/actions/search-posts.action.js +30 -28
  116. package/dist/providers/linkedin/actions/search-posts.action.js.map +1 -1
  117. package/dist/providers/linkedin/linkedin-base.action.d.ts.map +1 -1
  118. package/dist/providers/linkedin/linkedin-base.action.js +38 -33
  119. package/dist/providers/linkedin/linkedin-base.action.js.map +1 -1
  120. package/dist/providers/tiktok/tiktok-base.action.d.ts.map +1 -1
  121. package/dist/providers/tiktok/tiktok-base.action.js +26 -25
  122. package/dist/providers/tiktok/tiktok-base.action.js.map +1 -1
  123. package/dist/providers/twitter/actions/create-thread.action.d.ts.map +1 -1
  124. package/dist/providers/twitter/actions/create-thread.action.js +29 -25
  125. package/dist/providers/twitter/actions/create-thread.action.js.map +1 -1
  126. package/dist/providers/twitter/actions/create-tweet.action.d.ts.map +1 -1
  127. package/dist/providers/twitter/actions/create-tweet.action.js +23 -23
  128. package/dist/providers/twitter/actions/create-tweet.action.js.map +1 -1
  129. package/dist/providers/twitter/actions/delete-tweet.action.d.ts.map +1 -1
  130. package/dist/providers/twitter/actions/delete-tweet.action.js +19 -19
  131. package/dist/providers/twitter/actions/delete-tweet.action.js.map +1 -1
  132. package/dist/providers/twitter/actions/get-analytics.action.d.ts.map +1 -1
  133. package/dist/providers/twitter/actions/get-analytics.action.js +47 -40
  134. package/dist/providers/twitter/actions/get-analytics.action.js.map +1 -1
  135. package/dist/providers/twitter/actions/get-mentions.action.d.ts.map +1 -1
  136. package/dist/providers/twitter/actions/get-mentions.action.js +31 -30
  137. package/dist/providers/twitter/actions/get-mentions.action.js.map +1 -1
  138. package/dist/providers/twitter/actions/get-timeline.action.d.ts.map +1 -1
  139. package/dist/providers/twitter/actions/get-timeline.action.js +29 -29
  140. package/dist/providers/twitter/actions/get-timeline.action.js.map +1 -1
  141. package/dist/providers/twitter/actions/schedule-tweet.action.d.ts.map +1 -1
  142. package/dist/providers/twitter/actions/schedule-tweet.action.js +26 -26
  143. package/dist/providers/twitter/actions/schedule-tweet.action.js.map +1 -1
  144. package/dist/providers/twitter/actions/search-tweets.action.d.ts.map +1 -1
  145. package/dist/providers/twitter/actions/search-tweets.action.js +58 -56
  146. package/dist/providers/twitter/actions/search-tweets.action.js.map +1 -1
  147. package/dist/providers/twitter/twitter-base.action.d.ts.map +1 -1
  148. package/dist/providers/twitter/twitter-base.action.js +68 -58
  149. package/dist/providers/twitter/twitter-base.action.js.map +1 -1
  150. package/dist/providers/youtube/youtube-base.action.d.ts +1 -1
  151. package/dist/providers/youtube/youtube-base.action.d.ts.map +1 -1
  152. package/dist/providers/youtube/youtube-base.action.js +25 -22
  153. package/dist/providers/youtube/youtube-base.action.js.map +1 -1
  154. package/package.json +6 -5
  155. package/src/base/base-social.action.ts +224 -217
  156. package/src/providers/buffer/buffer-base.action.ts +441 -435
  157. package/src/providers/facebook/actions/boost-post.action.ts +386 -350
  158. package/src/providers/facebook/actions/create-album.action.ts +307 -291
  159. package/src/providers/facebook/actions/create-post.action.ts +227 -224
  160. package/src/providers/facebook/actions/get-page-insights.action.ts +403 -383
  161. package/src/providers/facebook/actions/get-page-posts.action.ts +225 -214
  162. package/src/providers/facebook/actions/get-post-insights.action.ts +316 -300
  163. package/src/providers/facebook/actions/respond-to-comments.action.ts +336 -319
  164. package/src/providers/facebook/actions/schedule-post.action.ts +292 -289
  165. package/src/providers/facebook/actions/search-posts.action.ts +413 -399
  166. package/src/providers/facebook/facebook-base.action.ts +670 -653
  167. package/src/providers/hootsuite/actions/bulk-schedule-posts.action.ts +257 -257
  168. package/src/providers/hootsuite/actions/create-scheduled-post.action.ts +189 -184
  169. package/src/providers/hootsuite/actions/delete-scheduled-post.action.ts +161 -160
  170. package/src/providers/hootsuite/actions/get-analytics.action.ts +254 -249
  171. package/src/providers/hootsuite/actions/get-scheduled-posts.action.ts +207 -206
  172. package/src/providers/hootsuite/actions/get-social-profiles.action.ts +205 -206
  173. package/src/providers/hootsuite/actions/search-posts.action.ts +369 -351
  174. package/src/providers/hootsuite/actions/update-scheduled-post.action.ts +209 -211
  175. package/src/providers/hootsuite/hootsuite-base.action.ts +307 -301
  176. package/src/providers/instagram/actions/create-post.action.ts +296 -276
  177. package/src/providers/instagram/actions/create-story.action.ts +394 -378
  178. package/src/providers/instagram/actions/get-account-insights.action.ts +420 -384
  179. package/src/providers/instagram/actions/get-business-posts.action.ts +242 -233
  180. package/src/providers/instagram/actions/get-comments.action.ts +377 -365
  181. package/src/providers/instagram/actions/get-post-insights.action.ts +273 -265
  182. package/src/providers/instagram/actions/schedule-post.action.ts +235 -233
  183. package/src/providers/instagram/actions/search-posts.action.ts +538 -512
  184. package/src/providers/instagram/instagram-base.action.ts +393 -368
  185. package/src/providers/linkedin/actions/create-article.action.ts +266 -275
  186. package/src/providers/linkedin/actions/create-post.action.ts +177 -179
  187. package/src/providers/linkedin/actions/get-followers.action.ts +211 -211
  188. package/src/providers/linkedin/actions/get-organization-posts.action.ts +147 -146
  189. package/src/providers/linkedin/actions/get-personal-posts.action.ts +139 -138
  190. package/src/providers/linkedin/actions/get-post-analytics.action.ts +189 -190
  191. package/src/providers/linkedin/actions/schedule-post.action.ts +189 -191
  192. package/src/providers/linkedin/actions/search-posts.action.ts +283 -275
  193. package/src/providers/linkedin/linkedin-base.action.ts +421 -407
  194. package/src/providers/tiktok/tiktok-base.action.ts +320 -305
  195. package/src/providers/twitter/actions/create-thread.action.ts +207 -203
  196. package/src/providers/twitter/actions/create-tweet.action.ts +188 -187
  197. package/src/providers/twitter/actions/delete-tweet.action.ts +129 -128
  198. package/src/providers/twitter/actions/get-analytics.action.ts +411 -402
  199. package/src/providers/twitter/actions/get-mentions.action.ts +219 -218
  200. package/src/providers/twitter/actions/get-timeline.action.ts +233 -232
  201. package/src/providers/twitter/actions/schedule-tweet.action.ts +222 -221
  202. package/src/providers/twitter/actions/search-tweets.action.ts +543 -540
  203. package/src/providers/twitter/twitter-base.action.ts +560 -541
  204. package/src/providers/youtube/youtube-base.action.ts +333 -320
@@ -1,6 +1,6 @@
1
1
  import { RegisterClass } from '@memberjunction/global';
2
2
  import { BaseSocialMediaAction, SocialPost, SocialAnalytics, MediaFile } from '../../base/base-social.action';
3
- import { LogError, LogStatus } from '@memberjunction/global';
3
+ import { LogError, LogStatus } from '@memberjunction/core';
4
4
  import axios, { AxiosInstance, AxiosError } from 'axios';
5
5
  import { BaseAction } from '@memberjunction/actions';
6
6
 
@@ -8,33 +8,33 @@ import { BaseAction } from '@memberjunction/actions';
8
8
  * TikTok video information
9
9
  */
10
10
  export interface TikTokVideo {
11
- id: string;
12
- share_url: string;
13
- title: string;
14
- description: string;
15
- duration: number;
16
- cover_image_url: string;
17
- share_count: number;
18
- view_count: number;
19
- like_count: number;
20
- comment_count: number;
21
- create_time: number;
11
+ id: string;
12
+ share_url: string;
13
+ title: string;
14
+ description: string;
15
+ duration: number;
16
+ cover_image_url: string;
17
+ share_count: number;
18
+ view_count: number;
19
+ like_count: number;
20
+ comment_count: number;
21
+ create_time: number;
22
22
  }
23
23
 
24
24
  /**
25
25
  * TikTok user information
26
26
  */
27
27
  export interface TikTokUser {
28
- open_id: string;
29
- union_id: string;
30
- avatar_url: string;
31
- display_name: string;
32
- bio_description: string;
33
- profile_deep_link: string;
34
- is_verified: boolean;
35
- follower_count: number;
36
- following_count: number;
37
- likes_count: number;
28
+ open_id: string;
29
+ union_id: string;
30
+ avatar_url: string;
31
+ display_name: string;
32
+ bio_description: string;
33
+ profile_deep_link: string;
34
+ is_verified: boolean;
35
+ follower_count: number;
36
+ following_count: number;
37
+ likes_count: number;
38
38
  }
39
39
 
40
40
  /**
@@ -43,300 +43,315 @@ export interface TikTokUser {
43
43
  */
44
44
  @RegisterClass(BaseAction, 'TikTokBaseAction')
45
45
  export abstract class TikTokBaseAction extends BaseSocialMediaAction {
46
- protected get platformName(): string {
47
- return 'TikTok';
48
- }
49
-
50
- protected get apiBaseUrl(): string {
51
- return 'https://open-api.tiktok.com';
52
- }
53
-
54
- private axiosInstance: AxiosInstance | null = null;
55
-
56
- /**
57
- * Initialize axios instance with interceptors
58
- */
59
- protected getAxiosInstance(): AxiosInstance {
60
- if (!this.axiosInstance) {
61
- this.axiosInstance = axios.create({
62
- baseURL: this.apiBaseUrl,
63
- timeout: 30000,
64
- headers: {
65
- 'Content-Type': 'application/json',
66
- Accept: 'application/json',
67
- },
68
- });
69
-
70
- // Request interceptor for logging
71
- this.axiosInstance.interceptors.request.use(
72
- (config) => {
73
- this.logApiRequest(config.method?.toUpperCase() || 'GET', config.url || '', config.data);
74
- return config;
75
- },
76
- (error) => {
77
- LogError(`TikTok API Request Error: ${error.message}`);
78
- return Promise.reject(error);
79
- }
80
- );
81
-
82
- // Response interceptor for logging and error handling
83
- this.axiosInstance.interceptors.response.use(
84
- (response) => {
85
- this.logApiResponse(response.data);
86
- return response;
87
- },
88
- async (error: AxiosError) => {
89
- if (error.response?.status === 429) {
90
- // Rate limit hit
91
- const retryAfter = error.response.headers['retry-after'];
92
- await this.handleRateLimit(retryAfter ? parseInt(retryAfter) : undefined);
93
-
94
- // Retry the request
95
- return this.axiosInstance?.request(error.config!);
96
- }
97
-
98
- LogError(`TikTok API Response Error: ${error.message}`);
99
- return Promise.reject(error);
100
- }
101
- );
46
+
47
+ protected get platformName(): string {
48
+ return 'TikTok';
102
49
  }
103
-
104
- return this.axiosInstance;
105
- }
106
-
107
- /**
108
- * Make authenticated TikTok API request
109
- */
110
- protected async makeTikTokRequest<T>(
111
- endpoint: string,
112
- method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
113
- data?: any,
114
- params?: any
115
- ): Promise<T> {
116
- const token = this.getAccessToken();
117
- if (!token) {
118
- throw new Error('No access token available for TikTok');
50
+
51
+ protected get apiBaseUrl(): string {
52
+ return 'https://open-api.tiktok.com';
119
53
  }
120
-
121
- const axios = this.getAxiosInstance();
122
-
123
- try {
124
- const response = await axios.request<T>({
125
- method,
126
- url: endpoint,
127
- data,
128
- params,
129
- headers: {
130
- Authorization: `Bearer ${token}`,
131
- },
132
- });
133
-
134
- return response.data;
135
- } catch (error) {
136
- if (error instanceof AxiosError) {
137
- if (error.response?.status === 401) {
138
- // Token might be expired, try to refresh
139
- await this.refreshAccessToken();
140
-
141
- // Retry with new token
142
- const newToken = this.getAccessToken();
143
- const retryResponse = await axios.request<T>({
144
- method,
145
- url: endpoint,
146
- data,
147
- params,
148
- headers: {
149
- Authorization: `Bearer ${newToken}`,
150
- },
151
- });
152
-
153
- return retryResponse.data;
54
+
55
+ private axiosInstance: AxiosInstance | null = null;
56
+
57
+ /**
58
+ * Initialize axios instance with interceptors
59
+ */
60
+ protected getAxiosInstance(): AxiosInstance {
61
+ if (!this.axiosInstance) {
62
+ this.axiosInstance = axios.create({
63
+ baseURL: this.apiBaseUrl,
64
+ timeout: 30000,
65
+ headers: {
66
+ 'Content-Type': 'application/json',
67
+ 'Accept': 'application/json'
68
+ }
69
+ });
70
+
71
+ // Request interceptor for logging
72
+ this.axiosInstance.interceptors.request.use(
73
+ (config) => {
74
+ this.logApiRequest(config.method?.toUpperCase() || 'GET', config.url || '', config.data);
75
+ return config;
76
+ },
77
+ (error) => {
78
+ LogError(`TikTok API Request Error: ${error.message}`);
79
+ return Promise.reject(error);
80
+ }
81
+ );
82
+
83
+ // Response interceptor for logging and error handling
84
+ this.axiosInstance.interceptors.response.use(
85
+ (response) => {
86
+ this.logApiResponse(response.data);
87
+ return response;
88
+ },
89
+ async (error: AxiosError) => {
90
+ if (error.response?.status === 429) {
91
+ // Rate limit hit
92
+ const retryAfter = error.response.headers['retry-after'];
93
+ await this.handleRateLimit(retryAfter ? parseInt(retryAfter) : undefined);
94
+
95
+ // Retry the request
96
+ return this.axiosInstance?.request(error.config!);
97
+ }
98
+
99
+ LogError(`TikTok API Response Error: ${error.message}`);
100
+ return Promise.reject(error);
101
+ }
102
+ );
154
103
  }
155
-
156
- const errorMessage = error.response?.data?.error?.message || error.message;
157
- throw new Error(`TikTok API error: ${errorMessage}`);
158
- }
159
- throw error;
104
+
105
+ return this.axiosInstance;
160
106
  }
161
- }
162
-
163
- /**
164
- * Refresh TikTok access token
165
- */
166
- protected async refreshAccessToken(): Promise<void> {
167
- const refreshToken = this.getRefreshToken();
168
- if (!refreshToken) {
169
- throw new Error('No refresh token available for TikTok');
107
+
108
+ /**
109
+ * Make authenticated TikTok API request
110
+ */
111
+ protected async makeTikTokRequest<T>(
112
+ endpoint: string,
113
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
114
+ data?: any,
115
+ params?: any
116
+ ): Promise<T> {
117
+ const token = this.getAccessToken();
118
+ if (!token) {
119
+ throw new Error('No access token available for TikTok');
120
+ }
121
+
122
+ const axios = this.getAxiosInstance();
123
+
124
+ try {
125
+ const response = await axios.request<T>({
126
+ method,
127
+ url: endpoint,
128
+ data,
129
+ params,
130
+ headers: {
131
+ 'Authorization': `Bearer ${token}`
132
+ }
133
+ });
134
+
135
+ return response.data;
136
+ } catch (error) {
137
+ if (error instanceof AxiosError) {
138
+ if (error.response?.status === 401) {
139
+ // Token might be expired, try to refresh
140
+ await this.refreshAccessToken();
141
+
142
+ // Retry with new token
143
+ const newToken = this.getAccessToken();
144
+ const retryResponse = await axios.request<T>({
145
+ method,
146
+ url: endpoint,
147
+ data,
148
+ params,
149
+ headers: {
150
+ 'Authorization': `Bearer ${newToken}`
151
+ }
152
+ });
153
+
154
+ return retryResponse.data;
155
+ }
156
+
157
+ const errorMessage = error.response?.data?.error?.message || error.message;
158
+ throw new Error(`TikTok API error: ${errorMessage}`);
159
+ }
160
+ throw error;
161
+ }
170
162
  }
171
-
172
- try {
173
- const response = await axios.post(`${this.apiBaseUrl}/oauth/refresh_token/`, {
174
- client_key: this.getCustomAttribute(2), // Store client key in CustomAttribute2
175
- grant_type: 'refresh_token',
176
- refresh_token: refreshToken,
177
- });
178
-
179
- const { access_token, refresh_token: newRefreshToken, expires_in } = response.data.data;
180
-
181
- // Update stored tokens
182
- await this.updateStoredTokens(access_token, newRefreshToken, expires_in);
183
-
184
- LogStatus('TikTok access token refreshed successfully');
185
- } catch (error) {
186
- LogError(`Failed to refresh TikTok access token: ${error}`);
187
- throw new Error('Failed to refresh TikTok access token');
163
+
164
+ /**
165
+ * Refresh TikTok access token
166
+ */
167
+ protected async refreshAccessToken(): Promise<void> {
168
+ const refreshToken = this.getRefreshToken();
169
+ if (!refreshToken) {
170
+ throw new Error('No refresh token available for TikTok');
171
+ }
172
+
173
+ try {
174
+ const response = await axios.post(`${this.apiBaseUrl}/oauth/refresh_token/`, {
175
+ client_key: this.getCustomAttribute(2), // Store client key in CustomAttribute2
176
+ grant_type: 'refresh_token',
177
+ refresh_token: refreshToken
178
+ });
179
+
180
+ const { access_token, refresh_token: newRefreshToken, expires_in } = response.data.data;
181
+
182
+ // Update stored tokens
183
+ await this.updateStoredTokens(access_token, newRefreshToken, expires_in);
184
+
185
+ LogStatus('TikTok access token refreshed successfully');
186
+ } catch (error) {
187
+ LogError(`Failed to refresh TikTok access token: ${error}`);
188
+ throw new Error('Failed to refresh TikTok access token');
189
+ }
188
190
  }
189
- }
190
-
191
- /**
192
- * Upload media to TikTok (requires special approval)
193
- */
194
- protected async uploadSingleMedia(file: MediaFile): Promise<string> {
195
- // TikTok video upload requires special approval
196
- // This is a placeholder implementation
197
- throw new Error('TikTok video upload requires special API approval. Please use TikTok Creator Studio for video uploads.');
198
- }
199
-
200
- /**
201
- * Search posts (videos) - TikTok only allows searching user's own videos
202
- */
203
- protected async searchPosts(params: any): Promise<SocialPost[]> {
204
- // TikTok doesn't provide a general search API for security/privacy reasons
205
- // We can only search within a user's own videos
206
- const videos = await this.getUserVideos();
207
-
208
- let filtered = videos;
209
-
210
- // Apply filters if provided
211
- if (params.query) {
212
- const query = params.query.toLowerCase();
213
- filtered = filtered.filter((video) => video.title.toLowerCase().includes(query) || video.description.toLowerCase().includes(query));
191
+
192
+ /**
193
+ * Upload media to TikTok (requires special approval)
194
+ */
195
+ protected async uploadSingleMedia(file: MediaFile): Promise<string> {
196
+ // TikTok video upload requires special approval
197
+ // This is a placeholder implementation
198
+ throw new Error('TikTok video upload requires special API approval. Please use TikTok Creator Studio for video uploads.');
214
199
  }
215
-
216
- if (params.hashtags && params.hashtags.length > 0) {
217
- filtered = filtered.filter((video) => {
218
- const videoHashtags = this.extractHashtags(video.description);
219
- return params.hashtags.some((tag: string) => videoHashtags.includes(tag.toLowerCase()));
220
- });
200
+
201
+ /**
202
+ * Search posts (videos) - TikTok only allows searching user's own videos
203
+ */
204
+ protected async searchPosts(params: any): Promise<SocialPost[]> {
205
+ // TikTok doesn't provide a general search API for security/privacy reasons
206
+ // We can only search within a user's own videos
207
+ const videos = await this.getUserVideos();
208
+
209
+ let filtered = videos;
210
+
211
+ // Apply filters if provided
212
+ if (params.query) {
213
+ const query = params.query.toLowerCase();
214
+ filtered = filtered.filter(video =>
215
+ video.title.toLowerCase().includes(query) ||
216
+ video.description.toLowerCase().includes(query)
217
+ );
218
+ }
219
+
220
+ if (params.hashtags && params.hashtags.length > 0) {
221
+ filtered = filtered.filter(video => {
222
+ const videoHashtags = this.extractHashtags(video.description);
223
+ return params.hashtags.some((tag: string) =>
224
+ videoHashtags.includes(tag.toLowerCase())
225
+ );
226
+ });
227
+ }
228
+
229
+ if (params.startDate) {
230
+ const startTime = new Date(params.startDate).getTime() / 1000;
231
+ filtered = filtered.filter(video => video.create_time >= startTime);
232
+ }
233
+
234
+ if (params.endDate) {
235
+ const endTime = new Date(params.endDate).getTime() / 1000;
236
+ filtered = filtered.filter(video => video.create_time <= endTime);
237
+ }
238
+
239
+ // Apply limit and offset
240
+ if (params.offset) {
241
+ filtered = filtered.slice(params.offset);
242
+ }
243
+
244
+ if (params.limit) {
245
+ filtered = filtered.slice(0, params.limit);
246
+ }
247
+
248
+ return filtered.map(video => this.normalizePost(video));
221
249
  }
222
-
223
- if (params.startDate) {
224
- const startTime = new Date(params.startDate).getTime() / 1000;
225
- filtered = filtered.filter((video) => video.create_time >= startTime);
250
+
251
+ /**
252
+ * Get user's videos
253
+ */
254
+ protected async getUserVideos(): Promise<TikTokVideo[]> {
255
+ const response = await this.makeTikTokRequest<any>(
256
+ '/v2/video/list/',
257
+ 'GET',
258
+ undefined,
259
+ {
260
+ fields: 'id,share_url,title,description,duration,cover_image_url,share_count,view_count,like_count,comment_count,create_time'
261
+ }
262
+ );
263
+
264
+ return response.data?.videos || [];
226
265
  }
227
-
228
- if (params.endDate) {
229
- const endTime = new Date(params.endDate).getTime() / 1000;
230
- filtered = filtered.filter((video) => video.create_time <= endTime);
266
+
267
+ /**
268
+ * Convert TikTok video to common post format
269
+ */
270
+ protected normalizePost(video: TikTokVideo): SocialPost {
271
+ return {
272
+ id: video.id,
273
+ platform: this.platformName,
274
+ profileId: this.getCustomAttribute(1) || '', // Store user ID in CustomAttribute1
275
+ content: video.description || video.title,
276
+ mediaUrls: [video.cover_image_url],
277
+ publishedAt: new Date(video.create_time * 1000),
278
+ analytics: {
279
+ impressions: video.view_count,
280
+ engagements: video.like_count + video.comment_count + video.share_count,
281
+ clicks: 0, // Not available in TikTok API
282
+ shares: video.share_count,
283
+ comments: video.comment_count,
284
+ likes: video.like_count,
285
+ reach: video.view_count,
286
+ videoViews: video.view_count,
287
+ platformMetrics: {
288
+ duration: video.duration,
289
+ shareUrl: video.share_url
290
+ }
291
+ },
292
+ platformSpecificData: {
293
+ ...video,
294
+ videoUrl: video.share_url
295
+ }
296
+ };
231
297
  }
232
-
233
- // Apply limit and offset
234
- if (params.offset) {
235
- filtered = filtered.slice(params.offset);
298
+
299
+ /**
300
+ * Normalize TikTok analytics to common format
301
+ */
302
+ protected normalizeAnalytics(platformData: any): SocialAnalytics {
303
+ return {
304
+ impressions: platformData.view_count || 0,
305
+ engagements: (platformData.like_count || 0) + (platformData.comment_count || 0) + (platformData.share_count || 0),
306
+ clicks: 0, // Not available in TikTok API
307
+ shares: platformData.share_count || 0,
308
+ comments: platformData.comment_count || 0,
309
+ likes: platformData.like_count || 0,
310
+ reach: platformData.view_count || 0,
311
+ videoViews: platformData.view_count || 0,
312
+ platformMetrics: platformData
313
+ };
236
314
  }
237
-
238
- if (params.limit) {
239
- filtered = filtered.slice(0, params.limit);
315
+
316
+ /**
317
+ * Extract hashtags from video description
318
+ */
319
+ protected extractHashtags(description: string): string[] {
320
+ const hashtagRegex = /#(\w+)/g;
321
+ const matches = description.match(hashtagRegex) || [];
322
+ return matches.map(tag => tag.substring(1).toLowerCase());
240
323
  }
241
-
242
- return filtered.map((video) => this.normalizePost(video));
243
- }
244
-
245
- /**
246
- * Get user's videos
247
- */
248
- protected async getUserVideos(): Promise<TikTokVideo[]> {
249
- const response = await this.makeTikTokRequest<any>('/v2/video/list/', 'GET', undefined, {
250
- fields: 'id,share_url,title,description,duration,cover_image_url,share_count,view_count,like_count,comment_count,create_time',
251
- });
252
-
253
- return response.data?.videos || [];
254
- }
255
-
256
- /**
257
- * Convert TikTok video to common post format
258
- */
259
- protected normalizePost(video: TikTokVideo): SocialPost {
260
- return {
261
- id: video.id,
262
- platform: this.platformName,
263
- profileId: this.getCustomAttribute(1) || '', // Store user ID in CustomAttribute1
264
- content: video.description || video.title,
265
- mediaUrls: [video.cover_image_url],
266
- publishedAt: new Date(video.create_time * 1000),
267
- analytics: {
268
- impressions: video.view_count,
269
- engagements: video.like_count + video.comment_count + video.share_count,
270
- clicks: 0, // Not available in TikTok API
271
- shares: video.share_count,
272
- comments: video.comment_count,
273
- likes: video.like_count,
274
- reach: video.view_count,
275
- videoViews: video.view_count,
276
- platformMetrics: {
277
- duration: video.duration,
278
- shareUrl: video.share_url,
279
- },
280
- },
281
- platformSpecificData: {
282
- ...video,
283
- videoUrl: video.share_url,
284
- },
285
- };
286
- }
287
-
288
- /**
289
- * Normalize TikTok analytics to common format
290
- */
291
- protected normalizeAnalytics(platformData: any): SocialAnalytics {
292
- return {
293
- impressions: platformData.view_count || 0,
294
- engagements: (platformData.like_count || 0) + (platformData.comment_count || 0) + (platformData.share_count || 0),
295
- clicks: 0, // Not available in TikTok API
296
- shares: platformData.share_count || 0,
297
- comments: platformData.comment_count || 0,
298
- likes: platformData.like_count || 0,
299
- reach: platformData.view_count || 0,
300
- videoViews: platformData.view_count || 0,
301
- platformMetrics: platformData,
302
- };
303
- }
304
-
305
- /**
306
- * Extract hashtags from video description
307
- */
308
- protected extractHashtags(description: string): string[] {
309
- const hashtagRegex = /#(\w+)/g;
310
- const matches = description.match(hashtagRegex) || [];
311
- return matches.map((tag) => tag.substring(1).toLowerCase());
312
- }
313
-
314
- /**
315
- * Get current user info
316
- */
317
- protected async getCurrentUser(): Promise<TikTokUser> {
318
- const response = await this.makeTikTokRequest<any>('/v2/user/info/', 'GET', undefined, {
319
- fields:
320
- 'open_id,union_id,avatar_url,display_name,bio_description,profile_deep_link,is_verified,follower_count,following_count,likes_count',
321
- });
322
-
323
- return response.data?.user;
324
- }
325
-
326
- /**
327
- * Validate TikTok-specific media requirements
328
- */
329
- protected validateMediaFile(file: MediaFile): void {
330
- const allowedTypes = ['video/mp4', 'video/quicktime', 'video/webm'];
331
-
332
- if (!allowedTypes.includes(file.mimeType)) {
333
- throw new Error(`TikTok only supports video files. Got: ${file.mimeType}`);
324
+
325
+ /**
326
+ * Get current user info
327
+ */
328
+ protected async getCurrentUser(): Promise<TikTokUser> {
329
+ const response = await this.makeTikTokRequest<any>(
330
+ '/v2/user/info/',
331
+ 'GET',
332
+ undefined,
333
+ {
334
+ fields: 'open_id,union_id,avatar_url,display_name,bio_description,profile_deep_link,is_verified,follower_count,following_count,likes_count'
335
+ }
336
+ );
337
+
338
+ return response.data?.user;
334
339
  }
335
-
336
- // Max file size: 287.6 MB
337
- const maxSize = 287.6 * 1024 * 1024;
338
- if (file.size > maxSize) {
339
- throw new Error(`File size exceeds TikTok limit of 287.6 MB. Got: ${(file.size / 1024 / 1024).toFixed(2)} MB`);
340
+
341
+ /**
342
+ * Validate TikTok-specific media requirements
343
+ */
344
+ protected validateMediaFile(file: MediaFile): void {
345
+ const allowedTypes = ['video/mp4', 'video/quicktime', 'video/webm'];
346
+
347
+ if (!allowedTypes.includes(file.mimeType)) {
348
+ throw new Error(`TikTok only supports video files. Got: ${file.mimeType}`);
349
+ }
350
+
351
+ // Max file size: 287.6 MB
352
+ const maxSize = 287.6 * 1024 * 1024;
353
+ if (file.size > maxSize) {
354
+ throw new Error(`File size exceeds TikTok limit of 287.6 MB. Got: ${(file.size / 1024 / 1024).toFixed(2)} MB`);
355
+ }
340
356
  }
341
- }
342
- }
357
+ }