@tiktool/live 2.5.2 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/proto.ts","../src/api.ts"],"sourcesContent":["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\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 }\r\n\r\n async connect(): Promise<void> {\r\n this.intentionalClose = false;\r\n\r\n const resp = await httpGet(`https://www.tiktok.com/@${this.uniqueId}/live`, {\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 let ttwid = '';\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 let roomId = '';\r\n let ownerUserId = '';\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 // Extract owner userId from parsed JSON — critical for 1v1 battle team identification\r\n // SIGI_STATE structure: { LiveRoom: { liveRoomUserInfo: { user: { id, uniqueId } } } }\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 this._roomId = roomId;\r\n this._ownerUserId = ownerUserId;\r\n\r\n const crMatch = html.match(/\"clusterRegion\"\\s*:\\s*\"([^\"]+)\"/);\r\n const clusterRegion = crMatch ? crMatch[1] : '';\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 — always include ttwid, add session cookies if provided\r\n let cookieHeader = `ttwid=${ttwid}`;\r\n if (this._sessionId) {\r\n cookieHeader += `; sessionid=${this._sessionId}; sessionid_ss=${this._sessionId}; sid_tt=${this._sessionId}`;\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 const badgeBuf = getBytes(f, 64);\r\n if (badgeBuf) {\r\n try {\r\n const badgeFields = decodeProto(badgeBuf);\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 };\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 const user = parseUser(ub);\r\n users.push({ user, score: points });\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, 1) || '';\r\n const battleStatus = getInt(f, 7);\r\n const teams: BattleTeam[] = [];\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,\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 * 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":["DEFAULT_SIGN_SERVER","DEFAULT_SIGN_SERVER","DEFAULT_SIGN_SERVER"]}
1
+ {"version":3,"sources":["../src/client.ts","../src/proto.ts","../src/api.ts"],"sourcesContent":["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 const user = parseUser(ub);\r\n users.push({ user, score: points });\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 * 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":["DEFAULT_SIGN_SERVER","DEFAULT_SIGN_SERVER","DEFAULT_SIGN_SERVER"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiktool/live",
3
- "version": "2.5.2",
3
+ "version": "2.6.1",
4
4
  "description": "TikTok LIVE API Client — Real-time chat, gifts, viewers & events from any TikTok livestream. Direct WebSocket connection.",
5
5
  "author": "tiktool",
6
6
  "license": "MIT",
@@ -54,7 +54,6 @@
54
54
  "prepublishOnly": "npm run build"
55
55
  },
56
56
  "dependencies": {
57
- "@tiktool/live": "^2.4.4",
58
57
  "ws": "^8.18.0"
59
58
  },
60
59
  "devDependencies": {
@@ -65,4 +64,4 @@
65
64
  "engines": {
66
65
  "node": ">=18"
67
66
  }
68
- }
67
+ }