@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,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/core';
3
+ import { LogError, LogStatus } from '@memberjunction/global';
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,315 +43,300 @@ export interface TikTokUser {
43
43
  */
44
44
  @RegisterClass(BaseAction, 'TikTokBaseAction')
45
45
  export abstract class TikTokBaseAction extends BaseSocialMediaAction {
46
-
47
- protected get platformName(): string {
48
- return 'TikTok';
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
+ );
49
102
  }
50
-
51
- protected get apiBaseUrl(): string {
52
- return 'https://open-api.tiktok.com';
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');
53
119
  }
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
- );
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;
103
154
  }
104
-
105
- return this.axiosInstance;
155
+
156
+ const errorMessage = error.response?.data?.error?.message || error.message;
157
+ throw new Error(`TikTok API error: ${errorMessage}`);
158
+ }
159
+ throw error;
106
160
  }
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
- }
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');
162
170
  }
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
- }
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');
190
188
  }
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.');
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));
199
214
  }
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));
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
+ });
249
221
  }
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 || [];
222
+
223
+ if (params.startDate) {
224
+ const startTime = new Date(params.startDate).getTime() / 1000;
225
+ filtered = filtered.filter((video) => video.create_time >= startTime);
265
226
  }
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
- };
227
+
228
+ if (params.endDate) {
229
+ const endTime = new Date(params.endDate).getTime() / 1000;
230
+ filtered = filtered.filter((video) => video.create_time <= endTime);
297
231
  }
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
- };
232
+
233
+ // Apply limit and offset
234
+ if (params.offset) {
235
+ filtered = filtered.slice(params.offset);
314
236
  }
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());
237
+
238
+ if (params.limit) {
239
+ filtered = filtered.slice(0, params.limit);
323
240
  }
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;
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}`);
339
334
  }
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
- }
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`);
356
340
  }
357
- }
341
+ }
342
+ }