@classytic/social 0.1.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 (121) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/LICENSE +21 -0
  3. package/README.md +368 -0
  4. package/dist/base-Bw7e52V8.mjs +246 -0
  5. package/dist/base-Bw7e52V8.mjs.map +1 -0
  6. package/dist/base-DBtKFiSX.d.mts +226 -0
  7. package/dist/base-DBtKFiSX.d.mts.map +1 -0
  8. package/dist/chunk-DQk6qfdC.mjs +18 -0
  9. package/dist/client/index.d.mts +44 -0
  10. package/dist/client/index.d.mts.map +1 -0
  11. package/dist/client/index.mjs +154 -0
  12. package/dist/client/index.mjs.map +1 -0
  13. package/dist/common/index.d.mts +3 -0
  14. package/dist/common/index.mjs +7 -0
  15. package/dist/contracts-Cdwa4zlg.d.mts +121 -0
  16. package/dist/contracts-Cdwa4zlg.d.mts.map +1 -0
  17. package/dist/contracts-lCa069IK.mjs +221 -0
  18. package/dist/contracts-lCa069IK.mjs.map +1 -0
  19. package/dist/env-Bl0cwwjC.mjs +955 -0
  20. package/dist/env-Bl0cwwjC.mjs.map +1 -0
  21. package/dist/env-DxOZHf0p.d.mts +394 -0
  22. package/dist/env-DxOZHf0p.d.mts.map +1 -0
  23. package/dist/errors-Cm6LeKf7.mjs +32 -0
  24. package/dist/errors-Cm6LeKf7.mjs.map +1 -0
  25. package/dist/facebook-l_4CghaA.mjs +95 -0
  26. package/dist/facebook-l_4CghaA.mjs.map +1 -0
  27. package/dist/http-DpcLSR1M.mjs +197 -0
  28. package/dist/http-DpcLSR1M.mjs.map +1 -0
  29. package/dist/index.d.mts +42 -0
  30. package/dist/index.d.mts.map +1 -0
  31. package/dist/index.mjs +71 -0
  32. package/dist/index.mjs.map +1 -0
  33. package/dist/instagram-BGaeUFU2.mjs +90 -0
  34. package/dist/instagram-BGaeUFU2.mjs.map +1 -0
  35. package/dist/linkedin-70whtVKa.mjs +101 -0
  36. package/dist/linkedin-70whtVKa.mjs.map +1 -0
  37. package/dist/meta-D3vcJU1c.mjs +126 -0
  38. package/dist/meta-D3vcJU1c.mjs.map +1 -0
  39. package/dist/pkce-jq5II68b.mjs +72 -0
  40. package/dist/pkce-jq5II68b.mjs.map +1 -0
  41. package/dist/polling-DZ1apXtA.mjs +25 -0
  42. package/dist/polling-DZ1apXtA.mjs.map +1 -0
  43. package/dist/providers/facebook.d.mts +135 -0
  44. package/dist/providers/facebook.d.mts.map +1 -0
  45. package/dist/providers/facebook.mjs +450 -0
  46. package/dist/providers/facebook.mjs.map +1 -0
  47. package/dist/providers/instagram.d.mts +122 -0
  48. package/dist/providers/instagram.d.mts.map +1 -0
  49. package/dist/providers/instagram.mjs +496 -0
  50. package/dist/providers/instagram.mjs.map +1 -0
  51. package/dist/providers/linkedin.d.mts +145 -0
  52. package/dist/providers/linkedin.d.mts.map +1 -0
  53. package/dist/providers/linkedin.mjs +574 -0
  54. package/dist/providers/linkedin.mjs.map +1 -0
  55. package/dist/providers/reddit.d.mts +102 -0
  56. package/dist/providers/reddit.d.mts.map +1 -0
  57. package/dist/providers/reddit.mjs +657 -0
  58. package/dist/providers/reddit.mjs.map +1 -0
  59. package/dist/providers/telegram.d.mts +139 -0
  60. package/dist/providers/telegram.d.mts.map +1 -0
  61. package/dist/providers/telegram.mjs +517 -0
  62. package/dist/providers/telegram.mjs.map +1 -0
  63. package/dist/providers/tiktok.d.mts +116 -0
  64. package/dist/providers/tiktok.d.mts.map +1 -0
  65. package/dist/providers/tiktok.mjs +676 -0
  66. package/dist/providers/tiktok.mjs.map +1 -0
  67. package/dist/providers/twitter.d.mts +150 -0
  68. package/dist/providers/twitter.d.mts.map +1 -0
  69. package/dist/providers/twitter.mjs +628 -0
  70. package/dist/providers/twitter.mjs.map +1 -0
  71. package/dist/providers/whatsapp.d.mts +79 -0
  72. package/dist/providers/whatsapp.d.mts.map +1 -0
  73. package/dist/providers/whatsapp.mjs +376 -0
  74. package/dist/providers/whatsapp.mjs.map +1 -0
  75. package/dist/providers/youtube.d.mts +153 -0
  76. package/dist/providers/youtube.d.mts.map +1 -0
  77. package/dist/providers/youtube.mjs +902 -0
  78. package/dist/providers/youtube.mjs.map +1 -0
  79. package/dist/reddit-B10kS4Se.mjs +126 -0
  80. package/dist/reddit-B10kS4Se.mjs.map +1 -0
  81. package/dist/schemas/index.d.mts +819 -0
  82. package/dist/schemas/index.d.mts.map +1 -0
  83. package/dist/schemas/index.mjs +31 -0
  84. package/dist/schemas/index.mjs.map +1 -0
  85. package/dist/security-BXhfebWm.d.mts +338 -0
  86. package/dist/security-BXhfebWm.d.mts.map +1 -0
  87. package/dist/shared-Fvc6xQku.mjs +100 -0
  88. package/dist/shared-Fvc6xQku.mjs.map +1 -0
  89. package/dist/telegram-FaUHpZgB.mjs +107 -0
  90. package/dist/telegram-FaUHpZgB.mjs.map +1 -0
  91. package/dist/tiktok-B_bMk4G-.mjs +94 -0
  92. package/dist/tiktok-B_bMk4G-.mjs.map +1 -0
  93. package/dist/twitter-BC22zfuc.mjs +98 -0
  94. package/dist/twitter-BC22zfuc.mjs.map +1 -0
  95. package/dist/types-BFE4psYI.d.mts +102 -0
  96. package/dist/types-BFE4psYI.d.mts.map +1 -0
  97. package/dist/types-Bv27tcT0.d.mts +230 -0
  98. package/dist/types-Bv27tcT0.d.mts.map +1 -0
  99. package/dist/types-BwkKyqpi.d.mts +253 -0
  100. package/dist/types-BwkKyqpi.d.mts.map +1 -0
  101. package/dist/types-CJrHMDV9.mjs +27 -0
  102. package/dist/types-CJrHMDV9.mjs.map +1 -0
  103. package/dist/types-ClbVc2rc.d.mts +117 -0
  104. package/dist/types-ClbVc2rc.d.mts.map +1 -0
  105. package/dist/types-D91N16Ym.d.mts +242 -0
  106. package/dist/types-D91N16Ym.d.mts.map +1 -0
  107. package/dist/types-DfLp_ibQ.d.mts +178 -0
  108. package/dist/types-DfLp_ibQ.d.mts.map +1 -0
  109. package/dist/types-DfjDgEoJ.d.mts +88 -0
  110. package/dist/types-DfjDgEoJ.d.mts.map +1 -0
  111. package/dist/types-Dp5Z9VBr.mjs +23 -0
  112. package/dist/types-Dp5Z9VBr.mjs.map +1 -0
  113. package/dist/types-hriBJTsU.d.mts +129 -0
  114. package/dist/types-hriBJTsU.d.mts.map +1 -0
  115. package/dist/types-rn6UuLL8.d.mts +184 -0
  116. package/dist/types-rn6UuLL8.d.mts.map +1 -0
  117. package/dist/whatsapp-CFp7ryR4.mjs +101 -0
  118. package/dist/whatsapp-CFp7ryR4.mjs.map +1 -0
  119. package/dist/youtube-Bs0fdY7H.mjs +98 -0
  120. package/dist/youtube-Bs0fdY7H.mjs.map +1 -0
  121. package/package.json +148 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tiktok.mjs","names":[],"sources":["../../src/providers/tiktok/index.ts"],"sourcesContent":["/**\n * TikTok Platform Provider\n * ========================\n * TikTok integration with OAuth2 and Content Posting API.\n *\n * Features:\n * - OAuth2 authorization flow (TikTok Login Kit)\n * - Video upload with 3-step process (init → binary PUT → poll status)\n * - Photo post support (via URL)\n * - Draft mode via SELF_ONLY privacy level\n * - Credential validation\n *\n * TikTok API Quirks:\n * - Uses `client_key` instead of `client_id`\n * - Token endpoint requires application/x-www-form-urlencoded\n * - Scopes are comma-separated (not space-separated)\n * - Token response includes `open_id` and `refresh_expires_in`\n * - No true draft API — SELF_ONLY privacy is the closest equivalent\n */\n\nimport {\n PlatformProvider,\n type AuthUrlOptions,\n type CredentialField,\n type ProviderMetadata,\n type UploadParams,\n type UploadResult,\n type TestResult,\n type AccountInfo,\n type OAuthTokens,\n type ProviderConfig,\n} from '../../base.js';\nimport { SocialError } from '../../errors.js';\nimport { pollUntilComplete } from '../../utils/polling.js';\nimport { PkceStore } from '../../common/oauth/index.js';\nimport { httpRequest } from '../../common/http.js';\nimport { TikTokCredentialsSchema } from '../../schemas/tiktok.js';\nimport type { z } from 'zod';\nimport { Readable } from 'stream';\nimport { createReadStream } from 'fs';\nimport { stat } from 'fs/promises';\nimport { randomBytes, createHash } from 'crypto';\nimport type {\n TikTokCredentials,\n TikTokCredentialData,\n TikTokUploadPhotoParams,\n TikTokPostInfo,\n TikTokPrivacy,\n TikTokVideo,\n TikTokAccountInfo,\n ListTikTokVideosOptions,\n ListTikTokVideosResult,\n TikTokPublishStatus,\n TikTokPublishStatusResponse,\n} from './types.js';\n\nexport type {\n TikTokCredentials,\n TikTokCredentialData,\n TikTokTokenData,\n TikTokUploadPhotoParams,\n TikTokPostInfo,\n TikTokPrivacy,\n TikTokPrivacyLevel,\n TikTokUploadSource,\n TikTokVideo,\n TikTokAccountInfo,\n ListTikTokVideosOptions,\n ListTikTokVideosResult,\n TikTokPublishStatus,\n TikTokPublishStatusResponse,\n} from './types.js';\n\n// ─── TikTok API Endpoints ───────────────────────────────────────────────────\n\nconst TIKTOK_AUTH_URL = 'https://www.tiktok.com/v2/auth/authorize';\nconst TIKTOK_TOKEN_URL = 'https://open.tiktokapis.com/v2/oauth/token/';\nconst TIKTOK_USER_INFO_URL = 'https://open.tiktokapis.com/v2/user/info/';\nconst TIKTOK_VIDEO_INIT_URL = 'https://open.tiktokapis.com/v2/post/publish/video/init/';\nconst TIKTOK_CONTENT_INIT_URL = 'https://open.tiktokapis.com/v2/post/publish/content/init/';\nconst TIKTOK_STATUS_URL = 'https://open.tiktokapis.com/v2/post/publish/status/fetch/';\nconst TIKTOK_VIDEO_LIST_URL = 'https://open.tiktokapis.com/v2/video/list/';\nconst TIKTOK_VIDEO_QUERY_URL = 'https://open.tiktokapis.com/v2/video/query/';\n\n// ─── Privacy Level Mapping ──────────────────────────────────────────────────\n\nconst PRIVACY_MAP: Record<string, string> = {\n public: 'PUBLIC_TO_EVERYONE',\n private: 'SELF_ONLY',\n unlisted: 'SELF_ONLY',\n followers: 'FOLLOWER_OF_CREATOR',\n friends: 'MUTUAL_FOLLOW_FRIENDS',\n};\n\n// ─── Video Query Fields ────────────────────────────────────────────────────\n\nconst VIDEO_QUERY_FIELDS = 'id,title,video_description,create_time,cover_image_url,share_url,duration,like_count,comment_count,share_count,view_count';\n\n// ─── PKCE Helpers ──────────────────────────────────────────────────────────\n\nfunction generateCodeVerifier(): string {\n return randomBytes(32).toString('base64url');\n}\n\nfunction generateCodeChallenge(verifier: string): string {\n return createHash('sha256').update(verifier).digest('base64url');\n}\n\n// ─── Provider ───────────────────────────────────────────────────────────────\n\nexport class TikTokProvider extends PlatformProvider {\n public defaultRedirectUri: string;\n public scopes: string[];\n /**\n * PKCE verifier store keyed by `state`. TTL'd to 10 minutes; entries are\n * single-use. For multi-instance deployments, persist verifiers alongside\n * `state` (session/Redis) and pass them back via `exchangeCode(code, creds, state)`.\n */\n private codeVerifiers = new PkceStore(10 * 60 * 1000);\n\n constructor(cfg: ProviderConfig = {}) {\n super(cfg);\n this.name = 'tiktok';\n this.displayName = 'TikTok';\n this.authType = 'oauth2';\n\n this.defaultRedirectUri =\n cfg.redirectUri ||\n `http://localhost:${cfg.port || 8060}/api/oauth/tiktok/callback`;\n\n // Default scopes for metadata display (full production scopes)\n // Actual scopes used in getAuthUrl() are determined per-request via options.environment\n // user.info.profile gives richer data than user.info.basic (username, bio, avatar_large_url, follower counts)\n this.scopes = ['user.info.profile', 'video.upload', 'video.publish'];\n }\n\n // ─── OAuth ──────────────────────────────────────────────────────────────\n\n /**\n * Get TikTok OAuth authorization URL\n * Quirk: uses `client_key` (not `client_id`), comma-separated scopes\n *\n * @param options.environment - Both sandbox and production request the same scopes.\n * Sandbox apps with \"Direct Post\" enabled support video.publish for test users.\n */\n getAuthUrl(state: string, credentials: Record<string, any> = {}, options?: AuthUrlOptions): string {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = generateCodeChallenge(codeVerifier);\n this.codeVerifiers.set(state, codeVerifier);\n\n const scopes = ['user.info.profile', 'video.upload', 'video.publish'];\n\n const params = new URLSearchParams({\n client_key: credentials.clientKey?.trim(),\n redirect_uri: credentials.redirectUri || this.defaultRedirectUri,\n scope: scopes.join(','),\n response_type: 'code',\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n });\n\n return `${TIKTOK_AUTH_URL}?${params.toString()}`;\n }\n\n /**\n * Exchange authorization code for tokens\n * Quirk: form-urlencoded body with client_key/client_secret (not Basic auth)\n */\n async exchangeCode(code: string, credentials: Record<string, any> = {}, state?: string): Promise<OAuthTokens> {\n const bodyParams: Record<string, string> = {\n client_key: credentials.clientKey?.trim(),\n client_secret: credentials.clientSecret?.trim(),\n code,\n grant_type: 'authorization_code',\n redirect_uri: credentials.redirectUri || this.defaultRedirectUri,\n };\n\n // Include PKCE code_verifier if available (single-use, removes after read).\n if (state) {\n const verifier = this.codeVerifiers.take(state);\n if (verifier) bodyParams.code_verifier = verifier;\n }\n\n const { data } = await httpRequest<any>('tiktok', {\n method: 'POST',\n url: TIKTOK_TOKEN_URL,\n urlencoded: bodyParams,\n timeout: 30_000,\n retry: { attempts: 1 },\n parseError: (raw) => {\n if (raw && typeof raw === 'object') {\n const r = raw as Record<string, unknown>;\n const message = (r.error_description as string) || (r.error as string) || 'TikTok token exchange failed';\n const isScopeError = String(message).toLowerCase().includes('scope');\n return {\n message,\n errorCode: (r.error as string) ?? null,\n hint: isScopeError\n ? 'Your TikTok app may not have the required scopes approved. Try connecting with Sandbox environment, or submit your app for TikTok audit to enable video.publish.'\n : null,\n };\n }\n return null;\n },\n });\n\n if (data.error || !data.access_token) {\n const message = data.error_description || data.error || 'TikTok token exchange failed';\n const isScopeError = String(message).toLowerCase().includes('scope');\n throw new SocialError('tiktok', message, {\n statusCode: 401,\n hint: isScopeError\n ? 'Your TikTok app may not have the required scopes approved. Try connecting with Sandbox environment, or submit your app for TikTok audit to enable video.publish.'\n : undefined,\n });\n }\n\n return {\n access_token: data.access_token,\n refresh_token: data.refresh_token,\n expires_in: data.expires_in,\n open_id: data.open_id,\n scope: data.scope,\n token_type: data.token_type,\n refresh_expires_in: data.refresh_expires_in,\n };\n }\n\n /**\n * Refresh access token\n */\n async refreshToken(refreshToken: string, credentials: Record<string, any> = {}): Promise<OAuthTokens> {\n const { data } = await httpRequest<any>('tiktok', {\n method: 'POST',\n url: TIKTOK_TOKEN_URL,\n urlencoded: {\n client_key: credentials.clientKey,\n client_secret: credentials.clientSecret,\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n },\n timeout: 30_000,\n retry: { attempts: 2 },\n });\n\n if (data.error || !data.access_token) {\n throw new SocialError('tiktok', data.error_description || data.error || 'TikTok token refresh failed', { statusCode: 401 });\n }\n\n return {\n access_token: data.access_token,\n refresh_token: data.refresh_token,\n expires_in: data.expires_in,\n open_id: data.open_id,\n scope: data.scope,\n token_type: data.token_type,\n refresh_expires_in: data.refresh_expires_in,\n };\n }\n\n // ─── Token Revocation ──────────────────────────────────────────────────\n\n /**\n * Revoke TikTok access token.\n * Forces fresh consent screen on next authorization — ensures newly\n * enabled scopes (e.g. video.publish after enabling Direct Post) are granted.\n */\n async revokeToken(accessToken: string, credData?: Record<string, any>): Promise<void> {\n if (!accessToken) {\n throw new SocialError('tiktok', 'Access token is required to revoke', { statusCode: 400 });\n }\n if (!credData?.clientKey || !credData?.clientSecret) {\n throw new SocialError('tiktok', 'clientKey and clientSecret are required to revoke', { statusCode: 400 });\n }\n\n await httpRequest('tiktok', {\n method: 'POST',\n url: 'https://open.tiktokapis.com/v2/oauth/revoke/',\n urlencoded: {\n client_key: credData.clientKey,\n client_secret: credData.clientSecret,\n token: accessToken,\n },\n timeout: 15_000,\n });\n }\n\n // ─── Account Info ───────────────────────────────────────────────────────\n\n /**\n * Get TikTok user profile\n * Quirk: requires explicit `fields` query param\n */\n async getAccountInfo(accessToken: string): Promise<AccountInfo> {\n // user.info.profile scope unlocks richer fields than user.info.basic\n const fields = 'open_id,avatar_large_url,avatar_url,display_name,username,bio_description';\n\n const { data: result } = await httpRequest<any>('tiktok', {\n method: 'GET',\n url: TIKTOK_USER_INFO_URL,\n query: { fields },\n bearer: accessToken,\n timeout: 30_000,\n retry: { attempts: 2 },\n });\n\n if (result.error?.code && result.error.code !== 'ok') {\n throw new SocialError('tiktok', result.error?.message || 'Failed to get TikTok account info');\n }\n\n const user = result.data?.user;\n if (!user) {\n throw new SocialError('tiktok', 'No user data returned from TikTok');\n }\n\n return {\n id: user.open_id,\n name: user.display_name || user.username,\n username: user.username,\n profileImage: user.avatar_large_url || user.avatar_url,\n bio: user.bio_description,\n };\n }\n\n /**\n * Test credential validity\n */\n async testCredential(credentialData: Record<string, any>): Promise<TestResult> {\n try {\n if (credentialData.oauthTokenData) {\n const tokenData = typeof credentialData.oauthTokenData === 'string'\n ? JSON.parse(credentialData.oauthTokenData)\n : credentialData.oauthTokenData;\n const accountInfo = await this.getAccountInfo(tokenData.access_token);\n\n return {\n status: 'OK',\n message: 'TikTok credential is valid',\n data: {\n channelId: accountInfo.id,\n channelTitle: accountInfo.name,\n profileImage: accountInfo.profileImage,\n },\n };\n }\n\n if (!credentialData.clientKey || !credentialData.clientSecret) {\n return {\n status: 'Error',\n message: 'TikTok credentials not configured — clientKey and clientSecret are required',\n };\n }\n\n return {\n status: 'Pending' as TestResult['status'],\n message: 'Credential needs OAuth authorization. Click \"Connect Account\" to link your TikTok.',\n };\n } catch (error: any) {\n return {\n status: 'Error',\n message: error.message || 'Failed to validate TikTok credential',\n };\n }\n }\n\n // ─── Video Upload ─────────────────────────────────────────────────────\n\n /**\n * Upload video to TikTok\n *\n * Smart mode selection:\n * - videoUrl → tries PULL_FROM_URL first (zero server load, TikTok fetches directly)\n * → falls back to FILE_UPLOAD if domain not verified (url_ownership_unverified)\n * - filePath → always FILE_UPLOAD (local file, no URL to pull from)\n *\n * FILE_UPLOAD uses stream-through: fetch(url) → pipe to TikTok (no server buffering).\n */\n async uploadVideo(params: UploadParams): Promise<UploadResult> {\n const {\n filePath,\n videoUrl,\n title = '',\n description = '',\n privacy = 'private',\n tokens,\n scheduledAt,\n onProgress,\n } = params;\n\n if (!filePath && !videoUrl) {\n throw new SocialError('tiktok', 'uploadVideo requires either filePath or videoUrl', { statusCode: 400 });\n }\n\n const accessToken = tokens.access_token;\n const privacyLevel = PRIVACY_MAP[privacy] || 'SELF_ONLY';\n\n // TikTok Content Posting API does not support scheduling.\n // schedule_time was silently ignored by the API. Throw early to prevent confusion.\n if (scheduledAt) {\n throw new SocialError('tiktok', 'Scheduling is not supported by TikTok Content Posting API. Post immediately or use TikTok Business API (requires separate approval).', { statusCode: 400, retryable: false });\n }\n\n // Build post_info (shared between all modes)\n const postInfo: TikTokPostInfo = {\n title: (title || description || '').substring(0, 2200),\n privacy_level: privacyLevel,\n disable_duet: false,\n disable_stitch: false,\n disable_comment: false,\n video_cover_timestamp_ms: 1000,\n };\n\n\n // ── Try PULL_FROM_URL first for remote URLs (zero server load) ──\n if (videoUrl && !filePath) {\n const pullResult = await this._tryPullFromUrl(accessToken, videoUrl, postInfo, onProgress);\n if (pullResult) {\n return {\n ...pullResult,\n status: scheduledAt ? 'scheduled' : (privacyLevel === 'SELF_ONLY' ? 'draft' : 'published'),\n uploadedAt: new Date(),\n scheduledAt: scheduledAt ? new Date(scheduledAt) : null,\n metadata: {\n title,\n privacy: privacyLevel,\n uploadMode: 'PULL_FROM_URL',\n publishId: pullResult.platformVideoId,\n ...(pullResult.metadata || {}),\n },\n } as UploadResult;\n }\n // PULL_FROM_URL failed (domain not verified) — fall through to FILE_UPLOAD\n }\n\n // ── FILE_UPLOAD (fallback for videoUrl, or primary for filePath) ──\n return this._uploadViaFile(accessToken, postInfo, {\n filePath,\n videoUrl,\n title,\n privacy: privacyLevel,\n scheduledAt,\n onProgress,\n });\n }\n\n /**\n * Attempt PULL_FROM_URL upload. Returns result on success, null if domain not verified.\n * Throws on any other error.\n */\n private async _tryPullFromUrl(\n accessToken: string,\n videoUrl: string,\n postInfo: TikTokPostInfo,\n onProgress?: (pct: number) => void,\n ): Promise<UploadResult | null> {\n const initBody = {\n post_info: postInfo,\n source_info: {\n source: 'PULL_FROM_URL',\n video_url: videoUrl,\n },\n };\n\n const initResponse = await fetch(TIKTOK_VIDEO_INIT_URL, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json; charset=UTF-8',\n },\n body: JSON.stringify(initBody),\n });\n\n const initData: any = await initResponse.json();\n const errorCode = initData.error?.code;\n\n // Domain not verified — signal caller to fall back to FILE_UPLOAD\n if (errorCode === 'url_ownership_unverified') {\n return null;\n }\n\n if (errorCode && errorCode !== 'ok') {\n const hint = errorCode === 'unaudited_client_can_only_post_to_private_accounts'\n ? 'Sandbox apps require the TikTok account to be set to Private. Go to TikTok > Settings > Privacy > Private Account. Or submit your app for TikTok audit to remove this restriction.'\n : undefined;\n throw new SocialError('tiktok', initData.error?.message || `PULL_FROM_URL init failed (${errorCode})`, { errorCode, hint });\n }\n\n const publish_id = initData.data.publish_id;\n\n if (onProgress) onProgress(10);\n\n // PULL_FROM_URL is async — TikTok fetches the video in background, poll for completion\n const publishResult = await this._pollPublishStatus(accessToken, publish_id);\n\n if (onProgress) onProgress(100);\n\n return {\n platformVideoId: publish_id,\n platformUrl: null as unknown as string,\n metadata: publishResult || {},\n } as UploadResult;\n }\n\n /**\n * FILE_UPLOAD with stream-through (fetch URL → pipe to TikTok, or local file → stream).\n */\n private async _uploadViaFile(\n accessToken: string,\n postInfo: TikTokPostInfo,\n opts: {\n filePath?: string;\n videoUrl?: string;\n title: string;\n privacy: string;\n scheduledAt?: string | Date | null;\n onProgress?: (pct: number) => void;\n },\n ): Promise<UploadResult> {\n const { filePath, videoUrl, title, privacy, scheduledAt, onProgress } = opts;\n\n // Resolve video source\n let videoStream: NodeJS.ReadableStream;\n let videoSize: number;\n\n if (filePath) {\n const fileStats = await stat(filePath);\n videoSize = fileStats.size;\n videoStream = createReadStream(filePath);\n } else {\n const fetchResponse = await fetch(videoUrl!);\n if (!fetchResponse.ok) {\n throw new SocialError('tiktok', `Failed to fetch video from URL: ${fetchResponse.status} ${fetchResponse.statusText}`, {\n statusCode: 502,\n hint: 'Ensure the video URL is publicly accessible',\n });\n }\n const contentLength = fetchResponse.headers.get('content-length');\n if (!contentLength) {\n throw new SocialError('tiktok', 'Video URL did not return Content-Length header (required for TikTok FILE_UPLOAD)', {\n statusCode: 502,\n hint: 'GCS and S3 always return Content-Length — ensure the URL points to cloud storage',\n });\n }\n videoSize = parseInt(contentLength, 10);\n videoStream = Readable.fromWeb(fetchResponse.body as any);\n }\n\n // FILE_UPLOAD init\n const initBody = {\n post_info: postInfo,\n source_info: {\n source: 'FILE_UPLOAD',\n video_size: videoSize,\n chunk_size: videoSize,\n total_chunk_count: 1,\n },\n };\n\n const initResponse = await fetch(TIKTOK_VIDEO_INIT_URL, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json; charset=UTF-8',\n },\n body: JSON.stringify(initBody),\n });\n\n const initData: any = await initResponse.json();\n\n if (initData.error?.code && initData.error.code !== 'ok') {\n throw new SocialError('tiktok', initData.error?.message || `Upload init failed (${initData.error?.code})`, { errorCode: initData.error?.code });\n }\n\n const publish_id = initData.data.publish_id;\n const { upload_url } = initData.data;\n\n if (onProgress) onProgress(10);\n\n // Binary PUT upload\n const uploadResponse = await fetch(upload_url, {\n method: 'PUT',\n headers: {\n 'Content-Type': 'video/mp4',\n 'Content-Length': String(videoSize),\n 'Content-Range': `bytes 0-${videoSize - 1}/${videoSize}`,\n },\n body: videoStream as any,\n duplex: 'half',\n } as any);\n\n if (!uploadResponse.ok) {\n const errText = await uploadResponse.text().catch(() => '');\n throw new SocialError('tiktok', `Video upload failed (HTTP ${uploadResponse.status}): ${errText}`, { statusCode: uploadResponse.status });\n }\n\n if (onProgress) onProgress(80);\n\n const publishResult = await this._pollPublishStatus(accessToken, publish_id);\n\n if (onProgress) onProgress(100);\n\n return {\n platformVideoId: publish_id,\n platformUrl: null as unknown as string,\n status: scheduledAt ? 'scheduled' : (privacy === 'SELF_ONLY' ? 'draft' : 'published'),\n uploadedAt: new Date(),\n scheduledAt: scheduledAt ? new Date(scheduledAt as string) : null,\n metadata: {\n title,\n privacy,\n uploadMode: 'FILE_UPLOAD',\n publishId: publish_id,\n ...(publishResult || {}),\n },\n } as UploadResult;\n }\n\n // ─── Photo Post ───────────────────────────────────────────────────────\n\n /**\n * Create a photo post on TikTok\n * TikTok-specific — not in the base class.\n *\n * Restrictions:\n * - Only JPG/JPEG/WEBP (no PNG)\n * - Max 35 images per post\n * - Images must be publicly accessible URLs\n *\n * @param params\n * @param params.imageUrls - Array of public image URLs\n * @param params.title - Post title (max 150 chars)\n * @param params.privacy - 'public' | 'private' | 'followers' | 'friends'\n * @param params.tokens - { access_token }\n */\n async uploadPhoto(params: TikTokUploadPhotoParams): Promise<UploadResult> {\n const {\n imageUrls,\n title = '',\n privacy = 'private',\n tokens,\n } = params;\n\n if (!imageUrls || imageUrls.length === 0) {\n throw new SocialError('tiktok', 'Photo post requires at least one image URL', { statusCode: 400 });\n }\n\n // Validate image formats — TikTok only supports JPG/JPEG/WEBP\n const validFormats = ['jpg', 'jpeg', 'webp'];\n for (const url of imageUrls) {\n const ext = url.split('?')[0].split('.').pop()?.toLowerCase();\n if (ext && !validFormats.includes(ext)) {\n throw new SocialError('tiktok', `Unsupported image format .${ext} — only JPG, JPEG, WEBP are allowed`, { statusCode: 400 });\n }\n }\n\n const accessToken = tokens.access_token;\n const privacyLevel = PRIVACY_MAP[privacy] || 'SELF_ONLY';\n\n const body = {\n post_info: {\n title: (title || '').substring(0, 2200),\n privacy_level: privacyLevel,\n disable_comment: false,\n },\n source_info: {\n source: 'PULL_FROM_URL',\n photo_cover_index: 0,\n photo_images: imageUrls.slice(0, 35),\n },\n media_type: 'PHOTO',\n };\n\n const response = await fetch(TIKTOK_CONTENT_INIT_URL, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json; charset=UTF-8',\n },\n body: JSON.stringify(body),\n });\n\n const data: any = await response.json();\n\n if (data.error?.code && data.error.code !== 'ok') {\n throw new SocialError('tiktok', data.error?.message || `Photo post failed (${data.error?.code})`, { errorCode: data.error?.code });\n }\n\n const publish_id: string = data.data?.publish_id;\n\n // Poll publish status (same as video — TikTok processes photos async)\n await this._pollPublishStatus(accessToken, publish_id);\n\n return {\n platformVideoId: publish_id,\n platformUrl: null as unknown as string,\n status: privacyLevel === 'SELF_ONLY' ? 'draft' : 'published',\n };\n }\n\n // ─── Read Primitives ─────────────────────────────────────────────────\n\n /**\n * List videos for the authenticated user (cursor-based pagination).\n * Requires scope: `video.list`\n *\n * @param accessToken - OAuth access token\n * @param options.maxCount - Number of videos per page (1-20, default 20)\n * @param options.cursor - Pagination cursor from a previous response\n */\n async listVideos(accessToken: string, options?: ListTikTokVideosOptions): Promise<ListTikTokVideosResult> {\n const maxCount = Math.min(Math.max(options?.maxCount ?? 20, 1), 20);\n const url = `${TIKTOK_VIDEO_LIST_URL}?fields=${VIDEO_QUERY_FIELDS}`;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n max_count: maxCount,\n ...(options?.cursor != null ? { cursor: options.cursor } : {}),\n }),\n });\n\n const result: any = await response.json();\n\n if (result.error?.code && result.error.code !== 'ok') {\n throw new SocialError('tiktok', result.error?.message || 'Failed to list TikTok videos', {\n statusCode: response.status >= 400 ? response.status : 502,\n errorCode: result.error.code,\n });\n }\n\n const videos = (result.data?.videos || []).map((v: any) => this._parseVideoItem(v));\n\n return {\n videos,\n cursor: result.data?.cursor ?? null,\n hasMore: result.data?.has_more ?? false,\n };\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('tiktok', error.message || 'Failed to list TikTok videos', { originalError: error });\n }\n }\n\n /**\n * Query specific videos by their IDs.\n *\n * @param accessToken - OAuth access token\n * @param videoIds - Array of TikTok video IDs to fetch\n */\n async getVideos(accessToken: string, videoIds: string[]): Promise<TikTokVideo[]> {\n if (!videoIds.length) return [];\n\n const url = `${TIKTOK_VIDEO_QUERY_URL}?fields=${VIDEO_QUERY_FIELDS}`;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n filters: { video_ids: videoIds },\n }),\n });\n\n const result: any = await response.json();\n\n if (result.error?.code && result.error.code !== 'ok') {\n throw new SocialError('tiktok', result.error?.message || 'Failed to query TikTok videos', {\n statusCode: response.status >= 400 ? response.status : 502,\n errorCode: result.error.code,\n });\n }\n\n const videos = result.data?.videos;\n if (!videos) {\n throw new SocialError('tiktok', 'No video data returned from TikTok', { statusCode: 404 });\n }\n\n return videos.map((v: any) => this._parseVideoItem(v));\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('tiktok', error.message || 'Failed to query TikTok videos', { originalError: error });\n }\n }\n\n // ─── Schema & Metadata ────────────────────────────────────────────────\n\n getCredentialZodSchema(): z.ZodType {\n return TikTokCredentialsSchema;\n }\n\n getCredentialSchema(): CredentialField[] {\n return [\n {\n name: 'clientKey',\n displayName: 'Client Key',\n type: 'text',\n required: true,\n description: 'TikTok App Client Key from TikTok Developer Portal',\n },\n {\n name: 'clientSecret',\n displayName: 'Client Secret',\n type: 'password',\n required: true,\n description: 'TikTok App Client Secret',\n },\n ];\n }\n\n getMetadata(): ProviderMetadata {\n return {\n name: this.name,\n displayName: this.displayName,\n authType: this.authType,\n icon: 'tiktok',\n brandColor: '#000000',\n supportsScheduling: true,\n supportsEnvironment: true,\n description: 'Share short-form videos on TikTok',\n scopes: this.scopes,\n scopeDescriptions: {\n 'user.info.profile': 'View your TikTok profile info (display name, username, avatar, bio)',\n 'video.upload': 'Upload videos to your TikTok account',\n 'video.publish': 'Publish videos directly (requires TikTok audit approval)',\n },\n setupGuide: [\n {\n step: 1,\n title: 'Create TikTok Developer Account',\n description: 'Go to developers.tiktok.com and sign up or log in with your TikTok account',\n },\n {\n step: 2,\n title: 'Create an App',\n description: 'Click \"Manage apps\" → \"Connect an app\". Fill in the app name and description',\n },\n {\n step: 3,\n title: 'Add Login Kit',\n description: 'In your app, go to \"Add products\" and enable \"Login Kit\". Set the redirect URI shown below',\n },\n {\n step: 4,\n title: 'Add Content Posting API',\n description: 'Enable \"Content Posting API\" product to allow video and photo uploads',\n },\n {\n step: 5,\n title: 'Set Redirect URI',\n description: `Add this as a redirect URI in your TikTok app settings: ${this.defaultRedirectUri}`,\n },\n {\n step: 6,\n title: 'Copy Credentials',\n description: 'Copy your Client Key and Client Secret from the app dashboard and paste them above',\n },\n {\n step: 7,\n title: 'Unaudited App Limitations',\n description: 'Before TikTok audit approval: posts are limited to SELF_ONLY (private/draft) privacy, max 5 users per day, and all test users must have private TikTok profiles',\n },\n ],\n redirectUriPattern: this.defaultRedirectUri,\n credentialSchema: this.getCredentialSchema(),\n };\n }\n\n // ─── Private Helpers ──────────────────────────────────────────────────\n\n /**\n * Parse a raw TikTok video API item into a normalized TikTokVideo object.\n * @private\n */\n private _parseVideoItem(item: any): TikTokVideo {\n return {\n id: item.id,\n title: item.title || '',\n description: item.video_description || '',\n createTime: item.create_time ?? 0,\n coverImageUrl: item.cover_image_url ?? null,\n shareUrl: item.share_url ?? null,\n duration: item.duration ?? null,\n viewCount: item.view_count ?? null,\n likeCount: item.like_count ?? null,\n commentCount: item.comment_count ?? null,\n shareCount: item.share_count ?? null,\n };\n }\n\n /**\n * Poll TikTok publish status until complete or failed\n * @private\n */\n async _pollPublishStatus(accessToken: string, publishId: string, maxAttempts: number = 15, intervalMs: number = 5000): Promise<any> {\n const result = await pollUntilComplete<any>({\n fn: async () => {\n const response = await fetch(TIKTOK_STATUS_URL, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ publish_id: publishId }),\n });\n return response.json();\n },\n isComplete: (data: any) => data.data?.status === 'PUBLISH_COMPLETE',\n getError: (data: any) => {\n if (data.data?.status === 'FAILED')\n return new SocialError('tiktok', `Publish failed: ${data.data?.fail_reason || 'Unknown reason'}`);\n return null;\n },\n maxAttempts,\n intervalMs,\n label: 'TikTokProvider',\n });\n return result?.data || null;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,150 @@
