@tiktool/live 2.4.3 → 2.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -363
- package/dist/index.d.mts +288 -135
- package/dist/index.d.ts +288 -135
- package/dist/index.js +455 -403
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +453 -396
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -28
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/proto.ts","../src/api.ts","../src/client.ts"],"sourcesContent":["export { TikTokLive } from './client.js';\r\nexport { resolveLivePage, resolveRoomId, fetchSignedUrl, callApi, solvePuzzle, solveRotate, solveShapes } from './api.js';\r\nexport type { LivePageInfo, SignedUrlResponse, CallApiOptions } from './api.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 PuzzleSolveResult,\r\n RotateSolveResult,\r\n ShapesSolveResult,\r\n} from './types.js';\r\n","import type { LiveEvent, TikTokUser, BattleTeam, BattleTeamUser } from './types.js';\r\n\r\nconst encoder = new TextEncoder();\r\nconst decoder = new TextDecoder();\r\n\r\nexport function concatBytes(...arrays: Uint8Array[]): Uint8Array {\r\n let totalLength = 0;\r\n for (const arr of arrays) totalLength += arr.length;\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const arr of arrays) {\r\n result.set(arr, offset);\r\n offset += arr.length;\r\n }\r\n return result;\r\n}\r\n\r\nfunction readInt32LE(buf: Uint8Array, offset: number): number {\r\n return buf[offset] |\r\n (buf[offset + 1] << 8) |\r\n (buf[offset + 2] << 16) |\r\n (buf[offset + 3] << 24);\r\n}\r\n\r\nfunction readBigInt64LE(buf: Uint8Array, offset: number): bigint {\r\n const lo = BigInt(buf[offset] |\r\n (buf[offset + 1] << 8) |\r\n (buf[offset + 2] << 16) |\r\n ((buf[offset + 3] << 24) >>> 0));\r\n const hi = BigInt(buf[offset + 4] |\r\n (buf[offset + 5] << 8) |\r\n (buf[offset + 6] << 16) |\r\n ((buf[offset + 7] << 24) >>> 0));\r\n return (hi << 32n) | (lo & 0xFFFFFFFFn);\r\n}\r\n\r\nexport function decodeVarint(buf: Uint8Array, offset: number): { value: number; offset: number } {\r\n let result = 0, shift = 0;\r\n while (offset < buf.length) {\r\n const byte = buf[offset++];\r\n result |= (byte & 0x7F) << shift;\r\n if ((byte & 0x80) === 0) break;\r\n shift += 7;\r\n }\r\n return { value: result >>> 0, offset };\r\n}\r\n\r\nexport function decodeVarint64(buf: Uint8Array, offset: number): { value: bigint; offset: number } {\r\n let result = 0n, shift = 0n;\r\n while (offset < buf.length) {\r\n const byte = BigInt(buf[offset++]);\r\n result |= (byte & 0x7Fn) << shift;\r\n if ((byte & 0x80n) === 0n) break;\r\n shift += 7n;\r\n }\r\n return { value: result, offset };\r\n}\r\n\r\nexport function encodeVarint(v: number | bigint): Uint8Array {\r\n const bytes: number[] = [];\r\n let n = typeof v === 'bigint' ? v : BigInt(v);\r\n do {\r\n let b = Number(n & 0x7Fn);\r\n n >>= 7n;\r\n if (n > 0n) b |= 0x80;\r\n bytes.push(b);\r\n } while (n > 0n);\r\n return new Uint8Array(bytes);\r\n}\r\n\r\nexport interface ProtoField {\r\n fn: number;\r\n wt: number;\r\n value: Uint8Array | number | bigint;\r\n}\r\n\r\nexport function encodeField(fn: number, wt: number, value: Uint8Array | bigint | number | string): Uint8Array {\r\n const tag = encodeVarint((fn << 3) | wt);\r\n if (wt === 0) {\r\n return concatBytes(tag, encodeVarint(typeof value === 'number' ? BigInt(value) : value as bigint));\r\n }\r\n const data = typeof value === 'string' ? encoder.encode(value) : value as Uint8Array;\r\n return concatBytes(tag, encodeVarint(data.length), data);\r\n}\r\n\r\nexport function decodeProto(buf: Uint8Array): ProtoField[] {\r\n const fields: ProtoField[] = [];\r\n let offset = 0;\r\n while (offset < buf.length) {\r\n const tagResult = decodeVarint(buf, offset);\r\n offset = tagResult.offset;\r\n const fn = tagResult.value >> 3;\r\n const wt = tagResult.value & 7;\r\n if (wt === 0) {\r\n const r = decodeVarint64(buf, offset);\r\n offset = r.offset;\r\n fields.push({ fn, wt, value: r.value });\r\n } else if (wt === 2) {\r\n const lenR = decodeVarint(buf, offset);\r\n offset = lenR.offset;\r\n const data = buf.subarray(offset, offset + lenR.value);\r\n offset += lenR.value;\r\n fields.push({ fn, wt, value: data });\r\n } else if (wt === 1) {\r\n fields.push({ fn, wt, value: readBigInt64LE(buf, offset) });\r\n offset += 8;\r\n } else if (wt === 5) {\r\n fields.push({ fn, wt, value: BigInt(readInt32LE(buf, offset)) });\r\n offset += 4;\r\n } else {\r\n break;\r\n }\r\n }\r\n return fields;\r\n}\r\n\r\nexport function getStr(fields: ProtoField[], fn: number): string {\r\n const f = fields.find(x => x.fn === fn && x.wt === 2);\r\n return f ? decoder.decode(f.value as Uint8Array) : '';\r\n}\r\n\r\nexport function getBytes(fields: ProtoField[], fn: number): Uint8Array | null {\r\n const f = fields.find(x => x.fn === fn && x.wt === 2);\r\n return f ? f.value as Uint8Array : null;\r\n}\r\n\r\nexport function getInt(fields: ProtoField[], fn: number): number {\r\n const f = fields.find(x => x.fn === fn && x.wt === 0);\r\n return f ? Number(f.value) : 0;\r\n}\r\n\r\nexport function getAllBytes(fields: ProtoField[], fn: number): Uint8Array[] {\r\n return fields.filter(x => x.fn === fn && x.wt === 2).map(x => x.value as Uint8Array);\r\n}\r\n\r\nexport function buildHeartbeat(roomId: string): Uint8Array {\r\n const payload = encodeField(1, 0, BigInt(roomId));\r\n return concatBytes(\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'hb'),\r\n encodeField(8, 2, payload),\r\n );\r\n}\r\n\r\nexport function buildImEnterRoom(roomId: string): Uint8Array {\r\n const inner = concatBytes(\r\n encodeField(1, 0, BigInt(roomId)),\r\n encodeField(4, 0, 12n),\r\n encodeField(5, 2, 'audience'),\r\n encodeField(6, 2, ''),\r\n encodeField(9, 2, ''),\r\n encodeField(10, 2, ''),\r\n );\r\n return concatBytes(\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'im_enter_room'),\r\n encodeField(8, 2, inner),\r\n );\r\n}\r\n\r\nexport function buildAck(id: bigint): Uint8Array {\r\n return concatBytes(\r\n encodeField(2, 0, id),\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'ack'),\r\n );\r\n}\r\n\r\n// NOTE: Army users in WebcastLinkMicArmies use a DIFFERENT layout where\r\n// field 38 contains avatar/image data, NOT uniqueId.\r\n// We detect this by checking if the string looks like a username (short, no URLs).\r\nfunction looksLikeUsername(s: string): boolean {\r\n return s.length > 0 && s.length <= 50 && !s.includes('://') && !s.includes('\\x00');\r\n}\r\n\r\nfunction parseUser(data: Uint8Array): TikTokUser {\r\n const f = decodeProto(data);\r\n const id = String(getInt(f, 1) || getStr(f, 1));\r\n const nickname = getStr(f, 3) || getStr(f, 2);\r\n\r\n // uniqueId: Try field 4 (LinkUser format) first, then field 38 (User format)\r\n // Validate that the result looks like a username, not binary/URL data\r\n let uniqueId = '';\r\n const uid4 = getStr(f, 4);\r\n const uid38 = getStr(f, 38);\r\n if (uid4 && looksLikeUsername(uid4)) {\r\n uniqueId = uid4;\r\n } else if (uid38 && looksLikeUsername(uid38)) {\r\n uniqueId = uid38;\r\n } else {\r\n const uid2 = getStr(f, 2);\r\n if (uid2 && looksLikeUsername(uid2)) uniqueId = uid2;\r\n }\r\n\r\n // profilePicture: Try field 9 (User format), field 4 (army users — avatar at field 4), field 3 (LinkUser format)\r\n // Can't use OR chain because field 3 = nickname bytes in User format (truthy but wrong)\r\n // Must try each and pick the first that yields a valid URL\r\n let profilePicture: string | undefined;\r\n for (const fieldNum of [9, 4, 3]) {\r\n if (profilePicture) break;\r\n const avatarBuf = getBytes(f, fieldNum);\r\n if (!avatarBuf) continue;\r\n try {\r\n const avatarFields = decodeProto(avatarBuf);\r\n const urlBufs = getAllBytes(avatarFields, 1);\r\n for (const urlBuf of urlBufs) {\r\n const url = decoder.decode(urlBuf);\r\n if (url.includes('://')) {\r\n profilePicture = url;\r\n break;\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n const badges: string[] = [];\r\n const badgeBuf = getBytes(f, 64);\r\n if (badgeBuf) {\r\n try {\r\n const badgeFields = decodeProto(badgeBuf);\r\n const badgeItems = getAllBytes(badgeFields, 21);\r\n for (const bi of badgeItems) {\r\n const bf = decodeProto(bi);\r\n const name = getStr(bf, 3) || getStr(bf, 2);\r\n if (name) badges.push(name);\r\n }\r\n } catch { }\r\n }\r\n\r\n return {\r\n id,\r\n nickname,\r\n uniqueId: uniqueId || nickname || id,\r\n profilePictureUrl: profilePicture,\r\n badges: badges.length > 0 ? badges : undefined,\r\n };\r\n}\r\n\r\nfunction parseBattleTeamFromArmies(itemBuf: Uint8Array): BattleTeam {\r\n const f = decodeProto(itemBuf);\r\n const hostUserId = String(getInt(f, 1));\r\n let teamScore = 0;\r\n const users: BattleTeamUser[] = [];\r\n\r\n const groups = getAllBytes(f, 2);\r\n for (const gb of groups) {\r\n try {\r\n const gf = decodeProto(gb);\r\n const points = getInt(gf, 2);\r\n teamScore += points;\r\n\r\n const userBufs = getAllBytes(gf, 1);\r\n for (const ub of userBufs) {\r\n try {\r\n const user = parseUser(ub);\r\n users.push({ user, score: points });\r\n } catch { }\r\n }\r\n } catch { }\r\n }\r\n\r\n return { hostUserId, score: teamScore, users };\r\n}\r\n\r\nexport function parseWebcastMessage(method: string, payload: Uint8Array): LiveEvent {\r\n const f = decodeProto(payload);\r\n const base = { timestamp: Date.now(), msgId: '' };\r\n\r\n const typeBuf = getBytes(f, 1);\r\n if (typeBuf) {\r\n try {\r\n const tf = decodeProto(typeBuf);\r\n const ts = getInt(tf, 4);\r\n if (ts) base.timestamp = ts;\r\n } catch { }\r\n }\r\n\r\n switch (method) {\r\n case 'WebcastChatMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n 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 let action = 1;\r\n const eventBuf = getBytes(f, 1);\r\n if (eventBuf) {\r\n const ef = decodeProto(eventBuf);\r\n const detailBuf = getBytes(ef, 8);\r\n if (detailBuf) {\r\n const df = decodeProto(detailBuf);\r\n const label = getStr(df, 2);\r\n if (label.includes('followed')) action = 2;\r\n else if (label.includes('share')) action = 3;\r\n }\r\n }\r\n return { ...base, type: 'member' as const, user, action };\r\n }\r\n\r\n case 'WebcastLikeMessage': {\r\n const userBuf = getBytes(f, 5);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n return {\r\n ...base, type: 'like' as const, user,\r\n likeCount: getInt(f, 2),\r\n totalLikes: getInt(f, 3),\r\n };\r\n }\r\n\r\n case 'WebcastGiftMessage': {\r\n const userBuf = getBytes(f, 7);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n const giftId = getInt(f, 2);\r\n const repeatCount = getInt(f, 5);\r\n const repeatEnd = getInt(f, 9) === 1;\r\n\r\n let giftName = '', diamondCount = 0, giftType = 0;\r\n let giftImageUrl = '';\r\n const detailsBuf = getBytes(f, 15);\r\n if (detailsBuf) {\r\n const df = decodeProto(detailsBuf);\r\n giftName = getStr(df, 16) || getStr(df, 2);\r\n diamondCount = getInt(df, 12);\r\n giftType = getInt(df, 11);\r\n\r\n const imgBuf = getBytes(df, 1);\r\n if (imgBuf) {\r\n const imgf = decodeProto(imgBuf);\r\n giftImageUrl = getStr(imgf, 1);\r\n }\r\n }\r\n\r\n let toUserId = '';\r\n const extraBuf = getBytes(f, 23);\r\n if (extraBuf) {\r\n const ef = decodeProto(extraBuf);\r\n toUserId = 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, giftPictureUrl: giftImageUrl,\r\n };\r\n }\r\n\r\n case 'WebcastSocialMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n let action = 'follow';\r\n const eventBuf = getBytes(f, 1);\r\n if (eventBuf) {\r\n const ef = decodeProto(eventBuf);\r\n const detailBuf = getBytes(ef, 8);\r\n if (detailBuf) {\r\n const df = decodeProto(detailBuf);\r\n const label = getStr(df, 2);\r\n if (label.includes('share')) action = 'share';\r\n else if (label.includes('follow')) action = 'follow';\r\n const displayType = getStr(df, 1);\r\n if (displayType === 'pm_mt_msg_viewer_share') action = 'share';\r\n }\r\n }\r\n return { ...base, type: 'social' as const, user, action };\r\n }\r\n\r\n case 'WebcastRoomUserSeqMessage': {\r\n const viewerCount = getInt(f, 3) || getInt(f, 2);\r\n const totalViewers = getInt(f, 1) || viewerCount;\r\n return { ...base, type: 'roomUserSeq' as const, totalViewers, viewerCount };\r\n }\r\n\r\n case 'WebcastLinkMicBattle': {\r\n 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 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 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 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 case 'WebcastSubNotifyMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n return { ...base, type: 'subscribe' as const, user, subMonth: getInt(f, 3) };\r\n }\r\n\r\n case 'WebcastEmoteChatMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n let emoteId = '', emoteUrl = '';\r\n const emoteBuf = getBytes(f, 3);\r\n if (emoteBuf) {\r\n const ef = decodeProto(emoteBuf);\r\n emoteId = getStr(ef, 1);\r\n const imageBuf = getBytes(ef, 2);\r\n if (imageBuf) {\r\n const imgFields = decodeProto(imageBuf);\r\n emoteUrl = getStr(imgFields, 1);\r\n }\r\n }\r\n return { ...base, type: 'emoteChat' as const, user, emoteId, emoteUrl };\r\n }\r\n\r\n case 'WebcastEnvelopeMessage': {\r\n const envelopeId = String(getInt(f, 1) || getStr(f, 1));\r\n return { ...base, type: 'envelope' as const, envelopeId, diamondCount: getInt(f, 3) };\r\n }\r\n\r\n case 'WebcastQuestionNewMessage': {\r\n let questionText = '', user: TikTokUser = { id: '0', nickname: '', uniqueId: '' };\r\n const detailBuf = getBytes(f, 2);\r\n if (detailBuf) {\r\n const df = decodeProto(detailBuf);\r\n questionText = getStr(df, 2);\r\n const userBuf = getBytes(df, 5);\r\n if (userBuf) user = parseUser(userBuf);\r\n }\r\n return { ...base, type: 'question' as const, user, questionText };\r\n }\r\n\r\n case 'WebcastRankUpdateMessage':\r\n case 'WebcastHourlyRankMessage': {\r\n const rankType = getStr(f, 1) || `rank_${getInt(f, 1)}`;\r\n const rankList: Array<{ user: TikTokUser; rank: number; score: number }> = [];\r\n const listBufs = getAllBytes(f, 2);\r\n for (const lb of listBufs) {\r\n try {\r\n const rf = decodeProto(lb);\r\n const userBuf = getBytes(rf, 1);\r\n const rank = getInt(rf, 2);\r\n const score = getInt(rf, 3);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n rankList.push({ user, rank, score });\r\n } catch { }\r\n }\r\n return { ...base, type: 'rankUpdate' as const, rankType, rankList };\r\n }\r\n\r\n case 'WebcastControlMessage':\r\n return { ...base, type: 'control' as const, action: getInt(f, 2) || getInt(f, 1) };\r\n\r\n case 'WebcastRoomMessage':\r\n case 'RoomMessage':\r\n return { ...base, type: 'room' as const, status: getInt(f, 2) };\r\n\r\n case 'WebcastLiveIntroMessage': {\r\n const roomId = String(getInt(f, 2));\r\n const title = getStr(f, 4) || getStr(f, 2);\r\n return { ...base, type: 'liveIntro' as const, roomId, title };\r\n }\r\n\r\n case 'WebcastLinkMicMethod':\r\n 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: Uint8Array): LiveEvent[] {\r\n const events: LiveEvent[] = [];\r\n const respFields = decodeProto(payload);\r\n\r\n for (const mf of respFields.filter(f => f.fn === 1 && f.wt === 2)) {\r\n const msgBuf = mf.value as Uint8Array;\r\n const msgFields = decodeProto(msgBuf);\r\n const method = getStr(msgFields, 1);\r\n const innerPayload = getBytes(msgFields, 2);\r\n if (!method || !innerPayload) continue;\r\n\r\n try {\r\n events.push(parseWebcastMessage(method, innerPayload));\r\n } catch { }\r\n }\r\n\r\n return events;\r\n}\r\n","/**\r\n * TikTool API utilities for the sign-and-return API flow.\r\n *\r\n * The API server returns signed URLs instead of fetching TikTok data directly.\r\n * These utilities handle the two-step flow:\r\n * 1. resolve_required — scrape TikTok HTML to get room_id\r\n * 2. fetch_signed_url — use the signed URL to get actual TikTok data\r\n *\r\n * @module\r\n */\r\n\r\nconst DEFAULT_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';\r\nconst DEFAULT_SIGN_SERVER = 'https://api.tik.tools';\r\n\r\nconst pageCache = new Map<string, { info: LivePageInfo; ts: number }>();\r\nconst PAGE_CACHE_TTL = 5 * 60 * 1000;\r\n\r\n/**\r\n * Resolved live page metadata from a TikTok live page.\r\n */\r\nexport interface LivePageInfo {\r\n /** Active room ID for the livestream */\r\n roomId: string;\r\n /** Session cookie required for WebSocket authentication */\r\n ttwid: string;\r\n /** Server cluster region (e.g. 'us', 'eu') */\r\n clusterRegion: string;\r\n}\r\n\r\n/**\r\n * Scrape a TikTok live page to extract room metadata.\r\n * Returns the room ID, session cookie, and cluster region.\r\n * Results are cached for 5 minutes. Returns `null` if the user is not live.\r\n */\r\nexport async function resolveLivePage(uniqueId: string): Promise<LivePageInfo | null> {\r\n const clean = uniqueId.replace(/^@/, '');\r\n const cached = pageCache.get(clean);\r\n if (cached && Date.now() - cached.ts < PAGE_CACHE_TTL) {\r\n return cached.info;\r\n }\r\n\r\n try {\r\n const resp = await fetch(`https://www.tiktok.com/@${clean}/live`, {\r\n headers: {\r\n 'User-Agent': DEFAULT_UA,\r\n 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\r\n 'Accept-Language': 'en-US,en;q=0.9',\r\n },\r\n redirect: 'follow',\r\n });\r\n\r\n if (!resp.ok) return null;\r\n\r\n // Extract ttwid session cookie\r\n let ttwid = '';\r\n const setCookies = resp.headers.get('set-cookie') || '';\r\n for (const part of setCookies.split(',')) {\r\n const trimmed = part.trim();\r\n if (trimmed.startsWith('ttwid=')) {\r\n ttwid = trimmed.split(';')[0].split('=').slice(1).join('=');\r\n break;\r\n }\r\n }\r\n if (!ttwid && typeof (resp.headers as any).getSetCookie === 'function') {\r\n for (const sc of (resp.headers as any).getSetCookie()) {\r\n if (typeof sc === 'string' && sc.startsWith('ttwid=')) {\r\n ttwid = sc.split(';')[0].split('=').slice(1).join('=');\r\n break;\r\n }\r\n }\r\n }\r\n\r\n const html = await resp.text();\r\n\r\n // Extract roomId from SIGI_STATE JSON block\r\n let roomId = '';\r\n const sigiMatch = html.match(/id=\"SIGI_STATE\"[^>]*>([^<]+)/);\r\n if (sigiMatch) {\r\n try {\r\n const json = JSON.parse(sigiMatch[1]);\r\n const jsonStr = JSON.stringify(json);\r\n const m = jsonStr.match(/\"roomId\"\\s*:\\s*\"(\\d+)\"/);\r\n if (m) roomId = m[1];\r\n } catch { }\r\n }\r\n\r\n // Fallback patterns\r\n if (!roomId) {\r\n const patterns = [\r\n /\"roomId\"\\s*:\\s*\"(\\d+)\"/,\r\n /room_id[=/](\\d{10,})/,\r\n /\"idStr\"\\s*:\\s*\"(\\d{10,})\"/,\r\n ];\r\n for (const p of patterns) {\r\n const m = html.match(p);\r\n if (m) { roomId = m[1]; break; }\r\n }\r\n }\r\n\r\n if (!roomId) return null;\r\n\r\n // Extract cluster region\r\n const crMatch = html.match(/\"clusterRegion\"\\s*:\\s*\"([^\"]+)\"/);\r\n const clusterRegion = crMatch ? crMatch[1] : '';\r\n\r\n const info: LivePageInfo = { roomId, ttwid, clusterRegion };\r\n pageCache.set(clean, { info, ts: Date.now() });\r\n return info;\r\n } catch { }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Resolve a TikTok username to a room ID.\r\n * Returns `null` if the user is not currently live.\r\n * Results are cached for 5 minutes.\r\n */\r\nexport async function resolveRoomId(uniqueId: string): Promise<string | null> {\r\n const info = await resolveLivePage(uniqueId);\r\n return info?.roomId ?? null;\r\n}\r\n\r\nexport interface SignedUrlResponse {\r\n status_code: number;\r\n action?: string;\r\n signed_url: string;\r\n headers: Record<string, string>;\r\n cookies: string;\r\n}\r\n\r\n/**\r\n * Execute a signed-URL API response: fetch the signed URL and return the\r\n * parsed JSON data from TikTok.\r\n */\r\nexport async function fetchSignedUrl(response: SignedUrlResponse): Promise<any> {\r\n if (!response.signed_url) {\r\n return null;\r\n }\r\n\r\n const headers: Record<string, string> = { ...(response.headers || {}) };\r\n if (response.cookies) {\r\n headers['Cookie'] = response.cookies;\r\n }\r\n\r\n const resp = await fetch(response.signed_url, { headers, redirect: 'follow' });\r\n const text = await resp.text();\r\n try {\r\n return JSON.parse(text);\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport interface CallApiOptions {\r\n /** API server URL (default: https://api.tik.tools) */\r\n serverUrl?: string;\r\n /** API key for authentication */\r\n apiKey: string;\r\n /** API endpoint path (e.g. '/webcast/room_video') */\r\n endpoint: string;\r\n /** TikTok unique_id to resolve */\r\n uniqueId: string;\r\n /** HTTP method (default: POST) */\r\n method?: 'GET' | 'POST';\r\n /** Additional body fields for POST requests */\r\n extraBody?: Record<string, any>;\r\n}\r\n\r\n/**\r\n * Call a TikTool API endpoint, handling the full\r\n * resolve_required → room_id → signed_url → fetch flow automatically.\r\n *\r\n * Returns the actual TikTok data, or `null` if the user is not live.\r\n */\r\nexport async function callApi(opts: CallApiOptions): Promise<any> {\r\n const serverUrl = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const isGet = opts.method === 'GET';\r\n const ak = encodeURIComponent(opts.apiKey);\r\n\r\n const url1 = isGet\r\n ? `${serverUrl}${opts.endpoint}?apiKey=${ak}&unique_id=${encodeURIComponent(opts.uniqueId)}`\r\n : `${serverUrl}${opts.endpoint}?apiKey=${ak}`;\r\n\r\n const fetchOpts1: RequestInit = isGet\r\n ? {}\r\n : {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ unique_id: opts.uniqueId, ...opts.extraBody }),\r\n };\r\n\r\n const resp1 = await fetch(url1, fetchOpts1);\r\n const data1 = await resp1.json() as any;\r\n\r\n // If the response contains a signed URL to fetch (with or without explicit action),\r\n // follow through and fetch the actual TikTok data\r\n if (data1.signed_url || data1.action === 'fetch_signed_url') {\r\n return fetchSignedUrl(data1);\r\n }\r\n\r\n if (data1.status_code === 0 && data1.action !== 'resolve_required') {\r\n return data1;\r\n }\r\n\r\n if (data1.action === 'resolve_required') {\r\n const roomId = await resolveRoomId(opts.uniqueId);\r\n if (!roomId) return null;\r\n\r\n const url2 = isGet\r\n ? `${serverUrl}${opts.endpoint}?apiKey=${ak}&room_id=${encodeURIComponent(roomId)}`\r\n : `${serverUrl}${opts.endpoint}?apiKey=${ak}`;\r\n\r\n const fetchOpts2: RequestInit = isGet\r\n ? {}\r\n : {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ room_id: roomId, ...opts.extraBody }),\r\n };\r\n\r\n const resp2 = await fetch(url2, fetchOpts2);\r\n const data2 = await resp2.json() as any;\r\n\r\n if (data2.signed_url || data2.action === 'fetch_signed_url') {\r\n return fetchSignedUrl(data2);\r\n }\r\n\r\n return data2;\r\n }\r\n\r\n return data1;\r\n}\r\n\r\n// ── CAPTCHA Solver API ──────────────────────────────────────────────\r\n\r\nimport type { PuzzleSolveResult, RotateSolveResult, ShapesSolveResult } from './types.js';\r\n\r\n/**\r\n * Solve a puzzle (slider) CAPTCHA using the TikTool solver.\r\n *\r\n * @param apiKey - API key for authentication\r\n * @param puzzleB64 - Base64-encoded background image (PNG/JPEG)\r\n * @param pieceB64 - Base64-encoded puzzle piece image (PNG/JPEG)\r\n * @param serverUrl - Custom server URL (default: https://api.tik.tools)\r\n * @returns Solve result with slide position and confidence\r\n */\r\nexport async function solvePuzzle(\r\n apiKey: string,\r\n puzzleB64: string,\r\n pieceB64: string,\r\n serverUrl?: string,\r\n): Promise<PuzzleSolveResult> {\r\n const base = (serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const resp = await fetch(`${base}/captcha/solve/puzzle?apiKey=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ puzzle: puzzleB64, piece: pieceB64 }),\r\n });\r\n const data = await resp.json() as any;\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Solver failed (status ${data.status_code})`);\r\n }\r\n return data.data as PuzzleSolveResult;\r\n}\r\n\r\n/**\r\n * Solve a rotate (whirl) CAPTCHA using the TikTool solver.\r\n *\r\n * @param apiKey - API key for authentication\r\n * @param outerB64 - Base64-encoded outer ring image (PNG/JPEG)\r\n * @param innerB64 - Base64-encoded inner rotated image (PNG/JPEG)\r\n * @param serverUrl - Custom server URL (default: https://api.tik.tools)\r\n * @returns Solve result with rotation angle and confidence\r\n */\r\nexport async function solveRotate(\r\n apiKey: string,\r\n outerB64: string,\r\n innerB64: string,\r\n serverUrl?: string,\r\n): Promise<RotateSolveResult> {\r\n const base = (serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const resp = await fetch(`${base}/captcha/solve/rotate?apiKey=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ outer: outerB64, inner: innerB64 }),\r\n });\r\n const data = await resp.json() as any;\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Solver failed (status ${data.status_code})`);\r\n }\r\n return data.data as RotateSolveResult;\r\n}\r\n\r\n/**\r\n * Solve a shapes (3D matching) CAPTCHA using the TikTool solver.\r\n *\r\n * @param apiKey - API key for authentication\r\n * @param imageB64 - Base64-encoded CAPTCHA image with shape grid (PNG/JPEG)\r\n * @param serverUrl - Custom server URL (default: https://api.tik.tools)\r\n * @returns Solve result with two matching shape coordinates and confidence\r\n */\r\nexport async function solveShapes(\r\n apiKey: string,\r\n imageB64: string,\r\n serverUrl?: string,\r\n): Promise<ShapesSolveResult> {\r\n const base = (serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const resp = await fetch(`${base}/captcha/solve/shapes?apiKey=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ image: imageB64 }),\r\n });\r\n const data = await resp.json() as any;\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Solver failed (status ${data.status_code})`);\r\n }\r\n return data.data as ShapesSolveResult;\r\n}\r\n","import type { TikTokLiveOptions, TikTokLiveEvents, RoomInfo, LiveEvent } from './types.js';\r\nimport {\r\n concatBytes,\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\nimport { resolveLivePage } from './api.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\ntype Listener = (...args: any[]) => void;\r\n\r\nclass TypedEmitter {\r\n private _listeners = new Map<string, Listener[]>();\r\n\r\n on(event: string, fn: Listener): this {\r\n const arr = this._listeners.get(event) || [];\r\n arr.push(fn);\r\n this._listeners.set(event, arr);\r\n return this;\r\n }\r\n\r\n once(event: string, fn: Listener): this {\r\n const wrapper: Listener = (...args) => {\r\n this.off(event, wrapper);\r\n fn(...args);\r\n };\r\n return this.on(event, wrapper);\r\n }\r\n\r\n off(event: string, fn: Listener): this {\r\n const arr = this._listeners.get(event);\r\n if (arr) {\r\n this._listeners.set(event, arr.filter(l => l !== fn));\r\n }\r\n return this;\r\n }\r\n\r\n emit(event: string, ...args: any[]): boolean {\r\n const arr = this._listeners.get(event);\r\n if (!arr || arr.length === 0) return false;\r\n for (const fn of [...arr]) fn(...args);\r\n return true;\r\n }\r\n\r\n removeAllListeners(event?: string): this {\r\n if (event) this._listeners.delete(event);\r\n else this._listeners.clear();\r\n return this;\r\n }\r\n}\r\n\r\nasync function gunzip(data: Uint8Array): Promise<Uint8Array> {\r\n if (typeof DecompressionStream !== 'undefined') {\r\n const ds = new DecompressionStream('gzip');\r\n const writer = ds.writable.getWriter();\r\n const reader = ds.readable.getReader();\r\n writer.write(data as any);\r\n writer.close();\r\n const chunks: Uint8Array[] = [];\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n chunks.push(value);\r\n }\r\n return concatBytes(...chunks);\r\n }\r\n try {\r\n const zlib = await import('zlib');\r\n return new Promise((resolve, reject) => {\r\n zlib.gunzip(data, (err: Error | null, result: Buffer) => {\r\n if (err) reject(err);\r\n else resolve(new Uint8Array(result));\r\n });\r\n });\r\n } catch {\r\n return data;\r\n }\r\n}\r\n\r\n// Pre-cache zlib for sync decompression (require fails in ESM)\r\nlet _zlib: any = null;\r\nlet _zlibLoadAttempted = false;\r\n\r\nasync function ensureZlib(): Promise<any> {\r\n if (_zlib) return _zlib;\r\n if (_zlibLoadAttempted) return null;\r\n _zlibLoadAttempted = true;\r\n try {\r\n _zlib = await import('zlib');\r\n } catch { }\r\n return _zlib;\r\n}\r\n\r\n// Attempt to load zlib eagerly at module init\r\nensureZlib();\r\n\r\nfunction gunzipSync(data: Uint8Array): Uint8Array | null {\r\n if (!_zlib) return null; // Signal that sync decompress is unavailable\r\n try {\r\n return new Uint8Array(_zlib.gunzipSync(data));\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\ntype WebSocketLike = {\r\n readonly readyState: number;\r\n send(data: ArrayBufferLike | string): void;\r\n close(code?: number, reason?: string): void;\r\n onopen: ((ev: any) => void) | null;\r\n onmessage: ((ev: any) => void) | null;\r\n onerror: ((ev: any) => void) | null;\r\n onclose: ((ev: any) => void) | null;\r\n};\r\n\r\ninterface WebSocketConstructor {\r\n new(url: string, protocols?: string | string[]): WebSocketLike;\r\n readonly OPEN: number;\r\n readonly CLOSED: number;\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\nasync function resolveWebSocket(userImpl?: any): Promise<WebSocketConstructor> {\r\n if (userImpl) return userImpl;\r\n\r\n if (typeof globalThis.WebSocket !== 'undefined') {\r\n return globalThis.WebSocket as unknown as WebSocketConstructor;\r\n }\r\n\r\n try {\r\n const ws = await import('ws');\r\n return (ws.default || ws) as unknown as WebSocketConstructor;\r\n } catch {\r\n throw new Error(\r\n 'No WebSocket implementation found. ' +\r\n 'Either use Node.js 22+ (native WebSocket), Cloudflare Workers, or install the \"ws\" package: npm i ws'\r\n );\r\n }\r\n}\r\n\r\nexport class TikTokLive extends TypedEmitter {\r\n private ws: WebSocketLike | null = null;\r\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\r\n private reconnectAttempts = 0;\r\n private intentionalClose = false;\r\n private _connected = false;\r\n private _eventCount = 0;\r\n private _roomId = '';\r\n private _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 private readonly webSocketImpl?: any;\r\n private WS!: WebSocketConstructor;\r\n\r\n constructor(options: TikTokLiveOptions) {\r\n super();\r\n this.uniqueId = options.uniqueId.replace(/^@/, '');\r\n this.signServerUrl = (options.signServerUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n if (!options.apiKey) throw new Error('apiKey is required. Get a free key at https://tik.tools');\r\n this.apiKey = options.apiKey;\r\n this.autoReconnect = options.autoReconnect ?? true;\r\n this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;\r\n this.heartbeatInterval = options.heartbeatInterval ?? 10_000;\r\n this.debug = options.debug ?? false;\r\n this.webSocketImpl = options.webSocketImpl;\r\n }\r\n\r\n async connect(): Promise<void> {\r\n this.intentionalClose = false;\r\n\r\n if (!this.WS) {\r\n this.WS = await resolveWebSocket(this.webSocketImpl);\r\n }\r\n\r\n const pageInfo = await resolveLivePage(this.uniqueId);\r\n if (!pageInfo) throw new Error(`User @${this.uniqueId} is not currently live`);\r\n if (!pageInfo.ttwid) throw new Error('Failed to obtain session cookie');\r\n\r\n const { roomId, ttwid, clusterRegion } = pageInfo;\r\n this._roomId = roomId;\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: 'Etc/UTC',\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 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 const connUrl = wsUrl + (wsUrl.includes('?') ? '&' : '?') + `ttwid=${ttwid}`;\r\n\r\n try {\r\n this.ws = new (this.WS as any)(connUrl, {\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 }) as WebSocketLike;\r\n } catch {\r\n this.ws = new this.WS(connUrl) as WebSocketLike;\r\n }\r\n\r\n const ws = this.ws;\r\n let settled = false;\r\n\r\n ws.onopen = () => {\r\n this._connected = true;\r\n this.reconnectAttempts = 0;\r\n\r\n const hb = buildHeartbeat(roomId);\r\n const enter = buildImEnterRoom(roomId);\r\n ws.send(hb.buffer.byteLength === hb.length ? hb.buffer : hb.buffer.slice(hb.byteOffset, hb.byteOffset + hb.byteLength));\r\n ws.send(enter.buffer.byteLength === enter.length ? enter.buffer : enter.buffer.slice(enter.byteOffset, enter.byteOffset + enter.byteLength));\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 as TypedEmitter).emit('connected');\r\n (this as TypedEmitter).emit('roomInfo', roomInfo);\r\n if (!settled) { settled = true; resolve(); }\r\n };\r\n\r\n ws.onmessage = (event: any) => {\r\n const raw = event.data !== undefined ? event.data : event;\r\n this.handleMessage(raw);\r\n };\r\n\r\n ws.onclose = (event: any) => {\r\n this._connected = false;\r\n this.stopHeartbeat();\r\n\r\n const code = event?.code ?? 1006;\r\n const reason = event?.reason ?? '';\r\n (this as TypedEmitter).emit('disconnected', code, reason?.toString?.() || '');\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 as TypedEmitter).emit('error', e)), delay);\r\n }\r\n };\r\n\r\n ws.onerror = (err: any) => {\r\n (this as TypedEmitter).emit('error', err instanceof Error ? err : new Error(String(err?.message || err)));\r\n if (!settled) { settled = true; reject(err); }\r\n };\r\n\r\n setTimeout(() => {\r\n if (!settled) {\r\n settled = true;\r\n ws.close();\r\n reject(new Error('Connection timeout'));\r\n }\r\n }, 15_000);\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 override on<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.on(event as string, listener as Listener);\r\n }\r\n\r\n override once<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.once(event as string, listener as Listener);\r\n }\r\n\r\n override off<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.off(event as string, listener as Listener);\r\n }\r\n\r\n override emit<K extends keyof TikTokLiveEvents>(event: K, ...args: Parameters<TikTokLiveEvents[K]>): boolean {\r\n return super.emit(event as string, ...args);\r\n }\r\n\r\n private async handleMessage(raw: any): Promise<void> {\r\n try {\r\n let bytes: Uint8Array;\r\n\r\n if (raw instanceof ArrayBuffer) {\r\n bytes = new Uint8Array(raw);\r\n } else if (raw instanceof Uint8Array) {\r\n bytes = raw;\r\n } else if (typeof Blob !== 'undefined' && raw instanceof Blob) {\r\n bytes = new Uint8Array(await raw.arrayBuffer());\r\n } else if (raw?.buffer instanceof ArrayBuffer) {\r\n bytes = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);\r\n } else {\r\n return;\r\n }\r\n\r\n this.handleFrame(bytes);\r\n } catch { }\r\n }\r\n\r\n private async handleFrame(buf: Uint8Array): Promise<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 && (this.ws.readyState === 1)) {\r\n const ack = buildAck(id);\r\n this.ws.send(ack.buffer.byteLength === ack.length ? ack.buffer : ack.buffer.slice(ack.byteOffset, ack.byteOffset + ack.byteLength));\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 sync first (fastest, works when zlib is loaded)\r\n const syncResult = gunzipSync(inner);\r\n if (syncResult) {\r\n inner = syncResult;\r\n } else {\r\n // Fallback to async gunzip (uses DecompressionStream or async import)\r\n inner = await gunzip(inner);\r\n }\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 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 if (evt.type === 'battleArmies') {\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 team.hostUser = host.user;\r\n this._battleHosts.set(team.hostUserId, host.user);\r\n } else {\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 as TypedEmitter).emit('event', evt);\r\n (this as TypedEmitter).emit(evt.type, 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 && this.ws.readyState === 1) {\r\n const hb = buildHeartbeat(roomId);\r\n this.ws.send(hb.buffer.byteLength === hb.length ? hb.buffer : hb.buffer.slice(hb.byteOffset, hb.byteOffset + hb.byteLength));\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"],"mappings":"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","names":["DEFAULT_UA","DEFAULT_SIGN_SERVER"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/proto.ts","../src/api.ts"],"sourcesContent":["export { TikTokLive } from './client.js';\r\nexport { getRanklist, getRegionalRanklist } from './api.js';\r\nexport type { GetRanklistOptions, GetRegionalRanklistOptions, RegionalRanklistSignedResponse } from './api.js';\r\n\r\nexport type {\r\n TikTokLiveOptions,\r\n TikTokLiveEvents,\r\n RoomInfo,\r\n LiveEvent,\r\n BaseEvent,\r\n ChatEvent,\r\n MemberEvent,\r\n LikeEvent,\r\n GiftEvent,\r\n SocialEvent,\r\n RoomUserSeqEvent,\r\n BattleEvent,\r\n BattleArmiesEvent,\r\n BattleTeam,\r\n BattleTeamUser,\r\n BattleTaskEvent,\r\n BarrageEvent,\r\n SubscribeEvent,\r\n EmoteChatEvent,\r\n EnvelopeEvent,\r\n QuestionEvent,\r\n ControlEvent,\r\n RoomEvent,\r\n LiveIntroEvent,\r\n RankUpdateEvent,\r\n LinkMicEvent,\r\n UnknownEvent,\r\n TikTokUser,\r\n RanklistUser,\r\n OnlineAudienceEntry,\r\n AnchorRankListEntry,\r\n RanklistSelfInfo,\r\n OnlineAudienceResponse,\r\n AnchorRankListResponse,\r\n EntranceTab,\r\n EntranceInfo,\r\n EntranceResponse,\r\n RanklistResponse,\r\n} from './types.js';\r\n","import { EventEmitter } from 'events';\r\nimport * as http from 'http';\r\nimport * as https from 'https';\r\nimport * as zlib from 'zlib';\r\nimport 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 private _ownerUserId = '';\r\n\r\n private readonly uniqueId: string;\r\n private readonly signServerUrl: string;\r\n private readonly apiKey: string;\r\n private readonly autoReconnect: boolean;\r\n private readonly maxReconnectAttempts: number;\r\n private readonly heartbeatInterval: number;\r\n private readonly debug: boolean;\r\n\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 let ownerUserId = '';\r\n const sigiMatch = html.match(/id=\"SIGI_STATE\"[^>]*>([^<]+)/);\r\n if (sigiMatch) {\r\n try {\r\n const json = JSON.parse(sigiMatch[1]);\r\n const jsonStr = JSON.stringify(json);\r\n const m = jsonStr.match(/\"roomId\"\\s*:\\s*\"(\\d+)\"/);\r\n if (m) roomId = m[1];\r\n // Extract owner userId from parsed JSON — critical for 1v1 battle team identification\r\n // SIGI_STATE structure: { LiveRoom: { liveRoomUserInfo: { user: { id, uniqueId } } } }\r\n const ownerUser = json?.LiveRoom?.liveRoomUserInfo?.user;\r\n if (ownerUser?.id) {\r\n ownerUserId = String(ownerUser.id);\r\n }\r\n } catch { }\r\n }\r\n if (!roomId) throw new Error(`User @${this.uniqueId} is not currently live`);\r\n this._roomId = roomId;\r\n this._ownerUserId = ownerUserId;\r\n\r\n const crMatch = html.match(/\"clusterRegion\"\\s*:\\s*\"([^\"]+)\"/);\r\n const clusterRegion = crMatch ? crMatch[1] : '';\r\n const wsHost = getWsHost(clusterRegion);\r\n\r\n const wsParams = new URLSearchParams({\r\n version_code: '270000', device_platform: 'web', cookie_enabled: 'true',\r\n screen_width: '1920', screen_height: '1080', browser_language: 'en-US',\r\n browser_platform: 'Win32', browser_name: 'Mozilla',\r\n browser_version: DEFAULT_UA.split('Mozilla/')[1] || '5.0',\r\n browser_online: 'true', tz_name: Intl.DateTimeFormat().resolvedOptions().timeZone,\r\n app_name: 'tiktok_web', sup_ws_ds_opt: '1', update_version_code: '2.0.0',\r\n compress: 'gzip', webcast_language: 'en', ws_direct: '1', aid: '1988',\r\n live_id: '12', app_language: 'en', client_enter: '1', room_id: roomId,\r\n identity: 'audience', history_comment_count: '6', last_rtt: '0',\r\n heartbeat_duration: '10000', resp_content_type: 'protobuf', did_rule: '3',\r\n });\r\n\r\n const rawWsUrl = `https://${wsHost}/webcast/im/ws_proxy/ws_reuse_supplement/?${wsParams}`;\r\n\r\n const signUrl = `${this.signServerUrl}/webcast/sign_url`;\r\n\r\n let wsUrl: string;\r\n try {\r\n const signResp = await fetch(signUrl, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'x-api-key': this.apiKey,\r\n },\r\n body: JSON.stringify({ url: rawWsUrl }),\r\n });\r\n const signData = await signResp.json() as Record<string, any>;\r\n\r\n if (signData.status_code === 0 && signData.data?.signed_url) {\r\n wsUrl = (signData.data.signed_url as string).replace(/^https:\\/\\//, 'wss://');\r\n } else {\r\n wsUrl = rawWsUrl.replace(/^https:\\/\\//, 'wss://');\r\n }\r\n } catch {\r\n wsUrl = rawWsUrl.replace(/^https:\\/\\//, 'wss://');\r\n }\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n 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 ownerUserId: ownerUserId || undefined,\r\n };\r\n\r\n this.emit('connected');\r\n this.emit('roomInfo', roomInfo);\r\n resolve();\r\n });\r\n\r\n this.ws.on('message', (rawData: Buffer) => {\r\n this.handleFrame(Buffer.from(rawData));\r\n });\r\n\r\n this.ws.on('close', (code, reason) => {\r\n this._connected = false;\r\n this.stopHeartbeat();\r\n\r\n const reasonStr = reason?.toString() || '';\r\n this.emit('disconnected', code, reasonStr);\r\n\r\n if (!this.intentionalClose && this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {\r\n this.reconnectAttempts++;\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts - 1), 30_000);\r\n setTimeout(() => this.connect().catch(e => this.emit('error', e)), delay);\r\n }\r\n });\r\n\r\n this.ws.on('error', (err) => {\r\n this.emit('error', err);\r\n if (!this._connected) reject(err);\r\n });\r\n });\r\n }\r\n\r\n disconnect(): void {\r\n this.intentionalClose = true;\r\n this.stopHeartbeat();\r\n if (this.ws) {\r\n this.ws.close(1000);\r\n this.ws = null;\r\n }\r\n this._connected = false;\r\n }\r\n\r\n get connected(): boolean {\r\n return this._connected;\r\n }\r\n\r\n get eventCount(): number {\r\n return this._eventCount;\r\n }\r\n\r\n get roomId(): string {\r\n return this._roomId;\r\n }\r\n\r\n on<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.on(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n once<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.once(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n off<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.off(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n emit<K extends keyof TikTokLiveEvents>(event: K, ...args: Parameters<TikTokLiveEvents[K]>): boolean {\r\n return super.emit(event, ...args);\r\n }\r\n\r\n private handleFrame(buf: Buffer): void {\r\n try {\r\n const fields = decodeProto(buf);\r\n const idField = fields.find(f => f.fn === 2 && f.wt === 0);\r\n const id = idField ? idField.value as bigint : 0n;\r\n const type = getStr(fields, 7);\r\n const binary = getBytes(fields, 8);\r\n\r\n if (id > 0n && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(buildAck(id));\r\n }\r\n\r\n if (type === 'msg' && binary && binary.length > 0) {\r\n let inner = binary;\r\n if (inner.length > 2 && inner[0] === 0x1f && inner[1] === 0x8b) {\r\n try { inner = zlib.gunzipSync(inner); } catch { }\r\n }\r\n\r\n const events = parseWebcastResponse(inner);\r\n for (const evt of events) {\r\n this._eventCount++;\r\n this.emit('event', evt);\r\n this.emit(evt.type as keyof TikTokLiveEvents, evt as any);\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n private startHeartbeat(roomId: string): void {\r\n this.stopHeartbeat();\r\n this.heartbeatTimer = setInterval(() => {\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(buildHeartbeat(roomId));\r\n }\r\n }, this.heartbeatInterval);\r\n }\r\n\r\n private stopHeartbeat(): void {\r\n if (this.heartbeatTimer) {\r\n clearInterval(this.heartbeatTimer);\r\n this.heartbeatTimer = null;\r\n }\r\n }\r\n}\r\n","import type { LiveEvent, TikTokUser, BattleTeam, BattleTeamUser } from './types.js';\r\n\r\nconst encoder = new TextEncoder();\r\nconst decoder = new TextDecoder();\r\n\r\n// TikTok LIVE emote name → Unicode emoji mapping\r\n// TikTok uses [emote_name] placeholders in chat text. These are resolved client-side.\r\nconst TIKTOK_EMOTE_MAP: Record<string, string> = {\r\n // Standard emojis\r\n 'happy': '😊', 'angry': '😡', 'cry': '😢', 'embarrassed': '😳',\r\n 'surprised': '😮', 'wronged': '😞', 'shout': '😤', 'flushed': '😳',\r\n 'yummy': '😋', 'complacent': '😌', 'drool': '🤤', 'scream': '😱',\r\n 'weep': '😭', 'speechless': '😶', 'funnyface': '🤪', 'laughwithtears': '😂',\r\n 'wicked': '😈', 'facewithrollingeyes': '🙄', 'sulk': '😒', 'thinking': '🤔',\r\n 'lovely': '🥰', 'greedy': '🤑', 'wow': '😯', 'joyful': '😃',\r\n 'hehe': '😁', 'slap': '👋', 'tears': '😿', 'stun': '😵',\r\n 'cute': '🥺', 'blink': '😉', 'disdain': '😏', 'astonish': '😲',\r\n 'cool': '😎', 'excited': '🤩', 'proud': '😤', 'smileface': '😊',\r\n 'evil': '👿', 'angel': '😇', 'laugh': '😆', 'pride': '🦁',\r\n 'nap': '😴', 'loveface': '😍', 'awkward': '😬', 'shock': '😨',\r\n 'funny': '😄', 'rage': '🤬',\r\n // Common aliases used in TikTok LIVE\r\n 'laughcry': '😂', 'heart': '❤️', 'like': '👍', 'love': '💕',\r\n 'shy': '🙈', 'smile': '😊',\r\n};\r\n\r\n/** Replace [emote_name] placeholders with Unicode emoji equivalents */\r\nfunction replaceEmotes(text: string, imageMap?: Record<string, string>): string {\r\n return text.replace(/\\[([a-zA-Z_]+)\\]/g, (match, name) => {\r\n const lower = name.toLowerCase();\r\n const emoji = TIKTOK_EMOTE_MAP[lower];\r\n return emoji || match; // Keep original if unknown\r\n });\r\n}\r\n\r\nexport function concatBytes(...arrays: Uint8Array[]): Uint8Array {\r\n let totalLength = 0;\r\n for (const arr of arrays) totalLength += arr.length;\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const arr of arrays) {\r\n result.set(arr, offset);\r\n offset += arr.length;\r\n }\r\n return result;\r\n}\r\n\r\nfunction readInt32LE(buf: Uint8Array, offset: number): number {\r\n return buf[offset] |\r\n (buf[offset + 1] << 8) |\r\n (buf[offset + 2] << 16) |\r\n (buf[offset + 3] << 24);\r\n}\r\n\r\nfunction readBigInt64LE(buf: Uint8Array, offset: number): bigint {\r\n const lo = BigInt(buf[offset] |\r\n (buf[offset + 1] << 8) |\r\n (buf[offset + 2] << 16) |\r\n ((buf[offset + 3] << 24) >>> 0));\r\n const hi = BigInt(buf[offset + 4] |\r\n (buf[offset + 5] << 8) |\r\n (buf[offset + 6] << 16) |\r\n ((buf[offset + 7] << 24) >>> 0));\r\n return (hi << 32n) | (lo & 0xFFFFFFFFn);\r\n}\r\n\r\nexport function decodeVarint(buf: Uint8Array, offset: number): { value: number; offset: number } {\r\n let result = 0, shift = 0;\r\n while (offset < buf.length) {\r\n const byte = buf[offset++];\r\n result |= (byte & 0x7F) << shift;\r\n if ((byte & 0x80) === 0) break;\r\n shift += 7;\r\n }\r\n return { value: result >>> 0, offset };\r\n}\r\n\r\nexport function decodeVarint64(buf: Uint8Array, offset: number): { value: bigint; offset: number } {\r\n let result = 0n, shift = 0n;\r\n while (offset < buf.length) {\r\n const byte = BigInt(buf[offset++]);\r\n result |= (byte & 0x7Fn) << shift;\r\n if ((byte & 0x80n) === 0n) break;\r\n shift += 7n;\r\n }\r\n return { value: result, offset };\r\n}\r\n\r\nexport function encodeVarint(v: number | bigint): Uint8Array {\r\n const bytes: number[] = [];\r\n let n = typeof v === 'bigint' ? v : BigInt(v);\r\n do {\r\n let b = Number(n & 0x7Fn);\r\n n >>= 7n;\r\n if (n > 0n) b |= 0x80;\r\n bytes.push(b);\r\n } while (n > 0n);\r\n return new Uint8Array(bytes);\r\n}\r\n\r\nexport interface ProtoField {\r\n fn: number;\r\n wt: number;\r\n value: Uint8Array | number | bigint;\r\n}\r\n\r\nexport function encodeField(fn: number, wt: number, value: Uint8Array | bigint | number | string): Uint8Array {\r\n const tag = encodeVarint((fn << 3) | wt);\r\n if (wt === 0) {\r\n return concatBytes(tag, encodeVarint(typeof value === 'number' ? BigInt(value) : value as bigint));\r\n }\r\n const data = typeof value === 'string' ? encoder.encode(value) : value as Uint8Array;\r\n return concatBytes(tag, encodeVarint(data.length), data);\r\n}\r\n\r\nexport function decodeProto(buf: Uint8Array): ProtoField[] {\r\n const fields: ProtoField[] = [];\r\n let offset = 0;\r\n while (offset < buf.length) {\r\n const tagResult = decodeVarint(buf, offset);\r\n offset = tagResult.offset;\r\n const fn = tagResult.value >> 3;\r\n const wt = tagResult.value & 7;\r\n if (wt === 0) {\r\n const r = decodeVarint64(buf, offset);\r\n offset = r.offset;\r\n fields.push({ fn, wt, value: r.value });\r\n } else if (wt === 2) {\r\n const lenR = decodeVarint(buf, offset);\r\n offset = lenR.offset;\r\n const data = buf.subarray(offset, offset + lenR.value);\r\n offset += lenR.value;\r\n fields.push({ fn, wt, value: data });\r\n } else if (wt === 1) {\r\n fields.push({ fn, wt, value: readBigInt64LE(buf, offset) });\r\n offset += 8;\r\n } else if (wt === 5) {\r\n fields.push({ fn, wt, value: BigInt(readInt32LE(buf, offset)) });\r\n offset += 4;\r\n } else {\r\n break;\r\n }\r\n }\r\n return fields;\r\n}\r\n\r\nexport function getStr(fields: ProtoField[], fn: number): string {\r\n const f = fields.find(x => x.fn === fn && x.wt === 2);\r\n return f ? decoder.decode(f.value as Uint8Array) : '';\r\n}\r\n\r\nexport function getBytes(fields: ProtoField[], fn: number): Uint8Array | null {\r\n const f = fields.find(x => x.fn === fn && x.wt === 2);\r\n return f ? f.value as Uint8Array : null;\r\n}\r\n\r\nexport function getInt(fields: ProtoField[], fn: number): number {\r\n const f = fields.find(x => x.fn === fn && x.wt === 0);\r\n return f ? Number(f.value) : 0;\r\n}\r\n\r\n/** Get a varint field as a string without Number precision loss (safe for 64-bit IDs). */\r\nexport function getIntStr(fields: ProtoField[], fn: number): string {\r\n const f = fields.find(x => x.fn === fn && x.wt === 0);\r\n return f ? String(f.value) : '';\r\n}\r\n\r\nexport function getAllBytes(fields: ProtoField[], fn: number): Uint8Array[] {\r\n return fields.filter(x => x.fn === fn && x.wt === 2).map(x => x.value as Uint8Array);\r\n}\r\n\r\nexport function buildHeartbeat(roomId: string): Uint8Array {\r\n const payload = encodeField(1, 0, BigInt(roomId));\r\n return concatBytes(\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'hb'),\r\n encodeField(8, 2, payload),\r\n );\r\n}\r\n\r\nexport function buildImEnterRoom(roomId: string): Uint8Array {\r\n const inner = concatBytes(\r\n encodeField(1, 0, BigInt(roomId)),\r\n encodeField(4, 0, 12n),\r\n encodeField(5, 2, 'audience'),\r\n encodeField(6, 2, ''),\r\n encodeField(9, 2, ''),\r\n encodeField(10, 2, ''),\r\n );\r\n return concatBytes(\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'im_enter_room'),\r\n encodeField(8, 2, inner),\r\n );\r\n}\r\n\r\nexport function buildAck(id: bigint): Uint8Array {\r\n return concatBytes(\r\n encodeField(2, 0, id),\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'ack'),\r\n );\r\n}\r\n\r\n// NOTE: Army users in WebcastLinkMicArmies use a DIFFERENT layout where\r\n// field 38 contains avatar/image data, NOT uniqueId.\r\n// We detect this by checking if the string looks like a username (short, no URLs).\r\nfunction looksLikeUsername(s: string): boolean {\r\n return s.length > 0 && s.length <= 50 && !s.includes('://') && !s.includes('\\x00');\r\n}\r\n\r\nfunction parseUser(data: Uint8Array): TikTokUser {\r\n const f = decodeProto(data);\r\n const id = getIntStr(f, 1) || getStr(f, 1);\r\n const nickname = getStr(f, 3) || getStr(f, 2);\r\n\r\n // uniqueId: Try field 4 (LinkUser format) first, then field 38 (User format)\r\n // Validate that the result looks like a username, not binary/URL data\r\n let uniqueId = '';\r\n const uid4 = getStr(f, 4);\r\n const uid38 = getStr(f, 38);\r\n if (uid4 && looksLikeUsername(uid4)) {\r\n uniqueId = uid4;\r\n } else if (uid38 && looksLikeUsername(uid38)) {\r\n uniqueId = uid38;\r\n } else {\r\n const uid2 = getStr(f, 2);\r\n if (uid2 && looksLikeUsername(uid2)) uniqueId = uid2;\r\n }\r\n\r\n // profilePicture: Try field 9 (User format), field 4 (army users — avatar at field 4), field 3 (LinkUser format)\r\n // Can't use OR chain because field 3 = nickname bytes in User format (truthy but wrong)\r\n // Must try each and pick the first that yields a valid URL\r\n let profilePicture: string | undefined;\r\n for (const fieldNum of [9, 4, 3]) {\r\n if (profilePicture) break;\r\n const avatarBuf = getBytes(f, fieldNum);\r\n if (!avatarBuf) continue;\r\n try {\r\n const avatarFields = decodeProto(avatarBuf);\r\n const urlBufs = getAllBytes(avatarFields, 1);\r\n for (const urlBuf of urlBufs) {\r\n const url = decoder.decode(urlBuf);\r\n if (url.includes('://')) {\r\n profilePicture = url;\r\n break;\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n const badges: string[] = [];\r\n const badgeBuf = getBytes(f, 64);\r\n if (badgeBuf) {\r\n try {\r\n const badgeFields = decodeProto(badgeBuf);\r\n const badgeItems = getAllBytes(badgeFields, 21);\r\n for (const bi of badgeItems) {\r\n const bf = decodeProto(bi);\r\n const name = getStr(bf, 3) || getStr(bf, 2);\r\n if (name) badges.push(name);\r\n }\r\n } catch { }\r\n }\r\n\r\n return {\r\n id,\r\n nickname,\r\n uniqueId: uniqueId || nickname || id,\r\n profilePictureUrl: profilePicture,\r\n badges: badges.length > 0 ? badges : undefined,\r\n };\r\n}\r\n\r\nfunction parseBattleTeamFromArmies(itemBuf: Uint8Array): BattleTeam {\r\n const f = decodeProto(itemBuf);\r\n const hostUserId = getIntStr(f, 1) || '0';\r\n let teamScore = 0;\r\n const users: BattleTeamUser[] = [];\r\n let hostUser: TikTokUser | undefined;\r\n\r\n // Field 2: Groups of gifter users (repeated)\r\n const groups = getAllBytes(f, 2);\r\n for (const gb of groups) {\r\n try {\r\n const gf = decodeProto(gb);\r\n const points = getInt(gf, 2);\r\n teamScore += points;\r\n\r\n const userBufs = getAllBytes(gf, 1);\r\n for (const ub of userBufs) {\r\n try {\r\n const user = parseUser(ub);\r\n users.push({ user, score: points });\r\n } catch { }\r\n }\r\n } catch { }\r\n }\r\n\r\n // Try to extract the HOST USER from other fields (3-8)\r\n // NOTE: Currently armies protobuf only has fields [1, 2], but kept\r\n // as a silent fallback in case TikTok adds host user data later.\r\n for (const fieldNum of [3, 4, 5, 6, 7, 8]) {\r\n if (hostUser) break;\r\n const buf = getBytes(f, fieldNum);\r\n if (!buf) continue;\r\n try {\r\n const parsed = parseUser(buf);\r\n if (parsed && (parsed.nickname || parsed.uniqueId) &&\r\n parsed.uniqueId !== parsed.id &&\r\n looksLikeUsername(parsed.uniqueId || '')) {\r\n hostUser = parsed;\r\n }\r\n } catch { }\r\n }\r\n\r\n return { hostUserId, score: teamScore, users, hostUser };\r\n}\r\n\r\nexport function parseWebcastMessage(method: string, payload: Uint8Array): LiveEvent {\r\n const f = decodeProto(payload);\r\n const base = { timestamp: Date.now(), msgId: '' };\r\n\r\n const typeBuf = getBytes(f, 1);\r\n if (typeBuf) {\r\n try {\r\n const tf = decodeProto(typeBuf);\r\n const ts = getInt(tf, 4);\r\n if (ts) base.timestamp = ts;\r\n } catch { }\r\n }\r\n\r\n switch (method) {\r\n case 'WebcastChatMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n const rawComment = getStr(f, 3);\r\n\r\n // Parse emote images from field 22 (emotes_with_index) if present\r\n const emoteImages: Record<string, string> = {};\r\n for (const field of f) {\r\n if (field.fn === 22 && field.wt === 2) {\r\n try {\r\n const ewi = decodeProto(field.value as Uint8Array);\r\n const emoteKey = getStr(ewi, 1);\r\n const imgBuf = getBytes(ewi, 2);\r\n if (imgBuf && emoteKey) {\r\n const imgFields = decodeProto(imgBuf);\r\n const url = getStr(imgFields, 1);\r\n if (url) emoteImages[emoteKey] = url;\r\n }\r\n } catch { }\r\n }\r\n }\r\n\r\n // Replace [emote_name] placeholders with Unicode equivalents\r\n const comment = replaceEmotes(rawComment, emoteImages);\r\n\r\n return { ...base, type: 'chat' as const, user, comment };\r\n }\r\n\r\n case 'WebcastMemberMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n let action = 1;\r\n const eventBuf = getBytes(f, 1);\r\n if (eventBuf) {\r\n const ef = decodeProto(eventBuf);\r\n const detailBuf = getBytes(ef, 8);\r\n if (detailBuf) {\r\n const df = decodeProto(detailBuf);\r\n const label = getStr(df, 2);\r\n if (label.includes('followed')) action = 2;\r\n else if (label.includes('share')) action = 3;\r\n }\r\n }\r\n return { ...base, type: 'member' as const, user, action };\r\n }\r\n\r\n case 'WebcastLikeMessage': {\r\n const userBuf = getBytes(f, 5);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n return {\r\n ...base, type: 'like' as const, user,\r\n likeCount: getInt(f, 2),\r\n totalLikes: getInt(f, 3),\r\n };\r\n }\r\n\r\n case 'WebcastGiftMessage': {\r\n const userBuf = getBytes(f, 7);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n const giftId = getInt(f, 2);\r\n const repeatCount = getInt(f, 5);\r\n const repeatEnd = getInt(f, 9) === 1;\r\n\r\n let giftName = '', diamondCount = 0, giftType = 0;\r\n let giftImageUrl = '';\r\n const detailsBuf = getBytes(f, 15);\r\n if (detailsBuf) {\r\n const df = decodeProto(detailsBuf);\r\n giftName = getStr(df, 16) || getStr(df, 2);\r\n diamondCount = getInt(df, 12);\r\n giftType = getInt(df, 11);\r\n\r\n const imgBuf = getBytes(df, 1);\r\n if (imgBuf) {\r\n const imgf = decodeProto(imgBuf);\r\n giftImageUrl = getStr(imgf, 1);\r\n }\r\n }\r\n\r\n let toUserId = '';\r\n const extraBuf = getBytes(f, 23);\r\n if (extraBuf) {\r\n const ef = decodeProto(extraBuf);\r\n toUserId = getIntStr(ef, 8) || '';\r\n }\r\n\r\n const groupId = toUserId || getStr(f, 11);\r\n\r\n return {\r\n ...base, type: 'gift' as const, user, giftId, giftName, diamondCount,\r\n repeatCount, repeatEnd, combo: repeatCount > 1 && !repeatEnd,\r\n giftType, groupId, giftPictureUrl: giftImageUrl,\r\n };\r\n }\r\n\r\n case 'WebcastSocialMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n let action = 'follow';\r\n const eventBuf = getBytes(f, 1);\r\n if (eventBuf) {\r\n const ef = decodeProto(eventBuf);\r\n const detailBuf = getBytes(ef, 8);\r\n if (detailBuf) {\r\n const df = decodeProto(detailBuf);\r\n const label = getStr(df, 2);\r\n if (label.includes('share')) action = 'share';\r\n else if (label.includes('follow')) action = 'follow';\r\n const displayType = getStr(df, 1);\r\n if (displayType === 'pm_mt_msg_viewer_share') action = 'share';\r\n }\r\n }\r\n return { ...base, type: 'social' as const, user, action };\r\n }\r\n\r\n case 'WebcastRoomUserSeqMessage': {\r\n const viewerCount = getInt(f, 3) || getInt(f, 2);\r\n const totalViewers = getInt(f, 1) || viewerCount;\r\n return { ...base, type: 'roomUserSeq' as const, totalViewers, viewerCount };\r\n }\r\n\r\n case 'WebcastLinkMicBattle': {\r\n // 🔥 FIX: Raw proto field mapping (verified from captured data):\r\n // field_1 = header sub-message (method name, msgId, roomId, timestamp)\r\n // field_2 = battleId (varint — the main battle identifier)\r\n // field_3 = battle settings sub-message containing timer data:\r\n // field_3.field_1 = battleId (duplicate)\r\n // field_3.field_2 = startTimeMs (millisecond timestamp)\r\n // field_3.field_3 = duration (seconds, e.g. 301)\r\n // field_3.field_5 = phase/status (1=active, 3=ended)\r\n // field_3.field_10 = lastUpdateTimeMs\r\n // field_4 = overall status (e.g. 5 = ended)\r\n // field_5 = team user list (repeated)\r\n // field_9 = detailed user data with avatars (repeated)\r\n // field_10 = battle user bufs (repeated — used for team parsing)\r\n const battleId = getIntStr(f, 2) || getIntStr(f, 1) || '';\r\n const overallStatus = getInt(f, 4);\r\n const teams: BattleTeam[] = [];\r\n\r\n // Parse battle settings from field_3 sub-message\r\n let battleDuration = 0;\r\n let battleSettings: { startTimeMs?: number; duration?: number; endTimeMs?: number } | undefined;\r\n const settingsBuf = getBytes(f, 3);\r\n if (settingsBuf) {\r\n try {\r\n const sf = decodeProto(settingsBuf);\r\n const startTimeMs = getInt(sf, 2);\r\n const duration = getInt(sf, 3);\r\n const phase = getInt(sf, 5);\r\n const endTimeMs = getInt(sf, 10);\r\n battleDuration = duration;\r\n battleSettings = {\r\n startTimeMs: startTimeMs || undefined,\r\n duration: duration || undefined,\r\n endTimeMs: endTimeMs || undefined,\r\n };\r\n } catch { }\r\n }\r\n\r\n // Determine status: use phase from settings, fall back to overall status\r\n // 🔥 FIX: field_4 sometimes returns a large TikTok ID instead of a status code.\r\n // Valid battle status codes are small (1=active, 3=ended, 5=finalizing).\r\n // Only use overallStatus if it looks like a real status code (1-10).\r\n const settingsPhase = settingsBuf ? (() => {\r\n try { return getInt(decodeProto(settingsBuf), 5); } catch { return 0; }\r\n })() : 0;\r\n const status = settingsPhase || (overallStatus > 0 && overallStatus <= 10 ? overallStatus : 0) || 1;\r\n\r\n const battleUserBufs = getAllBytes(f, 10);\r\n for (const bub of battleUserBufs) {\r\n try {\r\n const bf = decodeProto(bub);\r\n const groupBuf = getBytes(bf, 2);\r\n if (groupBuf) {\r\n const gf = decodeProto(groupBuf);\r\n const linkUserBuf = getBytes(gf, 1);\r\n if (linkUserBuf) {\r\n const user = parseUser(linkUserBuf);\r\n teams.push({\r\n hostUserId: user.id,\r\n score: 0,\r\n users: [{ user, score: 0 }],\r\n });\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n // Also try field_5 for team users (new proto format)\r\n if (teams.length === 0) {\r\n const teamBufs5 = getAllBytes(f, 5);\r\n for (const tb of teamBufs5) {\r\n try {\r\n const tf = decodeProto(tb);\r\n const userId = getIntStr(tf, 1);\r\n if (userId && userId !== '0') {\r\n teams.push({\r\n hostUserId: userId,\r\n score: 0,\r\n users: [],\r\n });\r\n }\r\n } catch { }\r\n }\r\n }\r\n\r\n if (teams.length === 0) {\r\n const teamBufs7 = getAllBytes(f, 7);\r\n for (const tb of teamBufs7) {\r\n try {\r\n teams.push(parseBattleTeamFromArmies(tb));\r\n } catch { }\r\n }\r\n }\r\n\r\n return { ...base, type: 'battle' as const, battleId, status, battleDuration, teams, battleSettings };\r\n }\r\n\r\n case 'WebcastLinkMicArmies': {\r\n const battleId = getIntStr(f, 1) || '';\r\n const battleStatus = getInt(f, 7);\r\n const teams: BattleTeam[] = [];\r\n\r\n const itemBufs = getAllBytes(f, 3);\r\n for (const ib of itemBufs) {\r\n try {\r\n teams.push(parseBattleTeamFromArmies(ib));\r\n } catch { }\r\n }\r\n\r\n return {\r\n ...base, type: 'battleArmies' as const, battleId, teams,\r\n status: battleStatus,\r\n };\r\n }\r\n\r\n case 'WebcastSubNotifyMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n return { ...base, type: 'subscribe' as const, user, subMonth: getInt(f, 3) };\r\n }\r\n\r\n case 'WebcastEmoteChatMessage': {\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n let emoteId = '', emoteUrl = '';\r\n const emoteBuf = getBytes(f, 3);\r\n if (emoteBuf) {\r\n const ef = decodeProto(emoteBuf);\r\n emoteId = getStr(ef, 1);\r\n const imageBuf = getBytes(ef, 2);\r\n if (imageBuf) {\r\n const imgFields = decodeProto(imageBuf);\r\n emoteUrl = getStr(imgFields, 1);\r\n }\r\n }\r\n return { ...base, type: 'emoteChat' as const, user, emoteId, emoteUrl };\r\n }\r\n\r\n case 'WebcastEnvelopeMessage': {\r\n const envelopeId = String(getInt(f, 1) || getStr(f, 1));\r\n return { ...base, type: 'envelope' as const, envelopeId, diamondCount: getInt(f, 3) };\r\n }\r\n\r\n case 'WebcastQuestionNewMessage': {\r\n let questionText = '', user: TikTokUser = { id: '0', nickname: '', uniqueId: '' };\r\n const detailBuf = getBytes(f, 2);\r\n if (detailBuf) {\r\n const df = decodeProto(detailBuf);\r\n questionText = getStr(df, 2);\r\n const userBuf = getBytes(df, 5);\r\n if (userBuf) user = parseUser(userBuf);\r\n }\r\n return { ...base, type: 'question' as const, user, questionText };\r\n }\r\n\r\n case 'WebcastRankUpdateMessage':\r\n case 'WebcastHourlyRankMessage': {\r\n const rankType = getStr(f, 1) || `rank_${getInt(f, 1)}`;\r\n const rankList: Array<{ user: TikTokUser; rank: number; score: number }> = [];\r\n const listBufs = getAllBytes(f, 2);\r\n for (const lb of listBufs) {\r\n try {\r\n const rf = decodeProto(lb);\r\n const userBuf = getBytes(rf, 1);\r\n const rank = getInt(rf, 2);\r\n const score = getInt(rf, 3);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n rankList.push({ user, rank, score });\r\n } catch { }\r\n }\r\n return { ...base, type: 'rankUpdate' as const, rankType, rankList };\r\n }\r\n\r\n case 'WebcastControlMessage':\r\n return { ...base, type: 'control' as const, action: getInt(f, 2) || getInt(f, 1) };\r\n\r\n case 'WebcastRoomMessage':\r\n case 'RoomMessage':\r\n return { ...base, type: 'room' as const, status: getInt(f, 2) };\r\n\r\n case 'WebcastLiveIntroMessage': {\r\n const roomId = String(getInt(f, 2));\r\n const title = getStr(f, 4) || getStr(f, 2);\r\n return { ...base, type: 'liveIntro' as const, roomId, title };\r\n }\r\n\r\n case 'WebcastLinkMicMethod': {\r\n const action = getStr(f, 1) || `action_${getInt(f, 1)}`;\r\n const users: TikTokUser[] = [];\r\n const userBufs = getAllBytes(f, 2);\r\n for (const ub of userBufs) {\r\n try { users.push(parseUser(ub)); } catch { }\r\n }\r\n return { ...base, type: 'linkMic' as const, action, users };\r\n }\r\n\r\n case 'WebcastLinkmicBattleTaskMessage': {\r\n // 🔥 Battle task/buff events (multipliers, bonus missions, timer updates)\r\n // Raw proto fields:\r\n // field_2 = taskAction (1=start, 2=update, 3=mission complete)\r\n // field_3 = bonus mission details sub-message:\r\n // field_3.field_1.field_1 = target score\r\n // field_3.field_1.field_2 = mission items with multiplier/duration\r\n // field_3.field_1.field_3 = countdown (field_1=target, field_2=duration, field_3=endTimestamp)\r\n // field_3.field_1.field_6 = mission start timestamp\r\n // field_5 = timer sub-message:\r\n // field_5.field_1 = timer type\r\n // field_5.field_2 = remaining seconds\r\n // field_5.field_3 = end timestamp (seconds)\r\n // field_6 = mission description with multiplier info\r\n // field_20 = battle reference ID\r\n const taskAction = getInt(f, 2);\r\n const battleRefId = getIntStr(f, 20) || '';\r\n\r\n // Extract timer info from field_5\r\n let timerType = 0;\r\n let remainingSeconds = 0;\r\n let endTimestampS = 0;\r\n const timerBuf = getBytes(f, 5);\r\n if (timerBuf) {\r\n try {\r\n const tf = decodeProto(timerBuf);\r\n timerType = getInt(tf, 1);\r\n remainingSeconds = getInt(tf, 2);\r\n endTimestampS = getInt(tf, 3);\r\n } catch { }\r\n }\r\n\r\n // Extract multiplier and mission details from field_3 or field_6\r\n let multiplier = 0;\r\n let missionDuration = 0;\r\n let missionTarget = 0;\r\n let missionType = '';\r\n\r\n // Try field_3.field_1 (bonus mission)\r\n const bonusBuf = getBytes(f, 3);\r\n if (bonusBuf) {\r\n try {\r\n const bf = decodeProto(bonusBuf);\r\n const missionBuf = getBytes(bf, 1);\r\n if (missionBuf) {\r\n const mf = decodeProto(missionBuf);\r\n missionTarget = getInt(mf, 1);\r\n // Check mission items (field_2, repeated) for multiplier\r\n const items = getAllBytes(mf, 2);\r\n for (const item of items) {\r\n try {\r\n const itemFields = decodeProto(item);\r\n const descBuf = getBytes(itemFields, 2);\r\n if (descBuf) {\r\n const descFields = decodeProto(descBuf);\r\n // Look for \"multi\" key in param pairs\r\n const paramBufs = getAllBytes(descFields, 2);\r\n for (const pb of paramBufs) {\r\n try {\r\n const pf = decodeProto(pb);\r\n const key = getStr(pf, 1);\r\n const val = getStr(pf, 2);\r\n if (key === 'multi' && val) multiplier = parseInt(val) || 0;\r\n if (key === 'dur' && val) missionDuration = parseInt(val) || 0;\r\n if (key === 'sum' && val) missionTarget = parseInt(val) || missionTarget;\r\n } catch { }\r\n }\r\n // Get mission type from field_1\r\n const typeStr = getStr(descFields, 1);\r\n if (typeStr) missionType = typeStr;\r\n }\r\n } catch { }\r\n }\r\n // Check countdown sub-message (field_3 of mission)\r\n const countdownBuf = getBytes(mf, 3);\r\n if (countdownBuf) {\r\n try {\r\n const cf = decodeProto(countdownBuf);\r\n if (!missionDuration) missionDuration = getInt(cf, 2);\r\n } catch { }\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n // Try field_6 for mission descriptions (e.g. mission complete with sum)\r\n if (!missionType) {\r\n const descBuf6 = getBytes(f, 6);\r\n if (descBuf6) {\r\n try {\r\n const d6 = decodeProto(descBuf6);\r\n const innerBuf = getBytes(d6, 1);\r\n if (innerBuf) {\r\n const innerF = decodeProto(innerBuf);\r\n const typeStr = getStr(innerF, 1);\r\n if (typeStr) missionType = typeStr;\r\n const paramBufs = getAllBytes(innerF, 2);\r\n for (const pb of paramBufs) {\r\n try {\r\n const pf = decodeProto(pb);\r\n const key = getStr(pf, 1);\r\n const val = getStr(pf, 2);\r\n if (key === 'multi' && val) multiplier = parseInt(val) || 0;\r\n if (key === 'sum' && val) missionTarget = parseInt(val) || missionTarget;\r\n } catch { }\r\n }\r\n }\r\n } catch { }\r\n }\r\n }\r\n\r\n return {\r\n ...base, type: 'battleTask' as const,\r\n taskAction, battleRefId, missionType, multiplier,\r\n missionDuration, missionTarget,\r\n remainingSeconds, endTimestampS, timerType,\r\n };\r\n }\r\n\r\n case 'WebcastBarrageMessage': {\r\n // Battle banner events: multipliers (x2/x3), gloves, bonus missions, etc.\r\n // Debug: dump raw fields to discover correct mapping\r\n const fieldSummary = f.map(x => `fn=${x.fn} wt=${x.wt} val=${x.wt === 0 ? x.value : x.wt === 2 ? `[bytes:${(x.value as Uint8Array).length}]` : x.value\r\n }`).join(' | ');\r\n console.log(`[proto] WebcastBarrageMessage fields: ${fieldSummary}`);\r\n\r\n const msgType = getInt(f, 3);\r\n const duration = getInt(f, 4);\r\n const displayType = getInt(f, 5);\r\n const subType = getInt(f, 6);\r\n\r\n let defaultPattern = '';\r\n let content = '';\r\n const contentBuf = getBytes(f, 2);\r\n if (contentBuf) {\r\n try {\r\n const cf = decodeProto(contentBuf);\r\n // field_1 = default pattern text\r\n defaultPattern = getStr(cf, 1) || '';\r\n // field_2 = pattern params (may have key=value pairs)\r\n const paramBufs = getAllBytes(cf, 2);\r\n const params: string[] = [];\r\n for (const pb of paramBufs) {\r\n try {\r\n const pf = decodeProto(pb);\r\n const key = getStr(pf, 1);\r\n const val = getStr(pf, 2);\r\n if (key && val) params.push(`${key}=${val}`);\r\n } catch { }\r\n }\r\n if (params.length > 0) content = params.join(', ');\r\n } catch { }\r\n }\r\n\r\n // Also try field_1 as a direct string (some types send the text directly)\r\n if (!defaultPattern) {\r\n defaultPattern = getStr(f, 1) || '';\r\n }\r\n\r\n console.log(`[proto] Barrage parsed: msgType=${msgType} subType=${subType} displayType=${displayType} duration=${duration} pattern=\"${defaultPattern}\" content=\"${content}\"`);\r\n\r\n return {\r\n ...base, type: 'barrage' as const,\r\n msgType, subType, displayType, duration,\r\n defaultPattern, content,\r\n };\r\n }\r\n\r\n default:\r\n return { ...base, type: 'unknown' as const, method };\r\n }\r\n}\r\n\r\nexport function parseWebcastResponse(payload: Uint8Array): LiveEvent[] {\r\n const events: LiveEvent[] = [];\r\n const respFields = decodeProto(payload);\r\n\r\n for (const mf of respFields.filter(f => f.fn === 1 && f.wt === 2)) {\r\n const msgBuf = mf.value as Uint8Array;\r\n const msgFields = decodeProto(msgBuf);\r\n const method = getStr(msgFields, 1);\r\n const innerPayload = getBytes(msgFields, 2);\r\n if (!method || !innerPayload) continue;\r\n\r\n try {\r\n events.push(parseWebcastMessage(method, innerPayload));\r\n } catch { }\r\n }\r\n\r\n return events;\r\n}\r\n","/**\r\n * TikTool API utilities for the sign-and-return API flow.\r\n *\r\n * The API server returns signed URLs instead of fetching TikTok data directly.\r\n * These utilities handle the two-step flow:\r\n * 1. resolve_required — scrape TikTok HTML to get room_id\r\n * 2. fetch_signed_url — use the signed URL to get actual TikTok data\r\n *\r\n * @module\r\n */\r\n\r\nconst DEFAULT_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';\r\nconst DEFAULT_SIGN_SERVER = 'https://api.tik.tools';\r\n\r\nconst pageCache = new Map<string, { info: LivePageInfo; ts: number }>();\r\nconst PAGE_CACHE_TTL = 5 * 60 * 1000;\r\n\r\n/**\r\n * Resolved live page metadata from a TikTok live page.\r\n */\r\nexport interface LivePageInfo {\r\n /** Active room ID for the livestream */\r\n roomId: string;\r\n /** Session cookie required for WebSocket authentication */\r\n ttwid: string;\r\n /** Server cluster region (e.g. 'us', 'eu') */\r\n clusterRegion: string;\r\n}\r\n\r\n/**\r\n * Scrape a TikTok live page to extract room metadata.\r\n * Returns the room ID, session cookie, and cluster region.\r\n * Results are cached for 5 minutes. Returns `null` if the user is not live.\r\n */\r\nexport async function resolveLivePage(uniqueId: string): Promise<LivePageInfo | null> {\r\n const clean = uniqueId.replace(/^@/, '');\r\n const cached = pageCache.get(clean);\r\n if (cached && Date.now() - cached.ts < PAGE_CACHE_TTL) {\r\n return cached.info;\r\n }\r\n\r\n try {\r\n const resp = await fetch(`https://www.tiktok.com/@${clean}/live`, {\r\n headers: {\r\n 'User-Agent': DEFAULT_UA,\r\n 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\r\n 'Accept-Language': 'en-US,en;q=0.9',\r\n },\r\n redirect: 'follow',\r\n });\r\n\r\n if (!resp.ok) return null;\r\n\r\n // Extract ttwid session cookie\r\n let ttwid = '';\r\n const setCookies = resp.headers.get('set-cookie') || '';\r\n for (const part of setCookies.split(',')) {\r\n const trimmed = part.trim();\r\n if (trimmed.startsWith('ttwid=')) {\r\n ttwid = trimmed.split(';')[0].split('=').slice(1).join('=');\r\n break;\r\n }\r\n }\r\n if (!ttwid && typeof (resp.headers as any).getSetCookie === 'function') {\r\n for (const sc of (resp.headers as any).getSetCookie()) {\r\n if (typeof sc === 'string' && sc.startsWith('ttwid=')) {\r\n ttwid = sc.split(';')[0].split('=').slice(1).join('=');\r\n break;\r\n }\r\n }\r\n }\r\n\r\n const html = await resp.text();\r\n\r\n // Extract roomId from SIGI_STATE JSON block\r\n let roomId = '';\r\n const sigiMatch = html.match(/id=\"SIGI_STATE\"[^>]*>([^<]+)/);\r\n if (sigiMatch) {\r\n try {\r\n const json = JSON.parse(sigiMatch[1]);\r\n const jsonStr = JSON.stringify(json);\r\n const m = jsonStr.match(/\"roomId\"\\s*:\\s*\"(\\d+)\"/);\r\n if (m) roomId = m[1];\r\n } catch { }\r\n }\r\n\r\n // Fallback patterns\r\n if (!roomId) {\r\n const patterns = [\r\n /\"roomId\"\\s*:\\s*\"(\\d+)\"/,\r\n /room_id[=/](\\d{10,})/,\r\n /\"idStr\"\\s*:\\s*\"(\\d{10,})\"/,\r\n ];\r\n for (const p of patterns) {\r\n const m = html.match(p);\r\n if (m) { roomId = m[1]; break; }\r\n }\r\n }\r\n\r\n if (!roomId) return null;\r\n\r\n // Extract cluster region\r\n const crMatch = html.match(/\"clusterRegion\"\\s*:\\s*\"([^\"]+)\"/);\r\n const clusterRegion = crMatch ? crMatch[1] : '';\r\n\r\n const info: LivePageInfo = { roomId, ttwid, clusterRegion };\r\n pageCache.set(clean, { info, ts: Date.now() });\r\n return info;\r\n } catch { }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Resolve a TikTok username to a room ID.\r\n * Returns `null` if the user is not currently live.\r\n * Results are cached for 5 minutes.\r\n */\r\nexport async function resolveRoomId(uniqueId: string): Promise<string | null> {\r\n const info = await resolveLivePage(uniqueId);\r\n return info?.roomId ?? null;\r\n}\r\n\r\nexport interface SignedUrlResponse {\r\n status_code: number;\r\n action?: string;\r\n signed_url: string;\r\n headers: Record<string, string>;\r\n cookies: string;\r\n}\r\n\r\n/**\r\n * Execute a signed-URL API response: fetch the signed URL and return the\r\n * parsed JSON data from TikTok.\r\n */\r\nexport async function fetchSignedUrl(response: SignedUrlResponse): Promise<any> {\r\n if (!response.signed_url) {\r\n return null;\r\n }\r\n\r\n const headers: Record<string, string> = { ...(response.headers || {}) };\r\n if (response.cookies) {\r\n headers['Cookie'] = response.cookies;\r\n }\r\n\r\n const resp = await fetch(response.signed_url, { headers, redirect: 'follow' });\r\n const text = await resp.text();\r\n try {\r\n return JSON.parse(text);\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport interface CallApiOptions {\r\n /** API server URL (default: https://api.tik.tools) */\r\n serverUrl?: string;\r\n /** API key for authentication */\r\n apiKey: string;\r\n /** API endpoint path (e.g. '/webcast/room_video') */\r\n endpoint: string;\r\n /** TikTok unique_id to resolve */\r\n uniqueId: string;\r\n /** HTTP method (default: POST) */\r\n method?: 'GET' | 'POST';\r\n /** Additional body fields for POST requests */\r\n extraBody?: Record<string, any>;\r\n}\r\n\r\n/**\r\n * Call a TikTool API endpoint, handling the full\r\n * resolve_required → room_id → signed_url → fetch flow automatically.\r\n *\r\n * Returns the actual TikTok data, or `null` if the user is not live.\r\n */\r\nexport async function callApi(opts: CallApiOptions): Promise<any> {\r\n const serverUrl = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const isGet = opts.method === 'GET';\r\n const ak = encodeURIComponent(opts.apiKey);\r\n\r\n const url1 = isGet\r\n ? `${serverUrl}${opts.endpoint}?apiKey=${ak}&unique_id=${encodeURIComponent(opts.uniqueId)}`\r\n : `${serverUrl}${opts.endpoint}?apiKey=${ak}`;\r\n\r\n const fetchOpts1: RequestInit = isGet\r\n ? {}\r\n : {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ unique_id: opts.uniqueId, ...opts.extraBody }),\r\n };\r\n\r\n const resp1 = await fetch(url1, fetchOpts1);\r\n const data1 = await resp1.json() as any;\r\n\r\n // If the response contains a signed URL to fetch (with or without explicit action),\r\n // follow through and fetch the actual TikTok data\r\n if (data1.signed_url || data1.action === 'fetch_signed_url') {\r\n return fetchSignedUrl(data1);\r\n }\r\n\r\n if (data1.status_code === 0 && data1.action !== 'resolve_required') {\r\n return data1;\r\n }\r\n\r\n if (data1.action === 'resolve_required') {\r\n const roomId = await resolveRoomId(opts.uniqueId);\r\n if (!roomId) return null;\r\n\r\n const url2 = isGet\r\n ? `${serverUrl}${opts.endpoint}?apiKey=${ak}&room_id=${encodeURIComponent(roomId)}`\r\n : `${serverUrl}${opts.endpoint}?apiKey=${ak}`;\r\n\r\n const fetchOpts2: RequestInit = isGet\r\n ? {}\r\n : {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ room_id: roomId, ...opts.extraBody }),\r\n };\r\n\r\n const resp2 = await fetch(url2, fetchOpts2);\r\n const data2 = await resp2.json() as any;\r\n\r\n if (data2.signed_url || data2.action === 'fetch_signed_url') {\r\n return fetchSignedUrl(data2);\r\n }\r\n\r\n return data2;\r\n }\r\n\r\n return data1;\r\n}\r\n\r\n// ── Ranklist API ────────────────────────────────────────────────────\r\n\r\nimport type { RanklistResponse } from './types.js';\r\n\r\nexport interface GetRanklistOptions {\r\n /** API server URL (default: https://api.tik.tools) */\r\n serverUrl?: string;\r\n /** API key for authentication */\r\n apiKey: string;\r\n /** TikTok username to look up (auto-resolves room_id and anchor_id) */\r\n uniqueId?: string;\r\n /** Direct room ID (skip resolution) */\r\n roomId?: string;\r\n /** Direct anchor/owner ID (skip resolution) */\r\n anchorId?: string;\r\n /**\r\n * TikTok session cookie string for authentication.\r\n * Required — ranklist endpoints return 20003 without login.\r\n * Example: \"sessionid=abc123; sid_guard=def456\"\r\n */\r\n sessionCookie?: string;\r\n /**\r\n * Which ranklist sub-endpoint to call:\r\n * - \"online_audience\" (default) — top gifters with scores\r\n * - \"anchor_rank_list\" — gifter ranking by rank_type\r\n * - \"entrance\" — entrance UI metadata, tabs, gap-to-rank\r\n */\r\n type?: 'online_audience' | 'anchor_rank_list' | 'entrance';\r\n /**\r\n * For \"anchor_rank_list\" type only:\r\n * - \"1\" = hourly ranking (default)\r\n * - \"8\" = daily ranking\r\n */\r\n rankType?: string;\r\n}\r\n\r\n/**\r\n * Fetch ranked user lists from TikTok via the sign server.\r\n *\r\n * Requires `sessionCookie` for authentication — without it, TikTok\r\n * returns status 20003 (\"Please login first\").\r\n *\r\n * @example\r\n * ```ts\r\n * const data = await getRanklist({\r\n * apiKey: 'your-key',\r\n * uniqueId: 'katarina.live',\r\n * sessionCookie: 'sessionid=abc; sid_guard=def',\r\n * type: 'online_audience',\r\n * });\r\n * console.log(data.data.ranks); // top gifters\r\n * ```\r\n */\r\nexport async function getRanklist(opts: GetRanklistOptions): Promise<RanklistResponse> {\r\n const base = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const ak = encodeURIComponent(opts.apiKey);\r\n\r\n const body: Record<string, any> = {};\r\n if (opts.uniqueId) body.unique_id = opts.uniqueId;\r\n if (opts.roomId) body.room_id = opts.roomId;\r\n if (opts.anchorId) body.anchor_id = opts.anchorId;\r\n if (opts.sessionCookie) body.session_cookie = opts.sessionCookie;\r\n if (opts.type) body.type = opts.type;\r\n if (opts.rankType) body.rank_type = opts.rankType;\r\n\r\n const resp = await fetch(`${base}/webcast/ranklist?apiKey=${ak}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(body),\r\n });\r\n\r\n const data = await resp.json() as any;\r\n\r\n // The server wraps TikTok's response in { status_code, data }\r\n if (data.status_code === 20003) {\r\n throw new Error(\r\n data.message || 'TikTok requires login. Provide sessionCookie with your TikTok session.'\r\n );\r\n }\r\n\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Ranklist failed (status ${data.status_code})`);\r\n }\r\n\r\n return data.data as RanklistResponse;\r\n}\r\n\r\n// ── CAPTCHA Solver API ──────────────────────────────────────────────\r\n\r\nimport type { PuzzleSolveResult, RotateSolveResult, ShapesSolveResult } from './types.js';\r\n\r\n/**\r\n * Solve a puzzle (slider) CAPTCHA using the TikTool solver.\r\n *\r\n * @param apiKey - API key for authentication\r\n * @param puzzleB64 - Base64-encoded background image (PNG/JPEG)\r\n * @param pieceB64 - Base64-encoded puzzle piece image (PNG/JPEG)\r\n * @param serverUrl - Custom server URL (default: https://api.tik.tools)\r\n * @returns Solve result with slide position and confidence\r\n */\r\nexport async function solvePuzzle(\r\n apiKey: string,\r\n puzzleB64: string,\r\n pieceB64: string,\r\n serverUrl?: string,\r\n): Promise<PuzzleSolveResult> {\r\n const base = (serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const resp = await fetch(`${base}/captcha/solve/puzzle?apiKey=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ puzzle: puzzleB64, piece: pieceB64 }),\r\n });\r\n const data = await resp.json() as any;\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Solver failed (status ${data.status_code})`);\r\n }\r\n return data.data as PuzzleSolveResult;\r\n}\r\n\r\n/**\r\n * Solve a rotate (whirl) CAPTCHA using the TikTool solver.\r\n *\r\n * @param apiKey - API key for authentication\r\n * @param outerB64 - Base64-encoded outer ring image (PNG/JPEG)\r\n * @param innerB64 - Base64-encoded inner rotated image (PNG/JPEG)\r\n * @param serverUrl - Custom server URL (default: https://api.tik.tools)\r\n * @returns Solve result with rotation angle and confidence\r\n */\r\nexport async function solveRotate(\r\n apiKey: string,\r\n outerB64: string,\r\n innerB64: string,\r\n serverUrl?: string,\r\n): Promise<RotateSolveResult> {\r\n const base = (serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const resp = await fetch(`${base}/captcha/solve/rotate?apiKey=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ outer: outerB64, inner: innerB64 }),\r\n });\r\n const data = await resp.json() as any;\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Solver failed (status ${data.status_code})`);\r\n }\r\n return data.data as RotateSolveResult;\r\n}\r\n\r\n/**\r\n * Solve a shapes (3D matching) CAPTCHA using the TikTool solver.\r\n *\r\n * @param apiKey - API key for authentication\r\n * @param imageB64 - Base64-encoded CAPTCHA image with shape grid (PNG/JPEG)\r\n * @param serverUrl - Custom server URL (default: https://api.tik.tools)\r\n * @returns Solve result with two matching shape coordinates and confidence\r\n */\r\nexport async function solveShapes(\r\n apiKey: string,\r\n imageB64: string,\r\n serverUrl?: string,\r\n): Promise<ShapesSolveResult> {\r\n const base = (serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const resp = await fetch(`${base}/captcha/solve/shapes?apiKey=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ image: imageB64 }),\r\n });\r\n const data = await resp.json() as any;\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Solver failed (status ${data.status_code})`);\r\n }\r\n return data.data as ShapesSolveResult;\r\n}\r\n\r\n// ── Regional Leaderboard API ────────────────────────────────────────\r\n\r\nexport interface GetRegionalRanklistOptions {\r\n /** API server URL (default: https://api.tik.tools) */\r\n serverUrl?: string;\r\n /** API key for authentication (Pro or Ultra tier required) */\r\n apiKey: string;\r\n /** TikTok username to look up (auto-resolves room_id and anchor_id) */\r\n uniqueId?: string;\r\n /** Direct room ID (skip resolution) */\r\n roomId?: string;\r\n /** Direct anchor/owner ID (skip resolution) */\r\n anchorId?: string;\r\n /**\r\n * Ranking period:\r\n * - \"1\" = Hourly\r\n * - \"8\" = Daily (default)\r\n * - \"15\" = Popular LIVE\r\n * - \"16\" = League\r\n */\r\n rankType?: '1' | '8' | '15' | '16';\r\n /**\r\n * Sub-endpoint type:\r\n * - \"list\" (default) — ranked users with scores\r\n * - \"entrance\" — available ranking tabs/metadata\r\n */\r\n type?: 'list' | 'entrance';\r\n /** Gap interval filter (default: \"0\") */\r\n gapInterval?: string;\r\n}\r\n\r\nexport interface RegionalRanklistSignedResponse {\r\n /** Always 0 on success */\r\n status_code: number;\r\n /** Always \"fetch_signed_url\" */\r\n action: string;\r\n /** The signed TikTok URL to POST */\r\n signed_url: string;\r\n /** HTTP method (always POST) */\r\n method: string;\r\n /** Required headers for the fetch */\r\n headers: Record<string, string>;\r\n /** URL-encoded POST body */\r\n body: string;\r\n /** Cookies to include (ttwid etc.) — append your sessionid */\r\n cookies: string;\r\n /** Human-readable note */\r\n note: string;\r\n}\r\n\r\n/**\r\n * Get a signed URL for fetching regional LIVE leaderboard data.\r\n *\r\n * **Two-step pattern**: TikTok sessions are IP-bound, so instead of\r\n * server-side fetching, this returns a signed URL with headers/body\r\n * that you POST from your own IP with your session cookie.\r\n *\r\n * Requires **Pro** or **Ultra** API key tier.\r\n *\r\n * @example\r\n * ```ts\r\n * // Step 1: Get signed URL\r\n * const signed = await getRegionalRanklist({\r\n * apiKey: 'your-pro-key',\r\n * roomId: '7607695933891218198',\r\n * anchorId: '7444599004337652758',\r\n * rankType: '8', // Daily\r\n * });\r\n *\r\n * // Step 2: Fetch from YOUR IP with YOUR session\r\n * const resp = await fetch(signed.signed_url, {\r\n * method: signed.method,\r\n * headers: { ...signed.headers, Cookie: `sessionid=YOUR_SID; ${signed.cookies}` },\r\n * body: signed.body,\r\n * });\r\n * const { data } = await resp.json();\r\n * data.rank_view.ranks.forEach((r, i) =>\r\n * console.log(`${i+1}. ${r.user.nickname} — ${r.score} pts`)\r\n * );\r\n * ```\r\n */\r\nexport async function getRegionalRanklist(opts: GetRegionalRanklistOptions): Promise<RegionalRanklistSignedResponse> {\r\n const base = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n const ak = encodeURIComponent(opts.apiKey);\r\n\r\n const body: Record<string, any> = {};\r\n if (opts.uniqueId) body.unique_id = opts.uniqueId;\r\n if (opts.roomId) body.room_id = opts.roomId;\r\n if (opts.anchorId) body.anchor_id = opts.anchorId;\r\n if (opts.rankType) body.rank_type = opts.rankType;\r\n if (opts.type) body.type = opts.type;\r\n if (opts.gapInterval) body.gap_interval = opts.gapInterval;\r\n\r\n const resp = await fetch(`${base}/webcast/ranklist/regional?apiKey=${ak}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(body),\r\n });\r\n\r\n const data = await resp.json() as any;\r\n\r\n if (data.status_code !== 0) {\r\n throw new Error(data.error || `Regional ranklist failed (status ${data.status_code})`);\r\n }\r\n\r\n return data as RegionalRanklistSignedResponse;\r\n}\r\n"],"mappings":"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","names":["WebSocket","DEFAULT_SIGN_SERVER","DEFAULT_SIGN_SERVER","DEFAULT_SIGN_SERVER"]}
|