@tiktool/live 2.6.2 → 2.6.4
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.
- package/README.md +68 -5
- package/dist/index.d.mts +150 -1
- package/dist/index.d.ts +150 -1
- package/dist/index.js +281 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +279 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/proto.ts","../src/captions.ts","../src/api.ts"],"sourcesContent":["export { TikTokLive } from './client.js';\r\nexport { TikTokCaptions } from './captions.js';\r\nexport { getRanklist, getRegionalRanklist } from './api.js';\r\nexport type { GetRanklistOptions, GetRegionalRanklistOptions, RegionalRanklistSignedResponse } from './api.js';\r\nexport type {\r\n TikTokCaptionsOptions,\r\n TikTokCaptionsEvents,\r\n CaptionData,\r\n TranslationData,\r\n CaptionCredits,\r\n CaptionStatus,\r\n CaptionError,\r\n} from './captions.js';\r\n\r\nexport type {\r\n TikTokLiveOptions,\r\n TikTokLiveEvents,\r\n RoomInfo,\r\n LiveEvent,\r\n BaseEvent,\r\n ChatEvent,\r\n MemberEvent,\r\n LikeEvent,\r\n GiftEvent,\r\n SocialEvent,\r\n RoomUserSeqEvent,\r\n BattleEvent,\r\n BattleArmiesEvent,\r\n BattleTeam,\r\n BattleTeamUser,\r\n BattleTaskEvent,\r\n BarrageEvent,\r\n SubscribeEvent,\r\n EmoteChatEvent,\r\n EnvelopeEvent,\r\n QuestionEvent,\r\n ControlEvent,\r\n RoomEvent,\r\n LiveIntroEvent,\r\n RankUpdateEvent,\r\n LinkMicEvent,\r\n UnknownEvent,\r\n TikTokUser,\r\n RanklistUser,\r\n OnlineAudienceEntry,\r\n AnchorRankListEntry,\r\n RanklistSelfInfo,\r\n OnlineAudienceResponse,\r\n AnchorRankListResponse,\r\n EntranceTab,\r\n EntranceInfo,\r\n EntranceResponse,\r\n RanklistResponse,\r\n} from './types.js';\r\n","import { EventEmitter } from 'events';\r\nimport * as http from 'http';\r\nimport * as https from 'https';\r\nimport * as zlib from 'zlib';\r\nimport { URL } from 'url';\r\nimport WebSocket from 'ws';\r\nimport type { TikTokLiveOptions, TikTokLiveEvents, RoomInfo, LiveEvent } from './types.js';\r\nimport {\r\n decodeProto,\r\n getStr,\r\n getBytes,\r\n buildHeartbeat,\r\n buildImEnterRoom,\r\n buildAck,\r\n parseWebcastResponse,\r\n} from './proto.js';\r\n\r\nconst DEFAULT_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';\r\nconst DEFAULT_SIGN_SERVER = 'https://api.tik.tools';\r\n\r\nfunction httpGet(url: string, headers: Record<string, string>, agent?: http.Agent): Promise<{\r\n status: number;\r\n headers: http.IncomingHttpHeaders;\r\n body: Buffer;\r\n}> {\r\n return new Promise((resolve, reject) => {\r\n const mod = url.startsWith('https') ? https : http;\r\n const opts: https.RequestOptions = { headers, agent };\r\n const parsed = new URL(url);\r\n const req = mod.get(parsed, opts, (res) => {\r\n const chunks: Buffer[] = [];\r\n const enc = res.headers['content-encoding'];\r\n const stream = (enc === 'gzip' || enc === 'br')\r\n ? res.pipe(enc === 'br' ? zlib.createBrotliDecompress() : zlib.createGunzip())\r\n : res;\r\n stream.on('data', (c: Buffer) => chunks.push(c));\r\n stream.on('end', () => resolve({\r\n status: res.statusCode || 0,\r\n headers: res.headers,\r\n body: Buffer.concat(chunks),\r\n }));\r\n stream.on('error', reject);\r\n });\r\n req.on('error', reject);\r\n req.setTimeout(15_000, () => { req.destroy(); reject(new Error('Request timeout')); });\r\n });\r\n}\r\n\r\nfunction getWsHost(clusterRegion: string): string {\r\n if (!clusterRegion) return 'webcast-ws.tiktok.com';\r\n const r = clusterRegion.toLowerCase();\r\n if (r.startsWith('eu') || r.includes('eu')) return 'webcast-ws.eu.tiktok.com';\r\n if (r.startsWith('us') || r.includes('us')) return 'webcast-ws.us.tiktok.com';\r\n return 'webcast-ws.tiktok.com';\r\n}\r\n\r\nexport class TikTokLive extends EventEmitter {\r\n private ws: WebSocket | null = null;\r\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\r\n private reconnectAttempts = 0;\r\n private intentionalClose = false;\r\n private _connected = false;\r\n private _eventCount = 0;\r\n private _roomId = '';\r\n private _ownerUserId = '';\r\n\r\n private readonly uniqueId: string;\r\n private readonly signServerUrl: string;\r\n private readonly apiKey: string;\r\n private readonly autoReconnect: boolean;\r\n private readonly maxReconnectAttempts: number;\r\n private readonly heartbeatInterval: number;\r\n private readonly debug: boolean;\r\n private _sessionId?: string;\r\n private _ttTargetIdc?: string;\r\n private readonly proxyAgent?: http.Agent;\r\n private readonly _presetRoomId?: string;\r\n private readonly _presetTtwid?: string;\r\n\r\n constructor(options: TikTokLiveOptions) {\r\n super();\r\n this.uniqueId = options.uniqueId.replace(/^@/, '');\r\n this.signServerUrl = (options.signServerUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n if (!options.apiKey) throw new Error('apiKey is required. Get a free key at https://tik.tools');\r\n this.apiKey = options.apiKey;\r\n this.autoReconnect = options.autoReconnect ?? true;\r\n this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;\r\n this.heartbeatInterval = options.heartbeatInterval ?? 10_000;\r\n this.debug = options.debug ?? false;\r\n this._sessionId = options.sessionId;\r\n this._ttTargetIdc = options.ttTargetIdc;\r\n if (options.agent) {\r\n this.proxyAgent = options.agent;\r\n }\r\n this._presetRoomId = options.roomId;\r\n this._presetTtwid = options.ttwid;\r\n }\r\n\r\n async connect(): Promise<void> {\r\n this.intentionalClose = false;\r\n\r\n let ttwid = this._presetTtwid || '';\r\n let roomId = this._presetRoomId || '';\r\n let ownerUserId = '';\r\n let clusterRegion = '';\r\n\r\n // If roomId AND ttwid are preset, skip the HTTP fetch entirely\r\n // If roomId is NOT preset, fetch the live page to discover it + get ttwid\r\n if (!roomId || !ttwid) {\r\n const targetUrl = `https://www.tiktok.com/@${this.uniqueId}/live`;\r\n const resp = await httpGet(targetUrl, {\r\n 'User-Agent': DEFAULT_UA,\r\n 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\r\n 'Accept-Encoding': 'gzip, deflate, br',\r\n 'Accept-Language': 'en-US,en;q=0.9',\r\n }, this.proxyAgent);\r\n\r\n for (const sc of [resp.headers['set-cookie'] || []].flat()) {\r\n if (typeof sc === 'string' && sc.startsWith('ttwid=')) {\r\n ttwid = sc.split(';')[0].split('=').slice(1).join('=');\r\n break;\r\n }\r\n }\r\n if (!ttwid) throw new Error('Failed to obtain session cookie');\r\n\r\n const html = resp.body.toString();\r\n const sigiMatch = html.match(/id=\"SIGI_STATE\"[^>]*>([^<]+)/);\r\n if (sigiMatch) {\r\n try {\r\n const json = JSON.parse(sigiMatch[1]);\r\n const jsonStr = JSON.stringify(json);\r\n const m = jsonStr.match(/\"roomId\"\\s*:\\s*\"(\\d+)\"/);\r\n if (m) roomId = m[1];\r\n const ownerUser = json?.LiveRoom?.liveRoomUserInfo?.user;\r\n if (ownerUser?.id) {\r\n ownerUserId = String(ownerUser.id);\r\n }\r\n } catch { }\r\n }\r\n if (!roomId) throw new Error(`User @${this.uniqueId} is not currently live`);\r\n const crMatch = html.match(/\"clusterRegion\"\\s*:\\s*\"([^\"]+)\"/);\r\n clusterRegion = crMatch ? crMatch[1] : '';\r\n }\r\n\r\n // roomId is now guaranteed — either preset or discovered from HTML\r\n this._roomId = roomId;\r\n this._ownerUserId = ownerUserId;\r\n\r\n const wsHost = getWsHost(clusterRegion);\r\n\r\n const wsParams = new URLSearchParams({\r\n version_code: '270000', device_platform: 'web', cookie_enabled: 'true',\r\n screen_width: '1920', screen_height: '1080', browser_language: 'en-US',\r\n browser_platform: 'Win32', browser_name: 'Mozilla',\r\n browser_version: DEFAULT_UA.split('Mozilla/')[1] || '5.0',\r\n browser_online: 'true', tz_name: Intl.DateTimeFormat().resolvedOptions().timeZone,\r\n app_name: 'tiktok_web', sup_ws_ds_opt: '1', update_version_code: '2.0.0',\r\n compress: 'gzip', webcast_language: 'en', ws_direct: '1', aid: '1988',\r\n live_id: '12', app_language: 'en', client_enter: '1', room_id: roomId,\r\n identity: 'audience', history_comment_count: '6', last_rtt: '0',\r\n heartbeat_duration: '10000', resp_content_type: 'protobuf', did_rule: '3',\r\n });\r\n\r\n const rawWsUrl = `https://${wsHost}/webcast/im/ws_proxy/ws_reuse_supplement/?${wsParams}`;\r\n\r\n const signUrl = `${this.signServerUrl}/webcast/sign_url`;\r\n\r\n let wsUrl: string;\r\n try {\r\n const signResp = await fetch(signUrl, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'x-api-key': this.apiKey,\r\n },\r\n body: JSON.stringify({ url: rawWsUrl }),\r\n });\r\n const signData = await signResp.json() as Record<string, any>;\r\n\r\n if (signData.status_code === 0 && signData.data?.signed_url) {\r\n wsUrl = (signData.data.signed_url as string).replace(/^https:\\/\\//, 'wss://');\r\n } else {\r\n wsUrl = rawWsUrl.replace(/^https:\\/\\//, 'wss://');\r\n }\r\n } catch {\r\n wsUrl = rawWsUrl.replace(/^https:\\/\\//, 'wss://');\r\n }\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n // Build cookie header — only include ttwid if we have it\r\n let cookieHeader = ttwid ? `ttwid=${ttwid}` : '';\r\n if (this._sessionId) {\r\n const sessionCookies = `sessionid=${this._sessionId}; sessionid_ss=${this._sessionId}; sid_tt=${this._sessionId}`;\r\n cookieHeader = cookieHeader ? `${cookieHeader}; ${sessionCookies}` : sessionCookies;\r\n if (this._ttTargetIdc) {\r\n cookieHeader += `; tt-target-idc=${this._ttTargetIdc}`;\r\n }\r\n }\r\n\r\n this.ws = new WebSocket(wsUrl, {\r\n headers: {\r\n 'User-Agent': DEFAULT_UA,\r\n 'Cookie': cookieHeader,\r\n 'Origin': 'https://www.tiktok.com',\r\n },\r\n agent: this.proxyAgent,\r\n });\r\n\r\n this.ws.on('open', () => {\r\n this._connected = true;\r\n this.reconnectAttempts = 0;\r\n\r\n this.ws!.send(buildHeartbeat(roomId));\r\n this.ws!.send(buildImEnterRoom(roomId));\r\n this.startHeartbeat(roomId);\r\n\r\n const roomInfo: RoomInfo = {\r\n roomId,\r\n wsHost,\r\n clusterRegion,\r\n connectedAt: new Date().toISOString(),\r\n ownerUserId: ownerUserId || undefined,\r\n };\r\n\r\n this.emit('connected');\r\n this.emit('roomInfo', roomInfo);\r\n resolve();\r\n });\r\n\r\n this.ws.on('message', (rawData: Buffer) => {\r\n this.handleFrame(Buffer.from(rawData));\r\n });\r\n\r\n this.ws.on('close', (code, reason) => {\r\n this._connected = false;\r\n this.stopHeartbeat();\r\n\r\n const reasonStr = reason?.toString() || '';\r\n this.emit('disconnected', code, reasonStr);\r\n\r\n if (!this.intentionalClose && this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {\r\n this.reconnectAttempts++;\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts - 1), 30_000);\r\n setTimeout(() => this.connect().catch(e => this.emit('error', e)), delay);\r\n }\r\n });\r\n\r\n this.ws.on('error', (err) => {\r\n this.emit('error', err);\r\n if (!this._connected) reject(err);\r\n });\r\n });\r\n }\r\n\r\n disconnect(): void {\r\n this.intentionalClose = true;\r\n this.stopHeartbeat();\r\n if (this.ws) {\r\n this.ws.close(1000);\r\n this.ws = null;\r\n }\r\n this._connected = false;\r\n }\r\n\r\n get connected(): boolean {\r\n return this._connected;\r\n }\r\n\r\n get eventCount(): number {\r\n return this._eventCount;\r\n }\r\n\r\n get roomId(): string {\r\n return this._roomId;\r\n }\r\n\r\n /** Get the stored session ID (if any) */\r\n get sessionId(): string | undefined {\r\n return this._sessionId;\r\n }\r\n\r\n /** Update the session ID at runtime (e.g. after TikTok login) */\r\n setSession(sessionId: string, ttTargetIdc?: string): void {\r\n this._sessionId = sessionId;\r\n if (ttTargetIdc) this._ttTargetIdc = ttTargetIdc;\r\n }\r\n\r\n /**\r\n * Build a cookie header string for authenticated API requests (e.g. ranklist).\r\n * Returns undefined if no session is set.\r\n */\r\n buildSessionCookieHeader(): string | undefined {\r\n if (!this._sessionId) return undefined;\r\n const parts = [\r\n `sessionid=${this._sessionId}`,\r\n `sessionid_ss=${this._sessionId}`,\r\n `sid_tt=${this._sessionId}`,\r\n ];\r\n if (this._ttTargetIdc) parts.push(`tt-target-idc=${this._ttTargetIdc}`);\r\n return parts.join('; ');\r\n }\r\n\r\n on<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.on(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n once<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.once(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n off<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.off(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n emit<K extends keyof TikTokLiveEvents>(event: K, ...args: Parameters<TikTokLiveEvents[K]>): boolean {\r\n return super.emit(event, ...args);\r\n }\r\n\r\n private handleFrame(buf: Buffer): void {\r\n try {\r\n const fields = decodeProto(buf);\r\n const idField = fields.find(f => f.fn === 2 && f.wt === 0);\r\n const id = idField ? idField.value as bigint : 0n;\r\n const type = getStr(fields, 7);\r\n const binary = getBytes(fields, 8);\r\n\r\n if (id > 0n && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(buildAck(id));\r\n }\r\n\r\n if (type === 'msg' && binary && binary.length > 0) {\r\n let inner = binary;\r\n if (inner.length > 2 && inner[0] === 0x1f && inner[1] === 0x8b) {\r\n try { inner = zlib.gunzipSync(inner); } catch { }\r\n }\r\n\r\n const events = parseWebcastResponse(inner);\r\n for (const evt of events) {\r\n this._eventCount++;\r\n this.emit('event', evt);\r\n this.emit(evt.type as keyof TikTokLiveEvents, evt as any);\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n private startHeartbeat(roomId: string): void {\r\n this.stopHeartbeat();\r\n this.heartbeatTimer = setInterval(() => {\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(buildHeartbeat(roomId));\r\n }\r\n }, this.heartbeatInterval);\r\n }\r\n\r\n private stopHeartbeat(): void {\r\n if (this.heartbeatTimer) {\r\n clearInterval(this.heartbeatTimer);\r\n this.heartbeatTimer = null;\r\n }\r\n }\r\n}\r\n","import type { LiveEvent, TikTokUser, BattleTeam, BattleTeamUser } from './types.js';\r\n\r\nconst encoder = new TextEncoder();\r\nconst decoder = new TextDecoder();\r\n\r\n// TikTok LIVE emote name → Unicode emoji mapping\r\n// TikTok uses [emote_name] placeholders in chat text. These are resolved client-side.\r\nconst TIKTOK_EMOTE_MAP: Record<string, string> = {\r\n // Standard emojis\r\n 'happy': '😊', 'angry': '😡', 'cry': '😢', 'embarrassed': '😳',\r\n 'surprised': '😮', 'wronged': '😞', 'shout': '😤', 'flushed': '😳',\r\n 'yummy': '😋', 'complacent': '😌', 'drool': '🤤', 'scream': '😱',\r\n 'weep': '😭', 'speechless': '😶', 'funnyface': '🤪', 'laughwithtears': '😂',\r\n 'wicked': '😈', 'facewithrollingeyes': '🙄', 'sulk': '😒', 'thinking': '🤔',\r\n 'lovely': '🥰', 'greedy': '🤑', 'wow': '😯', 'joyful': '😃',\r\n 'hehe': '😁', 'slap': '👋', 'tears': '😿', 'stun': '😵',\r\n 'cute': '🥺', 'blink': '😉', 'disdain': '😏', 'astonish': '😲',\r\n 'cool': '😎', 'excited': '🤩', 'proud': '😤', 'smileface': '😊',\r\n 'evil': '👿', 'angel': '😇', 'laugh': '😆', 'pride': '🦁',\r\n 'nap': '😴', 'loveface': '😍', 'awkward': '😬', 'shock': '😨',\r\n 'funny': '😄', 'rage': '🤬',\r\n // Common aliases used in TikTok LIVE\r\n 'laughcry': '😂', 'heart': '❤️', 'like': '👍', 'love': '💕',\r\n 'shy': '🙈', 'smile': '😊',\r\n};\r\n\r\n/** Replace [emote_name] placeholders with Unicode emoji equivalents */\r\nfunction replaceEmotes(text: string, imageMap?: Record<string, string>): string {\r\n return text.replace(/\\[([a-zA-Z_]+)\\]/g, (match, name) => {\r\n const lower = name.toLowerCase();\r\n const emoji = TIKTOK_EMOTE_MAP[lower];\r\n return emoji || match; // Keep original if unknown\r\n });\r\n}\r\n\r\nexport function concatBytes(...arrays: Uint8Array[]): Uint8Array {\r\n let totalLength = 0;\r\n for (const arr of arrays) totalLength += arr.length;\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const arr of arrays) {\r\n result.set(arr, offset);\r\n offset += arr.length;\r\n }\r\n return result;\r\n}\r\n\r\nfunction readInt32LE(buf: Uint8Array, offset: number): number {\r\n return buf[offset] |\r\n (buf[offset + 1] << 8) |\r\n (buf[offset + 2] << 16) |\r\n (buf[offset + 3] << 24);\r\n}\r\n\r\nfunction readBigInt64LE(buf: Uint8Array, offset: number): bigint {\r\n const lo = BigInt(buf[offset] |\r\n (buf[offset + 1] << 8) |\r\n (buf[offset + 2] << 16) |\r\n ((buf[offset + 3] << 24) >>> 0));\r\n const hi = BigInt(buf[offset + 4] |\r\n (buf[offset + 5] << 8) |\r\n (buf[offset + 6] << 16) |\r\n ((buf[offset + 7] << 24) >>> 0));\r\n return (hi << 32n) | (lo & 0xFFFFFFFFn);\r\n}\r\n\r\nexport function decodeVarint(buf: Uint8Array, offset: number): { value: number; offset: number } {\r\n let result = 0, shift = 0;\r\n while (offset < buf.length) {\r\n const byte = buf[offset++];\r\n result |= (byte & 0x7F) << shift;\r\n if ((byte & 0x80) === 0) break;\r\n shift += 7;\r\n }\r\n return { value: result >>> 0, offset };\r\n}\r\n\r\nexport function decodeVarint64(buf: Uint8Array, offset: number): { value: bigint; offset: number } {\r\n let result = 0n, shift = 0n;\r\n while (offset < buf.length) {\r\n const byte = BigInt(buf[offset++]);\r\n result |= (byte & 0x7Fn) << shift;\r\n if ((byte & 0x80n) === 0n) break;\r\n shift += 7n;\r\n }\r\n return { value: result, offset };\r\n}\r\n\r\nexport function encodeVarint(v: number | bigint): Uint8Array {\r\n const bytes: number[] = [];\r\n let n = typeof v === 'bigint' ? v : BigInt(v);\r\n do {\r\n let b = Number(n & 0x7Fn);\r\n n >>= 7n;\r\n if (n > 0n) b |= 0x80;\r\n bytes.push(b);\r\n } while (n > 0n);\r\n return new Uint8Array(bytes);\r\n}\r\n\r\nexport interface ProtoField {\r\n fn: number;\r\n wt: number;\r\n value: Uint8Array | number | bigint;\r\n}\r\n\r\nexport function encodeField(fn: number, wt: number, value: Uint8Array | bigint | number | string): Uint8Array {\r\n const tag = encodeVarint((fn << 3) | wt);\r\n if (wt === 0) {\r\n return concatBytes(tag, encodeVarint(typeof value === 'number' ? BigInt(value) : value as bigint));\r\n }\r\n const data = typeof value === 'string' ? encoder.encode(value) : value as Uint8Array;\r\n return concatBytes(tag, encodeVarint(data.length), data);\r\n}\r\n\r\nexport function decodeProto(buf: Uint8Array): ProtoField[] {\r\n const fields: ProtoField[] = [];\r\n let offset = 0;\r\n while (offset < buf.length) {\r\n const tagResult = decodeVarint(buf, offset);\r\n offset = tagResult.offset;\r\n const fn = tagResult.value >> 3;\r\n const wt = tagResult.value & 7;\r\n if (wt === 0) {\r\n const r = decodeVarint64(buf, offset);\r\n offset = r.offset;\r\n fields.push({ fn, wt, value: r.value });\r\n } else if (wt === 2) {\r\n const lenR = decodeVarint(buf, offset);\r\n offset = lenR.offset;\r\n const data = buf.subarray(offset, offset + lenR.value);\r\n offset += lenR.value;\r\n fields.push({ fn, wt, value: data });\r\n } else if (wt === 1) {\r\n fields.push({ fn, wt, value: readBigInt64LE(buf, offset) });\r\n offset += 8;\r\n } else if (wt === 5) {\r\n fields.push({ fn, wt, value: BigInt(readInt32LE(buf, offset)) });\r\n offset += 4;\r\n } else {\r\n break;\r\n }\r\n }\r\n return fields;\r\n}\r\n\r\nexport function getStr(fields: ProtoField[], fn: number): string {\r\n const f = fields.find(x => x.fn === fn && x.wt === 2);\r\n return f ? decoder.decode(f.value as Uint8Array) : '';\r\n}\r\n\r\nexport function getBytes(fields: ProtoField[], fn: number): Uint8Array | null {\r\n const f = fields.find(x => x.fn === fn && x.wt === 2);\r\n return f ? f.value as Uint8Array : null;\r\n}\r\n\r\nexport function getInt(fields: ProtoField[], fn: number): number {\r\n const f = fields.find(x => x.fn === fn && x.wt === 0);\r\n return f ? Number(f.value) : 0;\r\n}\r\n\r\n/** Get a varint field as a string without Number precision loss (safe for 64-bit IDs). */\r\nexport function getIntStr(fields: ProtoField[], fn: number): string {\r\n const f = fields.find(x => x.fn === fn && x.wt === 0);\r\n return f ? String(f.value) : '';\r\n}\r\n\r\nexport function getAllBytes(fields: ProtoField[], fn: number): Uint8Array[] {\r\n return fields.filter(x => x.fn === fn && x.wt === 2).map(x => x.value as Uint8Array);\r\n}\r\n\r\nexport function buildHeartbeat(roomId: string): Uint8Array {\r\n const payload = encodeField(1, 0, BigInt(roomId));\r\n return concatBytes(\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'hb'),\r\n encodeField(8, 2, payload),\r\n );\r\n}\r\n\r\nexport function buildImEnterRoom(roomId: string): Uint8Array {\r\n const inner = concatBytes(\r\n encodeField(1, 0, BigInt(roomId)),\r\n encodeField(4, 0, 12n),\r\n encodeField(5, 2, 'audience'),\r\n encodeField(6, 2, ''),\r\n encodeField(9, 2, ''),\r\n encodeField(10, 2, ''),\r\n );\r\n return concatBytes(\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'im_enter_room'),\r\n encodeField(8, 2, inner),\r\n );\r\n}\r\n\r\nexport function buildAck(id: bigint): Uint8Array {\r\n return concatBytes(\r\n encodeField(2, 0, id),\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'ack'),\r\n );\r\n}\r\n\r\n// NOTE: Army users in WebcastLinkMicArmies use a DIFFERENT layout where\r\n// field 38 contains avatar/image data, NOT uniqueId.\r\n// We detect this by checking if the string looks like a username (short, no URLs).\r\nfunction looksLikeUsername(s: string): boolean {\r\n return s.length > 0 && s.length <= 50 && !s.includes('://') && !s.includes('\\x00');\r\n}\r\n\r\nfunction parseUser(data: Uint8Array): TikTokUser {\r\n const f = decodeProto(data);\r\n const id = getIntStr(f, 1) || getStr(f, 1);\r\n const nickname = getStr(f, 3) || getStr(f, 2);\r\n\r\n // uniqueId: Try field 4 (LinkUser format) first, then field 38 (User format)\r\n // Validate that the result looks like a username, not binary/URL data\r\n let uniqueId = '';\r\n const uid4 = getStr(f, 4);\r\n const uid38 = getStr(f, 38);\r\n if (uid4 && looksLikeUsername(uid4)) {\r\n uniqueId = uid4;\r\n } else if (uid38 && looksLikeUsername(uid38)) {\r\n uniqueId = uid38;\r\n } else {\r\n const uid2 = getStr(f, 2);\r\n if (uid2 && looksLikeUsername(uid2)) uniqueId = uid2;\r\n }\r\n\r\n // profilePicture: Try field 9 (User format), field 4 (army users — avatar at field 4), field 3 (LinkUser format)\r\n // Can't use OR chain because field 3 = nickname bytes in User format (truthy but wrong)\r\n // Must try each and pick the first that yields a valid URL\r\n let profilePicture: string | undefined;\r\n for (const fieldNum of [9, 4, 3]) {\r\n if (profilePicture) break;\r\n const avatarBuf = getBytes(f, fieldNum);\r\n if (!avatarBuf) continue;\r\n try {\r\n const avatarFields = decodeProto(avatarBuf);\r\n const urlBufs = getAllBytes(avatarFields, 1);\r\n for (const urlBuf of urlBufs) {\r\n const url = decoder.decode(urlBuf);\r\n if (url.includes('://')) {\r\n profilePicture = url;\r\n break;\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n const badges: string[] = [];\r\n let payGrade: number | undefined;\r\n\r\n // field_64 is repeated — iterate ALL occurrences\r\n const badgeBufs = getAllBytes(f, 64);\r\n for (const badgeBuf of badgeBufs) {\r\n try {\r\n const badgeFields = decodeProto(badgeBuf);\r\n const badgeCategory = getInt(badgeFields, 2); // 20 = user level, 30 = fan level, 10 = moderator\r\n const badgeSubCat = getInt(badgeFields, 3); // 8 = grade/level\r\n\r\n // Extract user level (pay grade) from the user-level badge\r\n // Badge with category=20, subCategory=8 is the \"user level\" (gifter grade) badge\r\n if (badgeCategory === 20 && badgeSubCat === 8 && !payGrade) {\r\n const privBuf = getBytes(badgeFields, 12);\r\n if (privBuf) {\r\n const privFields = decodeProto(privBuf);\r\n const levelStr = getStr(privFields, 5);\r\n if (levelStr) {\r\n const level = parseInt(levelStr, 10);\r\n if (level > 0) payGrade = level;\r\n }\r\n }\r\n }\r\n\r\n // Also extract badge names from field_21 (existing behavior)\r\n const badgeItems = getAllBytes(badgeFields, 21);\r\n for (const bi of badgeItems) {\r\n const bf = decodeProto(bi);\r\n const name = getStr(bf, 3) || getStr(bf, 2);\r\n if (name) badges.push(name);\r\n }\r\n } catch { }\r\n }\r\n\r\n return {\r\n id,\r\n nickname,\r\n uniqueId: uniqueId || nickname || id,\r\n profilePictureUrl: profilePicture,\r\n badges: badges.length > 0 ? badges : undefined,\r\n payGrade,\r\n };\r\n}\r\n\r\nfunction parseBattleTeamFromArmies(itemBuf: Uint8Array): BattleTeam {\r\n const f = decodeProto(itemBuf);\r\n const hostUserId = getIntStr(f, 1) || '0';\r\n let teamScore = 0;\r\n const users: BattleTeamUser[] = [];\r\n let hostUser: TikTokUser | undefined;\r\n\r\n // Field 2: Groups of gifter users (repeated)\r\n const groups = getAllBytes(f, 2);\r\n for (const gb of groups) {\r\n try {\r\n const gf = decodeProto(gb);\r\n const points = getInt(gf, 2);\r\n teamScore += points;\r\n\r\n const userBufs = getAllBytes(gf, 1);\r\n for (const ub of userBufs) {\r\n try {\r\n // TikTok's LinkMicArmies user entry is a flat proto:\r\n // f1(varint) = userId\r\n // f2(varint) = individual score (diamonds gifted by this user)\r\n // f3(bytes) = nickname\r\n // f4(bytes) = avatar/profile data\r\n // f6(bytes) = badge data\r\n // The individual score is f2 varint — same buffer, no wrapper.\r\n const userFields = decodeProto(ub);\r\n const individualScore = getInt(userFields, 2);\r\n const user = parseUser(ub);\r\n users.push({ user, score: individualScore });\r\n } catch { }\r\n }\r\n } catch { }\r\n }\r\n\r\n // Try to extract the HOST USER from other fields (3-8)\r\n // NOTE: Currently armies protobuf only has fields [1, 2], but kept\r\n // as a silent fallback in case TikTok adds host user data later.\r\n for (const fieldNum of [3, 4, 5, 6, 7, 8]) {\r\n if (hostUser) break;\r\n const buf = getBytes(f, fieldNum);\r\n if (!buf) continue;\r\n try {\r\n const parsed = parseUser(buf);\r\n if (parsed && (parsed.nickname || parsed.uniqueId) &&\r\n parsed.uniqueId !== parsed.id &&\r\n looksLikeUsername(parsed.uniqueId || '')) {\r\n hostUser = parsed;\r\n }\r\n } catch { }\r\n }\r\n\r\n return { hostUserId, score: teamScore, users, hostUser };\r\n}\r\n\r\nexport function parseWebcastMessage(method: string, payload: Uint8Array): LiveEvent {\r\n const f = decodeProto(payload);\r\n const base = { timestamp: Date.now(), msgId: '' };\r\n\r\n const typeBuf = getBytes(f, 1);\r\n if (typeBuf) {\r\n try {\r\n const tf = decodeProto(typeBuf);\r\n const ts = getInt(tf, 4);\r\n if (ts) base.timestamp = ts;\r\n } catch { }\r\n }\r\n\r\n switch (method) {\r\n case 'WebcastChatMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n const rawComment = getStr(f, 3);\r\n\r\n // Parse emote images from field 22 (emotes_with_index) if present\r\n const emoteImages: Record<string, string> = {};\r\n for (const field of f) {\r\n if (field.fn === 22 && field.wt === 2) {\r\n try {\r\n const ewi = decodeProto(field.value as Uint8Array);\r\n const emoteKey = getStr(ewi, 1);\r\n const imgBuf = getBytes(ewi, 2);\r\n if (imgBuf && emoteKey) {\r\n const imgFields = decodeProto(imgBuf);\r\n const url = getStr(imgFields, 1);\r\n if (url) emoteImages[emoteKey] = url;\r\n }\r\n } catch { }\r\n }\r\n }\r\n\r\n // Replace [emote_name] placeholders with Unicode equivalents\r\n const comment = replaceEmotes(rawComment, emoteImages);\r\n\r\n return { ...base, type: 'chat' as const, user, comment };\r\n }\r\n\r\n case 'WebcastMemberMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n let action = 1;\r\n const eventBuf = getBytes(f, 1);\r\n if (eventBuf) {\r\n const ef = decodeProto(eventBuf);\r\n const detailBuf = getBytes(ef, 8);\r\n if (detailBuf) {\r\n const df = decodeProto(detailBuf);\r\n const label = getStr(df, 2);\r\n if (label.includes('followed')) action = 2;\r\n else if (label.includes('share')) action = 3;\r\n }\r\n }\r\n return { ...base, type: 'member' as const, user, action };\r\n }\r\n\r\n case 'WebcastLikeMessage': {\r\n const userBuf = getBytes(f, 5);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n return {\r\n ...base, type: 'like' as const, user,\r\n likeCount: getInt(f, 2),\r\n totalLikes: getInt(f, 3),\r\n };\r\n }\r\n\r\n case 'WebcastGiftMessage': {\r\n const userBuf = getBytes(f, 7);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n const giftId = getInt(f, 2);\r\n const repeatCount = getInt(f, 5);\r\n const repeatEnd = getInt(f, 9) === 1;\r\n\r\n let giftName = '', diamondCount = 0, giftType = 0;\r\n let giftImageUrl = '';\r\n const detailsBuf = getBytes(f, 15);\r\n if (detailsBuf) {\r\n const df = decodeProto(detailsBuf);\r\n giftName = getStr(df, 16) || getStr(df, 2);\r\n diamondCount = getInt(df, 12);\r\n giftType = getInt(df, 11);\r\n\r\n const imgBuf = getBytes(df, 1);\r\n if (imgBuf) {\r\n const imgf = decodeProto(imgBuf);\r\n giftImageUrl = getStr(imgf, 1);\r\n }\r\n }\r\n\r\n let toUserId = '';\r\n const extraBuf = getBytes(f, 23);\r\n if (extraBuf) {\r\n const ef = decodeProto(extraBuf);\r\n toUserId = getIntStr(ef, 8) || '';\r\n }\r\n\r\n const groupId = toUserId || getStr(f, 11);\r\n\r\n return {\r\n ...base, type: 'gift' as const, user, giftId, giftName, diamondCount,\r\n repeatCount, repeatEnd, combo: repeatCount > 1 && !repeatEnd,\r\n giftType, groupId, giftPictureUrl: giftImageUrl,\r\n };\r\n }\r\n\r\n case 'WebcastSocialMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n let action = 'follow';\r\n const eventBuf = getBytes(f, 1);\r\n if (eventBuf) {\r\n const ef = decodeProto(eventBuf);\r\n const detailBuf = getBytes(ef, 8);\r\n if (detailBuf) {\r\n const df = decodeProto(detailBuf);\r\n const label = getStr(df, 2);\r\n if (label.includes('share')) action = 'share';\r\n else if (label.includes('follow')) action = 'follow';\r\n const displayType = getStr(df, 1);\r\n if (displayType === 'pm_mt_msg_viewer_share') action = 'share';\r\n }\r\n }\r\n return { ...base, type: 'social' as const, user, action };\r\n }\r\n\r\n case 'WebcastRoomUserSeqMessage': {\r\n const viewerCount = getInt(f, 3) || getInt(f, 2);\r\n const totalViewers = getInt(f, 1) || viewerCount;\r\n return { ...base, type: 'roomUserSeq' as const, totalViewers, viewerCount };\r\n }\r\n\r\n case 'WebcastLinkMicBattle': {\r\n // Proto field mapping for WebcastLinkMicBattle:\r\n // field_1 = header sub-message (method name, msgId, roomId, timestamp)\r\n // field_2 = battleId (varint — the main battle identifier)\r\n // field_3 = battle settings sub-message containing timer data:\r\n // field_3.field_1 = battleId (duplicate)\r\n // field_3.field_2 = startTimeMs (millisecond timestamp)\r\n // field_3.field_3 = duration (seconds, e.g. 301)\r\n // field_3.field_5 = phase/status (1=active, 3=ended)\r\n // field_3.field_10 = lastUpdateTimeMs\r\n // field_4 = overall status (e.g. 5 = ended)\r\n // field_5 = team user list (repeated)\r\n // field_9 = detailed user data with avatars (repeated)\r\n // field_10 = battle user bufs (repeated — used for team parsing)\r\n const battleId = getIntStr(f, 2) || getIntStr(f, 1) || '';\r\n const overallStatus = getInt(f, 4);\r\n const teams: BattleTeam[] = [];\r\n\r\n // Parse battle settings from field_3 sub-message\r\n let battleDuration = 0;\r\n let battleSettings: { startTimeMs?: number; duration?: number; endTimeMs?: number } | undefined;\r\n const settingsBuf = getBytes(f, 3);\r\n if (settingsBuf) {\r\n try {\r\n const sf = decodeProto(settingsBuf);\r\n const startTimeMs = getInt(sf, 2);\r\n const duration = getInt(sf, 3);\r\n const phase = getInt(sf, 5);\r\n const endTimeMs = getInt(sf, 10);\r\n battleDuration = duration;\r\n battleSettings = {\r\n startTimeMs: startTimeMs || undefined,\r\n duration: duration || undefined,\r\n endTimeMs: endTimeMs || undefined,\r\n };\r\n } catch { }\r\n }\r\n\r\n // Determine status: use phase from settings, fall back to overall status\r\n // field_4 sometimes returns a large TikTok ID instead of a status code.\r\n // Valid battle status codes are small (1=active, 3=ended, 5=finalizing).\r\n // Only use overallStatus if it looks like a real status code (1-10).\r\n const settingsPhase = settingsBuf ? (() => {\r\n try { return getInt(decodeProto(settingsBuf), 5); } catch { return 0; }\r\n })() : 0;\r\n const status = settingsPhase || (overallStatus > 0 && overallStatus <= 10 ? overallStatus : 0) || 1;\r\n\r\n const battleUserBufs = getAllBytes(f, 10);\r\n for (const bub of battleUserBufs) {\r\n try {\r\n const bf = decodeProto(bub);\r\n const groupBuf = getBytes(bf, 2);\r\n if (groupBuf) {\r\n const gf = decodeProto(groupBuf);\r\n const linkUserBuf = getBytes(gf, 1);\r\n if (linkUserBuf) {\r\n const user = parseUser(linkUserBuf);\r\n teams.push({\r\n hostUserId: user.id,\r\n score: 0,\r\n users: [{ user, score: 0 }],\r\n });\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n // Also try field_5 for team users (new proto format)\r\n if (teams.length === 0) {\r\n const teamBufs5 = getAllBytes(f, 5);\r\n for (const tb of teamBufs5) {\r\n try {\r\n const tf = decodeProto(tb);\r\n const userId = getIntStr(tf, 1);\r\n if (userId && userId !== '0') {\r\n teams.push({\r\n hostUserId: userId,\r\n score: 0,\r\n users: [],\r\n });\r\n }\r\n } catch { }\r\n }\r\n }\r\n\r\n if (teams.length === 0) {\r\n const teamBufs7 = getAllBytes(f, 7);\r\n for (const tb of teamBufs7) {\r\n try {\r\n teams.push(parseBattleTeamFromArmies(tb));\r\n } catch { }\r\n }\r\n }\r\n\r\n return { ...base, type: 'battle' as const, battleId, status, battleDuration, teams, battleSettings };\r\n }\r\n\r\n case 'WebcastLinkMicArmies': {\r\n const battleId = getIntStr(f, 2) || getIntStr(f, 1) || '';\r\n const battleStatus = getInt(f, 7);\r\n const teams: BattleTeam[] = [];\r\n\r\n // Parse battle settings from field_18 sub-message\r\n // Same structure as WebcastLinkMicBattle field_3:\r\n // field_1 = battleId, field_2 = startTimeMs, field_3 = duration,\r\n // field_5 = status, field_10 = endTimeMs\r\n let battleSettings: { startTimeMs?: number; duration?: number; endTimeMs?: number } | undefined;\r\n const settingsBuf18 = getBytes(f, 18);\r\n if (settingsBuf18) {\r\n try {\r\n const sf = decodeProto(settingsBuf18);\r\n const startTimeMs = getInt(sf, 2);\r\n const duration = getInt(sf, 3);\r\n const endTimeMs = getInt(sf, 10);\r\n battleSettings = {\r\n startTimeMs: startTimeMs || undefined,\r\n duration: duration || undefined,\r\n endTimeMs: endTimeMs || undefined,\r\n };\r\n } catch { }\r\n }\r\n\r\n // Parse live server timestamps (update on every score tick)\r\n const scoreUpdateTime = getInt(f, 5);\r\n const giftSentTime = getInt(f, 6);\r\n\r\n const itemBufs = getAllBytes(f, 3);\r\n for (const ib of itemBufs) {\r\n try {\r\n teams.push(parseBattleTeamFromArmies(ib));\r\n } catch { }\r\n }\r\n\r\n return {\r\n ...base, type: 'battleArmies' as const, battleId, teams,\r\n status: battleStatus, battleSettings, scoreUpdateTime, giftSentTime,\r\n };\r\n }\r\n\r\n case 'WebcastSubNotifyMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n return { ...base, type: 'subscribe' as const, user, subMonth: getInt(f, 3) };\r\n }\r\n\r\n case 'WebcastEmoteChatMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n let emoteId = '', emoteUrl = '';\r\n const emoteBuf = getBytes(f, 3);\r\n if (emoteBuf) {\r\n const ef = decodeProto(emoteBuf);\r\n emoteId = getStr(ef, 1);\r\n const imageBuf = getBytes(ef, 2);\r\n if (imageBuf) {\r\n const imgFields = decodeProto(imageBuf);\r\n emoteUrl = getStr(imgFields, 1);\r\n }\r\n }\r\n return { ...base, type: 'emoteChat' as const, user, emoteId, emoteUrl };\r\n }\r\n\r\n case 'WebcastEnvelopeMessage': {\r\n const envelopeId = String(getInt(f, 1) || getStr(f, 1));\r\n return { ...base, type: 'envelope' as const, envelopeId, diamondCount: getInt(f, 3) };\r\n }\r\n\r\n case 'WebcastQuestionNewMessage': {\r\n let questionText = '', user: TikTokUser = { id: '0', nickname: '', uniqueId: '' };\r\n const detailBuf = getBytes(f, 2);\r\n if (detailBuf) {\r\n const df = decodeProto(detailBuf);\r\n questionText = getStr(df, 2);\r\n const userBuf = getBytes(df, 5);\r\n if (userBuf) user = parseUser(userBuf);\r\n }\r\n return { ...base, type: 'question' as const, user, questionText };\r\n }\r\n\r\n case 'WebcastRankUpdateMessage':\r\n case 'WebcastHourlyRankMessage': {\r\n const rankType = getStr(f, 1) || `rank_${getInt(f, 1)}`;\r\n const rankList: Array<{ user: TikTokUser; rank: number; score: number }> = [];\r\n const listBufs = getAllBytes(f, 2);\r\n for (const lb of listBufs) {\r\n try {\r\n const rf = decodeProto(lb);\r\n const userBuf = getBytes(rf, 1);\r\n const rank = getInt(rf, 2);\r\n const score = getInt(rf, 3);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n rankList.push({ user, rank, score });\r\n } catch { }\r\n }\r\n return { ...base, type: 'rankUpdate' as const, rankType, rankList };\r\n }\r\n\r\n case 'WebcastControlMessage':\r\n return { ...base, type: 'control' as const, action: getInt(f, 2) || getInt(f, 1) };\r\n\r\n case 'WebcastRoomMessage':\r\n case 'RoomMessage':\r\n return { ...base, type: 'room' as const, status: getInt(f, 2) };\r\n\r\n case 'WebcastLiveIntroMessage': {\r\n const roomId = String(getInt(f, 2));\r\n const title = getStr(f, 4) || getStr(f, 2);\r\n return { ...base, type: 'liveIntro' as const, roomId, title };\r\n }\r\n\r\n case 'WebcastLinkMicMethod': {\r\n const action = getStr(f, 1) || `action_${getInt(f, 1)}`;\r\n const users: TikTokUser[] = [];\r\n const userBufs = getAllBytes(f, 2);\r\n for (const ub of userBufs) {\r\n try { users.push(parseUser(ub)); } catch { }\r\n }\r\n return { ...base, type: 'linkMic' as const, action, users };\r\n }\r\n\r\n case 'WebcastLinkmicBattleTaskMessage': {\r\n // Battle task/buff events (multipliers, bonus missions, timer updates)\r\n // Raw proto fields:\r\n // field_2 = taskAction (1=start, 2=update, 3=mission complete)\r\n // field_3 = bonus mission details sub-message:\r\n // field_3.field_1.field_1 = target score\r\n // field_3.field_1.field_2 = mission items with multiplier/duration\r\n // field_3.field_1.field_3 = countdown (field_1=target, field_2=duration, field_3=endTimestamp)\r\n // field_3.field_1.field_6 = mission start timestamp\r\n // field_5 = timer sub-message:\r\n // field_5.field_1 = timer type\r\n // field_5.field_2 = remaining seconds\r\n // field_5.field_3 = end timestamp (seconds)\r\n // field_6 = mission description with multiplier info\r\n // field_20 = battle reference ID\r\n const taskAction = getInt(f, 2);\r\n const battleRefId = getIntStr(f, 20) || '';\r\n\r\n // Extract timer info from field_5\r\n let timerType = 0;\r\n let remainingSeconds = 0;\r\n let endTimestampS = 0;\r\n const timerBuf = getBytes(f, 5);\r\n if (timerBuf) {\r\n try {\r\n const tf = decodeProto(timerBuf);\r\n timerType = getInt(tf, 1);\r\n remainingSeconds = getInt(tf, 2);\r\n endTimestampS = getInt(tf, 3);\r\n } catch { }\r\n }\r\n\r\n // Extract multiplier and mission details from field_3 or field_6\r\n let multiplier = 0;\r\n let missionDuration = 0;\r\n let missionTarget = 0;\r\n let missionType = '';\r\n\r\n // Try field_3.field_1 (bonus mission)\r\n const bonusBuf = getBytes(f, 3);\r\n if (bonusBuf) {\r\n try {\r\n const bf = decodeProto(bonusBuf);\r\n const missionBuf = getBytes(bf, 1);\r\n if (missionBuf) {\r\n const mf = decodeProto(missionBuf);\r\n missionTarget = getInt(mf, 1);\r\n // Check mission items (field_2, repeated) for multiplier\r\n const items = getAllBytes(mf, 2);\r\n for (const item of items) {\r\n try {\r\n const itemFields = decodeProto(item);\r\n const descBuf = getBytes(itemFields, 2);\r\n if (descBuf) {\r\n const descFields = decodeProto(descBuf);\r\n // Look for \"multi\" key in param pairs\r\n const paramBufs = getAllBytes(descFields, 2);\r\n for (const pb of paramBufs) {\r\n try {\r\n const pf = decodeProto(pb);\r\n const key = getStr(pf, 1);\r\n const val = getStr(pf, 2);\r\n if (key === 'multi' && val) multiplier = parseInt(val) || 0;\r\n if (key === 'dur' && val) missionDuration = parseInt(val) || 0;\r\n if (key === 'sum' && val) missionTarget = parseInt(val) || missionTarget;\r\n } catch { }\r\n }\r\n // Get mission type from field_1\r\n const typeStr = getStr(descFields, 1);\r\n if (typeStr) missionType = typeStr;\r\n }\r\n } catch { }\r\n }\r\n // Check countdown sub-message (field_3 of mission)\r\n const countdownBuf = getBytes(mf, 3);\r\n if (countdownBuf) {\r\n try {\r\n const cf = decodeProto(countdownBuf);\r\n if (!missionDuration) missionDuration = getInt(cf, 2);\r\n } catch { }\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n // Try field_6 for mission descriptions (e.g. mission complete with sum)\r\n if (!missionType) {\r\n const descBuf6 = getBytes(f, 6);\r\n if (descBuf6) {\r\n try {\r\n const d6 = decodeProto(descBuf6);\r\n const innerBuf = getBytes(d6, 1);\r\n if (innerBuf) {\r\n const innerF = decodeProto(innerBuf);\r\n const typeStr = getStr(innerF, 1);\r\n if (typeStr) missionType = typeStr;\r\n const paramBufs = getAllBytes(innerF, 2);\r\n for (const pb of paramBufs) {\r\n try {\r\n const pf = decodeProto(pb);\r\n const key = getStr(pf, 1);\r\n const val = getStr(pf, 2);\r\n if (key === 'multi' && val) multiplier = parseInt(val) || 0;\r\n if (key === 'sum' && val) missionTarget = parseInt(val) || missionTarget;\r\n } catch { }\r\n }\r\n }\r\n } catch { }\r\n }\r\n }\r\n\r\n return {\r\n ...base, type: 'battleTask' as const,\r\n taskAction, battleRefId, missionType, multiplier,\r\n missionDuration, missionTarget,\r\n remainingSeconds, endTimestampS, timerType,\r\n };\r\n }\r\n\r\n case 'WebcastBarrageMessage': {\r\n\r\n\r\n const msgType = getInt(f, 3);\r\n const duration = getInt(f, 4);\r\n const displayType = getInt(f, 5);\r\n const subType = getInt(f, 6);\r\n\r\n let defaultPattern = '';\r\n let content = '';\r\n const contentBuf = getBytes(f, 2);\r\n if (contentBuf) {\r\n try {\r\n const cf = decodeProto(contentBuf);\r\n // field_1 = default pattern text\r\n defaultPattern = getStr(cf, 1) || '';\r\n // field_2 = pattern params (may have key=value pairs)\r\n const paramBufs = getAllBytes(cf, 2);\r\n const params: string[] = [];\r\n for (const pb of paramBufs) {\r\n try {\r\n const pf = decodeProto(pb);\r\n const key = getStr(pf, 1);\r\n const val = getStr(pf, 2);\r\n if (key && val) params.push(`${key}=${val}`);\r\n } catch { }\r\n }\r\n if (params.length > 0) content = params.join(', ');\r\n } catch { }\r\n }\r\n\r\n // Also try field_1 as a direct string (some types send the text directly)\r\n if (!defaultPattern) {\r\n defaultPattern = getStr(f, 1) || '';\r\n }\r\n\r\n\r\n\r\n return {\r\n ...base, type: 'barrage' as const,\r\n msgType, subType, displayType, duration,\r\n defaultPattern, content,\r\n };\r\n }\r\n\r\n default:\r\n return { ...base, type: 'unknown' as const, method };\r\n }\r\n}\r\n\r\nexport function parseWebcastResponse(payload: Uint8Array): LiveEvent[] {\r\n const events: LiveEvent[] = [];\r\n const respFields = decodeProto(payload);\r\n\r\n for (const mf of respFields.filter(f => f.fn === 1 && f.wt === 2)) {\r\n const msgBuf = mf.value as Uint8Array;\r\n const msgFields = decodeProto(msgBuf);\r\n const method = getStr(msgFields, 1);\r\n const innerPayload = getBytes(msgFields, 2);\r\n if (!method || !innerPayload) continue;\r\n\r\n try {\r\n events.push(parseWebcastMessage(method, innerPayload));\r\n } catch { }\r\n }\r\n\r\n return events;\r\n}\r\n","/**\r\n * TikTokCaptions — Real-time speech-to-text transcription and translation for TikTok LIVE streams.\r\n *\r\n * AI-powered audio transcription with speaker diarization, multi-language auto-detection,\r\n * real-time translation, and sub-second latency. Connects via WebSocket to the TikTool\r\n * captions relay for continuous streaming transcription.\r\n *\r\n * @example\r\n * ```ts\r\n * import { TikTokCaptions } from '@tiktool/live';\r\n *\r\n * const captions = new TikTokCaptions({\r\n * uniqueId: 'username',\r\n * apiKey: 'your-api-key',\r\n * language: 'en', // translate to English\r\n * });\r\n *\r\n * captions.on('caption', (data) => {\r\n * console.log(`[${data.language}] ${data.text}`);\r\n * });\r\n *\r\n * captions.on('translation', (data) => {\r\n * console.log(`[translated] ${data.text}`);\r\n * });\r\n *\r\n * await captions.start();\r\n * ```\r\n */\r\n\r\nimport { EventEmitter } from 'events';\r\nimport WebSocket from 'ws';\r\n\r\n\r\n\r\nexport interface TikTokCaptionsOptions {\r\n /** TikTok username to transcribe (without @) */\r\n uniqueId: string;\r\n /** API key for authentication */\r\n apiKey: string;\r\n /** Source language hint (e.g. 'en', 'ja'). Leave empty for auto-detection. */\r\n language?: string;\r\n /** Target language for real-time translation (e.g. 'en', 'es', 'fr'). One language per session. */\r\n translate?: string;\r\n /** Enable speaker diarization to identify individual speakers (default: true) */\r\n diarization?: boolean;\r\n /** Max session duration in minutes before auto-disconnect (default: 60, max: 300) */\r\n maxDurationMinutes?: number;\r\n /** Custom server URL (default: wss://api.tik.tools) */\r\n signServerUrl?: string;\r\n /** Enable debug logging */\r\n debug?: boolean;\r\n /** Auto-reconnect on disconnect */\r\n autoReconnect?: boolean;\r\n /** Max reconnect attempts (default: 5) */\r\n maxReconnectAttempts?: number;\r\n}\r\n\r\nexport interface CaptionData {\r\n /** Transcribed text */\r\n text: string;\r\n /** Detected source language */\r\n language: string;\r\n /** Whether this is the final version of this segment */\r\n isFinal: boolean;\r\n /** Confidence score (0-1) */\r\n confidence: number;\r\n /** Speaker identifier (if available) */\r\n speaker?: string;\r\n /** Start time in milliseconds */\r\n startMs?: number;\r\n /** End time in milliseconds */\r\n endMs?: number;\r\n}\r\n\r\nexport interface TranslationData {\r\n /** Translated text */\r\n text: string;\r\n /** Target language */\r\n language: string;\r\n /** Whether this is the final version */\r\n isFinal: boolean;\r\n /** Confidence score (0-1) */\r\n confidence: number;\r\n /** Speaker identifier (if available) */\r\n speaker?: string;\r\n}\r\n\r\nexport interface CaptionCredits {\r\n /** Credits remaining */\r\n remaining: number;\r\n /** Total credits purchased */\r\n total: number;\r\n /** Credits used in this session */\r\n used: number;\r\n /** Whether credits are low */\r\n warning: boolean;\r\n}\r\n\r\nexport interface CaptionStatus {\r\n /** Current status */\r\n status: 'connecting' | 'waiting' | 'live' | 'transcribing' | 'ended' |\r\n 'switching_language' | 'language_switched' | 'stream_ended';\r\n /** TikTok username */\r\n uniqueId?: string;\r\n /** Room ID (once resolved) */\r\n roomId?: string;\r\n /** Language (for language switch events) */\r\n language?: string;\r\n /** Status message */\r\n message?: string;\r\n}\r\n\r\nexport interface CaptionError {\r\n /** Error code */\r\n code: string;\r\n /** Human-readable error message */\r\n message: string;\r\n}\r\n\r\nexport interface TikTokCaptionsEvents {\r\n /** Fired for each transcription token */\r\n caption: (data: CaptionData) => void;\r\n /** Fired for each translated token */\r\n translation: (data: TranslationData) => void;\r\n /** Status changes (connecting, live, transcribing, etc.) */\r\n status: (data: CaptionStatus) => void;\r\n /** Credit balance updates */\r\n credits: (data: CaptionCredits) => void;\r\n /** Low credit warning */\r\n credits_low: (data: { remaining: number; total: number; percent: number }) => void;\r\n /** Error events */\r\n error: (data: CaptionError) => void;\r\n /** WebSocket connected */\r\n connected: () => void;\r\n /** WebSocket disconnected */\r\n disconnected: (code: number, reason: string) => void;\r\n}\r\n\r\nconst DEFAULT_CAPTIONS_SERVER = 'wss://api.tik.tools';\r\n\r\nexport class TikTokCaptions extends EventEmitter {\r\n private ws: WebSocket | null = null;\r\n private _connected = false;\r\n private intentionalClose = false;\r\n private reconnectAttempts = 0;\r\n\r\n private readonly uniqueId: string;\r\n private readonly apiKey: string;\r\n private readonly serverUrl: string;\r\n private readonly autoReconnect: boolean;\r\n private readonly maxReconnectAttempts: number;\r\n private readonly debug: boolean;\r\n private readonly _translate: string;\r\n private readonly _diarization: boolean;\r\n private readonly _maxDurationMinutes: number;\r\n private _language: string;\r\n\r\n constructor(options: TikTokCaptionsOptions) {\r\n super();\r\n this.uniqueId = options.uniqueId.replace(/^@/, '');\r\n if (!options.apiKey) throw new Error('apiKey is required. Get a free key at https://tik.tools');\r\n this.apiKey = options.apiKey;\r\n this._language = options.language || '';\r\n this._translate = options.translate || '';\r\n this._diarization = options.diarization ?? true;\r\n this._maxDurationMinutes = options.maxDurationMinutes ?? 60;\r\n this.serverUrl = (options.signServerUrl || DEFAULT_CAPTIONS_SERVER).replace(/\\/$/, '');\r\n this.autoReconnect = options.autoReconnect ?? true;\r\n this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;\r\n this.debug = options.debug ?? false;\r\n }\r\n\r\n /**\r\n * Start real-time captions for the configured TikTok user.\r\n * Connects to the captions WebSocket relay and begins transcription\r\n * once the user goes live (or immediately if already live).\r\n */\r\n async start(): Promise<void> {\r\n this.intentionalClose = false;\r\n\r\n const wsUrl = this.buildWsUrl();\r\n if (this.debug) console.log(`[Captions] Connecting to ${wsUrl}`);\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n this.ws = new WebSocket(wsUrl);\r\n\r\n this.ws.on('open', () => {\r\n this._connected = true;\r\n this.reconnectAttempts = 0;\r\n if (this.debug) console.log('[Captions] Connected');\r\n this.emit('connected');\r\n resolve();\r\n });\r\n\r\n this.ws.on('message', (data: Buffer | string) => {\r\n this.handleMessage(typeof data === 'string' ? data : data.toString());\r\n });\r\n\r\n this.ws.on('close', (code: number, reason: Buffer) => {\r\n this._connected = false;\r\n const reasonStr = reason?.toString() || '';\r\n if (this.debug) console.log(`[Captions] Disconnected: ${code} ${reasonStr}`);\r\n this.emit('disconnected', code, reasonStr);\r\n\r\n if (!this.intentionalClose && this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {\r\n this.reconnectAttempts++;\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts - 1), 30_000);\r\n if (this.debug) console.log(`[Captions] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\r\n setTimeout(() => this.start().catch(e => this.emit('error', { code: 'RECONNECT_FAILED', message: e.message })), delay);\r\n }\r\n });\r\n\r\n this.ws.on('error', (err: Error) => {\r\n this.emit('error', { code: 'WS_ERROR', message: err.message });\r\n if (!this._connected) reject(err);\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * Stop captions and disconnect.\r\n */\r\n stop(): void {\r\n this.intentionalClose = true;\r\n if (this.ws) {\r\n // Send stop action before closing\r\n this.send({ action: 'stop' });\r\n this.ws.close(1000);\r\n this.ws = null;\r\n }\r\n this._connected = false;\r\n }\r\n\r\n /**\r\n * Switch the translation target language on-the-fly.\r\n * Causes a brief interruption while the transcription engine reconfigures.\r\n */\r\n setLanguage(language: string): void {\r\n this._language = language;\r\n this.send({ action: 'set_language', language });\r\n }\r\n\r\n /**\r\n * Request a credit balance update from the server.\r\n */\r\n getCredits(): void {\r\n this.send({ action: 'get_credits' });\r\n }\r\n\r\n /** Whether the WebSocket is currently connected */\r\n get connected(): boolean {\r\n return this._connected;\r\n }\r\n\r\n /** The current target language */\r\n get language(): string {\r\n return this._language;\r\n }\r\n\r\n\r\n\r\n on<K extends keyof TikTokCaptionsEvents>(event: K, listener: TikTokCaptionsEvents[K]): this {\r\n return super.on(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n once<K extends keyof TikTokCaptionsEvents>(event: K, listener: TikTokCaptionsEvents[K]): this {\r\n return super.once(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n off<K extends keyof TikTokCaptionsEvents>(event: K, listener: TikTokCaptionsEvents[K]): this {\r\n return super.off(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n emit<K extends keyof TikTokCaptionsEvents>(event: K, ...args: Parameters<TikTokCaptionsEvents[K]>): boolean {\r\n return super.emit(event, ...args);\r\n }\r\n\r\n private buildWsUrl(): string {\r\n const base = this.serverUrl.replace(/^http/, 'ws');\r\n const params = new URLSearchParams({\r\n uniqueId: this.uniqueId,\r\n apiKey: this.apiKey,\r\n });\r\n if (this._language) params.set('language', this._language);\r\n if (this._translate) params.set('translate', this._translate);\r\n if (this._diarization !== undefined) params.set('diarization', String(this._diarization));\r\n if (this._maxDurationMinutes) params.set('max_duration_minutes', String(this._maxDurationMinutes));\r\n return `${base}/captions?${params}`;\r\n }\r\n\r\n private send(msg: Record<string, any>): void {\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify(msg));\r\n }\r\n }\r\n\r\n private handleMessage(raw: string): void {\r\n try {\r\n const msg = JSON.parse(raw);\r\n\r\n switch (msg.type) {\r\n case 'caption':\r\n this.emit('caption', {\r\n text: msg.text,\r\n language: msg.language,\r\n isFinal: msg.isFinal,\r\n confidence: msg.confidence,\r\n speaker: msg.speaker,\r\n startMs: msg.startMs,\r\n endMs: msg.endMs,\r\n });\r\n break;\r\n\r\n case 'translation':\r\n this.emit('translation', {\r\n text: msg.text,\r\n language: msg.language,\r\n isFinal: msg.isFinal,\r\n confidence: msg.confidence,\r\n speaker: msg.speaker,\r\n });\r\n break;\r\n\r\n case 'status':\r\n this.emit('status', {\r\n status: msg.status,\r\n uniqueId: msg.uniqueId,\r\n roomId: msg.roomId,\r\n language: msg.language,\r\n message: msg.message,\r\n });\r\n break;\r\n\r\n case 'credits':\r\n this.emit('credits', {\r\n remaining: msg.remaining,\r\n total: msg.total,\r\n used: msg.used,\r\n warning: msg.warning,\r\n });\r\n break;\r\n\r\n case 'credits_low':\r\n this.emit('credits_low', {\r\n remaining: msg.remaining,\r\n total: msg.total,\r\n percent: msg.percent,\r\n });\r\n break;\r\n\r\n case 'error':\r\n this.emit('error', {\r\n code: msg.code,\r\n message: msg.message,\r\n });\r\n break;\r\n\r\n default:\r\n if (this.debug) {\r\n console.log(`[Captions] Unknown message type: ${msg.type}`, msg);\r\n }\r\n }\r\n } catch {\r\n if (this.debug) console.error('[Captions] Failed to parse message:', raw);\r\n }\r\n }\r\n}\r\n","/**\r\n * TikTool API utilities for the sign-and-return API flow.\r\n *\r\n * The API server returns signed URLs instead of fetching TikTok data directly.\r\n * These utilities handle the two-step flow:\r\n * 1. resolve_required — scrape TikTok HTML to get room_id\r\n * 2. fetch_signed_url — use the signed URL to get actual TikTok data\r\n *\r\n * @module\r\n */\r\n\r\nconst DEFAULT_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';\r\nconst DEFAULT_SIGN_SERVER = 'https://api.tik.tools';\r\n\r\nconst pageCache = new Map<string, { info: LivePageInfo; ts: number }>();\r\nconst PAGE_CACHE_TTL = 5 * 60 * 1000;\r\n\r\n/**\r\n * Resolved live page metadata from a TikTok live page.\r\n */\r\nexport interface LivePageInfo {\r\n /** Active room ID for the livestream */\r\n roomId: string;\r\n /** Session cookie required for WebSocket authentication */\r\n ttwid: string;\r\n /** Server cluster region (e.g. 'us', 'eu') */\r\n clusterRegion: string;\r\n}\r\n\r\n/**\r\n * Scrape a TikTok live page to extract room metadata.\r\n * Returns the room ID, session cookie, and cluster region.\r\n * Results are cached for 5 minutes. Returns `null` if the user is not live.\r\n */\r\nexport async function resolveLivePage(uniqueId: string): Promise<LivePageInfo | null> {\r\n const clean = uniqueId.replace(/^@/, '');\r\n const cached = pageCache.get(clean);\r\n if (cached && Date.now() - cached.ts < PAGE_CACHE_TTL) {\r\n return cached.info;\r\n }\r\n\r\n try {\r\n const resp = await fetch(`https://www.tiktok.com/@${clean}/live`, {\r\n headers: {\r\n 'User-Agent': DEFAULT_UA,\r\n 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\r\n 'Accept-Language': 'en-US,en;q=0.9',\r\n },\r\n redirect: 'follow',\r\n });\r\n\r\n if (!resp.ok) return null;\r\n\r\n // Extract ttwid session cookie\r\n let ttwid = '';\r\n const setCookies = resp.headers.get('set-cookie') || '';\r\n for (const part of setCookies.split(',')) {\r\n const trimmed = part.trim();\r\n if (trimmed.startsWith('ttwid=')) {\r\n ttwid = trimmed.split(';')[0].split('=').slice(1).join('=');\r\n break;\r\n }\r\n }\r\n if (!ttwid && typeof (resp.headers as any).getSetCookie === 'function') {\r\n for (const sc of (resp.headers as any).getSetCookie()) {\r\n if (typeof sc === 'string' && sc.startsWith('ttwid=')) {\r\n ttwid = sc.split(';')[0].split('=').slice(1).join('=');\r\n break;\r\n }\r\n }\r\n }\r\n\r\n const html = await resp.text();\r\n\r\n // Extract roomId from SIGI_STATE JSON block\r\n let roomId = '';\r\n const sigiMatch = html.match(/id=\"SIGI_STATE\"[^>]*>([^<]+)/);\r\n if (sigiMatch) {\r\n try {\r\n const json = JSON.parse(sigiMatch[1]);\r\n const jsonStr = JSON.stringify(json);\r\n const m = jsonStr.match(/\"roomId\"\\s*:\\s*\"(\\d+)\"/);\r\n if (m) roomId = m[1];\r\n } catch { }\r\n }\r\n\r\n // Fallback patterns\r\n if (!roomId) {\r\n const patterns = [\r\n /\"roomId\"\\s*:\\s*\"(\\d+)\"/,\r\n /room_id[=/](\\d{10,})/,\r\n /\"idStr\"\\s*:\\s*\"(\\d{10,})\"/,\r\n ];\r\n for (const p of patterns) {\r\n const m = html.match(p);\r\n if (m) { roomId = m[1]; break; }\r\n }\r\n }\r\n\r\n if (!roomId) return null;\r\n\r\n // Extract cluster region\r\n const crMatch = html.match(/\"clusterRegion\"\\s*:\\s*\"([^\"]+)\"/);\r\n const clusterRegion = crMatch ? crMatch[1] : '';\r\n\r\n const info: LivePageInfo = { roomId, ttwid, clusterRegion };\r\n pageCache.set(clean, { info, ts: Date.now() });\r\n return info;\r\n } catch { }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Resolve a TikTok username to a room ID.\r\n * Returns `null` if the user is not currently live.\r\n * Results are cached for 5 minutes.\r\n */\r\nexport async function resolveRoomId(uniqueId: string): Promise<string | null> {\r\n const info = await resolveLivePage(uniqueId);\r\n return info?.roomId ?? null;\r\n}\r\n\r\nexport interface SignedUrlResponse {\r\n status_code: number;\r\n action?: string;\r\n signed_url: string;\r\n headers: Record<string, string>;\r\n cookies: string;\r\n}\r\n\r\n/**\r\n * Execute a signed-URL API response: fetch the signed URL and return the\r\n * parsed JSON data from TikTok.\r\n */\r\nexport async function fetchSignedUrl(response: SignedUrlResponse): Promise<any> {\r\n if (!response.signed_url) {\r\n return null;\r\n }\r\n\r\n const headers: Record<string, string> = { ...(response.headers || {}) };\r\n if (response.cookies) {\r\n headers['Cookie'] = response.cookies;\r\n }\r\n\r\n const resp = await fetch(response.signed_url, { headers, redirect: 'follow' });\r\n const text = await resp.text();\r\n try {\r\n return JSON.parse(text);\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport interface CallApiOptions {\r\n /** API server URL (default: https://api.tik.tools) */\r\n serverUrl?: string;\r\n /** API key for authentication */\r\n apiKey: string;\r\n /** API endpoint path (e.g. '/webcast/room_video') */\r\n endpoint: string;\r\n /** TikTok unique_id to resolve */\r\n uniqueId: string;\r\n /** HTTP method (default: POST) */\r\n method?: 'GET' | 'POST';\r\n /** Additional body fields for POST requests */\r\n extraBody?: Record<string, any>;\r\n}\r\n\r\n/**\r\n * Call a TikTool API endpoint, handling the full\r\n * resolve_required → room_id → signed_url → fetch flow automatically.\r\n *\r\n * Returns the actual TikTok data, or `null` if the user is not live.\r\n */\r\nexport async function callApi(opts: CallApiOptions): Promise<any> {\r\n const serverUrl = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const isGet = opts.method === 'GET';\r\n const ak = encodeURIComponent(opts.apiKey);\r\n\r\n const url1 = isGet\r\n ? `${serverUrl}${opts.endpoint}?apiKey=${ak}&unique_id=${encodeURIComponent(opts.uniqueId)}`\r\n : `${serverUrl}${opts.endpoint}?apiKey=${ak}`;\r\n\r\n const fetchOpts1: RequestInit = isGet\r\n ? {}\r\n : {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ unique_id: opts.uniqueId, ...opts.extraBody }),\r\n };\r\n\r\n const resp1 = await fetch(url1, fetchOpts1);\r\n const data1 = await resp1.json() as any;\r\n\r\n // If the response contains a signed URL to fetch (with or without explicit action),\r\n // follow through and fetch the actual TikTok data\r\n if (data1.signed_url || data1.action === 'fetch_signed_url') {\r\n return fetchSignedUrl(data1);\r\n }\r\n\r\n if (data1.status_code === 0 && data1.action !== 'resolve_required') {\r\n return data1;\r\n }\r\n\r\n if (data1.action === 'resolve_required') {\r\n const roomId = await resolveRoomId(opts.uniqueId);\r\n if (!roomId) return null;\r\n\r\n const url2 = isGet\r\n ? `${serverUrl}${opts.endpoint}?apiKey=${ak}&room_id=${encodeURIComponent(roomId)}`\r\n : `${serverUrl}${opts.endpoint}?apiKey=${ak}`;\r\n\r\n const fetchOpts2: RequestInit = isGet\r\n ? {}\r\n : {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ room_id: roomId, ...opts.extraBody }),\r\n };\r\n\r\n const resp2 = await fetch(url2, fetchOpts2);\r\n const data2 = await resp2.json() as any;\r\n\r\n if (data2.signed_url || data2.action === 'fetch_signed_url') {\r\n return fetchSignedUrl(data2);\r\n }\r\n\r\n return data2;\r\n }\r\n\r\n return data1;\r\n}\r\n\r\n// ── Ranklist API ────────────────────────────────────────────────────\r\n\r\nimport type { RanklistResponse } from './types.js';\r\n\r\nexport interface GetRanklistOptions {\r\n /** API server URL (default: https://api.tik.tools) */\r\n serverUrl?: string;\r\n /** API key for authentication */\r\n apiKey: string;\r\n /** TikTok username to look up (auto-resolves room_id and anchor_id) */\r\n uniqueId?: string;\r\n /** Direct room ID (skip resolution) */\r\n roomId?: string;\r\n /** Direct anchor/owner ID (skip resolution) */\r\n anchorId?: string;\r\n /**\r\n * TikTok session cookie string for authentication.\r\n * Required — ranklist endpoints return 20003 without login.\r\n * Example: \"sessionid=abc123; sid_guard=def456\"\r\n */\r\n sessionCookie?: string;\r\n /**\r\n * Which ranklist sub-endpoint to call:\r\n * - \"online_audience\" (default) — top gifters with scores\r\n * - \"anchor_rank_list\" — gifter ranking by rank_type\r\n * - \"entrance\" — entrance UI metadata, tabs, gap-to-rank\r\n */\r\n type?: 'online_audience' | 'anchor_rank_list' | 'entrance';\r\n /**\r\n * For \"anchor_rank_list\" type only:\r\n * - \"1\" = hourly ranking (default)\r\n * - \"8\" = daily ranking\r\n */\r\n rankType?: string;\r\n}\r\n\r\n/**\r\n * Fetch ranked user lists from TikTok via the sign server.\r\n *\r\n * When `sessionCookie` is provided, the server returns a **sign-and-return**\r\n * response with a signed URL that you must fetch from your own IP\r\n * (TikTok sessions are IP-bound). Check for `sign_and_return: true` in\r\n * the response to detect this mode.\r\n *\r\n * @example\r\n * ```ts\r\n * const data = await getRanklist({\r\n * apiKey: 'your-key',\r\n * uniqueId: 'katarina.live',\r\n * sessionCookie: 'sessionid=abc; sid_guard=def',\r\n * type: 'online_audience',\r\n * });\r\n *\r\n * if (data.sign_and_return) {\r\n * // Fetch the signed URL from YOUR IP with your session cookie\r\n * const resp = await fetch(data.signed_url, {\r\n * method: data.method,\r\n * headers: { ...data.headers, Cookie: sessionCookie },\r\n * ...(data.body ? { body: data.body } : {}),\r\n * });\r\n * const tikData = await resp.json();\r\n * console.log(tikData); // TikTok's raw response\r\n * } else {\r\n * console.log(data); // Direct TikTok response (no session)\r\n * }\r\n * ```\r\n */\r\nexport async function getRanklist(opts: GetRanklistOptions): Promise<RanklistResponse> {\r\n const base = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const ak = encodeURIComponent(opts.apiKey);\r\n\r\n const body: Record<string, any> = {};\r\n if (opts.uniqueId) body.unique_id = opts.uniqueId;\r\n if (opts.roomId) body.room_id = opts.roomId;\r\n if (opts.anchorId) body.anchor_id = opts.anchorId;\r\n if (opts.sessionCookie) body.session_cookie = opts.sessionCookie;\r\n if (opts.type) body.type = opts.type;\r\n if (opts.rankType) body.rank_type = opts.rankType;\r\n\r\n const resp = await fetch(`${base}/webcast/ranklist?apiKey=${ak}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(body),\r\n });\r\n\r\n const data = await resp.json() as any;\r\n\r\n // The server wraps TikTok's response in { status_code, data }\r\n if (data.status_code === 20003) {\r\n throw new Error(\r\n data.message || 'TikTok requires login. Provide sessionCookie with your TikTok session.'\r\n );\r\n }\r\n\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Ranklist failed (status ${data.status_code})`);\r\n }\r\n\r\n // Sign-and-return mode: session cookie was provided, return the full\r\n // response so the caller can fetch the signed URL from their own IP.\r\n if (data.sign_and_return) {\r\n return data as RanklistResponse;\r\n }\r\n\r\n return data.data as RanklistResponse;\r\n}\r\n\r\n// ── CAPTCHA Solver API ──────────────────────────────────────────────\r\n\r\nimport type { PuzzleSolveResult, RotateSolveResult, ShapesSolveResult } from './types.js';\r\n\r\n/**\r\n * Solve a puzzle (slider) CAPTCHA using the TikTool solver.\r\n *\r\n * @param apiKey - API key for authentication\r\n * @param puzzleB64 - Base64-encoded background image (PNG/JPEG)\r\n * @param pieceB64 - Base64-encoded puzzle piece image (PNG/JPEG)\r\n * @param serverUrl - Custom server URL (default: https://api.tik.tools)\r\n * @returns Solve result with slide position and confidence\r\n */\r\nexport async function solvePuzzle(\r\n apiKey: string,\r\n puzzleB64: string,\r\n pieceB64: string,\r\n serverUrl?: string,\r\n): Promise<PuzzleSolveResult> {\r\n const base = (serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const resp = await fetch(`${base}/captcha/solve/puzzle?apiKey=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ puzzle: puzzleB64, piece: pieceB64 }),\r\n });\r\n const data = await resp.json() as any;\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Solver failed (status ${data.status_code})`);\r\n }\r\n return data.data as PuzzleSolveResult;\r\n}\r\n\r\n/**\r\n * Solve a rotate (whirl) CAPTCHA using the TikTool solver.\r\n *\r\n * @param apiKey - API key for authentication\r\n * @param outerB64 - Base64-encoded outer ring image (PNG/JPEG)\r\n * @param innerB64 - Base64-encoded inner rotated image (PNG/JPEG)\r\n * @param serverUrl - Custom server URL (default: https://api.tik.tools)\r\n * @returns Solve result with rotation angle and confidence\r\n */\r\nexport async function solveRotate(\r\n apiKey: string,\r\n outerB64: string,\r\n innerB64: string,\r\n serverUrl?: string,\r\n): Promise<RotateSolveResult> {\r\n const base = (serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const resp = await fetch(`${base}/captcha/solve/rotate?apiKey=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ outer: outerB64, inner: innerB64 }),\r\n });\r\n const data = await resp.json() as any;\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Solver failed (status ${data.status_code})`);\r\n }\r\n return data.data as RotateSolveResult;\r\n}\r\n\r\n/**\r\n * Solve a shapes (3D matching) CAPTCHA using the TikTool solver.\r\n *\r\n * @param apiKey - API key for authentication\r\n * @param imageB64 - Base64-encoded CAPTCHA image with shape grid (PNG/JPEG)\r\n * @param serverUrl - Custom server URL (default: https://api.tik.tools)\r\n * @returns Solve result with two matching shape coordinates and confidence\r\n */\r\nexport async function solveShapes(\r\n apiKey: string,\r\n imageB64: string,\r\n serverUrl?: string,\r\n): Promise<ShapesSolveResult> {\r\n const base = (serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const resp = await fetch(`${base}/captcha/solve/shapes?apiKey=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ image: imageB64 }),\r\n });\r\n const data = await resp.json() as any;\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Solver failed (status ${data.status_code})`);\r\n }\r\n return data.data as ShapesSolveResult;\r\n}\r\n\r\n// ── Regional Leaderboard API ────────────────────────────────────────\r\n\r\nexport interface GetRegionalRanklistOptions {\r\n /** API server URL (default: https://api.tik.tools) */\r\n serverUrl?: string;\r\n /** API key for authentication (Pro or Ultra tier required) */\r\n apiKey: string;\r\n /** TikTok username to look up (auto-resolves room_id and anchor_id) */\r\n uniqueId?: string;\r\n /** Direct room ID (skip resolution) */\r\n roomId?: string;\r\n /** Direct anchor/owner ID (skip resolution) */\r\n anchorId?: string;\r\n /**\r\n * Ranking period:\r\n * - \"1\" = Hourly\r\n * - \"8\" = Daily (default)\r\n * - \"15\" = Popular LIVE\r\n * - \"16\" = League\r\n */\r\n rankType?: '1' | '8' | '15' | '16';\r\n /**\r\n * Sub-endpoint type:\r\n * - \"list\" (default) — ranked users with scores\r\n * - \"entrance\" — available ranking tabs/metadata\r\n */\r\n type?: 'list' | 'entrance';\r\n /** Gap interval filter (default: \"0\") */\r\n gapInterval?: string;\r\n /**\r\n * TikTok session cookie string for authentication.\r\n * Passed to the API server for proxied requests.\r\n * Example: \"sessionid=abc123; sessionid_ss=abc123\"\r\n */\r\n sessionCookie?: string;\r\n /**\r\n * Client's real IP address (for deployed server scenarios).\r\n * When running on a remote server, pass the end-user's IP so the\r\n * API server can proxy the TikTok request from the correct IP.\r\n */\r\n clientIp?: string;\r\n}\r\n\r\nexport interface RegionalRanklistSignedResponse {\r\n /** Always 0 on success */\r\n status_code: number;\r\n /** Always \"fetch_signed_url\" */\r\n action: string;\r\n /** The signed TikTok URL to POST */\r\n signed_url: string;\r\n /** HTTP method (always POST) */\r\n method: string;\r\n /** Required headers for the fetch */\r\n headers: Record<string, string>;\r\n /** URL-encoded POST body */\r\n body: string;\r\n /** Cookies to include (ttwid etc.) — append your sessionid */\r\n cookies: string;\r\n /** Human-readable note */\r\n note: string;\r\n}\r\n\r\n/**\r\n * Get a signed URL for fetching regional LIVE leaderboard data.\r\n *\r\n * **Two-step pattern**: TikTok sessions are IP-bound, so instead of\r\n * server-side fetching, this returns a signed URL with headers/body\r\n * that you POST from your own IP with your session cookie.\r\n *\r\n * Requires **Pro** or **Ultra** API key tier.\r\n *\r\n * @example\r\n * ```ts\r\n * // Step 1: Get signed URL\r\n * const signed = await getRegionalRanklist({\r\n * apiKey: 'your-pro-key',\r\n * roomId: '7607695933891218198',\r\n * anchorId: '7444599004337652758',\r\n * rankType: '8', // Daily\r\n * });\r\n *\r\n * // Step 2: Fetch from YOUR IP with YOUR session\r\n * const resp = await fetch(signed.signed_url, {\r\n * method: signed.method,\r\n * headers: { ...signed.headers, Cookie: `sessionid=YOUR_SID; ${signed.cookies}` },\r\n * body: signed.body,\r\n * });\r\n * const { data } = await resp.json();\r\n * data.rank_view.ranks.forEach((r, i) =>\r\n * console.log(`${i+1}. ${r.user.nickname} — ${r.score} pts`)\r\n * );\r\n * ```\r\n */\r\nexport async function getRegionalRanklist(opts: GetRegionalRanklistOptions): Promise<RegionalRanklistSignedResponse> {\r\n const base = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const ak = encodeURIComponent(opts.apiKey);\r\n\r\n const body: Record<string, any> = {};\r\n if (opts.uniqueId) body.unique_id = opts.uniqueId;\r\n if (opts.roomId) body.room_id = opts.roomId;\r\n if (opts.anchorId) body.anchor_id = opts.anchorId;\r\n if (opts.rankType) body.rank_type = opts.rankType;\r\n if (opts.type) body.type = opts.type;\r\n if (opts.gapInterval) body.gap_interval = opts.gapInterval;\r\n if (opts.sessionCookie) body.session_cookie = opts.sessionCookie;\r\n if (opts.clientIp) body.client_ip = opts.clientIp;\r\n\r\n const resp = await fetch(`${base}/webcast/ranklist/regional?apiKey=${ak}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(body),\r\n });\r\n\r\n const data = await resp.json() as any;\r\n\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Regional ranklist failed (status ${data.status_code})`);\r\n }\r\n\r\n return data as RegionalRanklistSignedResponse;\r\n}\r\n"],"mappings":"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","names":["WebSocket","import_events","import_ws","WebSocket","DEFAULT_SIGN_SERVER","DEFAULT_SIGN_SERVER","DEFAULT_SIGN_SERVER"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/proto.ts","../src/captions.ts","../src/api.ts"],"sourcesContent":["export { TikTokLive } from './client.js';\r\nexport { TikTokCaptions } from './captions.js';\r\nexport { getRanklist, getRegionalRanklist, getLiveFeed, fetchFeed } from './api.js';\r\nexport type { GetRanklistOptions, GetRegionalRanklistOptions, RegionalRanklistSignedResponse, GetLiveFeedOptions } from './api.js';\r\nexport type {\r\n TikTokCaptionsOptions,\r\n TikTokCaptionsEvents,\r\n CaptionData,\r\n TranslationData,\r\n CaptionCredits,\r\n CaptionStatus,\r\n CaptionError,\r\n} from './captions.js';\r\n\r\nexport type {\r\n TikTokLiveOptions,\r\n TikTokLiveEvents,\r\n RoomInfo,\r\n LiveEvent,\r\n BaseEvent,\r\n ChatEvent,\r\n MemberEvent,\r\n LikeEvent,\r\n GiftEvent,\r\n SocialEvent,\r\n RoomUserSeqEvent,\r\n BattleEvent,\r\n BattleArmiesEvent,\r\n BattleTeam,\r\n BattleTeamUser,\r\n BattleTaskEvent,\r\n BarrageEvent,\r\n SubscribeEvent,\r\n EmoteChatEvent,\r\n EnvelopeEvent,\r\n QuestionEvent,\r\n ControlEvent,\r\n RoomEvent,\r\n LiveIntroEvent,\r\n RankUpdateEvent,\r\n LinkMicEvent,\r\n UnknownEvent,\r\n TikTokUser,\r\n RanklistUser,\r\n OnlineAudienceEntry,\r\n AnchorRankListEntry,\r\n RanklistSelfInfo,\r\n OnlineAudienceResponse,\r\n AnchorRankListResponse,\r\n EntranceTab,\r\n EntranceInfo,\r\n EntranceResponse,\r\n RanklistResponse,\r\n FeedRoomOwner,\r\n FeedRoom,\r\n FeedSignedResponse,\r\n} from './types.js';\r\n","import { EventEmitter } from 'events';\r\nimport * as http from 'http';\r\nimport * as https from 'https';\r\nimport * as zlib from 'zlib';\r\nimport { URL } from 'url';\r\nimport WebSocket from 'ws';\r\nimport type { TikTokLiveOptions, TikTokLiveEvents, RoomInfo, LiveEvent } from './types.js';\r\nimport {\r\n decodeProto,\r\n getStr,\r\n getBytes,\r\n buildHeartbeat,\r\n buildImEnterRoom,\r\n buildAck,\r\n parseWebcastResponse,\r\n} from './proto.js';\r\n\r\nconst DEFAULT_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';\r\nconst DEFAULT_SIGN_SERVER = 'https://api.tik.tools';\r\n\r\nfunction httpGet(url: string, headers: Record<string, string>, agent?: http.Agent): Promise<{\r\n status: number;\r\n headers: http.IncomingHttpHeaders;\r\n body: Buffer;\r\n}> {\r\n return new Promise((resolve, reject) => {\r\n const mod = url.startsWith('https') ? https : http;\r\n const opts: https.RequestOptions = { headers, agent };\r\n const parsed = new URL(url);\r\n const req = mod.get(parsed, opts, (res) => {\r\n const chunks: Buffer[] = [];\r\n const enc = res.headers['content-encoding'];\r\n const stream = (enc === 'gzip' || enc === 'br')\r\n ? res.pipe(enc === 'br' ? zlib.createBrotliDecompress() : zlib.createGunzip())\r\n : res;\r\n stream.on('data', (c: Buffer) => chunks.push(c));\r\n stream.on('end', () => resolve({\r\n status: res.statusCode || 0,\r\n headers: res.headers,\r\n body: Buffer.concat(chunks),\r\n }));\r\n stream.on('error', reject);\r\n });\r\n req.on('error', reject);\r\n req.setTimeout(15_000, () => { req.destroy(); reject(new Error('Request timeout')); });\r\n });\r\n}\r\n\r\nfunction getWsHost(clusterRegion: string): string {\r\n if (!clusterRegion) return 'webcast-ws.tiktok.com';\r\n const r = clusterRegion.toLowerCase();\r\n if (r.startsWith('eu') || r.includes('eu')) return 'webcast-ws.eu.tiktok.com';\r\n if (r.startsWith('us') || r.includes('us')) return 'webcast-ws.us.tiktok.com';\r\n return 'webcast-ws.tiktok.com';\r\n}\r\n\r\nexport class TikTokLive extends EventEmitter {\r\n private ws: WebSocket | null = null;\r\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\r\n private reconnectAttempts = 0;\r\n private intentionalClose = false;\r\n private _connected = false;\r\n private _eventCount = 0;\r\n private _roomId = '';\r\n private _ownerUserId = '';\r\n\r\n private readonly uniqueId: string;\r\n private readonly signServerUrl: string;\r\n private readonly apiKey: string;\r\n private readonly autoReconnect: boolean;\r\n private readonly maxReconnectAttempts: number;\r\n private readonly heartbeatInterval: number;\r\n private readonly debug: boolean;\r\n private _sessionId?: string;\r\n private _ttTargetIdc?: string;\r\n private readonly proxyAgent?: http.Agent;\r\n private readonly _presetRoomId?: string;\r\n private readonly _presetTtwid?: string;\r\n\r\n constructor(options: TikTokLiveOptions) {\r\n super();\r\n this.uniqueId = options.uniqueId.replace(/^@/, '');\r\n this.signServerUrl = (options.signServerUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n if (!options.apiKey) throw new Error('apiKey is required. Get a free key at https://tik.tools');\r\n this.apiKey = options.apiKey;\r\n this.autoReconnect = options.autoReconnect ?? true;\r\n this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;\r\n this.heartbeatInterval = options.heartbeatInterval ?? 10_000;\r\n this.debug = options.debug ?? false;\r\n this._sessionId = options.sessionId;\r\n this._ttTargetIdc = options.ttTargetIdc;\r\n if (options.agent) {\r\n this.proxyAgent = options.agent;\r\n }\r\n this._presetRoomId = options.roomId;\r\n this._presetTtwid = options.ttwid;\r\n }\r\n\r\n async connect(): Promise<void> {\r\n this.intentionalClose = false;\r\n\r\n let ttwid = this._presetTtwid || '';\r\n let roomId = this._presetRoomId || '';\r\n let ownerUserId = '';\r\n let clusterRegion = '';\r\n\r\n // If roomId AND ttwid are preset, skip the HTTP fetch entirely\r\n // If roomId is NOT preset, fetch the live page to discover it + get ttwid\r\n if (!roomId || !ttwid) {\r\n const targetUrl = `https://www.tiktok.com/@${this.uniqueId}/live`;\r\n const resp = await httpGet(targetUrl, {\r\n 'User-Agent': DEFAULT_UA,\r\n 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\r\n 'Accept-Encoding': 'gzip, deflate, br',\r\n 'Accept-Language': 'en-US,en;q=0.9',\r\n }, this.proxyAgent);\r\n\r\n for (const sc of [resp.headers['set-cookie'] || []].flat()) {\r\n if (typeof sc === 'string' && sc.startsWith('ttwid=')) {\r\n ttwid = sc.split(';')[0].split('=').slice(1).join('=');\r\n break;\r\n }\r\n }\r\n if (!ttwid) throw new Error('Failed to obtain session cookie');\r\n\r\n const html = resp.body.toString();\r\n const sigiMatch = html.match(/id=\"SIGI_STATE\"[^>]*>([^<]+)/);\r\n if (sigiMatch) {\r\n try {\r\n const json = JSON.parse(sigiMatch[1]);\r\n const jsonStr = JSON.stringify(json);\r\n const m = jsonStr.match(/\"roomId\"\\s*:\\s*\"(\\d+)\"/);\r\n if (m) roomId = m[1];\r\n const ownerUser = json?.LiveRoom?.liveRoomUserInfo?.user;\r\n if (ownerUser?.id) {\r\n ownerUserId = String(ownerUser.id);\r\n }\r\n } catch { }\r\n }\r\n if (!roomId) throw new Error(`User @${this.uniqueId} is not currently live`);\r\n const crMatch = html.match(/\"clusterRegion\"\\s*:\\s*\"([^\"]+)\"/);\r\n clusterRegion = crMatch ? crMatch[1] : '';\r\n }\r\n\r\n // roomId is now guaranteed — either preset or discovered from HTML\r\n this._roomId = roomId;\r\n this._ownerUserId = ownerUserId;\r\n\r\n const wsHost = getWsHost(clusterRegion);\r\n\r\n const wsParams = new URLSearchParams({\r\n version_code: '270000', device_platform: 'web', cookie_enabled: 'true',\r\n screen_width: '1920', screen_height: '1080', browser_language: 'en-US',\r\n browser_platform: 'Win32', browser_name: 'Mozilla',\r\n browser_version: DEFAULT_UA.split('Mozilla/')[1] || '5.0',\r\n browser_online: 'true', tz_name: Intl.DateTimeFormat().resolvedOptions().timeZone,\r\n app_name: 'tiktok_web', sup_ws_ds_opt: '1', update_version_code: '2.0.0',\r\n compress: 'gzip', webcast_language: 'en', ws_direct: '1', aid: '1988',\r\n live_id: '12', app_language: 'en', client_enter: '1', room_id: roomId,\r\n identity: 'audience', history_comment_count: '6', last_rtt: '0',\r\n heartbeat_duration: '10000', resp_content_type: 'protobuf', did_rule: '3',\r\n });\r\n\r\n const rawWsUrl = `https://${wsHost}/webcast/im/ws_proxy/ws_reuse_supplement/?${wsParams}`;\r\n\r\n const signUrl = `${this.signServerUrl}/webcast/sign_url`;\r\n\r\n let wsUrl: string;\r\n try {\r\n const signResp = await fetch(signUrl, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'x-api-key': this.apiKey,\r\n },\r\n body: JSON.stringify({ url: rawWsUrl }),\r\n });\r\n const signData = await signResp.json() as Record<string, any>;\r\n\r\n if (signData.status_code === 0 && signData.data?.signed_url) {\r\n wsUrl = (signData.data.signed_url as string).replace(/^https:\\/\\//, 'wss://');\r\n } else {\r\n wsUrl = rawWsUrl.replace(/^https:\\/\\//, 'wss://');\r\n }\r\n } catch {\r\n wsUrl = rawWsUrl.replace(/^https:\\/\\//, 'wss://');\r\n }\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n // Build cookie header — only include ttwid if we have it\r\n let cookieHeader = ttwid ? `ttwid=${ttwid}` : '';\r\n if (this._sessionId) {\r\n const sessionCookies = `sessionid=${this._sessionId}; sessionid_ss=${this._sessionId}; sid_tt=${this._sessionId}`;\r\n cookieHeader = cookieHeader ? `${cookieHeader}; ${sessionCookies}` : sessionCookies;\r\n if (this._ttTargetIdc) {\r\n cookieHeader += `; tt-target-idc=${this._ttTargetIdc}`;\r\n }\r\n }\r\n\r\n this.ws = new WebSocket(wsUrl, {\r\n headers: {\r\n 'User-Agent': DEFAULT_UA,\r\n 'Cookie': cookieHeader,\r\n 'Origin': 'https://www.tiktok.com',\r\n },\r\n agent: this.proxyAgent,\r\n });\r\n\r\n this.ws.on('open', () => {\r\n this._connected = true;\r\n this.reconnectAttempts = 0;\r\n\r\n this.ws!.send(buildHeartbeat(roomId));\r\n this.ws!.send(buildImEnterRoom(roomId));\r\n this.startHeartbeat(roomId);\r\n\r\n const roomInfo: RoomInfo = {\r\n roomId,\r\n wsHost,\r\n clusterRegion,\r\n connectedAt: new Date().toISOString(),\r\n ownerUserId: ownerUserId || undefined,\r\n };\r\n\r\n this.emit('connected');\r\n this.emit('roomInfo', roomInfo);\r\n resolve();\r\n });\r\n\r\n this.ws.on('message', (rawData: Buffer) => {\r\n this.handleFrame(Buffer.from(rawData));\r\n });\r\n\r\n this.ws.on('close', (code, reason) => {\r\n this._connected = false;\r\n this.stopHeartbeat();\r\n\r\n const reasonStr = reason?.toString() || '';\r\n this.emit('disconnected', code, reasonStr);\r\n\r\n if (!this.intentionalClose && this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {\r\n this.reconnectAttempts++;\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts - 1), 30_000);\r\n setTimeout(() => this.connect().catch(e => this.emit('error', e)), delay);\r\n }\r\n });\r\n\r\n this.ws.on('error', (err) => {\r\n this.emit('error', err);\r\n if (!this._connected) reject(err);\r\n });\r\n });\r\n }\r\n\r\n disconnect(): void {\r\n this.intentionalClose = true;\r\n this.stopHeartbeat();\r\n if (this.ws) {\r\n this.ws.close(1000);\r\n this.ws = null;\r\n }\r\n this._connected = false;\r\n }\r\n\r\n get connected(): boolean {\r\n return this._connected;\r\n }\r\n\r\n get eventCount(): number {\r\n return this._eventCount;\r\n }\r\n\r\n get roomId(): string {\r\n return this._roomId;\r\n }\r\n\r\n /** Get the stored session ID (if any) */\r\n get sessionId(): string | undefined {\r\n return this._sessionId;\r\n }\r\n\r\n /** Update the session ID at runtime (e.g. after TikTok login) */\r\n setSession(sessionId: string, ttTargetIdc?: string): void {\r\n this._sessionId = sessionId;\r\n if (ttTargetIdc) this._ttTargetIdc = ttTargetIdc;\r\n }\r\n\r\n /**\r\n * Build a cookie header string for authenticated API requests (e.g. ranklist).\r\n * Returns undefined if no session is set.\r\n */\r\n buildSessionCookieHeader(): string | undefined {\r\n if (!this._sessionId) return undefined;\r\n const parts = [\r\n `sessionid=${this._sessionId}`,\r\n `sessionid_ss=${this._sessionId}`,\r\n `sid_tt=${this._sessionId}`,\r\n ];\r\n if (this._ttTargetIdc) parts.push(`tt-target-idc=${this._ttTargetIdc}`);\r\n return parts.join('; ');\r\n }\r\n\r\n on<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.on(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n once<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.once(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n off<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.off(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n emit<K extends keyof TikTokLiveEvents>(event: K, ...args: Parameters<TikTokLiveEvents[K]>): boolean {\r\n return super.emit(event, ...args);\r\n }\r\n\r\n private handleFrame(buf: Buffer): void {\r\n try {\r\n const fields = decodeProto(buf);\r\n const idField = fields.find(f => f.fn === 2 && f.wt === 0);\r\n const id = idField ? idField.value as bigint : 0n;\r\n const type = getStr(fields, 7);\r\n const binary = getBytes(fields, 8);\r\n\r\n if (id > 0n && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(buildAck(id));\r\n }\r\n\r\n if (type === 'msg' && binary && binary.length > 0) {\r\n let inner = binary;\r\n if (inner.length > 2 && inner[0] === 0x1f && inner[1] === 0x8b) {\r\n try { inner = zlib.gunzipSync(inner); } catch { }\r\n }\r\n\r\n const events = parseWebcastResponse(inner);\r\n for (const evt of events) {\r\n this._eventCount++;\r\n this.emit('event', evt);\r\n this.emit(evt.type as keyof TikTokLiveEvents, evt as any);\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n private startHeartbeat(roomId: string): void {\r\n this.stopHeartbeat();\r\n this.heartbeatTimer = setInterval(() => {\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(buildHeartbeat(roomId));\r\n }\r\n }, this.heartbeatInterval);\r\n }\r\n\r\n private stopHeartbeat(): void {\r\n if (this.heartbeatTimer) {\r\n clearInterval(this.heartbeatTimer);\r\n this.heartbeatTimer = null;\r\n }\r\n }\r\n}\r\n","import type { LiveEvent, TikTokUser, BattleTeam, BattleTeamUser } from './types.js';\r\n\r\nconst encoder = new TextEncoder();\r\nconst decoder = new TextDecoder();\r\n\r\n// TikTok LIVE emote name → Unicode emoji mapping\r\n// TikTok uses [emote_name] placeholders in chat text. These are resolved client-side.\r\nconst TIKTOK_EMOTE_MAP: Record<string, string> = {\r\n // Standard emojis\r\n 'happy': '😊', 'angry': '😡', 'cry': '😢', 'embarrassed': '😳',\r\n 'surprised': '😮', 'wronged': '😞', 'shout': '😤', 'flushed': '😳',\r\n 'yummy': '😋', 'complacent': '😌', 'drool': '🤤', 'scream': '😱',\r\n 'weep': '😭', 'speechless': '😶', 'funnyface': '🤪', 'laughwithtears': '😂',\r\n 'wicked': '😈', 'facewithrollingeyes': '🙄', 'sulk': '😒', 'thinking': '🤔',\r\n 'lovely': '🥰', 'greedy': '🤑', 'wow': '😯', 'joyful': '😃',\r\n 'hehe': '😁', 'slap': '👋', 'tears': '😿', 'stun': '😵',\r\n 'cute': '🥺', 'blink': '😉', 'disdain': '😏', 'astonish': '😲',\r\n 'cool': '😎', 'excited': '🤩', 'proud': '😤', 'smileface': '😊',\r\n 'evil': '👿', 'angel': '😇', 'laugh': '😆', 'pride': '🦁',\r\n 'nap': '😴', 'loveface': '😍', 'awkward': '😬', 'shock': '😨',\r\n 'funny': '😄', 'rage': '🤬',\r\n // Common aliases used in TikTok LIVE\r\n 'laughcry': '😂', 'heart': '❤️', 'like': '👍', 'love': '💕',\r\n 'shy': '🙈', 'smile': '😊',\r\n};\r\n\r\n/** Replace [emote_name] placeholders with Unicode emoji equivalents */\r\nfunction replaceEmotes(text: string, imageMap?: Record<string, string>): string {\r\n return text.replace(/\\[([a-zA-Z_]+)\\]/g, (match, name) => {\r\n const lower = name.toLowerCase();\r\n const emoji = TIKTOK_EMOTE_MAP[lower];\r\n return emoji || match; // Keep original if unknown\r\n });\r\n}\r\n\r\nexport function concatBytes(...arrays: Uint8Array[]): Uint8Array {\r\n let totalLength = 0;\r\n for (const arr of arrays) totalLength += arr.length;\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const arr of arrays) {\r\n result.set(arr, offset);\r\n offset += arr.length;\r\n }\r\n return result;\r\n}\r\n\r\nfunction readInt32LE(buf: Uint8Array, offset: number): number {\r\n return buf[offset] |\r\n (buf[offset + 1] << 8) |\r\n (buf[offset + 2] << 16) |\r\n (buf[offset + 3] << 24);\r\n}\r\n\r\nfunction readBigInt64LE(buf: Uint8Array, offset: number): bigint {\r\n const lo = BigInt(buf[offset] |\r\n (buf[offset + 1] << 8) |\r\n (buf[offset + 2] << 16) |\r\n ((buf[offset + 3] << 24) >>> 0));\r\n const hi = BigInt(buf[offset + 4] |\r\n (buf[offset + 5] << 8) |\r\n (buf[offset + 6] << 16) |\r\n ((buf[offset + 7] << 24) >>> 0));\r\n return (hi << 32n) | (lo & 0xFFFFFFFFn);\r\n}\r\n\r\nexport function decodeVarint(buf: Uint8Array, offset: number): { value: number; offset: number } {\r\n let result = 0, shift = 0;\r\n while (offset < buf.length) {\r\n const byte = buf[offset++];\r\n result |= (byte & 0x7F) << shift;\r\n if ((byte & 0x80) === 0) break;\r\n shift += 7;\r\n }\r\n return { value: result >>> 0, offset };\r\n}\r\n\r\nexport function decodeVarint64(buf: Uint8Array, offset: number): { value: bigint; offset: number } {\r\n let result = 0n, shift = 0n;\r\n while (offset < buf.length) {\r\n const byte = BigInt(buf[offset++]);\r\n result |= (byte & 0x7Fn) << shift;\r\n if ((byte & 0x80n) === 0n) break;\r\n shift += 7n;\r\n }\r\n return { value: result, offset };\r\n}\r\n\r\nexport function encodeVarint(v: number | bigint): Uint8Array {\r\n const bytes: number[] = [];\r\n let n = typeof v === 'bigint' ? v : BigInt(v);\r\n do {\r\n let b = Number(n & 0x7Fn);\r\n n >>= 7n;\r\n if (n > 0n) b |= 0x80;\r\n bytes.push(b);\r\n } while (n > 0n);\r\n return new Uint8Array(bytes);\r\n}\r\n\r\nexport interface ProtoField {\r\n fn: number;\r\n wt: number;\r\n value: Uint8Array | number | bigint;\r\n}\r\n\r\nexport function encodeField(fn: number, wt: number, value: Uint8Array | bigint | number | string): Uint8Array {\r\n const tag = encodeVarint((fn << 3) | wt);\r\n if (wt === 0) {\r\n return concatBytes(tag, encodeVarint(typeof value === 'number' ? BigInt(value) : value as bigint));\r\n }\r\n const data = typeof value === 'string' ? encoder.encode(value) : value as Uint8Array;\r\n return concatBytes(tag, encodeVarint(data.length), data);\r\n}\r\n\r\nexport function decodeProto(buf: Uint8Array): ProtoField[] {\r\n const fields: ProtoField[] = [];\r\n let offset = 0;\r\n while (offset < buf.length) {\r\n const tagResult = decodeVarint(buf, offset);\r\n offset = tagResult.offset;\r\n const fn = tagResult.value >> 3;\r\n const wt = tagResult.value & 7;\r\n if (wt === 0) {\r\n const r = decodeVarint64(buf, offset);\r\n offset = r.offset;\r\n fields.push({ fn, wt, value: r.value });\r\n } else if (wt === 2) {\r\n const lenR = decodeVarint(buf, offset);\r\n offset = lenR.offset;\r\n const data = buf.subarray(offset, offset + lenR.value);\r\n offset += lenR.value;\r\n fields.push({ fn, wt, value: data });\r\n } else if (wt === 1) {\r\n fields.push({ fn, wt, value: readBigInt64LE(buf, offset) });\r\n offset += 8;\r\n } else if (wt === 5) {\r\n fields.push({ fn, wt, value: BigInt(readInt32LE(buf, offset)) });\r\n offset += 4;\r\n } else {\r\n break;\r\n }\r\n }\r\n return fields;\r\n}\r\n\r\nexport function getStr(fields: ProtoField[], fn: number): string {\r\n const f = fields.find(x => x.fn === fn && x.wt === 2);\r\n return f ? decoder.decode(f.value as Uint8Array) : '';\r\n}\r\n\r\nexport function getBytes(fields: ProtoField[], fn: number): Uint8Array | null {\r\n const f = fields.find(x => x.fn === fn && x.wt === 2);\r\n return f ? f.value as Uint8Array : null;\r\n}\r\n\r\nexport function getInt(fields: ProtoField[], fn: number): number {\r\n const f = fields.find(x => x.fn === fn && x.wt === 0);\r\n return f ? Number(f.value) : 0;\r\n}\r\n\r\n/** Get a varint field as a string without Number precision loss (safe for 64-bit IDs). */\r\nexport function getIntStr(fields: ProtoField[], fn: number): string {\r\n const f = fields.find(x => x.fn === fn && x.wt === 0);\r\n return f ? String(f.value) : '';\r\n}\r\n\r\nexport function getAllBytes(fields: ProtoField[], fn: number): Uint8Array[] {\r\n return fields.filter(x => x.fn === fn && x.wt === 2).map(x => x.value as Uint8Array);\r\n}\r\n\r\nexport function buildHeartbeat(roomId: string): Uint8Array {\r\n const payload = encodeField(1, 0, BigInt(roomId));\r\n return concatBytes(\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'hb'),\r\n encodeField(8, 2, payload),\r\n );\r\n}\r\n\r\nexport function buildImEnterRoom(roomId: string): Uint8Array {\r\n const inner = concatBytes(\r\n encodeField(1, 0, BigInt(roomId)),\r\n encodeField(4, 0, 12n),\r\n encodeField(5, 2, 'audience'),\r\n encodeField(6, 2, ''),\r\n encodeField(9, 2, ''),\r\n encodeField(10, 2, ''),\r\n );\r\n return concatBytes(\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'im_enter_room'),\r\n encodeField(8, 2, inner),\r\n );\r\n}\r\n\r\nexport function buildAck(id: bigint): Uint8Array {\r\n return concatBytes(\r\n encodeField(2, 0, id),\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'ack'),\r\n );\r\n}\r\n\r\n// NOTE: Army users in WebcastLinkMicArmies use a DIFFERENT layout where\r\n// field 38 contains avatar/image data, NOT uniqueId.\r\n// We detect this by checking if the string looks like a username (short, no URLs).\r\nfunction looksLikeUsername(s: string): boolean {\r\n return s.length > 0 && s.length <= 50 && !s.includes('://') && !s.includes('\\x00');\r\n}\r\n\r\nfunction parseUser(data: Uint8Array): TikTokUser {\r\n const f = decodeProto(data);\r\n const id = getIntStr(f, 1) || getStr(f, 1);\r\n const nickname = getStr(f, 3) || getStr(f, 2);\r\n\r\n // uniqueId: Try field 4 (LinkUser format) first, then field 38 (User format)\r\n // Validate that the result looks like a username, not binary/URL data\r\n let uniqueId = '';\r\n const uid4 = getStr(f, 4);\r\n const uid38 = getStr(f, 38);\r\n if (uid4 && looksLikeUsername(uid4)) {\r\n uniqueId = uid4;\r\n } else if (uid38 && looksLikeUsername(uid38)) {\r\n uniqueId = uid38;\r\n } else {\r\n const uid2 = getStr(f, 2);\r\n if (uid2 && looksLikeUsername(uid2)) uniqueId = uid2;\r\n }\r\n\r\n // profilePicture: Try field 9 (User format), field 4 (army users — avatar at field 4), field 3 (LinkUser format)\r\n // Can't use OR chain because field 3 = nickname bytes in User format (truthy but wrong)\r\n // Must try each and pick the first that yields a valid URL\r\n let profilePicture: string | undefined;\r\n for (const fieldNum of [9, 4, 3]) {\r\n if (profilePicture) break;\r\n const avatarBuf = getBytes(f, fieldNum);\r\n if (!avatarBuf) continue;\r\n try {\r\n const avatarFields = decodeProto(avatarBuf);\r\n const urlBufs = getAllBytes(avatarFields, 1);\r\n for (const urlBuf of urlBufs) {\r\n const url = decoder.decode(urlBuf);\r\n if (url.includes('://')) {\r\n profilePicture = url;\r\n break;\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n const badges: string[] = [];\r\n let payGrade: number | undefined;\r\n\r\n // field_64 is repeated — iterate ALL occurrences\r\n const badgeBufs = getAllBytes(f, 64);\r\n for (const badgeBuf of badgeBufs) {\r\n try {\r\n const badgeFields = decodeProto(badgeBuf);\r\n const badgeCategory = getInt(badgeFields, 2); // 20 = user level, 30 = fan level, 10 = moderator\r\n const badgeSubCat = getInt(badgeFields, 3); // 8 = grade/level\r\n\r\n // Extract user level (pay grade) from the user-level badge\r\n // Badge with category=20, subCategory=8 is the \"user level\" (gifter grade) badge\r\n if (badgeCategory === 20 && badgeSubCat === 8 && !payGrade) {\r\n const privBuf = getBytes(badgeFields, 12);\r\n if (privBuf) {\r\n const privFields = decodeProto(privBuf);\r\n const levelStr = getStr(privFields, 5);\r\n if (levelStr) {\r\n const level = parseInt(levelStr, 10);\r\n if (level > 0) payGrade = level;\r\n }\r\n }\r\n }\r\n\r\n // Also extract badge names from field_21 (existing behavior)\r\n const badgeItems = getAllBytes(badgeFields, 21);\r\n for (const bi of badgeItems) {\r\n const bf = decodeProto(bi);\r\n const name = getStr(bf, 3) || getStr(bf, 2);\r\n if (name) badges.push(name);\r\n }\r\n } catch { }\r\n }\r\n\r\n return {\r\n id,\r\n nickname,\r\n uniqueId: uniqueId || nickname || id,\r\n profilePictureUrl: profilePicture,\r\n badges: badges.length > 0 ? badges : undefined,\r\n payGrade,\r\n };\r\n}\r\n\r\nfunction parseBattleTeamFromArmies(itemBuf: Uint8Array): BattleTeam {\r\n const f = decodeProto(itemBuf);\r\n const hostUserId = getIntStr(f, 1) || '0';\r\n let teamScore = 0;\r\n const users: BattleTeamUser[] = [];\r\n let hostUser: TikTokUser | undefined;\r\n\r\n // Field 2: Groups of gifter users (repeated)\r\n const groups = getAllBytes(f, 2);\r\n for (const gb of groups) {\r\n try {\r\n const gf = decodeProto(gb);\r\n const points = getInt(gf, 2);\r\n teamScore += points;\r\n\r\n const userBufs = getAllBytes(gf, 1);\r\n for (const ub of userBufs) {\r\n try {\r\n // TikTok's LinkMicArmies user entry is a flat proto:\r\n // f1(varint) = userId\r\n // f2(varint) = individual score (diamonds gifted by this user)\r\n // f3(bytes) = nickname\r\n // f4(bytes) = avatar/profile data\r\n // f6(bytes) = badge data\r\n // The individual score is f2 varint — same buffer, no wrapper.\r\n const userFields = decodeProto(ub);\r\n const individualScore = getInt(userFields, 2);\r\n const user = parseUser(ub);\r\n users.push({ user, score: individualScore });\r\n } catch { }\r\n }\r\n } catch { }\r\n }\r\n\r\n // Try to extract the HOST USER from other fields (3-8)\r\n // NOTE: Currently armies protobuf only has fields [1, 2], but kept\r\n // as a silent fallback in case TikTok adds host user data later.\r\n for (const fieldNum of [3, 4, 5, 6, 7, 8]) {\r\n if (hostUser) break;\r\n const buf = getBytes(f, fieldNum);\r\n if (!buf) continue;\r\n try {\r\n const parsed = parseUser(buf);\r\n if (parsed && (parsed.nickname || parsed.uniqueId) &&\r\n parsed.uniqueId !== parsed.id &&\r\n looksLikeUsername(parsed.uniqueId || '')) {\r\n hostUser = parsed;\r\n }\r\n } catch { }\r\n }\r\n\r\n return { hostUserId, score: teamScore, users, hostUser };\r\n}\r\n\r\nexport function parseWebcastMessage(method: string, payload: Uint8Array): LiveEvent {\r\n const f = decodeProto(payload);\r\n const base = { timestamp: Date.now(), msgId: '' };\r\n\r\n const typeBuf = getBytes(f, 1);\r\n if (typeBuf) {\r\n try {\r\n const tf = decodeProto(typeBuf);\r\n const ts = getInt(tf, 4);\r\n if (ts) base.timestamp = ts;\r\n } catch { }\r\n }\r\n\r\n switch (method) {\r\n case 'WebcastChatMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n const rawComment = getStr(f, 3);\r\n\r\n // Parse emote images from field 22 (emotes_with_index) if present\r\n const emoteImages: Record<string, string> = {};\r\n for (const field of f) {\r\n if (field.fn === 22 && field.wt === 2) {\r\n try {\r\n const ewi = decodeProto(field.value as Uint8Array);\r\n const emoteKey = getStr(ewi, 1);\r\n const imgBuf = getBytes(ewi, 2);\r\n if (imgBuf && emoteKey) {\r\n const imgFields = decodeProto(imgBuf);\r\n const url = getStr(imgFields, 1);\r\n if (url) emoteImages[emoteKey] = url;\r\n }\r\n } catch { }\r\n }\r\n }\r\n\r\n // Replace [emote_name] placeholders with Unicode equivalents\r\n const comment = replaceEmotes(rawComment, emoteImages);\r\n\r\n return { ...base, type: 'chat' as const, user, comment };\r\n }\r\n\r\n case 'WebcastMemberMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n let action = 1;\r\n const eventBuf = getBytes(f, 1);\r\n if (eventBuf) {\r\n const ef = decodeProto(eventBuf);\r\n const detailBuf = getBytes(ef, 8);\r\n if (detailBuf) {\r\n const df = decodeProto(detailBuf);\r\n const label = getStr(df, 2);\r\n if (label.includes('followed')) action = 2;\r\n else if (label.includes('share')) action = 3;\r\n }\r\n }\r\n return { ...base, type: 'member' as const, user, action };\r\n }\r\n\r\n case 'WebcastLikeMessage': {\r\n const userBuf = getBytes(f, 5);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n return {\r\n ...base, type: 'like' as const, user,\r\n likeCount: getInt(f, 2),\r\n totalLikes: getInt(f, 3),\r\n };\r\n }\r\n\r\n case 'WebcastGiftMessage': {\r\n const userBuf = getBytes(f, 7);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n const giftId = getInt(f, 2);\r\n const repeatCount = getInt(f, 5);\r\n const repeatEnd = getInt(f, 9) === 1;\r\n\r\n let giftName = '', diamondCount = 0, giftType = 0;\r\n let giftImageUrl = '';\r\n const detailsBuf = getBytes(f, 15);\r\n if (detailsBuf) {\r\n const df = decodeProto(detailsBuf);\r\n giftName = getStr(df, 16) || getStr(df, 2);\r\n diamondCount = getInt(df, 12);\r\n giftType = getInt(df, 11);\r\n\r\n const imgBuf = getBytes(df, 1);\r\n if (imgBuf) {\r\n const imgf = decodeProto(imgBuf);\r\n giftImageUrl = getStr(imgf, 1);\r\n }\r\n }\r\n\r\n let toUserId = '';\r\n const extraBuf = getBytes(f, 23);\r\n if (extraBuf) {\r\n const ef = decodeProto(extraBuf);\r\n toUserId = getIntStr(ef, 8) || '';\r\n }\r\n\r\n const groupId = toUserId || getStr(f, 11);\r\n\r\n return {\r\n ...base, type: 'gift' as const, user, giftId, giftName, diamondCount,\r\n repeatCount, repeatEnd, combo: repeatCount > 1 && !repeatEnd,\r\n giftType, groupId, giftPictureUrl: giftImageUrl,\r\n };\r\n }\r\n\r\n case 'WebcastSocialMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n let action = 'follow';\r\n const eventBuf = getBytes(f, 1);\r\n if (eventBuf) {\r\n const ef = decodeProto(eventBuf);\r\n const detailBuf = getBytes(ef, 8);\r\n if (detailBuf) {\r\n const df = decodeProto(detailBuf);\r\n const label = getStr(df, 2);\r\n if (label.includes('share')) action = 'share';\r\n else if (label.includes('follow')) action = 'follow';\r\n const displayType = getStr(df, 1);\r\n if (displayType === 'pm_mt_msg_viewer_share') action = 'share';\r\n }\r\n }\r\n return { ...base, type: 'social' as const, user, action };\r\n }\r\n\r\n case 'WebcastRoomUserSeqMessage': {\r\n const viewerCount = getInt(f, 3) || getInt(f, 2);\r\n const totalViewers = getInt(f, 1) || viewerCount;\r\n return { ...base, type: 'roomUserSeq' as const, totalViewers, viewerCount };\r\n }\r\n\r\n case 'WebcastLinkMicBattle': {\r\n // Proto field mapping for WebcastLinkMicBattle:\r\n // field_1 = header sub-message (method name, msgId, roomId, timestamp)\r\n // field_2 = battleId (varint — the main battle identifier)\r\n // field_3 = battle settings sub-message containing timer data:\r\n // field_3.field_1 = battleId (duplicate)\r\n // field_3.field_2 = startTimeMs (millisecond timestamp)\r\n // field_3.field_3 = duration (seconds, e.g. 301)\r\n // field_3.field_5 = phase/status (1=active, 3=ended)\r\n // field_3.field_10 = lastUpdateTimeMs\r\n // field_4 = overall status (e.g. 5 = ended)\r\n // field_5 = team user list (repeated)\r\n // field_9 = detailed user data with avatars (repeated)\r\n // field_10 = battle user bufs (repeated — used for team parsing)\r\n const battleId = getIntStr(f, 2) || getIntStr(f, 1) || '';\r\n const overallStatus = getInt(f, 4);\r\n const teams: BattleTeam[] = [];\r\n\r\n // Parse battle settings from field_3 sub-message\r\n let battleDuration = 0;\r\n let battleSettings: { startTimeMs?: number; duration?: number; endTimeMs?: number } | undefined;\r\n const settingsBuf = getBytes(f, 3);\r\n if (settingsBuf) {\r\n try {\r\n const sf = decodeProto(settingsBuf);\r\n const startTimeMs = getInt(sf, 2);\r\n const duration = getInt(sf, 3);\r\n const phase = getInt(sf, 5);\r\n const endTimeMs = getInt(sf, 10);\r\n battleDuration = duration;\r\n battleSettings = {\r\n startTimeMs: startTimeMs || undefined,\r\n duration: duration || undefined,\r\n endTimeMs: endTimeMs || undefined,\r\n };\r\n } catch { }\r\n }\r\n\r\n // Determine status: use phase from settings, fall back to overall status\r\n // field_4 sometimes returns a large TikTok ID instead of a status code.\r\n // Valid battle status codes are small (1=active, 3=ended, 5=finalizing).\r\n // Only use overallStatus if it looks like a real status code (1-10).\r\n const settingsPhase = settingsBuf ? (() => {\r\n try { return getInt(decodeProto(settingsBuf), 5); } catch { return 0; }\r\n })() : 0;\r\n const status = settingsPhase || (overallStatus > 0 && overallStatus <= 10 ? overallStatus : 0) || 1;\r\n\r\n const battleUserBufs = getAllBytes(f, 10);\r\n for (const bub of battleUserBufs) {\r\n try {\r\n const bf = decodeProto(bub);\r\n const groupBuf = getBytes(bf, 2);\r\n if (groupBuf) {\r\n const gf = decodeProto(groupBuf);\r\n const linkUserBuf = getBytes(gf, 1);\r\n if (linkUserBuf) {\r\n const user = parseUser(linkUserBuf);\r\n teams.push({\r\n hostUserId: user.id,\r\n score: 0,\r\n users: [{ user, score: 0 }],\r\n });\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n // Also try field_5 for team users (new proto format)\r\n if (teams.length === 0) {\r\n const teamBufs5 = getAllBytes(f, 5);\r\n for (const tb of teamBufs5) {\r\n try {\r\n const tf = decodeProto(tb);\r\n const userId = getIntStr(tf, 1);\r\n if (userId && userId !== '0') {\r\n teams.push({\r\n hostUserId: userId,\r\n score: 0,\r\n users: [],\r\n });\r\n }\r\n } catch { }\r\n }\r\n }\r\n\r\n if (teams.length === 0) {\r\n const teamBufs7 = getAllBytes(f, 7);\r\n for (const tb of teamBufs7) {\r\n try {\r\n teams.push(parseBattleTeamFromArmies(tb));\r\n } catch { }\r\n }\r\n }\r\n\r\n return { ...base, type: 'battle' as const, battleId, status, battleDuration, teams, battleSettings };\r\n }\r\n\r\n case 'WebcastLinkMicArmies': {\r\n const battleId = getIntStr(f, 2) || getIntStr(f, 1) || '';\r\n const battleStatus = getInt(f, 7);\r\n const teams: BattleTeam[] = [];\r\n\r\n // Parse battle settings from field_18 sub-message\r\n // Same structure as WebcastLinkMicBattle field_3:\r\n // field_1 = battleId, field_2 = startTimeMs, field_3 = duration,\r\n // field_5 = status, field_10 = endTimeMs\r\n let battleSettings: { startTimeMs?: number; duration?: number; endTimeMs?: number } | undefined;\r\n const settingsBuf18 = getBytes(f, 18);\r\n if (settingsBuf18) {\r\n try {\r\n const sf = decodeProto(settingsBuf18);\r\n const startTimeMs = getInt(sf, 2);\r\n const duration = getInt(sf, 3);\r\n const endTimeMs = getInt(sf, 10);\r\n battleSettings = {\r\n startTimeMs: startTimeMs || undefined,\r\n duration: duration || undefined,\r\n endTimeMs: endTimeMs || undefined,\r\n };\r\n } catch { }\r\n }\r\n\r\n // Parse live server timestamps (update on every score tick)\r\n const scoreUpdateTime = getInt(f, 5);\r\n const giftSentTime = getInt(f, 6);\r\n\r\n const itemBufs = getAllBytes(f, 3);\r\n for (const ib of itemBufs) {\r\n try {\r\n teams.push(parseBattleTeamFromArmies(ib));\r\n } catch { }\r\n }\r\n\r\n return {\r\n ...base, type: 'battleArmies' as const, battleId, teams,\r\n status: battleStatus, battleSettings, scoreUpdateTime, giftSentTime,\r\n };\r\n }\r\n\r\n case 'WebcastSubNotifyMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n return { ...base, type: 'subscribe' as const, user, subMonth: getInt(f, 3) };\r\n }\r\n\r\n case 'WebcastEmoteChatMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n let emoteId = '', emoteUrl = '';\r\n const emoteBuf = getBytes(f, 3);\r\n if (emoteBuf) {\r\n const ef = decodeProto(emoteBuf);\r\n emoteId = getStr(ef, 1);\r\n const imageBuf = getBytes(ef, 2);\r\n if (imageBuf) {\r\n const imgFields = decodeProto(imageBuf);\r\n emoteUrl = getStr(imgFields, 1);\r\n }\r\n }\r\n return { ...base, type: 'emoteChat' as const, user, emoteId, emoteUrl };\r\n }\r\n\r\n case 'WebcastEnvelopeMessage': {\r\n const envelopeId = String(getInt(f, 1) || getStr(f, 1));\r\n return { ...base, type: 'envelope' as const, envelopeId, diamondCount: getInt(f, 3) };\r\n }\r\n\r\n case 'WebcastQuestionNewMessage': {\r\n let questionText = '', user: TikTokUser = { id: '0', nickname: '', uniqueId: '' };\r\n const detailBuf = getBytes(f, 2);\r\n if (detailBuf) {\r\n const df = decodeProto(detailBuf);\r\n questionText = getStr(df, 2);\r\n const userBuf = getBytes(df, 5);\r\n if (userBuf) user = parseUser(userBuf);\r\n }\r\n return { ...base, type: 'question' as const, user, questionText };\r\n }\r\n\r\n case 'WebcastRankUpdateMessage':\r\n case 'WebcastHourlyRankMessage': {\r\n const rankType = getStr(f, 1) || `rank_${getInt(f, 1)}`;\r\n const rankList: Array<{ user: TikTokUser; rank: number; score: number }> = [];\r\n const listBufs = getAllBytes(f, 2);\r\n for (const lb of listBufs) {\r\n try {\r\n const rf = decodeProto(lb);\r\n const userBuf = getBytes(rf, 1);\r\n const rank = getInt(rf, 2);\r\n const score = getInt(rf, 3);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n rankList.push({ user, rank, score });\r\n } catch { }\r\n }\r\n return { ...base, type: 'rankUpdate' as const, rankType, rankList };\r\n }\r\n\r\n case 'WebcastControlMessage':\r\n return { ...base, type: 'control' as const, action: getInt(f, 2) || getInt(f, 1) };\r\n\r\n case 'WebcastRoomMessage':\r\n case 'RoomMessage':\r\n return { ...base, type: 'room' as const, status: getInt(f, 2) };\r\n\r\n case 'WebcastLiveIntroMessage': {\r\n const roomId = String(getInt(f, 2));\r\n const title = getStr(f, 4) || getStr(f, 2);\r\n return { ...base, type: 'liveIntro' as const, roomId, title };\r\n }\r\n\r\n case 'WebcastLinkMicMethod': {\r\n const action = getStr(f, 1) || `action_${getInt(f, 1)}`;\r\n const users: TikTokUser[] = [];\r\n const userBufs = getAllBytes(f, 2);\r\n for (const ub of userBufs) {\r\n try { users.push(parseUser(ub)); } catch { }\r\n }\r\n return { ...base, type: 'linkMic' as const, action, users };\r\n }\r\n\r\n case 'WebcastLinkmicBattleTaskMessage': {\r\n // Battle task/buff events (multipliers, bonus missions, timer updates)\r\n // Raw proto fields:\r\n // field_2 = taskAction (1=start, 2=update, 3=mission complete)\r\n // field_3 = bonus mission details sub-message:\r\n // field_3.field_1.field_1 = target score\r\n // field_3.field_1.field_2 = mission items with multiplier/duration\r\n // field_3.field_1.field_3 = countdown (field_1=target, field_2=duration, field_3=endTimestamp)\r\n // field_3.field_1.field_6 = mission start timestamp\r\n // field_5 = timer sub-message:\r\n // field_5.field_1 = timer type\r\n // field_5.field_2 = remaining seconds\r\n // field_5.field_3 = end timestamp (seconds)\r\n // field_6 = mission description with multiplier info\r\n // field_20 = battle reference ID\r\n const taskAction = getInt(f, 2);\r\n const battleRefId = getIntStr(f, 20) || '';\r\n\r\n // Extract timer info from field_5\r\n let timerType = 0;\r\n let remainingSeconds = 0;\r\n let endTimestampS = 0;\r\n const timerBuf = getBytes(f, 5);\r\n if (timerBuf) {\r\n try {\r\n const tf = decodeProto(timerBuf);\r\n timerType = getInt(tf, 1);\r\n remainingSeconds = getInt(tf, 2);\r\n endTimestampS = getInt(tf, 3);\r\n } catch { }\r\n }\r\n\r\n // Extract multiplier and mission details from field_3 or field_6\r\n let multiplier = 0;\r\n let missionDuration = 0;\r\n let missionTarget = 0;\r\n let missionType = '';\r\n\r\n // Try field_3.field_1 (bonus mission)\r\n const bonusBuf = getBytes(f, 3);\r\n if (bonusBuf) {\r\n try {\r\n const bf = decodeProto(bonusBuf);\r\n const missionBuf = getBytes(bf, 1);\r\n if (missionBuf) {\r\n const mf = decodeProto(missionBuf);\r\n missionTarget = getInt(mf, 1);\r\n // Check mission items (field_2, repeated) for multiplier\r\n const items = getAllBytes(mf, 2);\r\n for (const item of items) {\r\n try {\r\n const itemFields = decodeProto(item);\r\n const descBuf = getBytes(itemFields, 2);\r\n if (descBuf) {\r\n const descFields = decodeProto(descBuf);\r\n // Look for \"multi\" key in param pairs\r\n const paramBufs = getAllBytes(descFields, 2);\r\n for (const pb of paramBufs) {\r\n try {\r\n const pf = decodeProto(pb);\r\n const key = getStr(pf, 1);\r\n const val = getStr(pf, 2);\r\n if (key === 'multi' && val) multiplier = parseInt(val) || 0;\r\n if (key === 'dur' && val) missionDuration = parseInt(val) || 0;\r\n if (key === 'sum' && val) missionTarget = parseInt(val) || missionTarget;\r\n } catch { }\r\n }\r\n // Get mission type from field_1\r\n const typeStr = getStr(descFields, 1);\r\n if (typeStr) missionType = typeStr;\r\n }\r\n } catch { }\r\n }\r\n // Check countdown sub-message (field_3 of mission)\r\n const countdownBuf = getBytes(mf, 3);\r\n if (countdownBuf) {\r\n try {\r\n const cf = decodeProto(countdownBuf);\r\n if (!missionDuration) missionDuration = getInt(cf, 2);\r\n } catch { }\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n // Try field_6 for mission descriptions (e.g. mission complete with sum)\r\n if (!missionType) {\r\n const descBuf6 = getBytes(f, 6);\r\n if (descBuf6) {\r\n try {\r\n const d6 = decodeProto(descBuf6);\r\n const innerBuf = getBytes(d6, 1);\r\n if (innerBuf) {\r\n const innerF = decodeProto(innerBuf);\r\n const typeStr = getStr(innerF, 1);\r\n if (typeStr) missionType = typeStr;\r\n const paramBufs = getAllBytes(innerF, 2);\r\n for (const pb of paramBufs) {\r\n try {\r\n const pf = decodeProto(pb);\r\n const key = getStr(pf, 1);\r\n const val = getStr(pf, 2);\r\n if (key === 'multi' && val) multiplier = parseInt(val) || 0;\r\n if (key === 'sum' && val) missionTarget = parseInt(val) || missionTarget;\r\n } catch { }\r\n }\r\n }\r\n } catch { }\r\n }\r\n }\r\n\r\n return {\r\n ...base, type: 'battleTask' as const,\r\n taskAction, battleRefId, missionType, multiplier,\r\n missionDuration, missionTarget,\r\n remainingSeconds, endTimestampS, timerType,\r\n };\r\n }\r\n\r\n case 'WebcastBarrageMessage': {\r\n\r\n\r\n const msgType = getInt(f, 3);\r\n const duration = getInt(f, 4);\r\n const displayType = getInt(f, 5);\r\n const subType = getInt(f, 6);\r\n\r\n let defaultPattern = '';\r\n let content = '';\r\n const contentBuf = getBytes(f, 2);\r\n if (contentBuf) {\r\n try {\r\n const cf = decodeProto(contentBuf);\r\n // field_1 = default pattern text\r\n defaultPattern = getStr(cf, 1) || '';\r\n // field_2 = pattern params (may have key=value pairs)\r\n const paramBufs = getAllBytes(cf, 2);\r\n const params: string[] = [];\r\n for (const pb of paramBufs) {\r\n try {\r\n const pf = decodeProto(pb);\r\n const key = getStr(pf, 1);\r\n const val = getStr(pf, 2);\r\n if (key && val) params.push(`${key}=${val}`);\r\n } catch { }\r\n }\r\n if (params.length > 0) content = params.join(', ');\r\n } catch { }\r\n }\r\n\r\n // Also try field_1 as a direct string (some types send the text directly)\r\n if (!defaultPattern) {\r\n defaultPattern = getStr(f, 1) || '';\r\n }\r\n\r\n\r\n\r\n return {\r\n ...base, type: 'barrage' as const,\r\n msgType, subType, displayType, duration,\r\n defaultPattern, content,\r\n };\r\n }\r\n\r\n default:\r\n return { ...base, type: 'unknown' as const, method };\r\n }\r\n}\r\n\r\nexport function parseWebcastResponse(payload: Uint8Array): LiveEvent[] {\r\n const events: LiveEvent[] = [];\r\n const respFields = decodeProto(payload);\r\n\r\n for (const mf of respFields.filter(f => f.fn === 1 && f.wt === 2)) {\r\n const msgBuf = mf.value as Uint8Array;\r\n const msgFields = decodeProto(msgBuf);\r\n const method = getStr(msgFields, 1);\r\n const innerPayload = getBytes(msgFields, 2);\r\n if (!method || !innerPayload) continue;\r\n\r\n try {\r\n events.push(parseWebcastMessage(method, innerPayload));\r\n } catch { }\r\n }\r\n\r\n return events;\r\n}\r\n","/**\r\n * TikTokCaptions — Real-time speech-to-text transcription and translation for TikTok LIVE streams.\r\n *\r\n * AI-powered audio transcription with speaker diarization, multi-language auto-detection,\r\n * real-time translation, and sub-second latency. Connects via WebSocket to the TikTool\r\n * captions relay for continuous streaming transcription.\r\n *\r\n * @example\r\n * ```ts\r\n * import { TikTokCaptions } from '@tiktool/live';\r\n *\r\n * const captions = new TikTokCaptions({\r\n * uniqueId: 'username',\r\n * apiKey: 'your-api-key',\r\n * language: 'en', // translate to English\r\n * });\r\n *\r\n * captions.on('caption', (data) => {\r\n * console.log(`[${data.language}] ${data.text}`);\r\n * });\r\n *\r\n * captions.on('translation', (data) => {\r\n * console.log(`[translated] ${data.text}`);\r\n * });\r\n *\r\n * await captions.start();\r\n * ```\r\n */\r\n\r\nimport { EventEmitter } from 'events';\r\nimport WebSocket from 'ws';\r\n\r\n// ── FLV Audio Extractor ──────────────────────────────────────────────\r\n// Extracts AAC audio frames from FLV byte stream and wraps them in ADTS\r\n// headers for Soniox ingestion. Uses Uint8Array for Node.js + browser compat.\r\n\r\nconst FLV_TAG_AUDIO = 8;\r\nconst FLV_HEADER_SIZE = 9;\r\nconst FLV_PREV_TAG_SIZE = 4;\r\n\r\nclass FlvAudioExtractor {\r\n private buffer = new Uint8Array(0);\r\n private headerParsed = false;\r\n private onAudio: (data: Uint8Array) => void;\r\n private aacProfile = 2;\r\n private sampleRateIndex = 4;\r\n private channelConfig = 2;\r\n private ascParsed = false;\r\n\r\n constructor(onAudio: (data: Uint8Array) => void) {\r\n this.onAudio = onAudio;\r\n }\r\n\r\n private parseASC(asc: Uint8Array): void {\r\n if (asc.length < 2) return;\r\n this.aacProfile = (asc[0] >> 3) & 0x1F;\r\n this.sampleRateIndex = ((asc[0] & 0x07) << 1) | ((asc[1] >> 7) & 0x01);\r\n this.channelConfig = (asc[1] >> 3) & 0x0F;\r\n this.ascParsed = true;\r\n }\r\n\r\n private buildAdtsHeader(frameLength: number): Uint8Array {\r\n const adts = new Uint8Array(7);\r\n const fullLength = frameLength + 7;\r\n const profile = this.aacProfile - 1;\r\n adts[0] = 0xFF;\r\n adts[1] = 0xF1;\r\n adts[2] = ((profile & 0x03) << 6) |\r\n ((this.sampleRateIndex & 0x0F) << 2) |\r\n ((this.channelConfig >> 2) & 0x01);\r\n adts[3] = ((this.channelConfig & 0x03) << 6) |\r\n ((fullLength >> 11) & 0x03);\r\n adts[4] = (fullLength >> 3) & 0xFF;\r\n adts[5] = ((fullLength & 0x07) << 5) | 0x1F;\r\n adts[6] = 0xFC;\r\n return adts;\r\n }\r\n\r\n push(chunk: Uint8Array): void {\r\n const newBuf = new Uint8Array(this.buffer.length + chunk.length);\r\n newBuf.set(this.buffer, 0);\r\n newBuf.set(chunk, this.buffer.length);\r\n this.buffer = newBuf;\r\n\r\n if (!this.headerParsed) {\r\n if (this.buffer.length < FLV_HEADER_SIZE + FLV_PREV_TAG_SIZE) return;\r\n if (this.buffer[0] !== 0x46 || this.buffer[1] !== 0x4C || this.buffer[2] !== 0x56) return;\r\n const dv = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);\r\n const dataOffset = dv.getUint32(5);\r\n this.buffer = this.buffer.subarray(dataOffset + FLV_PREV_TAG_SIZE);\r\n this.headerParsed = true;\r\n }\r\n\r\n while (this.buffer.length >= 11) {\r\n const tagType = this.buffer[0] & 0x1F;\r\n const dataSize = (this.buffer[1] << 16) | (this.buffer[2] << 8) | this.buffer[3];\r\n const totalTagSize = 11 + dataSize + FLV_PREV_TAG_SIZE;\r\n if (this.buffer.length < totalTagSize) break;\r\n\r\n if (tagType === FLV_TAG_AUDIO) {\r\n const audioData = this.buffer.subarray(11, 11 + dataSize);\r\n if (audioData.length > 0) {\r\n const soundFormat = (audioData[0] >> 4) & 0x0F;\r\n if (soundFormat === 10 && audioData.length > 2) {\r\n const aacPacketType = audioData[1];\r\n if (aacPacketType === 0) {\r\n this.parseASC(audioData.subarray(2));\r\n } else if (aacPacketType === 1 && this.ascParsed) {\r\n const rawFrame = audioData.subarray(2);\r\n const adtsHeader = this.buildAdtsHeader(rawFrame.length);\r\n const adtsFrame = new Uint8Array(adtsHeader.length + rawFrame.length);\r\n adtsFrame.set(adtsHeader, 0);\r\n adtsFrame.set(rawFrame, adtsHeader.length);\r\n this.onAudio(adtsFrame);\r\n }\r\n }\r\n }\r\n }\r\n\r\n this.buffer = this.buffer.subarray(totalTagSize);\r\n }\r\n }\r\n\r\n reset(): void {\r\n this.buffer = new Uint8Array(0);\r\n this.headerParsed = false;\r\n this.ascParsed = false;\r\n }\r\n}\r\n\r\nexport interface TikTokCaptionsOptions {\r\n /** TikTok username to transcribe (without @) */\r\n uniqueId: string;\r\n /** API key for authentication */\r\n apiKey: string;\r\n /** Source language hint (e.g. 'en', 'ja'). Leave empty for auto-detection. */\r\n language?: string;\r\n /** Target language for real-time translation (e.g. 'en', 'es', 'fr'). One language per session. */\r\n translate?: string;\r\n /** Enable speaker diarization to identify individual speakers (default: true) */\r\n diarization?: boolean;\r\n /** Max session duration in minutes before auto-disconnect (default: 60, max: 300) */\r\n maxDurationMinutes?: number;\r\n /** Custom server URL (default: wss://api.tik.tools) */\r\n signServerUrl?: string;\r\n /** Enable debug logging */\r\n debug?: boolean;\r\n /** Auto-reconnect on disconnect */\r\n autoReconnect?: boolean;\r\n /** Max reconnect attempts (default: 5) */\r\n maxReconnectAttempts?: number;\r\n}\r\n\r\nexport interface CaptionData {\r\n /** Transcribed text */\r\n text: string;\r\n /** Detected source language */\r\n language: string;\r\n /** Whether this is the final version of this segment */\r\n isFinal: boolean;\r\n /** Confidence score (0-1) */\r\n confidence: number;\r\n /** Speaker identifier (if available) */\r\n speaker?: string;\r\n /** Start time in milliseconds */\r\n startMs?: number;\r\n /** End time in milliseconds */\r\n endMs?: number;\r\n}\r\n\r\nexport interface TranslationData {\r\n /** Translated text */\r\n text: string;\r\n /** Target language */\r\n language: string;\r\n /** Whether this is the final version */\r\n isFinal: boolean;\r\n /** Confidence score (0-1) */\r\n confidence: number;\r\n /** Speaker identifier (if available) */\r\n speaker?: string;\r\n}\r\n\r\nexport interface CaptionCredits {\r\n /** Credits remaining */\r\n remaining: number;\r\n /** Total credits purchased */\r\n total: number;\r\n /** Credits used in this session */\r\n used: number;\r\n /** Whether credits are low */\r\n warning: boolean;\r\n}\r\n\r\nexport interface CaptionStatus {\r\n /** Current status */\r\n status: 'connecting' | 'waiting' | 'live' | 'transcribing' | 'ended' |\r\n 'switching_language' | 'language_switched' | 'stream_ended';\r\n /** TikTok username */\r\n uniqueId?: string;\r\n /** Room ID (once resolved) */\r\n roomId?: string;\r\n /** Language (for language switch events) */\r\n language?: string;\r\n /** Status message */\r\n message?: string;\r\n}\r\n\r\nexport interface CaptionError {\r\n /** Error code */\r\n code: string;\r\n /** Human-readable error message */\r\n message: string;\r\n}\r\n\r\nexport interface TikTokCaptionsEvents {\r\n /** Fired for each transcription token */\r\n caption: (data: CaptionData) => void;\r\n /** Fired for each translated token */\r\n translation: (data: TranslationData) => void;\r\n /** Status changes (connecting, live, transcribing, etc.) */\r\n status: (data: CaptionStatus) => void;\r\n /** Credit balance updates */\r\n credits: (data: CaptionCredits) => void;\r\n /** Low credit warning */\r\n credits_low: (data: { remaining: number; total: number; percent: number }) => void;\r\n /** Error events */\r\n error: (data: CaptionError) => void;\r\n /** WebSocket connected */\r\n connected: () => void;\r\n /** WebSocket disconnected */\r\n disconnected: (code: number, reason: string) => void;\r\n}\r\n\r\nconst DEFAULT_CAPTIONS_SERVER = 'wss://api.tik.tools';\r\n\r\nexport class TikTokCaptions extends EventEmitter {\r\n private ws: WebSocket | null = null;\r\n private _connected = false;\r\n private intentionalClose = false;\r\n private reconnectAttempts = 0;\r\n\r\n private readonly uniqueId: string;\r\n private readonly apiKey: string;\r\n private readonly serverUrl: string;\r\n private readonly autoReconnect: boolean;\r\n private readonly maxReconnectAttempts: number;\r\n private readonly debug: boolean;\r\n private readonly _translate: string;\r\n private readonly _diarization: boolean;\r\n private readonly _maxDurationMinutes: number;\r\n private _language: string;\r\n private streamAbortController: AbortController | null = null;\r\n private flvExtractor: FlvAudioExtractor | null = null;\r\n private streamUrl: string | null = null;\r\n\r\n constructor(options: TikTokCaptionsOptions) {\r\n super();\r\n this.uniqueId = options.uniqueId.replace(/^@/, '');\r\n if (!options.apiKey) throw new Error('apiKey is required. Get a free key at https://tik.tools');\r\n this.apiKey = options.apiKey;\r\n this._language = options.language || '';\r\n this._translate = options.translate || '';\r\n this._diarization = options.diarization ?? true;\r\n this._maxDurationMinutes = options.maxDurationMinutes ?? 60;\r\n this.serverUrl = (options.signServerUrl || DEFAULT_CAPTIONS_SERVER).replace(/\\/$/, '');\r\n this.autoReconnect = options.autoReconnect ?? true;\r\n this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;\r\n this.debug = options.debug ?? false;\r\n }\r\n\r\n /**\r\n * Start real-time captions for the configured TikTok user.\r\n * Connects to the captions WebSocket relay and begins transcription\r\n * once the user goes live (or immediately if already live).\r\n */\r\n async start(): Promise<void> {\r\n this.intentionalClose = false;\r\n\r\n const wsUrl = this.buildWsUrl();\r\n if (this.debug) console.log(`[Captions] Connecting to ${wsUrl}`);\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n this.ws = new WebSocket(wsUrl);\r\n\r\n this.ws.on('open', () => {\r\n this._connected = true;\r\n this.reconnectAttempts = 0;\r\n if (this.debug) console.log('[Captions] Connected');\r\n this.emit('connected');\r\n resolve();\r\n });\r\n\r\n this.ws.on('message', (data: Buffer | string) => {\r\n this.handleMessage(typeof data === 'string' ? data : data.toString());\r\n });\r\n\r\n this.ws.on('close', (code: number, reason: Buffer) => {\r\n this._connected = false;\r\n const reasonStr = reason?.toString() || '';\r\n if (this.debug) console.log(`[Captions] Disconnected: ${code} ${reasonStr}`);\r\n this.emit('disconnected', code, reasonStr);\r\n\r\n if (!this.intentionalClose && this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {\r\n this.reconnectAttempts++;\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts - 1), 30_000);\r\n if (this.debug) console.log(`[Captions] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\r\n setTimeout(() => this.start().catch(e => this.emit('error', { code: 'RECONNECT_FAILED', message: e.message })), delay);\r\n }\r\n });\r\n\r\n this.ws.on('error', (err: Error) => {\r\n this.emit('error', { code: 'WS_ERROR', message: err.message });\r\n if (!this._connected) reject(err);\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * Stop captions and disconnect.\r\n */\r\n stop(): void {\r\n this.intentionalClose = true;\r\n // Abort stream download if active\r\n if (this.streamAbortController) {\r\n this.streamAbortController.abort();\r\n this.streamAbortController = null;\r\n }\r\n if (this.flvExtractor) {\r\n this.flvExtractor.reset();\r\n this.flvExtractor = null;\r\n }\r\n if (this.ws) {\r\n // Send stop action before closing\r\n this.send({ action: 'stop' });\r\n this.ws.close(1000);\r\n this.ws = null;\r\n }\r\n this._connected = false;\r\n }\r\n\r\n /**\r\n * Switch the translation target language on-the-fly.\r\n * Causes a brief interruption while the transcription engine reconfigures.\r\n */\r\n setLanguage(language: string): void {\r\n this._language = language;\r\n this.send({ action: 'set_language', language });\r\n }\r\n\r\n /**\r\n * Request a credit balance update from the server.\r\n */\r\n getCredits(): void {\r\n this.send({ action: 'get_credits' });\r\n }\r\n\r\n /** Whether the WebSocket is currently connected */\r\n get connected(): boolean {\r\n return this._connected;\r\n }\r\n\r\n /** The current target language */\r\n get language(): string {\r\n return this._language;\r\n }\r\n\r\n\r\n\r\n on<K extends keyof TikTokCaptionsEvents>(event: K, listener: TikTokCaptionsEvents[K]): this {\r\n return super.on(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n once<K extends keyof TikTokCaptionsEvents>(event: K, listener: TikTokCaptionsEvents[K]): this {\r\n return super.once(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n off<K extends keyof TikTokCaptionsEvents>(event: K, listener: TikTokCaptionsEvents[K]): this {\r\n return super.off(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n emit<K extends keyof TikTokCaptionsEvents>(event: K, ...args: Parameters<TikTokCaptionsEvents[K]>): boolean {\r\n return super.emit(event, ...args);\r\n }\r\n\r\n private buildWsUrl(): string {\r\n const base = this.serverUrl.replace(/^http/, 'ws');\r\n const params = new URLSearchParams({\r\n uniqueId: this.uniqueId,\r\n apiKey: this.apiKey,\r\n });\r\n if (this._language) params.set('language', this._language);\r\n if (this._translate) params.set('translate', this._translate);\r\n if (this._diarization !== undefined) params.set('diarization', String(this._diarization));\r\n if (this._maxDurationMinutes) params.set('max_duration_minutes', String(this._maxDurationMinutes));\r\n return `${base}/captions?${params}`;\r\n }\r\n\r\n private send(msg: Record<string, any>): void {\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify(msg));\r\n }\r\n }\r\n\r\n private handleMessage(raw: string): void {\r\n try {\r\n const msg = JSON.parse(raw);\r\n\r\n switch (msg.type) {\r\n case 'stream_info':\r\n // Server resolved stream URLs — connect to the FLV stream\r\n if (this.debug) console.log(`[Captions] Received stream_info: flv=${!!msg.flvUrl}, hls=${!!msg.hlsUrl}, ao=${!!msg.audioOnlyUrl}`);\r\n this.connectToStream(msg);\r\n break;\r\n\r\n case 'caption':\r\n this.emit('caption', {\r\n text: msg.text,\r\n language: msg.language,\r\n isFinal: msg.isFinal,\r\n confidence: msg.confidence,\r\n speaker: msg.speaker,\r\n startMs: msg.startMs,\r\n endMs: msg.endMs,\r\n });\r\n break;\r\n\r\n case 'translation':\r\n this.emit('translation', {\r\n text: msg.text,\r\n language: msg.language,\r\n isFinal: msg.isFinal,\r\n confidence: msg.confidence,\r\n speaker: msg.speaker,\r\n });\r\n break;\r\n\r\n case 'status':\r\n this.emit('status', {\r\n status: msg.status,\r\n uniqueId: msg.uniqueId,\r\n roomId: msg.roomId,\r\n language: msg.language,\r\n message: msg.message,\r\n });\r\n break;\r\n\r\n case 'credits':\r\n this.emit('credits', {\r\n remaining: msg.remaining,\r\n total: msg.total,\r\n used: msg.used,\r\n warning: msg.warning,\r\n });\r\n break;\r\n\r\n case 'credits_low':\r\n this.emit('credits_low', {\r\n remaining: msg.remaining,\r\n total: msg.total,\r\n percent: msg.percent,\r\n });\r\n break;\r\n\r\n case 'error':\r\n this.emit('error', {\r\n code: msg.code,\r\n message: msg.message,\r\n });\r\n break;\r\n\r\n // Handle interim/final captions from server (sentence-level accumulation)\r\n case 'interim':\r\n this.emit('caption', {\r\n text: msg.text,\r\n language: msg.language,\r\n isFinal: false,\r\n confidence: msg.confidence || 0,\r\n speaker: msg.speaker,\r\n });\r\n break;\r\n\r\n case 'final':\r\n this.emit('caption', {\r\n text: msg.text,\r\n language: msg.language,\r\n isFinal: true,\r\n confidence: msg.confidence || 0,\r\n speaker: msg.speaker,\r\n });\r\n break;\r\n\r\n case 'translation_interim':\r\n this.emit('translation', {\r\n text: msg.text,\r\n language: msg.language,\r\n isFinal: false,\r\n confidence: msg.confidence || 0,\r\n speaker: msg.speaker,\r\n });\r\n break;\r\n\r\n case 'translation_final':\r\n this.emit('translation', {\r\n text: msg.text,\r\n language: msg.language,\r\n isFinal: true,\r\n confidence: msg.confidence || 0,\r\n speaker: msg.speaker,\r\n });\r\n break;\r\n\r\n default:\r\n if (this.debug) {\r\n if (this.debug) console.log(`[Captions] Unknown message type: ${msg.type}`, msg);\r\n }\r\n }\r\n } catch {\r\n if (this.debug) console.error('[Captions] Failed to parse message:', raw);\r\n }\r\n }\r\n\r\n /**\r\n * Connect to the TikTok FLV stream and extract audio.\r\n * Sends binary audio buffers to the server via WebSocket.\r\n */\r\n private async connectToStream(streamInfo: { flvUrl?: string; hlsUrl?: string; audioOnlyUrl?: string }): Promise<void> {\r\n // Prefer audio-only URL (smallest bandwidth), fall back to regular FLV\r\n const url = streamInfo.audioOnlyUrl || streamInfo.flvUrl;\r\n if (!url) {\r\n this.emit('error', { code: 'NO_STREAM_URL', message: 'Server did not provide a usable stream URL' });\r\n return;\r\n }\r\n\r\n this.streamUrl = url;\r\n if (this.debug) console.log(`[Captions] connectToStream: URL selected: ${url.substring(0, 80)}...`);\r\n\r\n // Abort any previous stream\r\n if (this.streamAbortController) {\r\n this.streamAbortController.abort();\r\n }\r\n this.streamAbortController = new AbortController();\r\n\r\n // Set up FLV audio extractor — sends ADTS-wrapped AAC to server\r\n let audioFramesSent = 0;\r\n let audioBytesSent = 0;\r\n this.flvExtractor = new FlvAudioExtractor((adtsFrame: Uint8Array) => {\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(adtsFrame);\r\n audioFramesSent++;\r\n audioBytesSent += adtsFrame.length;\r\n if (this.debug && (audioFramesSent <= 3 || audioFramesSent % 100 === 0)) {\r\n if (this.debug) console.log(`[Captions] Audio frame #${audioFramesSent}: ${adtsFrame.length}b (total: ${audioBytesSent}b)`);\r\n }\r\n } else if (this.debug && audioFramesSent === 0) {\r\n if (this.debug) console.log(`[Captions] WARNING: WS not open (readyState=${this.ws?.readyState}), cannot send audio`);\r\n }\r\n });\r\n\r\n try {\r\n if (this.debug) console.log(`[Captions] connectToStream: calling fetch()...`);\r\n const resp = await fetch(url, {\r\n signal: this.streamAbortController.signal,\r\n headers: {\r\n 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',\r\n },\r\n });\r\n\r\n if (this.debug) console.log(`[Captions] connectToStream: fetch returned status=${resp.status}, hasBody=${!!resp.body}`);\r\n\r\n if (!resp.ok || !resp.body) {\r\n throw new Error(`FLV stream HTTP ${resp.status}`);\r\n }\r\n\r\n if (this.debug) console.log(`[Captions] FLV stream connected (${resp.status})`);\r\n\r\n // Stream the response body\r\n const reader = (resp.body as any).getReader ?\r\n (resp.body as ReadableStream<Uint8Array>).getReader() : null;\r\n\r\n if (this.debug) console.log(`[Captions] connectToStream: hasReader=${!!reader}, hasAsyncIterator=${typeof (resp.body as any)[Symbol.asyncIterator] === 'function'}`);\r\n\r\n if (reader) {\r\n // Browser / modern Node.js ReadableStream\r\n const processStream = async () => {\r\n let chunks = 0;\r\n try {\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done || this.intentionalClose) {\r\n if (this.debug) console.log(`[Captions] FLV stream ended (done=${done}, intentionalClose=${this.intentionalClose}), chunks=${chunks}, audioFrames=${audioFramesSent}`);\r\n break;\r\n }\r\n chunks++;\r\n if (value && this.flvExtractor) {\r\n this.flvExtractor.push(value);\r\n }\r\n if (this.debug && chunks <= 3) {\r\n if (this.debug) console.log(`[Captions] FLV chunk #${chunks}: ${value?.length || 0}b`);\r\n }\r\n }\r\n } catch (err: any) {\r\n if (err.name !== 'AbortError' && !this.intentionalClose) {\r\n if (this.debug) console.error('[Captions] FLV stream read error:', err.message);\r\n this.emit('error', { code: 'STREAM_READ_ERROR', message: err.message });\r\n } else if (this.debug) {\r\n if (this.debug) console.log(`[Captions] FLV stream aborted after ${chunks} chunks, ${audioFramesSent} audio frames`);\r\n }\r\n }\r\n };\r\n processStream();\r\n } else if (typeof (resp.body as any)[Symbol.asyncIterator] === 'function') {\r\n // Node.js streams (undici body)\r\n const processNodeStream = async () => {\r\n let chunks = 0;\r\n try {\r\n for await (const chunk of resp.body as any) {\r\n if (this.intentionalClose) break;\r\n chunks++;\r\n const u8 = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\r\n if (this.flvExtractor) {\r\n this.flvExtractor.push(u8);\r\n }\r\n if (this.debug && chunks <= 3) {\r\n if (this.debug) console.log(`[Captions] FLV chunk #${chunks}: ${u8.length}b`);\r\n }\r\n }\r\n if (this.debug) console.log(`[Captions] Node stream ended, chunks=${chunks}, audioFrames=${audioFramesSent}`);\r\n } catch (err: any) {\r\n if (err.name !== 'AbortError' && !this.intentionalClose) {\r\n if (this.debug) console.error('[Captions] FLV stream read error:', err.message);\r\n this.emit('error', { code: 'STREAM_READ_ERROR', message: err.message });\r\n } else if (this.debug) {\r\n if (this.debug) console.log(`[Captions] FLV node stream aborted after ${chunks} chunks, ${audioFramesSent} audio frames`);\r\n }\r\n }\r\n };\r\n processNodeStream();\r\n } else {\r\n if (this.debug) console.error(`[Captions] ERROR: resp.body has no getReader() and no asyncIterator!`);\r\n }\r\n } catch (err: any) {\r\n if (err.name !== 'AbortError' && !this.intentionalClose) {\r\n if (this.debug) console.error('[Captions] FLV stream connect error:', err.message);\r\n this.emit('error', { code: 'STREAM_CONNECT_ERROR', message: err.message });\r\n }\r\n }\r\n }\r\n}\r\n","/**\r\n * TikTool API utilities for the sign-and-return API flow.\r\n *\r\n * The API server returns signed URLs instead of fetching TikTok data directly.\r\n * These utilities handle the two-step flow:\r\n * 1. resolve_required — scrape TikTok HTML to get room_id\r\n * 2. fetch_signed_url — use the signed URL to get actual TikTok data\r\n *\r\n * @module\r\n */\r\n\r\nconst DEFAULT_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';\r\nconst DEFAULT_SIGN_SERVER = 'https://api.tik.tools';\r\n\r\nconst pageCache = new Map<string, { info: LivePageInfo; ts: number }>();\r\nconst PAGE_CACHE_TTL = 5 * 60 * 1000;\r\n\r\n/**\r\n * Resolved live page metadata from a TikTok live page.\r\n */\r\nexport interface LivePageInfo {\r\n /** Active room ID for the livestream */\r\n roomId: string;\r\n /** Session cookie required for WebSocket authentication */\r\n ttwid: string;\r\n /** Server cluster region (e.g. 'us', 'eu') */\r\n clusterRegion: string;\r\n}\r\n\r\n/**\r\n * Scrape a TikTok live page to extract room metadata.\r\n * Returns the room ID, session cookie, and cluster region.\r\n * Results are cached for 5 minutes. Returns `null` if the user is not live.\r\n */\r\nexport async function resolveLivePage(uniqueId: string): Promise<LivePageInfo | null> {\r\n const clean = uniqueId.replace(/^@/, '');\r\n const cached = pageCache.get(clean);\r\n if (cached && Date.now() - cached.ts < PAGE_CACHE_TTL) {\r\n return cached.info;\r\n }\r\n\r\n try {\r\n const resp = await fetch(`https://www.tiktok.com/@${clean}/live`, {\r\n headers: {\r\n 'User-Agent': DEFAULT_UA,\r\n 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\r\n 'Accept-Language': 'en-US,en;q=0.9',\r\n },\r\n redirect: 'follow',\r\n });\r\n\r\n if (!resp.ok) return null;\r\n\r\n // Extract ttwid session cookie\r\n let ttwid = '';\r\n const setCookies = resp.headers.get('set-cookie') || '';\r\n for (const part of setCookies.split(',')) {\r\n const trimmed = part.trim();\r\n if (trimmed.startsWith('ttwid=')) {\r\n ttwid = trimmed.split(';')[0].split('=').slice(1).join('=');\r\n break;\r\n }\r\n }\r\n if (!ttwid && typeof (resp.headers as any).getSetCookie === 'function') {\r\n for (const sc of (resp.headers as any).getSetCookie()) {\r\n if (typeof sc === 'string' && sc.startsWith('ttwid=')) {\r\n ttwid = sc.split(';')[0].split('=').slice(1).join('=');\r\n break;\r\n }\r\n }\r\n }\r\n\r\n const html = await resp.text();\r\n\r\n // Extract roomId from SIGI_STATE JSON block\r\n let roomId = '';\r\n const sigiMatch = html.match(/id=\"SIGI_STATE\"[^>]*>([^<]+)/);\r\n if (sigiMatch) {\r\n try {\r\n const json = JSON.parse(sigiMatch[1]);\r\n const jsonStr = JSON.stringify(json);\r\n const m = jsonStr.match(/\"roomId\"\\s*:\\s*\"(\\d+)\"/);\r\n if (m) roomId = m[1];\r\n } catch { }\r\n }\r\n\r\n // Fallback patterns\r\n if (!roomId) {\r\n const patterns = [\r\n /\"roomId\"\\s*:\\s*\"(\\d+)\"/,\r\n /room_id[=/](\\d{10,})/,\r\n /\"idStr\"\\s*:\\s*\"(\\d{10,})\"/,\r\n ];\r\n for (const p of patterns) {\r\n const m = html.match(p);\r\n if (m) { roomId = m[1]; break; }\r\n }\r\n }\r\n\r\n if (!roomId) return null;\r\n\r\n // Extract cluster region\r\n const crMatch = html.match(/\"clusterRegion\"\\s*:\\s*\"([^\"]+)\"/);\r\n const clusterRegion = crMatch ? crMatch[1] : '';\r\n\r\n const info: LivePageInfo = { roomId, ttwid, clusterRegion };\r\n pageCache.set(clean, { info, ts: Date.now() });\r\n return info;\r\n } catch { }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Resolve a TikTok username to a room ID.\r\n * Returns `null` if the user is not currently live.\r\n * Results are cached for 5 minutes.\r\n */\r\nexport async function resolveRoomId(uniqueId: string): Promise<string | null> {\r\n const info = await resolveLivePage(uniqueId);\r\n return info?.roomId ?? null;\r\n}\r\n\r\nexport interface SignedUrlResponse {\r\n status_code: number;\r\n action?: string;\r\n signed_url: string;\r\n headers: Record<string, string>;\r\n cookies: string;\r\n}\r\n\r\n/**\r\n * Execute a signed-URL API response: fetch the signed URL and return the\r\n * parsed JSON data from TikTok.\r\n */\r\nexport async function fetchSignedUrl(response: SignedUrlResponse): Promise<any> {\r\n if (!response.signed_url) {\r\n return null;\r\n }\r\n\r\n const headers: Record<string, string> = { ...(response.headers || {}) };\r\n if (response.cookies) {\r\n headers['Cookie'] = response.cookies;\r\n }\r\n\r\n const resp = await fetch(response.signed_url, { headers, redirect: 'follow' });\r\n const text = await resp.text();\r\n try {\r\n return JSON.parse(text);\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport interface CallApiOptions {\r\n /** API server URL (default: https://api.tik.tools) */\r\n serverUrl?: string;\r\n /** API key for authentication */\r\n apiKey: string;\r\n /** API endpoint path (e.g. '/webcast/room_video') */\r\n endpoint: string;\r\n /** TikTok unique_id to resolve */\r\n uniqueId: string;\r\n /** HTTP method (default: POST) */\r\n method?: 'GET' | 'POST';\r\n /** Additional body fields for POST requests */\r\n extraBody?: Record<string, any>;\r\n}\r\n\r\n/**\r\n * Call a TikTool API endpoint, handling the full\r\n * resolve_required → room_id → signed_url → fetch flow automatically.\r\n *\r\n * Returns the actual TikTok data, or `null` if the user is not live.\r\n */\r\nexport async function callApi(opts: CallApiOptions): Promise<any> {\r\n const serverUrl = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const isGet = opts.method === 'GET';\r\n const ak = encodeURIComponent(opts.apiKey);\r\n\r\n const url1 = isGet\r\n ? `${serverUrl}${opts.endpoint}?apiKey=${ak}&unique_id=${encodeURIComponent(opts.uniqueId)}`\r\n : `${serverUrl}${opts.endpoint}?apiKey=${ak}`;\r\n\r\n const fetchOpts1: RequestInit = isGet\r\n ? {}\r\n : {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ unique_id: opts.uniqueId, ...opts.extraBody }),\r\n };\r\n\r\n const resp1 = await fetch(url1, fetchOpts1);\r\n const data1 = await resp1.json() as any;\r\n\r\n // If the response contains a signed URL to fetch (with or without explicit action),\r\n // follow through and fetch the actual TikTok data\r\n if (data1.signed_url || data1.action === 'fetch_signed_url') {\r\n return fetchSignedUrl(data1);\r\n }\r\n\r\n if (data1.status_code === 0 && data1.action !== 'resolve_required') {\r\n return data1;\r\n }\r\n\r\n if (data1.action === 'resolve_required') {\r\n const roomId = await resolveRoomId(opts.uniqueId);\r\n if (!roomId) return null;\r\n\r\n const url2 = isGet\r\n ? `${serverUrl}${opts.endpoint}?apiKey=${ak}&room_id=${encodeURIComponent(roomId)}`\r\n : `${serverUrl}${opts.endpoint}?apiKey=${ak}`;\r\n\r\n const fetchOpts2: RequestInit = isGet\r\n ? {}\r\n : {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ room_id: roomId, ...opts.extraBody }),\r\n };\r\n\r\n const resp2 = await fetch(url2, fetchOpts2);\r\n const data2 = await resp2.json() as any;\r\n\r\n if (data2.signed_url || data2.action === 'fetch_signed_url') {\r\n return fetchSignedUrl(data2);\r\n }\r\n\r\n return data2;\r\n }\r\n\r\n return data1;\r\n}\r\n\r\n// ── Ranklist API ────────────────────────────────────────────────────\r\n\r\nimport type { RanklistResponse } from './types.js';\r\n\r\nexport interface GetRanklistOptions {\r\n /** API server URL (default: https://api.tik.tools) */\r\n serverUrl?: string;\r\n /** API key for authentication */\r\n apiKey: string;\r\n /** TikTok username to look up (auto-resolves room_id and anchor_id) */\r\n uniqueId?: string;\r\n /** Direct room ID (skip resolution) */\r\n roomId?: string;\r\n /** Direct anchor/owner ID (skip resolution) */\r\n anchorId?: string;\r\n /**\r\n * TikTok session cookie string for authentication.\r\n * Required — ranklist endpoints return 20003 without login.\r\n * Example: \"sessionid=abc123; sid_guard=def456\"\r\n */\r\n sessionCookie?: string;\r\n /**\r\n * Which ranklist sub-endpoint to call:\r\n * - \"online_audience\" (default) — top gifters with scores\r\n * - \"anchor_rank_list\" — gifter ranking by rank_type\r\n * - \"entrance\" — entrance UI metadata, tabs, gap-to-rank\r\n */\r\n type?: 'online_audience' | 'anchor_rank_list' | 'entrance';\r\n /**\r\n * For \"anchor_rank_list\" type only:\r\n * - \"1\" = hourly ranking (default)\r\n * - \"8\" = daily ranking\r\n */\r\n rankType?: string;\r\n}\r\n\r\n/**\r\n * Fetch ranked user lists from TikTok via the sign server.\r\n *\r\n * When `sessionCookie` is provided, the server returns a **sign-and-return**\r\n * response with a signed URL that you must fetch from your own IP\r\n * (TikTok sessions are IP-bound). Check for `sign_and_return: true` in\r\n * the response to detect this mode.\r\n *\r\n * @example\r\n * ```ts\r\n * const data = await getRanklist({\r\n * apiKey: 'your-key',\r\n * uniqueId: 'katarina.live',\r\n * sessionCookie: 'sessionid=abc; sid_guard=def',\r\n * type: 'online_audience',\r\n * });\r\n *\r\n * if (data.sign_and_return) {\r\n * // Fetch the signed URL from YOUR IP with your session cookie\r\n * const resp = await fetch(data.signed_url, {\r\n * method: data.method,\r\n * headers: { ...data.headers, Cookie: sessionCookie },\r\n * ...(data.body ? { body: data.body } : {}),\r\n * });\r\n * const tikData = await resp.json();\r\n * console.log(tikData); // TikTok's raw response\r\n * } else {\r\n * console.log(data); // Direct TikTok response (no session)\r\n * }\r\n * ```\r\n */\r\nexport async function getRanklist(opts: GetRanklistOptions): Promise<RanklistResponse> {\r\n const base = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const ak = encodeURIComponent(opts.apiKey);\r\n\r\n const body: Record<string, any> = {};\r\n if (opts.uniqueId) body.unique_id = opts.uniqueId;\r\n if (opts.roomId) body.room_id = opts.roomId;\r\n if (opts.anchorId) body.anchor_id = opts.anchorId;\r\n if (opts.sessionCookie) body.session_cookie = opts.sessionCookie;\r\n if (opts.type) body.type = opts.type;\r\n if (opts.rankType) body.rank_type = opts.rankType;\r\n\r\n const resp = await fetch(`${base}/webcast/ranklist?apiKey=${ak}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(body),\r\n });\r\n\r\n const data = await resp.json() as any;\r\n\r\n // The server wraps TikTok's response in { status_code, data }\r\n if (data.status_code === 20003) {\r\n throw new Error(\r\n data.message || 'TikTok requires login. Provide sessionCookie with your TikTok session.'\r\n );\r\n }\r\n\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Ranklist failed (status ${data.status_code})`);\r\n }\r\n\r\n // Sign-and-return mode: session cookie was provided, return the full\r\n // response so the caller can fetch the signed URL from their own IP.\r\n if (data.sign_and_return) {\r\n return data as RanklistResponse;\r\n }\r\n\r\n return data.data as RanklistResponse;\r\n}\r\n\r\n// ── CAPTCHA Solver API ──────────────────────────────────────────────\r\n\r\nimport type { PuzzleSolveResult, RotateSolveResult, ShapesSolveResult } from './types.js';\r\n\r\n/**\r\n * Solve a puzzle (slider) CAPTCHA using the TikTool solver.\r\n *\r\n * @param apiKey - API key for authentication\r\n * @param puzzleB64 - Base64-encoded background image (PNG/JPEG)\r\n * @param pieceB64 - Base64-encoded puzzle piece image (PNG/JPEG)\r\n * @param serverUrl - Custom server URL (default: https://api.tik.tools)\r\n * @returns Solve result with slide position and confidence\r\n */\r\nexport async function solvePuzzle(\r\n apiKey: string,\r\n puzzleB64: string,\r\n pieceB64: string,\r\n serverUrl?: string,\r\n): Promise<PuzzleSolveResult> {\r\n const base = (serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const resp = await fetch(`${base}/captcha/solve/puzzle?apiKey=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ puzzle: puzzleB64, piece: pieceB64 }),\r\n });\r\n const data = await resp.json() as any;\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Solver failed (status ${data.status_code})`);\r\n }\r\n return data.data as PuzzleSolveResult;\r\n}\r\n\r\n/**\r\n * Solve a rotate (whirl) CAPTCHA using the TikTool solver.\r\n *\r\n * @param apiKey - API key for authentication\r\n * @param outerB64 - Base64-encoded outer ring image (PNG/JPEG)\r\n * @param innerB64 - Base64-encoded inner rotated image (PNG/JPEG)\r\n * @param serverUrl - Custom server URL (default: https://api.tik.tools)\r\n * @returns Solve result with rotation angle and confidence\r\n */\r\nexport async function solveRotate(\r\n apiKey: string,\r\n outerB64: string,\r\n innerB64: string,\r\n serverUrl?: string,\r\n): Promise<RotateSolveResult> {\r\n const base = (serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const resp = await fetch(`${base}/captcha/solve/rotate?apiKey=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ outer: outerB64, inner: innerB64 }),\r\n });\r\n const data = await resp.json() as any;\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Solver failed (status ${data.status_code})`);\r\n }\r\n return data.data as RotateSolveResult;\r\n}\r\n\r\n/**\r\n * Solve a shapes (3D matching) CAPTCHA using the TikTool solver.\r\n *\r\n * @param apiKey - API key for authentication\r\n * @param imageB64 - Base64-encoded CAPTCHA image with shape grid (PNG/JPEG)\r\n * @param serverUrl - Custom server URL (default: https://api.tik.tools)\r\n * @returns Solve result with two matching shape coordinates and confidence\r\n */\r\nexport async function solveShapes(\r\n apiKey: string,\r\n imageB64: string,\r\n serverUrl?: string,\r\n): Promise<ShapesSolveResult> {\r\n const base = (serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const resp = await fetch(`${base}/captcha/solve/shapes?apiKey=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ image: imageB64 }),\r\n });\r\n const data = await resp.json() as any;\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Solver failed (status ${data.status_code})`);\r\n }\r\n return data.data as ShapesSolveResult;\r\n}\r\n\r\n// ── Regional Leaderboard API ────────────────────────────────────────\r\n\r\nexport interface GetRegionalRanklistOptions {\r\n /** API server URL (default: https://api.tik.tools) */\r\n serverUrl?: string;\r\n /** API key for authentication (Pro or Ultra tier required) */\r\n apiKey: string;\r\n /** TikTok username to look up (auto-resolves room_id and anchor_id) */\r\n uniqueId?: string;\r\n /** Direct room ID (skip resolution) */\r\n roomId?: string;\r\n /** Direct anchor/owner ID (skip resolution) */\r\n anchorId?: string;\r\n /**\r\n * Ranking period:\r\n * - \"1\" = Hourly\r\n * - \"8\" = Daily (default)\r\n * - \"15\" = Popular LIVE\r\n * - \"16\" = League\r\n */\r\n rankType?: '1' | '8' | '15' | '16';\r\n /**\r\n * Sub-endpoint type:\r\n * - \"list\" (default) — ranked users with scores\r\n * - \"entrance\" — available ranking tabs/metadata\r\n */\r\n type?: 'list' | 'entrance';\r\n /** Gap interval filter (default: \"0\") */\r\n gapInterval?: string;\r\n /**\r\n * TikTok session cookie string for authentication.\r\n * Passed to the API server for proxied requests.\r\n * Example: \"sessionid=abc123; sessionid_ss=abc123\"\r\n */\r\n sessionCookie?: string;\r\n /**\r\n * Client's real IP address (for deployed server scenarios).\r\n * When running on a remote server, pass the end-user's IP so the\r\n * API server can proxy the TikTok request from the correct IP.\r\n */\r\n clientIp?: string;\r\n}\r\n\r\nexport interface RegionalRanklistSignedResponse {\r\n /** Always 0 on success */\r\n status_code: number;\r\n /** Always \"fetch_signed_url\" */\r\n action: string;\r\n /** The signed TikTok URL to POST */\r\n signed_url: string;\r\n /** HTTP method (always POST) */\r\n method: string;\r\n /** Required headers for the fetch */\r\n headers: Record<string, string>;\r\n /** URL-encoded POST body */\r\n body: string;\r\n /** Cookies to include (ttwid etc.) — append your sessionid */\r\n cookies: string;\r\n /** Human-readable note */\r\n note: string;\r\n}\r\n\r\n/**\r\n * Get a signed URL for fetching regional LIVE leaderboard data.\r\n *\r\n * **Two-step pattern**: TikTok sessions are IP-bound, so instead of\r\n * server-side fetching, this returns a signed URL with headers/body\r\n * that you POST from your own IP with your session cookie.\r\n *\r\n * Requires **Pro** or **Ultra** API key tier.\r\n *\r\n * @example\r\n * ```ts\r\n * // Step 1: Get signed URL\r\n * const signed = await getRegionalRanklist({\r\n * apiKey: 'your-pro-key',\r\n * roomId: '7607695933891218198',\r\n * anchorId: '7444599004337652758',\r\n * rankType: '8', // Daily\r\n * });\r\n *\r\n * // Step 2: Fetch from YOUR IP with YOUR session\r\n * const resp = await fetch(signed.signed_url, {\r\n * method: signed.method,\r\n * headers: { ...signed.headers, Cookie: `sessionid=YOUR_SID; ${signed.cookies}` },\r\n * body: signed.body,\r\n * });\r\n * const { data } = await resp.json();\r\n * data.rank_view.ranks.forEach((r, i) =>\r\n * console.log(`${i+1}. ${r.user.nickname} — ${r.score} pts`)\r\n * );\r\n * ```\r\n */\r\nexport async function getRegionalRanklist(opts: GetRegionalRanklistOptions): Promise<RegionalRanklistSignedResponse> {\r\n const base = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const ak = encodeURIComponent(opts.apiKey);\r\n\r\n const body: Record<string, any> = {};\r\n if (opts.uniqueId) body.unique_id = opts.uniqueId;\r\n if (opts.roomId) body.room_id = opts.roomId;\r\n if (opts.anchorId) body.anchor_id = opts.anchorId;\r\n if (opts.rankType) body.rank_type = opts.rankType;\r\n if (opts.type) body.type = opts.type;\r\n if (opts.gapInterval) body.gap_interval = opts.gapInterval;\r\n if (opts.sessionCookie) body.session_cookie = opts.sessionCookie;\r\n if (opts.clientIp) body.client_ip = opts.clientIp;\r\n\r\n const resp = await fetch(`${base}/webcast/ranklist/regional?apiKey=${ak}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(body),\r\n });\r\n\r\n const data = await resp.json() as any;\r\n\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Regional ranklist failed (status ${data.status_code})`);\r\n }\r\n\r\n return data as RegionalRanklistSignedResponse;\r\n}\r\n\r\n// ── Feed Discovery API ──────────────────────────────────────────────\r\n\r\nimport type { FeedSignedResponse, FeedRoom } from './types.js';\r\n\r\nexport interface GetLiveFeedOptions {\r\n /** API server URL (default: https://api.tik.tools) */\r\n serverUrl?: string;\r\n /** API key for authentication (Pro or Ultra tier required) */\r\n apiKey: string;\r\n /** Region code (default: 'US') */\r\n region?: string;\r\n /**\r\n * Feed channel:\r\n * - '87' = Recommended (default)\r\n * - '86' = Suggested\r\n * - '42' = Following\r\n * - '1111006' = Gaming\r\n */\r\n channelId?: string;\r\n /** Number of rooms to return (max 50, default 20) */\r\n count?: number;\r\n /** Pagination cursor from previous response (default: '0') */\r\n maxTime?: string;\r\n /** TikTok sessionid cookie — required for populated results */\r\n sessionId?: string;\r\n /** TikTok ttwid cookie */\r\n ttwid?: string;\r\n /** TikTok msToken cookie */\r\n msToken?: string;\r\n}\r\n\r\n/**\r\n * Get a signed URL for fetching the TikTok LIVE feed.\r\n *\r\n * **Two-step pattern**: Returns a signed URL with headers and cookies.\r\n * Fetch the signed URL from your own IP to get the feed data.\r\n *\r\n * Requires **Pro** or **Ultra** API key tier.\r\n *\r\n * @example\r\n * ```ts\r\n * // Step 1: Get signed URL\r\n * const signed = await getLiveFeed({\r\n * apiKey: 'your-pro-key',\r\n * sessionId: 'your-tiktok-sessionid',\r\n * region: 'US',\r\n * count: 10,\r\n * });\r\n *\r\n * // Step 2: Fetch from YOUR IP\r\n * const resp = await fetch(signed.signed_url, {\r\n * headers: { ...signed.headers, Cookie: signed.cookies || '' },\r\n * });\r\n * const data = await resp.json();\r\n * console.log(`Found ${data.data?.length || 0} live rooms`);\r\n *\r\n * // Step 3: Load more (pagination)\r\n * const nextSigned = await getLiveFeed({\r\n * apiKey: 'your-pro-key',\r\n * sessionId: 'your-tiktok-sessionid',\r\n * maxTime: data.extra?.max_time || '0',\r\n * });\r\n * ```\r\n */\r\nexport async function getLiveFeed(opts: GetLiveFeedOptions): Promise<FeedSignedResponse> {\r\n const base = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const params = new URLSearchParams();\r\n params.set('apiKey', opts.apiKey);\r\n if (opts.region) params.set('region', opts.region);\r\n if (opts.channelId) params.set('channel_id', opts.channelId);\r\n if (opts.count !== undefined) params.set('count', String(Math.min(opts.count, 50)));\r\n if (opts.maxTime) params.set('max_time', opts.maxTime);\r\n if (opts.sessionId) params.set('session_id', opts.sessionId);\r\n if (opts.ttwid) params.set('ttwid', opts.ttwid);\r\n if (opts.msToken) params.set('ms_token', opts.msToken);\r\n\r\n const resp = await fetch(`${base}/webcast/feed?${params.toString()}`);\r\n const data = await resp.json() as any;\r\n\r\n if (resp.status === 429) {\r\n throw new Error(data.error || 'Feed daily limit reached. Upgrade your plan for more calls.');\r\n }\r\n if (!resp.ok) {\r\n throw new Error(data.error || `Feed request failed (HTTP ${resp.status})`);\r\n }\r\n\r\n return data as FeedSignedResponse;\r\n}\r\n\r\n/**\r\n * Convenience: Get the feed AND fetch the signed URL in one step.\r\n * Returns the parsed JSON feed data from TikTok.\r\n *\r\n * @example\r\n * ```ts\r\n * const feed = await fetchFeed({\r\n * apiKey: 'your-pro-key',\r\n * sessionId: 'your-tiktok-sessionid',\r\n * region: 'GR',\r\n * count: 10,\r\n * });\r\n * for (const entry of feed.data || []) {\r\n * const room = entry.data;\r\n * console.log(`🔴 @${room.owner.display_id}: \"${room.title}\" — ${room.user_count} viewers`);\r\n * }\r\n * ```\r\n */\r\nexport async function fetchFeed(opts: GetLiveFeedOptions): Promise<any> {\r\n const signed = await getLiveFeed(opts);\r\n const headers: Record<string, string> = { ...(signed.headers || {}) };\r\n if (signed.cookies) {\r\n headers['Cookie'] = signed.cookies;\r\n }\r\n const resp = await fetch(signed.signed_url, { headers, redirect: 'follow' });\r\n const text = await resp.text();\r\n try {\r\n return JSON.parse(text);\r\n } catch {\r\n return null;\r\n }\r\n}\r\n"],"mappings":"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","names":["WebSocket","import_events","import_ws","WebSocket","DEFAULT_SIGN_SERVER","DEFAULT_SIGN_SERVER","DEFAULT_SIGN_SERVER"]}
|