1
+ import { d as ProviderMetadata, g as UploadResult, i as CredentialField, l as ProviderConfig, o as OAuthTokens, p as TestResult, r as AuthUrlOptions, s as PlatformProvider, t as AccountInfo } from "../base-DBtKFiSX.mjs";
2
+ import { a as Tweet, c as TweetSearchResult, d as UserField, i as SendDmResult, l as TwitterCredentials, n as CreateTweetResult, o as TweetField, r as SendDmParams, s as TweetSearchParams, t as CreateTweetParams, u as TwitterUser } from "../types-DfLp_ibQ.mjs";
3
+ import { z } from "zod";
4
+
5
+ //#region src/providers/twitter/index.d.ts
6
+ declare class TwitterProvider extends PlatformProvider {
7
+ constructor(config?: ProviderConfig);
8
+ getAuthUrl(state: string, credData?: Record<string, any>, options?: AuthUrlOptions): string;
9
+ /**
10
+ * Async auth URL generation with proper S256 PKCE challenge.
11
+ * Prefer this over getAuthUrl() when async is acceptable.
12
+ */
13
+ getAuthUrlAsync(state: string, credData?: Record<string, any>): Promise<string>;
14
+ exchangeCode(code: string, credData?: Record<string, any>, state?: string): Promise<OAuthTokens>;
15
+ refreshToken(refreshToken: string, credData?: Record<string, any>): Promise<OAuthTokens>;
16
+ revokeToken(accessToken: string, credData?: Record<string, any>): Promise<void>;
17
+ getAccountInfo(accessToken: string): Promise<AccountInfo>;
18
+ testCredential(credentialData: Record<string, any>): Promise<TestResult>;
19
+ /**
20
+ * Create a tweet (or reply / quote tweet).
21
+ */
22
+ createTweet(accessToken: string, params: CreateTweetParams): Promise<CreateTweetResult>;
23
+ /**
24
+ * Delete a tweet by ID.
25
+ */
26
+ deleteTweet(accessToken: string, tweetId: string): Promise<{
27
+ deleted: boolean;
28
+ }>;
29
+ /**
30
+ * Get a single tweet by ID.
31
+ */
32
+ getTweet(accessToken: string, tweetId: string, fields?: TweetField[]): Promise<Tweet>;
33
+ /**
34
+ * Search recent tweets (last 7 days on Basic tier).
35
+ */
36
+ searchTweets(accessToken: string, params: TweetSearchParams): Promise<TweetSearchResult>;
37
+ /**
38
+ * Like a tweet.
39
+ */
40
+ likeTweet(accessToken: string, tweetId: string): Promise<{
41
+ liked: boolean;
42
+ }>;
43
+ /**
44
+ * Unlike a tweet.
45
+ */
46
+ unlikeTweet(accessToken: string, tweetId: string): Promise<{
47
+ liked: boolean;
48
+ }>;
49
+ /**
50
+ * Retweet a tweet.
51
+ */
52
+ retweet(accessToken: string, tweetId: string): Promise<{
53
+ retweeted: boolean;
54
+ }>;
55
+ /**
56
+ * Remove a retweet.
57
+ */
58
+ unretweet(accessToken: string, tweetId: string): Promise<{
59
+ retweeted: boolean;
60
+ }>;
61
+ /**
62
+ * Bookmark a tweet.
63
+ */
64
+ bookmarkTweet(accessToken: string, tweetId: string): Promise<{
65
+ bookmarked: boolean;
66
+ }>;
67
+ /**
68
+ * Remove a bookmark.
69
+ */
70
+ removeBookmark(accessToken: string, tweetId: string): Promise<{
71
+ bookmarked: boolean;
72
+ }>;
73
+ /**
74
+ * Follow a user.
75
+ */
76
+ followUser(accessToken: string, targetUserId: string): Promise<{
77
+ following: boolean;
78
+ pendingFollow: boolean;
79
+ }>;
80
+ /**
81
+ * Unfollow a user.
82
+ */
83
+ unfollowUser(accessToken: string, targetUserId: string): Promise<{
84
+ following: boolean;
85
+ }>;
86
+ /**
87
+ * Get followers of a user.
88
+ */
89
+ getFollowers(accessToken: string, userId: string, opts?: {
90
+ maxResults?: number;
91
+ paginationToken?: string;
92
+ userFields?: UserField[];
93
+ }): Promise<{
94
+ users: TwitterUser[];
95
+ nextToken?: string;
96
+ }>;
97
+ /**
98
+ * Get users that a user is following.
99
+ */
100
+ getFollowing(accessToken: string, userId: string, opts?: {
101
+ maxResults?: number;
102
+ paginationToken?: string;
103
+ userFields?: UserField[];
104
+ }): Promise<{
105
+ users: TwitterUser[];
106
+ nextToken?: string;
107
+ }>;
108
+ /**
109
+ * Get a user by username.
110
+ */
111
+ getUserByUsername(accessToken: string, username: string, fields?: UserField[]): Promise<TwitterUser>;
112
+ /**
113
+ * Get a user by ID.
114
+ */
115
+ getUserById(accessToken: string, userId: string, fields?: UserField[]): Promise<TwitterUser>;
116
+ /**
117
+ * Send a direct message to a user.
118
+ */
119
+ sendDirectMessage(accessToken: string, params: SendDmParams): Promise<SendDmResult>;
120
+ /**
121
+ * Mute a user.
122
+ */
123
+ muteUser(accessToken: string, targetUserId: string): Promise<{
124
+ muting: boolean;
125
+ }>;
126
+ /**
127
+ * Unmute a user.
128
+ */
129
+ unmuteUser(accessToken: string, targetUserId: string): Promise<{
130
+ muting: boolean;
131
+ }>;
132
+ uploadPhoto(params: any): Promise<UploadResult>;
133
+ deletePost(accessToken: string, tweetId: string): Promise<unknown>;
134
+ sendMessage(accessToken: string, participantId: string, text: string): Promise<unknown>;
135
+ getCredentialZodSchema(): z.ZodType;
136
+ getCredentialSchema(): CredentialField[];
137
+ getMetadata(): ProviderMetadata;
138
+ /** Cached user ID for the authenticated user (per-request) */
139
+ private _myIdCache;
140
+ private _getMyId;
141
+ /**
142
+ * Core API request helper.
143
+ */
144
+ private _api;
145
+ private _parseTweet;
146
+ private _parseUser;
147
+ }
148
+ //#endregion
149
+ export { type CreateTweetParams, type CreateTweetResult, type SendDmParams, type SendDmResult, type Tweet, type TweetSearchParams, type TweetSearchResult, type TwitterCredentials, TwitterProvider, type TwitterUser };
150
+ //# sourceMappingURL=twitter.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twitter.d.mts","names":[],"sources":["../../src/providers/twitter/index.ts"],"mappings":"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"}