@tiktool/live 1.0.0 → 1.2.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 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 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.apiKey\r\n ? `${this.signServerUrl}/webcast/sign_url?apiKey=${this.apiKey}`\r\n : `${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: { 'Content-Type': 'application/json' },\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 } 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 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 return { id, nickname, uniqueId: uniqueId || nickname || id };\r\n}\r\n\r\nexport function parseWebcastMessage(method: string, payload: Buffer): LiveEvent {\r\n const f = decodeProto(payload);\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n const base = { timestamp: Date.now(), msgId: String(getInt(f, 1) || '') };\r\n\r\n switch (method) {\r\n case 'WebcastChatMessage':\r\n return { ...base, type: 'chat' as const, user, comment: getStr(f, 3) };\r\n\r\n case 'WebcastMemberMessage':\r\n return { ...base, type: 'member' as const, user, action: getInt(f, 3) };\r\n\r\n case 'WebcastLikeMessage':\r\n return { ...base, type: 'like' as const, user, likeCount: getInt(f, 5), totalLikes: getInt(f, 6) || getInt(f, 7) };\r\n\r\n case 'WebcastGiftMessage': {\r\n const giftBuf = getBytes(f, 3);\r\n let giftName = '', giftId = 0, diamondCount = 0;\r\n if (giftBuf) {\r\n const gf = decodeProto(giftBuf);\r\n giftId = getInt(gf, 1);\r\n giftName = getStr(gf, 2);\r\n diamondCount = getInt(gf, 5);\r\n }\r\n const repeatCount = getInt(f, 5);\r\n const repeatEnd = getInt(f, 9) === 1;\r\n return { ...base, type: 'gift' as const, user, giftId, giftName, diamondCount, repeatCount, repeatEnd, combo: repeatCount > 1 && !repeatEnd };\r\n }\r\n\r\n case 'WebcastSocialMessage': {\r\n const action = getInt(f, 3);\r\n const actionMap: Record<number, string> = { 1: 'follow', 2: 'share', 3: 'like' };\r\n return { ...base, type: 'social' as const, user, action: actionMap[action] || `action_${action}` };\r\n }\r\n\r\n case 'WebcastRoomUserSeqMessage':\r\n return { ...base, type: 'roomUserSeq' as const, viewerCount: getInt(f, 3), totalViewers: getInt(f, 4) };\r\n\r\n case 'WebcastLinkMicBattle':\r\n return { ...base, type: 'battle' as const, status: getInt(f, 3) };\r\n\r\n case 'WebcastLinkMicArmies':\r\n return { ...base, type: 'battleArmies' as const };\r\n\r\n case 'WebcastSubNotifyMessage':\r\n return { ...base, type: 'subscribe' as const, user, subMonth: getInt(f, 8) };\r\n\r\n case 'WebcastEmoteChatMessage':\r\n return { ...base, type: 'emoteChat' as const, user, emoteId: getStr(f, 3) };\r\n\r\n case 'WebcastEnvelopeMessage':\r\n return { ...base, type: 'envelope' as const, diamondCount: getInt(f, 3) };\r\n\r\n case 'WebcastQuestionNewMessage':\r\n return { ...base, type: 'question' as const, user, questionText: getStr(f, 3) };\r\n\r\n case 'WebcastRankUpdateMessage':\r\n case 'WebcastHourlyRankMessage':\r\n return { ...base, type: 'rankUpdate' as const, rankType: getStr(f, 3) };\r\n\r\n case 'WebcastControlMessage':\r\n return { ...base, type: 'control' as const, action: getInt(f, 2) };\r\n\r\n case 'WebcastRoomMessage':\r\n case 'RoomMessage':\r\n return { ...base, type: 'room' as const, status: getStr(f, 3) };\r\n\r\n case 'WebcastLiveIntroMessage':\r\n return { ...base, type: 'liveIntro' as const, title: getStr(f, 3) };\r\n\r\n case 'WebcastLinkMicMethod':\r\n case 'WebcastLinkmicBattleTaskMessage':\r\n return { ...base, type: 'linkMic' as const, action: getInt(f, 3) };\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAA6B;AAC7B,WAAsB;AACtB,YAAuB;AACvB,WAAsB;AACtB,gBAAsB;;;ACFf,SAAS,aAAa,KAAa,QAAmD;AACzF,MAAI,SAAS,GAAG,QAAQ;AACxB,SAAO,SAAS,IAAI,QAAQ;AACxB,UAAM,OAAO,IAAI,QAAQ;AACzB,eAAW,OAAO,QAAS;AAC3B,SAAK,OAAO,SAAU,EAAG;AACzB,aAAS;AAAA,EACb;AACA,SAAO,EAAE,OAAO,WAAW,GAAG,OAAO;AACzC;AAEO,SAAS,eAAe,KAAa,QAAmD;AAC3F,MAAI,SAAS,IAAI,QAAQ;AACzB,SAAO,SAAS,IAAI,QAAQ;AACxB,UAAM,OAAO,OAAO,IAAI,QAAQ,CAAC;AACjC,eAAW,OAAO,UAAU;AAC5B,SAAK,OAAO,WAAW,GAAI;AAC3B,aAAS;AAAA,EACb;AACA,SAAO,EAAE,OAAO,QAAQ,OAAO;AACnC;AAEO,SAAS,aAAa,GAA4B;AACrD,QAAM,QAAkB,CAAC;AACzB,MAAI,IAAI,OAAO,MAAM,WAAW,IAAI,OAAO,CAAC;AAC5C,KAAG;AACC,QAAI,IAAI,OAAO,IAAI,KAAK;AACxB,UAAM;AACN,QAAI,IAAI,GAAI,MAAK;AACjB,UAAM,KAAK,CAAC;AAAA,EAChB,SAAS,IAAI;AACb,SAAO,OAAO,KAAK,KAAK;AAC5B;AAQO,SAAS,YAAY,IAAY,IAAY,OAAkD;AAClG,QAAM,MAAM,aAAc,MAAM,IAAK,EAAE;AACvC,MAAI,OAAO,GAAG;AACV,WAAO,OAAO,OAAO,CAAC,KAAK,aAAa,OAAO,UAAU,WAAW,OAAO,KAAK,IAAI,KAAe,CAAC,CAAC;AAAA,EACzG;AACA,QAAM,OAAO,OAAO,UAAU,WAAW,OAAO,KAAK,KAAK,IAAI;AAC9D,SAAO,OAAO,OAAO,CAAC,KAAK,aAAa,KAAK,MAAM,GAAG,IAAI,CAAC;AAC/D;AAEO,SAAS,YAAY,KAA2B;AACnD,QAAM,SAAuB,CAAC;AAC9B,MAAI,SAAS;AACb,SAAO,SAAS,IAAI,QAAQ;AACxB,UAAM,YAAY,aAAa,KAAK,MAAM;AAC1C,aAAS,UAAU;AACnB,UAAM,KAAK,UAAU,SAAS;AAC9B,UAAM,KAAK,UAAU,QAAQ;AAC7B,QAAI,OAAO,GAAG;AACV,YAAM,IAAI,eAAe,KAAK,MAAM;AACpC,eAAS,EAAE;AACX,aAAO,KAAK,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,CAAC;AAAA,IAC1C,WAAW,OAAO,GAAG;AACjB,YAAM,OAAO,aAAa,KAAK,MAAM;AACrC,eAAS,KAAK;AACd,YAAM,OAAO,IAAI,SAAS,QAAQ,SAAS,KAAK,KAAK;AACrD,gBAAU,KAAK;AACf,aAAO,KAAK,EAAE,IAAI,IAAI,OAAO,KAAK,CAAC;AAAA,IACvC,WAAW,OAAO,GAAG;AACjB,aAAO,KAAK,EAAE,IAAI,IAAI,OAAO,IAAI,eAAe,MAAM,EAAE,CAAC;AACzD,gBAAU;AAAA,IACd,WAAW,OAAO,GAAG;AACjB,aAAO,KAAK,EAAE,IAAI,IAAI,OAAO,OAAO,IAAI,YAAY,MAAM,CAAC,EAAE,CAAC;AAC9D,gBAAU;AAAA,IACd,OAAO;AACH;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AACX;AAEO,SAAS,OAAO,QAAsB,IAAoB;AAC7D,QAAM,IAAI,OAAO,KAAK,OAAK,EAAE,OAAO,MAAM,EAAE,OAAO,CAAC;AACpD,SAAO,IAAK,EAAE,MAAiB,SAAS,OAAO,IAAI;AACvD;AAEO,SAAS,SAAS,QAAsB,IAA2B;AACtE,QAAM,IAAI,OAAO,KAAK,OAAK,EAAE,OAAO,MAAM,EAAE,OAAO,CAAC;AACpD,SAAO,IAAI,EAAE,QAAkB;AACnC;AAEO,SAAS,OAAO,QAAsB,IAAoB;AAC7D,QAAM,IAAI,OAAO,KAAK,OAAK,EAAE,OAAO,MAAM,EAAE,OAAO,CAAC;AACpD,SAAO,IAAI,OAAO,EAAE,KAAK,IAAI;AACjC;AAEO,SAAS,eAAe,QAAwB;AACnD,QAAM,UAAU,YAAY,GAAG,GAAG,OAAO,MAAM,CAAC;AAChD,SAAO,OAAO,OAAO;AAAA,IACjB,YAAY,GAAG,GAAG,IAAI;AAAA,IACtB,YAAY,GAAG,GAAG,IAAI;AAAA,IACtB,YAAY,GAAG,GAAG,OAAO;AAAA,EAC7B,CAAC;AACL;AAEO,SAAS,iBAAiB,QAAwB;AACrD,QAAM,QAAQ,OAAO,OAAO;AAAA,IACxB,YAAY,GAAG,GAAG,OAAO,MAAM,CAAC;AAAA,IAChC,YAAY,GAAG,GAAG,GAAG;AAAA,IACrB,YAAY,GAAG,GAAG,UAAU;AAAA,IAC5B,YAAY,GAAG,GAAG,EAAE;AAAA,IACpB,YAAY,GAAG,GAAG,EAAE;AAAA,IACpB,YAAY,IAAI,GAAG,EAAE;AAAA,EACzB,CAAC;AACD,SAAO,OAAO,OAAO;AAAA,IACjB,YAAY,GAAG,GAAG,IAAI;AAAA,IACtB,YAAY,GAAG,GAAG,eAAe;AAAA,IACjC,YAAY,GAAG,GAAG,KAAK;AAAA,EAC3B,CAAC;AACL;AAEO,SAAS,SAAS,IAAoB;AACzC,SAAO,OAAO,OAAO;AAAA,IACjB,YAAY,GAAG,GAAG,EAAE;AAAA,IACpB,YAAY,GAAG,GAAG,IAAI;AAAA,IACtB,YAAY,GAAG,GAAG,KAAK;AAAA,EAC3B,CAAC;AACL;AAEA,SAAS,UAAU,MAA0B;AACzC,QAAM,IAAI,YAAY,IAAI;AAC1B,QAAM,KAAK,OAAO,OAAO,GAAG,CAAC,KAAK,OAAO,GAAG,CAAC,CAAC;AAC9C,QAAM,WAAW,OAAO,GAAG,CAAC,KAAK,OAAO,GAAG,CAAC;AAC5C,QAAM,WAAW,OAAO,GAAG,EAAE,KAAK,OAAO,GAAG,CAAC,KAAK,OAAO,GAAG,CAAC;AAC7D,SAAO,EAAE,IAAI,UAAU,UAAU,YAAY,YAAY,GAAG;AAChE;AAEO,SAAS,oBAAoB,QAAgB,SAA4B;AAC5E,QAAM,IAAI,YAAY,OAAO;AAC7B,QAAM,UAAU,SAAS,GAAG,CAAC;AAC7B,QAAM,OAAO,UAAU,UAAU,OAAO,IAAI,EAAE,IAAI,KAAK,UAAU,IAAI,UAAU,GAAG;AAClF,QAAM,OAAO,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,OAAO,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;AAExE,UAAQ,QAAQ;AAAA,IACZ,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,QAAiB,MAAM,SAAS,OAAO,GAAG,CAAC,EAAE;AAAA,IAEzE,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,UAAmB,MAAM,QAAQ,OAAO,GAAG,CAAC,EAAE;AAAA,IAE1E,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,QAAiB,MAAM,WAAW,OAAO,GAAG,CAAC,GAAG,YAAY,OAAO,GAAG,CAAC,KAAK,OAAO,GAAG,CAAC,EAAE;AAAA,IAErH,KAAK,sBAAsB;AACvB,YAAM,UAAU,SAAS,GAAG,CAAC;AAC7B,UAAI,WAAW,IAAI,SAAS,GAAG,eAAe;AAC9C,UAAI,SAAS;AACT,cAAM,KAAK,YAAY,OAAO;AAC9B,iBAAS,OAAO,IAAI,CAAC;AACrB,mBAAW,OAAO,IAAI,CAAC;AACvB,uBAAe,OAAO,IAAI,CAAC;AAAA,MAC/B;AACA,YAAM,cAAc,OAAO,GAAG,CAAC;AAC/B,YAAM,YAAY,OAAO,GAAG,CAAC,MAAM;AACnC,aAAO,EAAE,GAAG,MAAM,MAAM,QAAiB,MAAM,QAAQ,UAAU,cAAc,aAAa,WAAW,OAAO,cAAc,KAAK,CAAC,UAAU;AAAA,IAChJ;AAAA,IAEA,KAAK,wBAAwB;AACzB,YAAM,SAAS,OAAO,GAAG,CAAC;AAC1B,YAAM,YAAoC,EAAE,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO;AAC/E,aAAO,EAAE,GAAG,MAAM,MAAM,UAAmB,MAAM,QAAQ,UAAU,MAAM,KAAK,UAAU,MAAM,GAAG;AAAA,IACrG;AAAA,IAEA,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,eAAwB,aAAa,OAAO,GAAG,CAAC,GAAG,cAAc,OAAO,GAAG,CAAC,EAAE;AAAA,IAE1G,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,UAAmB,QAAQ,OAAO,GAAG,CAAC,EAAE;AAAA,IAEpE,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,eAAwB;AAAA,IAEpD,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,aAAsB,MAAM,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,IAE/E,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,aAAsB,MAAM,SAAS,OAAO,GAAG,CAAC,EAAE;AAAA,IAE9E,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,YAAqB,cAAc,OAAO,GAAG,CAAC,EAAE;AAAA,IAE5E,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,YAAqB,MAAM,cAAc,OAAO,GAAG,CAAC,EAAE;AAAA,IAElF,KAAK;AAAA,IACL,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,cAAuB,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,IAE1E,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,WAAoB,QAAQ,OAAO,GAAG,CAAC,EAAE;AAAA,IAErE,KAAK;AAAA,IACL,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,QAAiB,QAAQ,OAAO,GAAG,CAAC,EAAE;AAAA,IAElE,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,aAAsB,OAAO,OAAO,GAAG,CAAC,EAAE;AAAA,IAEtE,KAAK;AAAA,IACL,KAAK;AACD,aAAO,EAAE,GAAG,MAAM,MAAM,WAAoB,QAAQ,OAAO,GAAG,CAAC,EAAE;AAAA,IAErE;AACI,aAAO,EAAE,GAAG,MAAM,MAAM,WAAoB,OAAO;AAAA,EAC3D;AACJ;AAEO,SAAS,qBAAqB,SAA8B;AAC/D,QAAM,SAAsB,CAAC;AAC7B,QAAM,aAAa,YAAY,OAAO;AAEtC,aAAW,MAAM,WAAW,OAAO,OAAK,EAAE,OAAO,KAAK,EAAE,OAAO,CAAC,GAAG;AAC/D,UAAM,SAAS,GAAG;AAClB,UAAM,YAAY,YAAY,MAAM;AACpC,UAAM,SAAS,OAAO,WAAW,CAAC;AAClC,UAAM,eAAe,SAAS,WAAW,CAAC;AAC1C,QAAI,CAAC,UAAU,CAAC,aAAc;AAE9B,QAAI;AACA,aAAO,KAAK,oBAAoB,QAAQ,YAAY,CAAC;AAAA,IACzD,QAAQ;AAAA,IAAE;AAAA,EACd;AAEA,SAAO;AACX;;;AD3NA,IAAM,aAAa;AACnB,IAAM,sBAAsB;AAE5B,SAAS,QAAQ,KAAa,SAI3B;AACC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACpC,UAAM,MAAM,IAAI,WAAW,OAAO,IAAI,QAAQ;AAC9C,UAAM,MAAM,IAAI,IAAI,KAAK,EAAE,QAAQ,GAAG,CAAC,QAAQ;AAC3C,YAAM,SAAmB,CAAC;AAC1B,YAAM,MAAM,IAAI,QAAQ,kBAAkB;AAC1C,YAAM,SAAU,QAAQ,UAAU,QAAQ,OACpC,IAAI,KAAK,QAAQ,OAAY,4BAAuB,IAAS,kBAAa,CAAC,IAC3E;AACN,aAAO,GAAG,QAAQ,CAAC,MAAc,OAAO,KAAK,CAAC,CAAC;AAC/C,aAAO,GAAG,OAAO,MAAM,QAAQ;AAAA,QAC3B,QAAQ,IAAI,cAAc;AAAA,QAC1B,SAAS,IAAI;AAAA,QACb,MAAM,OAAO,OAAO,MAAM;AAAA,MAC9B,CAAC,CAAC;AACF,aAAO,GAAG,SAAS,MAAM;AAAA,IAC7B,CAAC;AACD,QAAI,GAAG,SAAS,MAAM;AACtB,QAAI,WAAW,MAAQ,MAAM;AAAE,UAAI,QAAQ;AAAG,aAAO,IAAI,MAAM,iBAAiB,CAAC;AAAA,IAAG,CAAC;AAAA,EACzF,CAAC;AACL;AAEA,SAAS,UAAU,eAA+B;AAC9C,MAAI,CAAC,cAAe,QAAO;AAC3B,QAAM,IAAI,cAAc,YAAY;AACpC,MAAI,EAAE,WAAW,IAAI,KAAK,EAAE,SAAS,IAAI,EAAG,QAAO;AACnD,MAAI,EAAE,WAAW,IAAI,KAAK,EAAE,SAAS,IAAI,EAAG,QAAO;AACnD,SAAO;AACX;AAEO,IAAM,aAAN,cAAyB,2BAAa;AAAA,EACjC,KAAuB;AAAA,EACvB,iBAAwD;AAAA,EACxD,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,aAAa;AAAA,EACb,cAAc;AAAA,EACd,UAAU;AAAA,EAED;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAA4B;AACpC,UAAM;AACN,SAAK,WAAW,QAAQ,SAAS,QAAQ,MAAM,EAAE;AACjD,SAAK,iBAAiB,QAAQ,iBAAiB,qBAAqB,QAAQ,OAAO,EAAE;AACrF,SAAK,SAAS,QAAQ;AACtB,SAAK,gBAAgB,QAAQ,iBAAiB;AAC9C,SAAK,uBAAuB,QAAQ,wBAAwB;AAC5D,SAAK,oBAAoB,QAAQ,qBAAqB;AACtD,SAAK,QAAQ,QAAQ,SAAS;AAAA,EAClC;AAAA,EAEA,MAAM,UAAyB;AAC3B,SAAK,mBAAmB;AAExB,UAAM,OAAO,MAAM,QAAQ,2BAA2B,KAAK,QAAQ,SAAS;AAAA,MACxE,cAAc;AAAA,MACd,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,mBAAmB;AAAA,IACvB,CAAC;AAED,QAAI,QAAQ;AACZ,eAAW,MAAM,CAAC,KAAK,QAAQ,YAAY,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG;AACxD,UAAI,OAAO,OAAO,YAAY,GAAG,WAAW,QAAQ,GAAG;AACnD,gBAAQ,GAAG,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,GAAG,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG;AACrD;AAAA,MACJ;AAAA,IACJ;AACA,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,iCAAiC;AAE7D,UAAM,OAAO,KAAK,KAAK,SAAS;AAChC,QAAI,SAAS;AACb,UAAM,YAAY,KAAK,MAAM,8BAA8B;AAC3D,QAAI,WAAW;AACX,UAAI;AACA,cAAM,OAAO,KAAK,MAAM,UAAU,CAAC,CAAC;AACpC,cAAM,UAAU,KAAK,UAAU,IAAI;AACnC,cAAM,IAAI,QAAQ,MAAM,wBAAwB;AAChD,YAAI,EAAG,UAAS,EAAE,CAAC;AAAA,MACvB,QAAQ;AAAA,MAAE;AAAA,IACd;AACA,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,SAAS,KAAK,QAAQ,wBAAwB;AAC3E,SAAK,UAAU;AAEf,UAAM,UAAU,KAAK,MAAM,iCAAiC;AAC5D,UAAM,gBAAgB,UAAU,QAAQ,CAAC,IAAI;AAC7C,UAAM,SAAS,UAAU,aAAa;AAEtC,UAAM,WAAW,IAAI,gBAAgB;AAAA,MACjC,cAAc;AAAA,MAAU,iBAAiB;AAAA,MAAO,gBAAgB;AAAA,MAChE,cAAc;AAAA,MAAQ,eAAe;AAAA,MAAQ,kBAAkB;AAAA,MAC/D,kBAAkB;AAAA,MAAS,cAAc;AAAA,MACzC,iBAAiB,WAAW,MAAM,UAAU,EAAE,CAAC,KAAK;AAAA,MACpD,gBAAgB;AAAA,MAAQ,SAAS,KAAK,eAAe,EAAE,gBAAgB,EAAE;AAAA,MACzE,UAAU;AAAA,MAAc,eAAe;AAAA,MAAK,qBAAqB;AAAA,MACjE,UAAU;AAAA,MAAQ,kBAAkB;AAAA,MAAM,WAAW;AAAA,MAAK,KAAK;AAAA,MAC/D,SAAS;AAAA,MAAM,cAAc;AAAA,MAAM,cAAc;AAAA,MAAK,SAAS;AAAA,MAC/D,UAAU;AAAA,MAAY,uBAAuB;AAAA,MAAK,UAAU;AAAA,MAC5D,oBAAoB;AAAA,MAAS,mBAAmB;AAAA,MAAY,UAAU;AAAA,IAC1E,CAAC;AAED,UAAM,WAAW,WAAW,MAAM,6CAA6C,QAAQ;AAEvF,UAAM,UAAU,KAAK,SACf,GAAG,KAAK,aAAa,4BAA4B,KAAK,MAAM,KAC5D,GAAG,KAAK,aAAa;AAE3B,QAAI;AACJ,QAAI;AACA,YAAM,WAAW,MAAM,MAAM,SAAS;AAAA,QAClC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,KAAK,SAAS,CAAC;AAAA,MAC1C,CAAC;AACD,YAAM,WAAW,MAAM,SAAS,KAAK;AAErC,UAAI,SAAS,gBAAgB,KAAK,SAAS,MAAM,YAAY;AACzD,gBAAS,SAAS,KAAK,WAAsB,QAAQ,eAAe,QAAQ;AAAA,MAChF,OAAO;AACH,gBAAQ,SAAS,QAAQ,eAAe,QAAQ;AAAA,MACpD;AAAA,IACJ,QAAQ;AACJ,cAAQ,SAAS,QAAQ,eAAe,QAAQ;AAAA,IACpD;AAEA,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC1C,WAAK,KAAK,IAAI,UAAAA,QAAU,OAAO;AAAA,QAC3B,SAAS;AAAA,UACL,cAAc;AAAA,UACd,UAAU,SAAS,KAAK;AAAA,UACxB,UAAU;AAAA,QACd;AAAA,MACJ,CAAC;AAED,WAAK,GAAG,GAAG,QAAQ,MAAM;AACrB,aAAK,aAAa;AAClB,aAAK,oBAAoB;AAEzB,aAAK,GAAI,KAAK,eAAe,MAAM,CAAC;AACpC,aAAK,GAAI,KAAK,iBAAiB,MAAM,CAAC;AACtC,aAAK,eAAe,MAAM;AAE1B,cAAM,WAAqB;AAAA,UACvB;AAAA,UACA;AAAA,UACA;AAAA,UACA,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,QACxC;AAEA,aAAK,KAAK,WAAW;AACrB,aAAK,KAAK,YAAY,QAAQ;AAC9B,gBAAQ;AAAA,MACZ,CAAC;AAED,WAAK,GAAG,GAAG,WAAW,CAAC,YAAoB;AACvC,aAAK,YAAY,OAAO,KAAK,OAAO,CAAC;AAAA,MACzC,CAAC;AAED,WAAK,GAAG,GAAG,SAAS,CAAC,MAAM,WAAW;AAClC,aAAK,aAAa;AAClB,aAAK,cAAc;AAEnB,cAAM,YAAY,QAAQ,SAAS,KAAK;AACxC,aAAK,KAAK,gBAAgB,MAAM,SAAS;AAEzC,YAAI,CAAC,KAAK,oBAAoB,KAAK,iBAAiB,KAAK,oBAAoB,KAAK,sBAAsB;AACpG,eAAK;AACL,gBAAM,QAAQ,KAAK,IAAI,MAAO,KAAK,IAAI,GAAG,KAAK,oBAAoB,CAAC,GAAG,GAAM;AAC7E,qBAAW,MAAM,KAAK,QAAQ,EAAE,MAAM,OAAK,KAAK,KAAK,SAAS,CAAC,CAAC,GAAG,KAAK;AAAA,QAC5E;AAAA,MACJ,CAAC;AAED,WAAK,GAAG,GAAG,SAAS,CAAC,QAAQ;AACzB,aAAK,KAAK,SAAS,GAAG;AACtB,YAAI,CAAC,KAAK,WAAY,QAAO,GAAG;AAAA,MACpC,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AAAA,EAEA,aAAmB;AACf,SAAK,mBAAmB;AACxB,SAAK,cAAc;AACnB,QAAI,KAAK,IAAI;AACT,WAAK,GAAG,MAAM,GAAI;AAClB,WAAK,KAAK;AAAA,IACd;AACA,SAAK,aAAa;AAAA,EACtB;AAAA,EAEA,IAAI,YAAqB;AACrB,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,IAAI,aAAqB;AACrB,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,IAAI,SAAiB;AACjB,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,GAAqC,OAAU,UAAqC;AAChF,WAAO,MAAM,GAAG,OAAO,QAAoC;AAAA,EAC/D;AAAA,EAEA,KAAuC,OAAU,UAAqC;AAClF,WAAO,MAAM,KAAK,OAAO,QAAoC;AAAA,EACjE;AAAA,EAEA,IAAsC,OAAU,UAAqC;AACjF,WAAO,MAAM,IAAI,OAAO,QAAoC;AAAA,EAChE;AAAA,EAEA,KAAuC,UAAa,MAAgD;AAChG,WAAO,MAAM,KAAK,OAAO,GAAG,IAAI;AAAA,EACpC;AAAA,EAEQ,YAAY,KAAmB;AACnC,QAAI;AACA,YAAM,SAAS,YAAY,GAAG;AAC9B,YAAM,UAAU,OAAO,KAAK,OAAK,EAAE,OAAO,KAAK,EAAE,OAAO,CAAC;AACzD,YAAM,KAAK,UAAU,QAAQ,QAAkB;AAC/C,YAAM,OAAO,OAAO,QAAQ,CAAC;AAC7B,YAAM,SAAS,SAAS,QAAQ,CAAC;AAEjC,UAAI,KAAK,MAAM,KAAK,IAAI,eAAe,UAAAA,QAAU,MAAM;AACnD,aAAK,GAAG,KAAK,SAAS,EAAE,CAAC;AAAA,MAC7B;AAEA,UAAI,SAAS,SAAS,UAAU,OAAO,SAAS,GAAG;AAC/C,YAAI,QAAQ;AACZ,YAAI,MAAM,SAAS,KAAK,MAAM,CAAC,MAAM,MAAQ,MAAM,CAAC,MAAM,KAAM;AAC5D,cAAI;AAAE,oBAAa,gBAAW,KAAK;AAAA,UAAG,QAAQ;AAAA,UAAE;AAAA,QACpD;AAEA,cAAM,SAAS,qBAAqB,KAAK;AACzC,mBAAW,OAAO,QAAQ;AACtB,eAAK;AACL,eAAK,KAAK,SAAS,GAAG;AACtB,eAAK,KAAK,IAAI,MAAgC,GAAU;AAAA,QAC5D;AAAA,MACJ;AAAA,IACJ,QAAQ;AAAA,IAAE;AAAA,EACd;AAAA,EAEQ,eAAe,QAAsB;AACzC,SAAK,cAAc;AACnB,SAAK,iBAAiB,YAAY,MAAM;AACpC,UAAI,KAAK,IAAI,eAAe,UAAAA,QAAU,MAAM;AACxC,aAAK,GAAG,KAAK,eAAe,MAAM,CAAC;AAAA,MACvC;AAAA,IACJ,GAAG,KAAK,iBAAiB;AAAA,EAC7B;AAAA,EAEQ,gBAAsB;AAC1B,QAAI,KAAK,gBAAgB;AACrB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IAC1B;AAAA,EACJ;AACJ;","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\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"]}
package/dist/index.mjs CHANGED
@@ -87,6 +87,9 @@ function getInt(fields, fn) {
87
87
  const f = fields.find((x) => x.fn === fn && x.wt === 0);
88
88
  return f ? Number(f.value) : 0;
89
89
  }
90
+ function getAllBytes(fields, fn) {
91
+ return fields.filter((x) => x.fn === fn && x.wt === 2).map((x) => x.value);
92
+ }
90
93
  function buildHeartbeat(roomId) {
91
94
  const payload = encodeField(1, 0, BigInt(roomId));
92
95
  return Buffer.concat([
@@ -122,65 +125,202 @@ function parseUser(data) {
122
125
  const id = String(getInt(f, 1) || getStr(f, 1));
123
126
  const nickname = getStr(f, 3) || getStr(f, 5);
124
127
  const uniqueId = getStr(f, 38) || getStr(f, 4) || getStr(f, 2);
125
- return { id, nickname, uniqueId: uniqueId || nickname || id };
128
+ let profilePicture;
129
+ const avatarBuf = getBytes(f, 9);
130
+ if (avatarBuf) {
131
+ try {
132
+ const avatarFields = decodeProto(avatarBuf);
133
+ const urlBuf = getBytes(avatarFields, 1);
134
+ if (urlBuf) profilePicture = urlBuf.toString("utf-8");
135
+ } catch {
136
+ }
137
+ }
138
+ return { id, nickname, uniqueId: uniqueId || nickname || id, profilePicture };
139
+ }
140
+ function parseBattleTeam(teamBuf) {
141
+ const fields = decodeProto(teamBuf);
142
+ const hostUserId = String(getInt(fields, 1));
143
+ const score = getInt(fields, 2);
144
+ const users = [];
145
+ const userFields = getAllBytes(fields, 3);
146
+ for (const uf of userFields) {
147
+ 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 });
153
+ } catch {
154
+ }
155
+ }
156
+ return { hostUserId, score, users };
126
157
  }
127
158
  function parseWebcastMessage(method, payload) {
128
159
  const f = decodeProto(payload);
129
- const userBuf = getBytes(f, 2);
130
- const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
131
160
  const base = { timestamp: Date.now(), msgId: String(getInt(f, 1) || "") };
132
161
  switch (method) {
133
- case "WebcastChatMessage":
162
+ case "WebcastChatMessage": {
163
+ const userBuf = getBytes(f, 2);
164
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
134
165
  return { ...base, type: "chat", user, comment: getStr(f, 3) };
135
- case "WebcastMemberMessage":
136
- return { ...base, type: "member", user, action: getInt(f, 3) };
137
- case "WebcastLikeMessage":
138
- return { ...base, type: "like", user, likeCount: getInt(f, 5), totalLikes: getInt(f, 6) || getInt(f, 7) };
166
+ }
167
+ case "WebcastMemberMessage": {
168
+ const userBuf = getBytes(f, 2);
169
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
170
+ return { ...base, type: "member", user, action: getInt(f, 1) };
171
+ }
172
+ case "WebcastLikeMessage": {
173
+ const userBuf = getBytes(f, 5);
174
+ 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) };
176
+ }
139
177
  case "WebcastGiftMessage": {
140
- const giftBuf = getBytes(f, 3);
141
- let giftName = "", giftId = 0, diamondCount = 0;
142
- if (giftBuf) {
143
- const gf = decodeProto(giftBuf);
144
- giftId = getInt(gf, 1);
145
- giftName = getStr(gf, 2);
146
- diamondCount = getInt(gf, 5);
147
- }
178
+ const userBuf = getBytes(f, 7);
179
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
180
+ const giftId = getInt(f, 1);
148
181
  const repeatCount = getInt(f, 5);
149
182
  const repeatEnd = getInt(f, 9) === 1;
150
- return { ...base, type: "gift", user, giftId, giftName, diamondCount, repeatCount, repeatEnd, combo: repeatCount > 1 && !repeatEnd };
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);
198
+ }
199
+ }
200
+ return {
201
+ ...base,
202
+ type: "gift",
203
+ user,
204
+ giftId,
205
+ giftName,
206
+ diamondCount,
207
+ repeatCount,
208
+ repeatEnd,
209
+ combo: repeatCount > 1 && !repeatEnd,
210
+ giftType,
211
+ groupId
212
+ };
151
213
  }
152
214
  case "WebcastSocialMessage": {
153
- const action = getInt(f, 3);
215
+ const userBuf = getBytes(f, 2);
216
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
217
+ const actionInt = getInt(f, 1);
154
218
  const actionMap = { 1: "follow", 2: "share", 3: "like" };
155
- return { ...base, type: "social", user, action: actionMap[action] || `action_${action}` };
219
+ return { ...base, type: "social", user, action: actionMap[actionInt] || `action_${actionInt}` };
156
220
  }
157
221
  case "WebcastRoomUserSeqMessage":
158
- return { ...base, type: "roomUserSeq", viewerCount: getInt(f, 3), totalViewers: getInt(f, 4) };
159
- case "WebcastLinkMicBattle":
160
- return { ...base, type: "battle", status: getInt(f, 3) };
161
- case "WebcastLinkMicArmies":
162
- return { ...base, type: "battleArmies" };
163
- case "WebcastSubNotifyMessage":
164
- return { ...base, type: "subscribe", user, subMonth: getInt(f, 8) };
165
- case "WebcastEmoteChatMessage":
166
- return { ...base, type: "emoteChat", user, emoteId: getStr(f, 3) };
167
- case "WebcastEnvelopeMessage":
168
- return { ...base, type: "envelope", diamondCount: getInt(f, 3) };
169
- case "WebcastQuestionNewMessage":
170
- return { ...base, type: "question", user, questionText: getStr(f, 3) };
222
+ return {
223
+ ...base,
224
+ type: "roomUserSeq",
225
+ totalViewers: getInt(f, 1) || getInt(f, 3),
226
+ viewerCount: getInt(f, 2) || getInt(f, 4)
227
+ };
228
+ case "WebcastLinkMicBattle": {
229
+ const battleId = String(getInt(f, 1) || getStr(f, 1));
230
+ const status = getInt(f, 2);
231
+ const battleDuration = getInt(f, 3);
232
+ const teams = [];
233
+ const teamBufs = getAllBytes(f, 7);
234
+ for (const tb of teamBufs) {
235
+ try {
236
+ teams.push(parseBattleTeam(tb));
237
+ } catch {
238
+ }
239
+ }
240
+ return { ...base, type: "battle", battleId, status, battleDuration, teams };
241
+ }
242
+ case "WebcastLinkMicArmies": {
243
+ const battleId = String(getInt(f, 1) || getStr(f, 1));
244
+ const teams = [];
245
+ const teamBufs = getAllBytes(f, 3);
246
+ for (const tb of teamBufs) {
247
+ try {
248
+ teams.push(parseBattleTeam(tb));
249
+ } catch {
250
+ }
251
+ }
252
+ return { ...base, type: "battleArmies", battleId, teams };
253
+ }
254
+ case "WebcastSubNotifyMessage": {
255
+ const userBuf = getBytes(f, 2);
256
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
257
+ return { ...base, type: "subscribe", user, subMonth: getInt(f, 3) };
258
+ }
259
+ case "WebcastEmoteChatMessage": {
260
+ const userBuf = getBytes(f, 2);
261
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
262
+ let emoteId = "", emoteUrl = "";
263
+ const emoteBuf = getBytes(f, 3);
264
+ if (emoteBuf) {
265
+ const ef = decodeProto(emoteBuf);
266
+ emoteId = getStr(ef, 1);
267
+ const imageBuf = getBytes(ef, 2);
268
+ if (imageBuf) {
269
+ const imgFields = decodeProto(imageBuf);
270
+ emoteUrl = getStr(imgFields, 1);
271
+ }
272
+ }
273
+ return { ...base, type: "emoteChat", user, emoteId, emoteUrl };
274
+ }
275
+ case "WebcastEnvelopeMessage": {
276
+ const envelopeId = String(getInt(f, 1) || getStr(f, 1));
277
+ return { ...base, type: "envelope", envelopeId, diamondCount: getInt(f, 3) };
278
+ }
279
+ 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) };
283
+ }
171
284
  case "WebcastRankUpdateMessage":
172
- case "WebcastHourlyRankMessage":
173
- return { ...base, type: "rankUpdate", rankType: getStr(f, 3) };
285
+ case "WebcastHourlyRankMessage": {
286
+ const rankType = getStr(f, 1) || `rank_${getInt(f, 1)}`;
287
+ const rankList = [];
288
+ const listBufs = getAllBytes(f, 2);
289
+ for (const lb of listBufs) {
290
+ try {
291
+ const rf = decodeProto(lb);
292
+ const userBuf = getBytes(rf, 1);
293
+ const rank = getInt(rf, 2);
294
+ const score = getInt(rf, 3);
295
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
296
+ rankList.push({ user, rank, score });
297
+ } catch {
298
+ }
299
+ }
300
+ return { ...base, type: "rankUpdate", rankType, rankList };
301
+ }
174
302
  case "WebcastControlMessage":
175
- return { ...base, type: "control", action: getInt(f, 2) };
303
+ return { ...base, type: "control", action: getInt(f, 2) || getInt(f, 1) };
176
304
  case "WebcastRoomMessage":
177
305
  case "RoomMessage":
178
- return { ...base, type: "room", status: getStr(f, 3) };
179
- case "WebcastLiveIntroMessage":
180
- return { ...base, type: "liveIntro", title: getStr(f, 3) };
306
+ return { ...base, type: "room", status: getInt(f, 2) };
307
+ case "WebcastLiveIntroMessage": {
308
+ const roomId = String(getInt(f, 1));
309
+ return { ...base, type: "liveIntro", roomId, title: getStr(f, 4) || getStr(f, 2) };
310
+ }
181
311
  case "WebcastLinkMicMethod":
182
- case "WebcastLinkmicBattleTaskMessage":
183
- return { ...base, type: "linkMic", action: getInt(f, 3) };
312
+ case "WebcastLinkmicBattleTaskMessage": {
313
+ const action = getStr(f, 1) || `action_${getInt(f, 1)}`;
314
+ const users = [];
315
+ const userBufs = getAllBytes(f, 2);
316
+ for (const ub of userBufs) {
317
+ try {
318
+ users.push(parseUser(ub));
319
+ } catch {
320
+ }
321
+ }
322
+ return { ...base, type: "linkMic", action, users };
323
+ }
184
324
  default:
185
325
  return { ...base, type: "unknown", method };
186
326
  }
@@ -253,6 +393,7 @@ var TikTokLive = class extends EventEmitter {
253
393
  super();
254
394
  this.uniqueId = options.uniqueId.replace(/^@/, "");
255
395
  this.signServerUrl = (options.signServerUrl || DEFAULT_SIGN_SERVER).replace(/\/$/, "");
396
+ if (!options.apiKey) throw new Error("apiKey is required. Get a free key at https://tik.tools");
256
397
  this.apiKey = options.apiKey;
257
398
  this.autoReconnect = options.autoReconnect ?? true;
258
399
  this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
@@ -323,12 +464,15 @@ var TikTokLive = class extends EventEmitter {
323
464
  did_rule: "3"
324
465
  });
325
466
  const rawWsUrl = `https://${wsHost}/webcast/im/ws_proxy/ws_reuse_supplement/?${wsParams}`;
326
- const signUrl = this.apiKey ? `${this.signServerUrl}/webcast/sign_url?apiKey=${this.apiKey}` : `${this.signServerUrl}/webcast/sign_url`;
467
+ const signUrl = `${this.signServerUrl}/webcast/sign_url`;
327
468
  let wsUrl;
328
469
  try {
329
470
  const signResp = await fetch(signUrl, {
330
471
  method: "POST",
331
- headers: { "Content-Type": "application/json" },
472
+ headers: {
473
+ "Content-Type": "application/json",
474
+ "x-api-key": this.apiKey
475
+ },
332
476
  body: JSON.stringify({ url: rawWsUrl })
333
477
  });
334
478
  const signData = await signResp.json();