@tiktool/live 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/proto.ts"],"sourcesContent":["export { TikTokLive } from './client.js';\r\n\r\nexport type {\r\n TikTokLiveOptions,\r\n TikTokLiveEvents,\r\n RoomInfo,\r\n LiveEvent,\r\n BaseEvent,\r\n ChatEvent,\r\n MemberEvent,\r\n LikeEvent,\r\n GiftEvent,\r\n SocialEvent,\r\n RoomUserSeqEvent,\r\n BattleEvent,\r\n BattleArmiesEvent,\r\n BattleTeam,\r\n BattleTeamUser,\r\n SubscribeEvent,\r\n EmoteChatEvent,\r\n EnvelopeEvent,\r\n QuestionEvent,\r\n ControlEvent,\r\n RoomEvent,\r\n LiveIntroEvent,\r\n RankUpdateEvent,\r\n LinkMicEvent,\r\n UnknownEvent,\r\n TikTokUser,\r\n} from './types.js';\r\n","import { EventEmitter } from 'events';\r\nimport * as http from 'http';\r\nimport * as https from 'https';\r\nimport * as zlib from 'zlib';\r\nimport 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>): 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 req = mod.get(url, { headers }, (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\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\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 }\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 });\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 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 if (!roomId) throw new Error(`User @${this.uniqueId} is not currently live`);\r\n this._roomId = roomId;\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 this.ws = new WebSocket(wsUrl, {\r\n headers: {\r\n 'User-Agent': DEFAULT_UA,\r\n 'Cookie': `ttwid=${ttwid}`,\r\n 'Origin': 'https://www.tiktok.com',\r\n },\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 };\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 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\nexport function decodeVarint(buf: Buffer, 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: Buffer, 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): Buffer {\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 Buffer.from(bytes);\r\n}\r\n\r\nexport interface ProtoField {\r\n fn: number;\r\n wt: number;\r\n value: Buffer | number | bigint;\r\n}\r\n\r\nexport function encodeField(fn: number, wt: number, value: Buffer | bigint | number | string): Buffer {\r\n const tag = encodeVarint((fn << 3) | wt);\r\n if (wt === 0) {\r\n return Buffer.concat([tag, encodeVarint(typeof value === 'number' ? BigInt(value) : value as bigint)]);\r\n }\r\n const data = typeof value === 'string' ? Buffer.from(value) : value as Buffer;\r\n return Buffer.concat([tag, encodeVarint(data.length), data]);\r\n}\r\n\r\nexport function decodeProto(buf: Buffer): 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: buf.readBigInt64LE(offset) });\r\n offset += 8;\r\n } else if (wt === 5) {\r\n fields.push({ fn, wt, value: BigInt(buf.readInt32LE(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 ? (f.value as Buffer).toString('utf-8') : '';\r\n}\r\n\r\nexport function getBytes(fields: ProtoField[], fn: number): Buffer | null {\r\n const f = fields.find(x => x.fn === fn && x.wt === 2);\r\n return f ? f.value as Buffer : 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\nexport function getAllBytes(fields: ProtoField[], fn: number): Buffer[] {\r\n return fields.filter(x => x.fn === fn && x.wt === 2).map(x => x.value as Buffer);\r\n}\r\n\r\nexport function buildHeartbeat(roomId: string): Buffer {\r\n const payload = encodeField(1, 0, BigInt(roomId));\r\n return Buffer.concat([\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): Buffer {\r\n const inner = Buffer.concat([\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 Buffer.concat([\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): Buffer {\r\n return Buffer.concat([\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\nfunction parseUser(data: Buffer): TikTokUser {\r\n const f = decodeProto(data);\r\n const id = String(getInt(f, 1) || getStr(f, 1));\r\n const nickname = getStr(f, 3) || getStr(f, 5);\r\n const uniqueId = getStr(f, 38) || getStr(f, 4) || getStr(f, 2);\r\n\r\n let profilePicture: string | undefined;\r\n const avatarBuf = getBytes(f, 9);\r\n if (avatarBuf) {\r\n try {\r\n const avatarFields = decodeProto(avatarBuf);\r\n const urlBuf = getBytes(avatarFields, 1);\r\n if (urlBuf) profilePicture = urlBuf.toString('utf-8');\r\n } catch { }\r\n }\r\n\r\n return { id, nickname, uniqueId: uniqueId || nickname || id, profilePicture };\r\n}\r\n\r\nfunction parseBattleTeam(teamBuf: Buffer): BattleTeam {\r\n const fields = decodeProto(teamBuf);\r\n const hostUserId = String(getInt(fields, 1));\r\n const score = getInt(fields, 2);\r\n const users: BattleTeamUser[] = [];\r\n\r\n const userFields = getAllBytes(fields, 3);\r\n for (const uf of userFields) {\r\n try {\r\n const uFields = decodeProto(uf);\r\n const userBuf = getBytes(uFields, 1);\r\n const userScore = getInt(uFields, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n users.push({ user, score: userScore });\r\n } catch { }\r\n }\r\n\r\n return { hostUserId, score, users };\r\n}\r\n\r\nexport function parseWebcastMessage(method: string, payload: Buffer): LiveEvent {\r\n const f = decodeProto(payload);\r\n const base = { timestamp: Date.now(), msgId: String(getInt(f, 1) || '') };\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 return { ...base, type: 'chat' as const, user, comment: getStr(f, 3) };\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 return { ...base, type: 'member' as const, user, action: getInt(f, 1) };\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 { ...base, type: 'like' as const, user, likeCount: getInt(f, 1), totalLikes: getInt(f, 2) || getInt(f, 7) };\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, 1);\r\n const repeatCount = getInt(f, 5);\r\n const repeatEnd = getInt(f, 9) === 1;\r\n const giftType = getInt(f, 6);\r\n const groupId = getStr(f, 11);\r\n\r\n let giftName = '', diamondCount = 0;\r\n const giftInfoBuf = getBytes(f, 15);\r\n if (giftInfoBuf) {\r\n const gf = decodeProto(giftInfoBuf);\r\n giftName = getStr(gf, 1);\r\n diamondCount = getInt(gf, 5) || getInt(gf, 2);\r\n }\r\n if (!giftName) {\r\n const giftBuf3 = getBytes(f, 3);\r\n if (giftBuf3) {\r\n const gf3 = decodeProto(giftBuf3);\r\n if (!giftName) giftName = getStr(gf3, 2);\r\n if (!diamondCount) diamondCount = getInt(gf3, 5);\r\n }\r\n }\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,\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 const actionInt = getInt(f, 1);\r\n const actionMap: Record<number, string> = { 1: 'follow', 2: 'share', 3: 'like' };\r\n return { ...base, type: 'social' as const, user, action: actionMap[actionInt] || `action_${actionInt}` };\r\n }\r\n\r\n case 'WebcastRoomUserSeqMessage':\r\n return {\r\n ...base, type: 'roomUserSeq' as const,\r\n totalViewers: getInt(f, 1) || getInt(f, 3),\r\n viewerCount: getInt(f, 2) || getInt(f, 4),\r\n };\r\n\r\n case 'WebcastLinkMicBattle': {\r\n const battleId = String(getInt(f, 1) || getStr(f, 1));\r\n const status = getInt(f, 2);\r\n const battleDuration = getInt(f, 3);\r\n const teams: BattleTeam[] = [];\r\n const teamBufs = getAllBytes(f, 7);\r\n for (const tb of teamBufs) {\r\n try { teams.push(parseBattleTeam(tb)); } catch { }\r\n }\r\n return { ...base, type: 'battle' as const, battleId, status, battleDuration, teams };\r\n }\r\n\r\n case 'WebcastLinkMicArmies': {\r\n const battleId = String(getInt(f, 1) || getStr(f, 1));\r\n const teams: BattleTeam[] = [];\r\n const teamBufs = getAllBytes(f, 3);\r\n for (const tb of teamBufs) {\r\n try { teams.push(parseBattleTeam(tb)); } catch { }\r\n }\r\n return { ...base, type: 'battleArmies' as const, battleId, teams };\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 const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n return { ...base, type: 'question' as const, user, questionText: getStr(f, 3) || getStr(f, 4) };\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, 1));\r\n return { ...base, type: 'liveIntro' as const, roomId, title: getStr(f, 4) || getStr(f, 2) };\r\n }\r\n\r\n case 'WebcastLinkMicMethod':\r\n case 'WebcastLinkmicBattleTaskMessage': {\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 default:\r\n return { ...base, type: 'unknown' as const, method };\r\n }\r\n}\r\n\r\nexport function parseWebcastResponse(payload: Buffer): 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 Buffer;\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"],"mappings":"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","names":["WebSocket"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/proto.ts"],"sourcesContent":["export { TikTokLive } from './client.js';\r\n\r\nexport type {\r\n TikTokLiveOptions,\r\n TikTokLiveEvents,\r\n RoomInfo,\r\n LiveEvent,\r\n BaseEvent,\r\n ChatEvent,\r\n MemberEvent,\r\n LikeEvent,\r\n GiftEvent,\r\n SocialEvent,\r\n RoomUserSeqEvent,\r\n BattleEvent,\r\n BattleArmiesEvent,\r\n BattleTeam,\r\n BattleTeamUser,\r\n SubscribeEvent,\r\n EmoteChatEvent,\r\n EnvelopeEvent,\r\n QuestionEvent,\r\n ControlEvent,\r\n RoomEvent,\r\n LiveIntroEvent,\r\n RankUpdateEvent,\r\n LinkMicEvent,\r\n UnknownEvent,\r\n TikTokUser,\r\n} from './types.js';\r\n","import { EventEmitter } from 'events';\r\nimport * as http from 'http';\r\nimport * as https from 'https';\r\nimport * as zlib from 'zlib';\r\nimport 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>): 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 req = mod.get(url, { headers }, (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 // Cache host identities from battle events for enriching battleArmies\r\n private _battleHosts = new Map<string, { id: string; nickname: string; uniqueId: string; profilePicture?: string }>();\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\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 }\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 });\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 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 if (!roomId) throw new Error(`User @${this.uniqueId} is not currently live`);\r\n this._roomId = roomId;\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 this.ws = new WebSocket(wsUrl, {\r\n headers: {\r\n 'User-Agent': DEFAULT_UA,\r\n 'Cookie': `ttwid=${ttwid}`,\r\n 'Origin': 'https://www.tiktok.com',\r\n },\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 };\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 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\r\n // Cache host identities from battle events (hosts are in users[])\r\n if (evt.type === 'battle') {\r\n for (const team of evt.teams) {\r\n const host = team.users.find(u => u.user.id === team.hostUserId);\r\n if (host) {\r\n this._battleHosts.set(team.hostUserId, host.user);\r\n team.hostUser = host.user;\r\n }\r\n }\r\n }\r\n\r\n // Enrich battleArmies teams with cached host identities\r\n if (evt.type === 'battleArmies') {\r\n for (const team of evt.teams) {\r\n // Try users array first (unlikely for armies but check anyway)\r\n const host = team.users.find(u => u.user.id === team.hostUserId);\r\n if (host) {\r\n team.hostUser = host.user;\r\n this._battleHosts.set(team.hostUserId, host.user);\r\n } else {\r\n // Use cached host from previous battle event\r\n const cached = this._battleHosts.get(team.hostUserId);\r\n if (cached) team.hostUser = cached;\r\n }\r\n }\r\n }\r\n\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\nexport function decodeVarint(buf: Buffer, 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: Buffer, 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): Buffer {\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 Buffer.from(bytes);\r\n}\r\n\r\nexport interface ProtoField {\r\n fn: number;\r\n wt: number;\r\n value: Buffer | number | bigint;\r\n}\r\n\r\nexport function encodeField(fn: number, wt: number, value: Buffer | bigint | number | string): Buffer {\r\n const tag = encodeVarint((fn << 3) | wt);\r\n if (wt === 0) {\r\n return Buffer.concat([tag, encodeVarint(typeof value === 'number' ? BigInt(value) : value as bigint)]);\r\n }\r\n const data = typeof value === 'string' ? Buffer.from(value) : value as Buffer;\r\n return Buffer.concat([tag, encodeVarint(data.length), data]);\r\n}\r\n\r\nexport function decodeProto(buf: Buffer): 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: buf.readBigInt64LE(offset) });\r\n offset += 8;\r\n } else if (wt === 5) {\r\n fields.push({ fn, wt, value: BigInt(buf.readInt32LE(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 ? (f.value as Buffer).toString('utf-8') : '';\r\n}\r\n\r\nexport function getBytes(fields: ProtoField[], fn: number): Buffer | null {\r\n const f = fields.find(x => x.fn === fn && x.wt === 2);\r\n return f ? f.value as Buffer : 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\nexport function getAllBytes(fields: ProtoField[], fn: number): Buffer[] {\r\n return fields.filter(x => x.fn === fn && x.wt === 2).map(x => x.value as Buffer);\r\n}\r\n\r\nexport function buildHeartbeat(roomId: string): Buffer {\r\n const payload = encodeField(1, 0, BigInt(roomId));\r\n return Buffer.concat([\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): Buffer {\r\n const inner = Buffer.concat([\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 Buffer.concat([\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): Buffer {\r\n return Buffer.concat([\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// ── User parsing ────────────────────────────────────────────────────\r\n// Proto: User { userId=1, nickname=3, profilePicture=9, extraAttributes=22, badge=64, uniqueId=38 }\r\n// Proto: LinkUser { userId=1, nickname=2, profilePicture=3, uniqueId=4 }\r\n// Proto: ProfilePicture { repeated urls=1 }\r\n\r\nfunction parseUser(data: Buffer): TikTokUser {\r\n const f = decodeProto(data);\r\n const id = String(getInt(f, 1) || getStr(f, 1));\r\n const nickname = getStr(f, 3) || getStr(f, 2);\r\n const uniqueId = getStr(f, 38) || getStr(f, 4) || getStr(f, 2) || '';\r\n\r\n let profilePicture: string | undefined;\r\n const avatarBuf = getBytes(f, 9) || getBytes(f, 3);\r\n if (avatarBuf) {\r\n try {\r\n const avatarFields = decodeProto(avatarBuf);\r\n // ProfilePicture.urls is repeated string at field 1\r\n const urlBufs = getAllBytes(avatarFields, 1);\r\n if (urlBufs.length > 0) profilePicture = urlBufs[0].toString('utf-8');\r\n } catch { }\r\n }\r\n\r\n // Badges from field 64 → field 21 (repeated UserBadge)\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 profilePicture,\r\n badges: badges.length > 0 ? badges : undefined,\r\n };\r\n}\r\n\r\n// ── Battle parsing ──────────────────────────────────────────────────\r\n// Proto: WebcastLinkMicBattle { repeated battleUsers=10 }\r\n// battleUsers → WebcastLinkMicBattleItems { battleGroup=2 }\r\n// battleGroup → WebcastLinkMicBattleGroup { LinkUser user=1 }\r\n//\r\n// Proto: WebcastLinkMicArmies { repeated battleItems=3, battleStatus=7 }\r\n// battleItems → WebcastLinkMicArmiesItems { hostUserId=1, repeated battleGroups=2 }\r\n// battleGroups → WebcastLinkMicArmiesGroup { repeated users=1, points=2 }\r\n\r\nfunction parseBattleTeamFromArmies(itemBuf: Buffer): BattleTeam {\r\n const f = decodeProto(itemBuf);\r\n const hostUserId = String(getInt(f, 1));\r\n let teamScore = 0;\r\n const users: BattleTeamUser[] = [];\r\n\r\n // battleGroups at field 2\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 // users at field 1 (repeated)\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 return { hostUserId, score: teamScore, users };\r\n}\r\n\r\n// ── Message-specific parsers ────────────────────────────────────────\r\n\r\nexport function parseWebcastMessage(method: string, payload: Buffer): LiveEvent {\r\n const f = decodeProto(payload);\r\n const base = { timestamp: Date.now(), msgId: '' };\r\n\r\n // Try to extract msgId from MessageType (field 1 submessage, field 4 = timestamp)\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 // Proto: WebcastChatMessage { MessageType type=1, User user=2, string comment=3 }\r\n case 'WebcastChatMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n return { ...base, type: 'chat' as const, user, comment: getStr(f, 3) };\r\n }\r\n\r\n // Proto: WebcastMemberMessage { User user=2, WebcastMessageEvent event=1 }\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 // Proto: WebcastLikeMessage { User user=5, WebcastMessageEvent event=1, int32 likeCount=2, int32 totalLikeCount=3 }\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 // Proto: WebcastGiftMessage {\r\n // User user=7, int32 giftId=2, int32 repeatCount=5, int32 repeatEnd=9,\r\n // GiftDetails giftDetails=15, GiftExtra giftExtra=23\r\n // }\r\n // GiftDetails { GiftImage giftImage=1, string giftName=16, string describe=2,\r\n // int32 giftType=11, int32 diamondCount=12 }\r\n // GiftExtra { uint64 timestamp=6, uint64 toUserId=8 }\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 = String(getInt(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,\r\n };\r\n }\r\n\r\n // Proto: WebcastSocialMessage { User user=2, WebcastMessageEvent event=1 }\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 // Proto: WebcastRoomUserSeqMessage { int32 viewerCount=3 }\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 // Proto: WebcastLinkMicBattle { repeated WebcastLinkMicBattleItems battleUsers=10 }\r\n // battleUsers → { battleGroup=2 → { LinkUser user=1 } }\r\n case 'WebcastLinkMicBattle': {\r\n const battleId = String(getInt(f, 1) || '');\r\n const status = getInt(f, 2) || 1;\r\n const battleDuration = getInt(f, 3);\r\n const teams: BattleTeam[] = [];\r\n\r\n // battleUsers at field 10\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 7 as fallback (some message versions)\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 };\r\n }\r\n\r\n // Proto: WebcastLinkMicArmies { repeated battleItems=3, int32 battleStatus=7 }\r\n // battleItems → { hostUserId=1, repeated battleGroups=2 }\r\n // battleGroups → { repeated users=1, int32 points=2 }\r\n case 'WebcastLinkMicArmies': {\r\n const battleId = String(getInt(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 // Proto: WebcastSubNotifyMessage { User user=2, ... subMonth=3 }\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 // Proto: WebcastQuestionNewMessage { MessageType type=1, QuestionDetails questionDetails=2 }\r\n // QuestionDetails { string questionText=2, User user=5 }\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 // Proto: WebcastControlMessage { int32 action=2 }\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 // Proto: WebcastLiveIntroMessage { uint64 id=2, string description=4, User user=5 }\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 case 'WebcastLinkmicBattleTaskMessage': {\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 default:\r\n return { ...base, type: 'unknown' as const, method };\r\n }\r\n}\r\n\r\nexport function parseWebcastResponse(payload: Buffer): 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 Buffer;\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"],"mappings":"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","names":["WebSocket"]}
package/dist/index.mjs CHANGED
@@ -123,80 +123,147 @@ function buildAck(id) {
123
123
  function parseUser(data) {
124
124
  const f = decodeProto(data);
125
125
  const id = String(getInt(f, 1) || getStr(f, 1));
126
- const nickname = getStr(f, 3) || getStr(f, 5);
127
- const uniqueId = getStr(f, 38) || getStr(f, 4) || getStr(f, 2);
126
+ const nickname = getStr(f, 3) || getStr(f, 2);
127
+ const uniqueId = getStr(f, 38) || getStr(f, 4) || getStr(f, 2) || "";
128
128
  let profilePicture;
129
- const avatarBuf = getBytes(f, 9);
129
+ const avatarBuf = getBytes(f, 9) || getBytes(f, 3);
130
130
  if (avatarBuf) {
131
131
  try {
132
132
  const avatarFields = decodeProto(avatarBuf);
133
- const urlBuf = getBytes(avatarFields, 1);
134
- if (urlBuf) profilePicture = urlBuf.toString("utf-8");
133
+ const urlBufs = getAllBytes(avatarFields, 1);
134
+ if (urlBufs.length > 0) profilePicture = urlBufs[0].toString("utf-8");
135
135
  } catch {
136
136
  }
137
137
  }
138
- return { id, nickname, uniqueId: uniqueId || nickname || id, profilePicture };
138
+ const badges = [];
139
+ const badgeBuf = getBytes(f, 64);
140
+ if (badgeBuf) {
141
+ try {
142
+ const badgeFields = decodeProto(badgeBuf);
143
+ const badgeItems = getAllBytes(badgeFields, 21);
144
+ for (const bi of badgeItems) {
145
+ const bf = decodeProto(bi);
146
+ const name = getStr(bf, 3) || getStr(bf, 2);
147
+ if (name) badges.push(name);
148
+ }
149
+ } catch {
150
+ }
151
+ }
152
+ return {
153
+ id,
154
+ nickname,
155
+ uniqueId: uniqueId || nickname || id,
156
+ profilePicture,
157
+ badges: badges.length > 0 ? badges : void 0
158
+ };
139
159
  }
140
- function parseBattleTeam(teamBuf) {
141
- const fields = decodeProto(teamBuf);
142
- const hostUserId = String(getInt(fields, 1));
143
- const score = getInt(fields, 2);
160
+ function parseBattleTeamFromArmies(itemBuf) {
161
+ const f = decodeProto(itemBuf);
162
+ const hostUserId = String(getInt(f, 1));
163
+ let teamScore = 0;
144
164
  const users = [];
145
- const userFields = getAllBytes(fields, 3);
146
- for (const uf of userFields) {
165
+ const groups = getAllBytes(f, 2);
166
+ for (const gb of groups) {
147
167
  try {
148
- const uFields = decodeProto(uf);
149
- const userBuf = getBytes(uFields, 1);
150
- const userScore = getInt(uFields, 2);
151
- const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
152
- users.push({ user, score: userScore });
168
+ const gf = decodeProto(gb);
169
+ const points = getInt(gf, 2);
170
+ teamScore += points;
171
+ const userBufs = getAllBytes(gf, 1);
172
+ for (const ub of userBufs) {
173
+ try {
174
+ const user = parseUser(ub);
175
+ users.push({ user, score: points });
176
+ } catch {
177
+ }
178
+ }
153
179
  } catch {
154
180
  }
155
181
  }
156
- return { hostUserId, score, users };
182
+ return { hostUserId, score: teamScore, users };
157
183
  }
158
184
  function parseWebcastMessage(method, payload) {
159
185
  const f = decodeProto(payload);
160
- const base = { timestamp: Date.now(), msgId: String(getInt(f, 1) || "") };
186
+ const base = { timestamp: Date.now(), msgId: "" };
187
+ const typeBuf = getBytes(f, 1);
188
+ if (typeBuf) {
189
+ try {
190
+ const tf = decodeProto(typeBuf);
191
+ const ts = getInt(tf, 4);
192
+ if (ts) base.timestamp = ts;
193
+ } catch {
194
+ }
195
+ }
161
196
  switch (method) {
197
+ // Proto: WebcastChatMessage { MessageType type=1, User user=2, string comment=3 }
162
198
  case "WebcastChatMessage": {
163
199
  const userBuf = getBytes(f, 2);
164
200
  const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
165
201
  return { ...base, type: "chat", user, comment: getStr(f, 3) };
166
202
  }
203
+ // Proto: WebcastMemberMessage { User user=2, WebcastMessageEvent event=1 }
167
204
  case "WebcastMemberMessage": {
168
205
  const userBuf = getBytes(f, 2);
169
206
  const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
170
- return { ...base, type: "member", user, action: getInt(f, 1) };
207
+ let action = 1;
208
+ const eventBuf = getBytes(f, 1);
209
+ if (eventBuf) {
210
+ const ef = decodeProto(eventBuf);
211
+ const detailBuf = getBytes(ef, 8);
212
+ if (detailBuf) {
213
+ const df = decodeProto(detailBuf);
214
+ const label = getStr(df, 2);
215
+ if (label.includes("followed")) action = 2;
216
+ else if (label.includes("share")) action = 3;
217
+ }
218
+ }
219
+ return { ...base, type: "member", user, action };
171
220
  }
221
+ // Proto: WebcastLikeMessage { User user=5, WebcastMessageEvent event=1, int32 likeCount=2, int32 totalLikeCount=3 }
172
222
  case "WebcastLikeMessage": {
173
223
  const userBuf = getBytes(f, 5);
174
224
  const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
175
- return { ...base, type: "like", user, likeCount: getInt(f, 1), totalLikes: getInt(f, 2) || getInt(f, 7) };
225
+ return {
226
+ ...base,
227
+ type: "like",
228
+ user,
229
+ likeCount: getInt(f, 2),
230
+ totalLikes: getInt(f, 3)
231
+ };
176
232
  }
233
+ // Proto: WebcastGiftMessage {
234
+ // User user=7, int32 giftId=2, int32 repeatCount=5, int32 repeatEnd=9,
235
+ // GiftDetails giftDetails=15, GiftExtra giftExtra=23
236
+ // }
237
+ // GiftDetails { GiftImage giftImage=1, string giftName=16, string describe=2,
238
+ // int32 giftType=11, int32 diamondCount=12 }
239
+ // GiftExtra { uint64 timestamp=6, uint64 toUserId=8 }
177
240
  case "WebcastGiftMessage": {
178
241
  const userBuf = getBytes(f, 7);
179
242
  const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
180
- const giftId = getInt(f, 1);
243
+ const giftId = getInt(f, 2);
181
244
  const repeatCount = getInt(f, 5);
182
245
  const repeatEnd = getInt(f, 9) === 1;
183
- const giftType = getInt(f, 6);
184
- const groupId = getStr(f, 11);
185
- let giftName = "", diamondCount = 0;
186
- const giftInfoBuf = getBytes(f, 15);
187
- if (giftInfoBuf) {
188
- const gf = decodeProto(giftInfoBuf);
189
- giftName = getStr(gf, 1);
190
- diamondCount = getInt(gf, 5) || getInt(gf, 2);
191
- }
192
- if (!giftName) {
193
- const giftBuf3 = getBytes(f, 3);
194
- if (giftBuf3) {
195
- const gf3 = decodeProto(giftBuf3);
196
- if (!giftName) giftName = getStr(gf3, 2);
197
- if (!diamondCount) diamondCount = getInt(gf3, 5);
246
+ let giftName = "", diamondCount = 0, giftType = 0;
247
+ let giftImageUrl = "";
248
+ const detailsBuf = getBytes(f, 15);
249
+ if (detailsBuf) {
250
+ const df = decodeProto(detailsBuf);
251
+ giftName = getStr(df, 16) || getStr(df, 2);
252
+ diamondCount = getInt(df, 12);
253
+ giftType = getInt(df, 11);
254
+ const imgBuf = getBytes(df, 1);
255
+ if (imgBuf) {
256
+ const imgf = decodeProto(imgBuf);
257
+ giftImageUrl = getStr(imgf, 1);
198
258
  }
199
259
  }
260
+ let toUserId = "";
261
+ const extraBuf = getBytes(f, 23);
262
+ if (extraBuf) {
263
+ const ef = decodeProto(extraBuf);
264
+ toUserId = String(getInt(ef, 8));
265
+ }
266
+ const groupId = toUserId || getStr(f, 11);
200
267
  return {
201
268
  ...base,
202
269
  type: "gift",
@@ -211,46 +278,93 @@ function parseWebcastMessage(method, payload) {
211
278
  groupId
212
279
  };
213
280
  }
281
+ // Proto: WebcastSocialMessage { User user=2, WebcastMessageEvent event=1 }
214
282
  case "WebcastSocialMessage": {
215
283
  const userBuf = getBytes(f, 2);
216
284
  const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
217
- const actionInt = getInt(f, 1);
218
- const actionMap = { 1: "follow", 2: "share", 3: "like" };
219
- return { ...base, type: "social", user, action: actionMap[actionInt] || `action_${actionInt}` };
285
+ let action = "follow";
286
+ const eventBuf = getBytes(f, 1);
287
+ if (eventBuf) {
288
+ const ef = decodeProto(eventBuf);
289
+ const detailBuf = getBytes(ef, 8);
290
+ if (detailBuf) {
291
+ const df = decodeProto(detailBuf);
292
+ const label = getStr(df, 2);
293
+ if (label.includes("share")) action = "share";
294
+ else if (label.includes("follow")) action = "follow";
295
+ const displayType = getStr(df, 1);
296
+ if (displayType === "pm_mt_msg_viewer_share") action = "share";
297
+ }
298
+ }
299
+ return { ...base, type: "social", user, action };
220
300
  }
221
- case "WebcastRoomUserSeqMessage":
222
- return {
223
- ...base,
224
- type: "roomUserSeq",
225
- totalViewers: getInt(f, 1) || getInt(f, 3),
226
- viewerCount: getInt(f, 2) || getInt(f, 4)
227
- };
301
+ // Proto: WebcastRoomUserSeqMessage { int32 viewerCount=3 }
302
+ case "WebcastRoomUserSeqMessage": {
303
+ const viewerCount = getInt(f, 3) || getInt(f, 2);
304
+ const totalViewers = getInt(f, 1) || viewerCount;
305
+ return { ...base, type: "roomUserSeq", totalViewers, viewerCount };
306
+ }
307
+ // Proto: WebcastLinkMicBattle { repeated WebcastLinkMicBattleItems battleUsers=10 }
308
+ // battleUsers → { battleGroup=2 → { LinkUser user=1 } }
228
309
  case "WebcastLinkMicBattle": {
229
- const battleId = String(getInt(f, 1) || getStr(f, 1));
230
- const status = getInt(f, 2);
310
+ const battleId = String(getInt(f, 1) || "");
311
+ const status = getInt(f, 2) || 1;
231
312
  const battleDuration = getInt(f, 3);
232
313
  const teams = [];
233
- const teamBufs = getAllBytes(f, 7);
234
- for (const tb of teamBufs) {
314
+ const battleUserBufs = getAllBytes(f, 10);
315
+ for (const bub of battleUserBufs) {
235
316
  try {
236
- teams.push(parseBattleTeam(tb));
317
+ const bf = decodeProto(bub);
318
+ const groupBuf = getBytes(bf, 2);
319
+ if (groupBuf) {
320
+ const gf = decodeProto(groupBuf);
321
+ const linkUserBuf = getBytes(gf, 1);
322
+ if (linkUserBuf) {
323
+ const user = parseUser(linkUserBuf);
324
+ teams.push({
325
+ hostUserId: user.id,
326
+ score: 0,
327
+ users: [{ user, score: 0 }]
328
+ });
329
+ }
330
+ }
237
331
  } catch {
238
332
  }
239
333
  }
334
+ if (teams.length === 0) {
335
+ const teamBufs7 = getAllBytes(f, 7);
336
+ for (const tb of teamBufs7) {
337
+ try {
338
+ teams.push(parseBattleTeamFromArmies(tb));
339
+ } catch {
340
+ }
341
+ }
342
+ }
240
343
  return { ...base, type: "battle", battleId, status, battleDuration, teams };
241
344
  }
345
+ // Proto: WebcastLinkMicArmies { repeated battleItems=3, int32 battleStatus=7 }
346
+ // battleItems → { hostUserId=1, repeated battleGroups=2 }
347
+ // battleGroups → { repeated users=1, int32 points=2 }
242
348
  case "WebcastLinkMicArmies": {
243
- const battleId = String(getInt(f, 1) || getStr(f, 1));
349
+ const battleId = String(getInt(f, 1) || "");
350
+ const battleStatus = getInt(f, 7);
244
351
  const teams = [];
245
- const teamBufs = getAllBytes(f, 3);
246
- for (const tb of teamBufs) {
352
+ const itemBufs = getAllBytes(f, 3);
353
+ for (const ib of itemBufs) {
247
354
  try {
248
- teams.push(parseBattleTeam(tb));
355
+ teams.push(parseBattleTeamFromArmies(ib));
249
356
  } catch {
250
357
  }
251
358
  }
252
- return { ...base, type: "battleArmies", battleId, teams };
359
+ return {
360
+ ...base,
361
+ type: "battleArmies",
362
+ battleId,
363
+ teams,
364
+ status: battleStatus
365
+ };
253
366
  }
367
+ // Proto: WebcastSubNotifyMessage { User user=2, ... subMonth=3 }
254
368
  case "WebcastSubNotifyMessage": {
255
369
  const userBuf = getBytes(f, 2);
256
370
  const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
@@ -276,10 +390,18 @@ function parseWebcastMessage(method, payload) {
276
390
  const envelopeId = String(getInt(f, 1) || getStr(f, 1));
277
391
  return { ...base, type: "envelope", envelopeId, diamondCount: getInt(f, 3) };
278
392
  }
393
+ // Proto: WebcastQuestionNewMessage { MessageType type=1, QuestionDetails questionDetails=2 }
394
+ // QuestionDetails { string questionText=2, User user=5 }
279
395
  case "WebcastQuestionNewMessage": {
280
- const userBuf = getBytes(f, 2);
281
- const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
282
- return { ...base, type: "question", user, questionText: getStr(f, 3) || getStr(f, 4) };
396
+ let questionText = "", user = { id: "0", nickname: "", uniqueId: "" };
397
+ const detailBuf = getBytes(f, 2);
398
+ if (detailBuf) {
399
+ const df = decodeProto(detailBuf);
400
+ questionText = getStr(df, 2);
401
+ const userBuf = getBytes(df, 5);
402
+ if (userBuf) user = parseUser(userBuf);
403
+ }
404
+ return { ...base, type: "question", user, questionText };
283
405
  }
284
406
  case "WebcastRankUpdateMessage":
285
407
  case "WebcastHourlyRankMessage": {
@@ -299,14 +421,17 @@ function parseWebcastMessage(method, payload) {
299
421
  }
300
422
  return { ...base, type: "rankUpdate", rankType, rankList };
301
423
  }
424
+ // Proto: WebcastControlMessage { int32 action=2 }
302
425
  case "WebcastControlMessage":
303
426
  return { ...base, type: "control", action: getInt(f, 2) || getInt(f, 1) };
304
427
  case "WebcastRoomMessage":
305
428
  case "RoomMessage":
306
429
  return { ...base, type: "room", status: getInt(f, 2) };
430
+ // Proto: WebcastLiveIntroMessage { uint64 id=2, string description=4, User user=5 }
307
431
  case "WebcastLiveIntroMessage": {
308
- const roomId = String(getInt(f, 1));
309
- return { ...base, type: "liveIntro", roomId, title: getStr(f, 4) || getStr(f, 2) };
432
+ const roomId = String(getInt(f, 2));
433
+ const title = getStr(f, 4) || getStr(f, 2);
434
+ return { ...base, type: "liveIntro", roomId, title };
310
435
  }
311
436
  case "WebcastLinkMicMethod":
312
437
  case "WebcastLinkmicBattleTaskMessage": {
@@ -382,6 +507,8 @@ var TikTokLive = class extends EventEmitter {
382
507
  _connected = false;
383
508
  _eventCount = 0;
384
509
  _roomId = "";
510
+ // Cache host identities from battle events for enriching battleArmies
511
+ _battleHosts = /* @__PURE__ */ new Map();
385
512
  uniqueId;
386
513
  signServerUrl;
387
514
  apiKey;
@@ -579,6 +706,27 @@ var TikTokLive = class extends EventEmitter {
579
706
  const events = parseWebcastResponse(inner);
580
707
  for (const evt of events) {
581
708
  this._eventCount++;
709
+ if (evt.type === "battle") {
710
+ for (const team of evt.teams) {
711
+ const host = team.users.find((u) => u.user.id === team.hostUserId);
712
+ if (host) {
713
+ this._battleHosts.set(team.hostUserId, host.user);
714
+ team.hostUser = host.user;
715
+ }
716
+ }
717
+ }
718
+ if (evt.type === "battleArmies") {
719
+ for (const team of evt.teams) {
720
+ const host = team.users.find((u) => u.user.id === team.hostUserId);
721
+ if (host) {
722
+ team.hostUser = host.user;
723
+ this._battleHosts.set(team.hostUserId, host.user);
724
+ } else {
725
+ const cached = this._battleHosts.get(team.hostUserId);
726
+ if (cached) team.hostUser = cached;
727
+ }
728
+ }
729
+ }
582
730
  this.emit("event", evt);
583
731
  this.emit(evt.type, evt);
584
732
  }