@tiktool/live 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +251 -0
- package/dist/index.d.mts +173 -0
- package/dist/index.d.ts +173 -0
- package/dist/index.js +500 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +463 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +67 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
TikTokLive: () => TikTokLive
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/client.ts
|
|
38
|
+
var import_events = require("events");
|
|
39
|
+
var http = __toESM(require("http"));
|
|
40
|
+
var https = __toESM(require("https"));
|
|
41
|
+
var zlib = __toESM(require("zlib"));
|
|
42
|
+
var import_ws = __toESM(require("ws"));
|
|
43
|
+
|
|
44
|
+
// src/proto.ts
|
|
45
|
+
function decodeVarint(buf, offset) {
|
|
46
|
+
let result = 0, shift = 0;
|
|
47
|
+
while (offset < buf.length) {
|
|
48
|
+
const byte = buf[offset++];
|
|
49
|
+
result |= (byte & 127) << shift;
|
|
50
|
+
if ((byte & 128) === 0) break;
|
|
51
|
+
shift += 7;
|
|
52
|
+
}
|
|
53
|
+
return { value: result >>> 0, offset };
|
|
54
|
+
}
|
|
55
|
+
function decodeVarint64(buf, offset) {
|
|
56
|
+
let result = 0n, shift = 0n;
|
|
57
|
+
while (offset < buf.length) {
|
|
58
|
+
const byte = BigInt(buf[offset++]);
|
|
59
|
+
result |= (byte & 0x7Fn) << shift;
|
|
60
|
+
if ((byte & 0x80n) === 0n) break;
|
|
61
|
+
shift += 7n;
|
|
62
|
+
}
|
|
63
|
+
return { value: result, offset };
|
|
64
|
+
}
|
|
65
|
+
function encodeVarint(v) {
|
|
66
|
+
const bytes = [];
|
|
67
|
+
let n = typeof v === "bigint" ? v : BigInt(v);
|
|
68
|
+
do {
|
|
69
|
+
let b = Number(n & 0x7Fn);
|
|
70
|
+
n >>= 7n;
|
|
71
|
+
if (n > 0n) b |= 128;
|
|
72
|
+
bytes.push(b);
|
|
73
|
+
} while (n > 0n);
|
|
74
|
+
return Buffer.from(bytes);
|
|
75
|
+
}
|
|
76
|
+
function encodeField(fn, wt, value) {
|
|
77
|
+
const tag = encodeVarint(fn << 3 | wt);
|
|
78
|
+
if (wt === 0) {
|
|
79
|
+
return Buffer.concat([tag, encodeVarint(typeof value === "number" ? BigInt(value) : value)]);
|
|
80
|
+
}
|
|
81
|
+
const data = typeof value === "string" ? Buffer.from(value) : value;
|
|
82
|
+
return Buffer.concat([tag, encodeVarint(data.length), data]);
|
|
83
|
+
}
|
|
84
|
+
function decodeProto(buf) {
|
|
85
|
+
const fields = [];
|
|
86
|
+
let offset = 0;
|
|
87
|
+
while (offset < buf.length) {
|
|
88
|
+
const tagResult = decodeVarint(buf, offset);
|
|
89
|
+
offset = tagResult.offset;
|
|
90
|
+
const fn = tagResult.value >> 3;
|
|
91
|
+
const wt = tagResult.value & 7;
|
|
92
|
+
if (wt === 0) {
|
|
93
|
+
const r = decodeVarint64(buf, offset);
|
|
94
|
+
offset = r.offset;
|
|
95
|
+
fields.push({ fn, wt, value: r.value });
|
|
96
|
+
} else if (wt === 2) {
|
|
97
|
+
const lenR = decodeVarint(buf, offset);
|
|
98
|
+
offset = lenR.offset;
|
|
99
|
+
const data = buf.subarray(offset, offset + lenR.value);
|
|
100
|
+
offset += lenR.value;
|
|
101
|
+
fields.push({ fn, wt, value: data });
|
|
102
|
+
} else if (wt === 1) {
|
|
103
|
+
fields.push({ fn, wt, value: buf.readBigInt64LE(offset) });
|
|
104
|
+
offset += 8;
|
|
105
|
+
} else if (wt === 5) {
|
|
106
|
+
fields.push({ fn, wt, value: BigInt(buf.readInt32LE(offset)) });
|
|
107
|
+
offset += 4;
|
|
108
|
+
} else {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return fields;
|
|
113
|
+
}
|
|
114
|
+
function getStr(fields, fn) {
|
|
115
|
+
const f = fields.find((x) => x.fn === fn && x.wt === 2);
|
|
116
|
+
return f ? f.value.toString("utf-8") : "";
|
|
117
|
+
}
|
|
118
|
+
function getBytes(fields, fn) {
|
|
119
|
+
const f = fields.find((x) => x.fn === fn && x.wt === 2);
|
|
120
|
+
return f ? f.value : null;
|
|
121
|
+
}
|
|
122
|
+
function getInt(fields, fn) {
|
|
123
|
+
const f = fields.find((x) => x.fn === fn && x.wt === 0);
|
|
124
|
+
return f ? Number(f.value) : 0;
|
|
125
|
+
}
|
|
126
|
+
function buildHeartbeat(roomId) {
|
|
127
|
+
const payload = encodeField(1, 0, BigInt(roomId));
|
|
128
|
+
return Buffer.concat([
|
|
129
|
+
encodeField(6, 2, "pb"),
|
|
130
|
+
encodeField(7, 2, "hb"),
|
|
131
|
+
encodeField(8, 2, payload)
|
|
132
|
+
]);
|
|
133
|
+
}
|
|
134
|
+
function buildImEnterRoom(roomId) {
|
|
135
|
+
const inner = Buffer.concat([
|
|
136
|
+
encodeField(1, 0, BigInt(roomId)),
|
|
137
|
+
encodeField(4, 0, 12n),
|
|
138
|
+
encodeField(5, 2, "audience"),
|
|
139
|
+
encodeField(6, 2, ""),
|
|
140
|
+
encodeField(9, 2, ""),
|
|
141
|
+
encodeField(10, 2, "")
|
|
142
|
+
]);
|
|
143
|
+
return Buffer.concat([
|
|
144
|
+
encodeField(6, 2, "pb"),
|
|
145
|
+
encodeField(7, 2, "im_enter_room"),
|
|
146
|
+
encodeField(8, 2, inner)
|
|
147
|
+
]);
|
|
148
|
+
}
|
|
149
|
+
function buildAck(id) {
|
|
150
|
+
return Buffer.concat([
|
|
151
|
+
encodeField(2, 0, id),
|
|
152
|
+
encodeField(6, 2, "pb"),
|
|
153
|
+
encodeField(7, 2, "ack")
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
function parseUser(data) {
|
|
157
|
+
const f = decodeProto(data);
|
|
158
|
+
const id = String(getInt(f, 1) || getStr(f, 1));
|
|
159
|
+
const nickname = getStr(f, 3) || getStr(f, 5);
|
|
160
|
+
const uniqueId = getStr(f, 38) || getStr(f, 4) || getStr(f, 2);
|
|
161
|
+
return { id, nickname, uniqueId: uniqueId || nickname || id };
|
|
162
|
+
}
|
|
163
|
+
function parseWebcastMessage(method, payload) {
|
|
164
|
+
const f = decodeProto(payload);
|
|
165
|
+
const userBuf = getBytes(f, 2);
|
|
166
|
+
const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
|
|
167
|
+
const base = { timestamp: Date.now(), msgId: String(getInt(f, 1) || "") };
|
|
168
|
+
switch (method) {
|
|
169
|
+
case "WebcastChatMessage":
|
|
170
|
+
return { ...base, type: "chat", user, comment: getStr(f, 3) };
|
|
171
|
+
case "WebcastMemberMessage":
|
|
172
|
+
return { ...base, type: "member", user, action: getInt(f, 3) };
|
|
173
|
+
case "WebcastLikeMessage":
|
|
174
|
+
return { ...base, type: "like", user, likeCount: getInt(f, 5), totalLikes: getInt(f, 6) || getInt(f, 7) };
|
|
175
|
+
case "WebcastGiftMessage": {
|
|
176
|
+
const giftBuf = getBytes(f, 3);
|
|
177
|
+
let giftName = "", giftId = 0, diamondCount = 0;
|
|
178
|
+
if (giftBuf) {
|
|
179
|
+
const gf = decodeProto(giftBuf);
|
|
180
|
+
giftId = getInt(gf, 1);
|
|
181
|
+
giftName = getStr(gf, 2);
|
|
182
|
+
diamondCount = getInt(gf, 5);
|
|
183
|
+
}
|
|
184
|
+
const repeatCount = getInt(f, 5);
|
|
185
|
+
const repeatEnd = getInt(f, 9) === 1;
|
|
186
|
+
return { ...base, type: "gift", user, giftId, giftName, diamondCount, repeatCount, repeatEnd, combo: repeatCount > 1 && !repeatEnd };
|
|
187
|
+
}
|
|
188
|
+
case "WebcastSocialMessage": {
|
|
189
|
+
const action = getInt(f, 3);
|
|
190
|
+
const actionMap = { 1: "follow", 2: "share", 3: "like" };
|
|
191
|
+
return { ...base, type: "social", user, action: actionMap[action] || `action_${action}` };
|
|
192
|
+
}
|
|
193
|
+
case "WebcastRoomUserSeqMessage":
|
|
194
|
+
return { ...base, type: "roomUserSeq", viewerCount: getInt(f, 3), totalViewers: getInt(f, 4) };
|
|
195
|
+
case "WebcastLinkMicBattle":
|
|
196
|
+
return { ...base, type: "battle", status: getInt(f, 3) };
|
|
197
|
+
case "WebcastLinkMicArmies":
|
|
198
|
+
return { ...base, type: "battleArmies" };
|
|
199
|
+
case "WebcastSubNotifyMessage":
|
|
200
|
+
return { ...base, type: "subscribe", user, subMonth: getInt(f, 8) };
|
|
201
|
+
case "WebcastEmoteChatMessage":
|
|
202
|
+
return { ...base, type: "emoteChat", user, emoteId: getStr(f, 3) };
|
|
203
|
+
case "WebcastEnvelopeMessage":
|
|
204
|
+
return { ...base, type: "envelope", diamondCount: getInt(f, 3) };
|
|
205
|
+
case "WebcastQuestionNewMessage":
|
|
206
|
+
return { ...base, type: "question", user, questionText: getStr(f, 3) };
|
|
207
|
+
case "WebcastRankUpdateMessage":
|
|
208
|
+
case "WebcastHourlyRankMessage":
|
|
209
|
+
return { ...base, type: "rankUpdate", rankType: getStr(f, 3) };
|
|
210
|
+
case "WebcastControlMessage":
|
|
211
|
+
return { ...base, type: "control", action: getInt(f, 2) };
|
|
212
|
+
case "WebcastRoomMessage":
|
|
213
|
+
case "RoomMessage":
|
|
214
|
+
return { ...base, type: "room", status: getStr(f, 3) };
|
|
215
|
+
case "WebcastLiveIntroMessage":
|
|
216
|
+
return { ...base, type: "liveIntro", title: getStr(f, 3) };
|
|
217
|
+
case "WebcastLinkMicMethod":
|
|
218
|
+
case "WebcastLinkmicBattleTaskMessage":
|
|
219
|
+
return { ...base, type: "linkMic", action: getInt(f, 3) };
|
|
220
|
+
default:
|
|
221
|
+
return { ...base, type: "unknown", method };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function parseWebcastResponse(payload) {
|
|
225
|
+
const events = [];
|
|
226
|
+
const respFields = decodeProto(payload);
|
|
227
|
+
for (const mf of respFields.filter((f) => f.fn === 1 && f.wt === 2)) {
|
|
228
|
+
const msgBuf = mf.value;
|
|
229
|
+
const msgFields = decodeProto(msgBuf);
|
|
230
|
+
const method = getStr(msgFields, 1);
|
|
231
|
+
const innerPayload = getBytes(msgFields, 2);
|
|
232
|
+
if (!method || !innerPayload) continue;
|
|
233
|
+
try {
|
|
234
|
+
events.push(parseWebcastMessage(method, innerPayload));
|
|
235
|
+
} catch {
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return events;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/client.ts
|
|
242
|
+
var 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";
|
|
243
|
+
var DEFAULT_SIGN_SERVER = "https://api.tik.tools";
|
|
244
|
+
function httpGet(url, headers) {
|
|
245
|
+
return new Promise((resolve, reject) => {
|
|
246
|
+
const mod = url.startsWith("https") ? https : http;
|
|
247
|
+
const req = mod.get(url, { headers }, (res) => {
|
|
248
|
+
const chunks = [];
|
|
249
|
+
const enc = res.headers["content-encoding"];
|
|
250
|
+
const stream = enc === "gzip" || enc === "br" ? res.pipe(enc === "br" ? zlib.createBrotliDecompress() : zlib.createGunzip()) : res;
|
|
251
|
+
stream.on("data", (c) => chunks.push(c));
|
|
252
|
+
stream.on("end", () => resolve({
|
|
253
|
+
status: res.statusCode || 0,
|
|
254
|
+
headers: res.headers,
|
|
255
|
+
body: Buffer.concat(chunks)
|
|
256
|
+
}));
|
|
257
|
+
stream.on("error", reject);
|
|
258
|
+
});
|
|
259
|
+
req.on("error", reject);
|
|
260
|
+
req.setTimeout(15e3, () => {
|
|
261
|
+
req.destroy();
|
|
262
|
+
reject(new Error("Request timeout"));
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
function getWsHost(clusterRegion) {
|
|
267
|
+
if (!clusterRegion) return "webcast-ws.tiktok.com";
|
|
268
|
+
const r = clusterRegion.toLowerCase();
|
|
269
|
+
if (r.startsWith("eu") || r.includes("eu")) return "webcast-ws.eu.tiktok.com";
|
|
270
|
+
if (r.startsWith("us") || r.includes("us")) return "webcast-ws.us.tiktok.com";
|
|
271
|
+
return "webcast-ws.tiktok.com";
|
|
272
|
+
}
|
|
273
|
+
var TikTokLive = class extends import_events.EventEmitter {
|
|
274
|
+
ws = null;
|
|
275
|
+
heartbeatTimer = null;
|
|
276
|
+
reconnectAttempts = 0;
|
|
277
|
+
intentionalClose = false;
|
|
278
|
+
_connected = false;
|
|
279
|
+
_eventCount = 0;
|
|
280
|
+
_roomId = "";
|
|
281
|
+
uniqueId;
|
|
282
|
+
signServerUrl;
|
|
283
|
+
apiKey;
|
|
284
|
+
autoReconnect;
|
|
285
|
+
maxReconnectAttempts;
|
|
286
|
+
heartbeatInterval;
|
|
287
|
+
debug;
|
|
288
|
+
constructor(options) {
|
|
289
|
+
super();
|
|
290
|
+
this.uniqueId = options.uniqueId.replace(/^@/, "");
|
|
291
|
+
this.signServerUrl = (options.signServerUrl || DEFAULT_SIGN_SERVER).replace(/\/$/, "");
|
|
292
|
+
this.apiKey = options.apiKey;
|
|
293
|
+
this.autoReconnect = options.autoReconnect ?? true;
|
|
294
|
+
this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
|
|
295
|
+
this.heartbeatInterval = options.heartbeatInterval ?? 1e4;
|
|
296
|
+
this.debug = options.debug ?? false;
|
|
297
|
+
}
|
|
298
|
+
async connect() {
|
|
299
|
+
this.intentionalClose = false;
|
|
300
|
+
const resp = await httpGet(`https://www.tiktok.com/@${this.uniqueId}/live`, {
|
|
301
|
+
"User-Agent": DEFAULT_UA,
|
|
302
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
303
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
304
|
+
"Accept-Language": "en-US,en;q=0.9"
|
|
305
|
+
});
|
|
306
|
+
let ttwid = "";
|
|
307
|
+
for (const sc of [resp.headers["set-cookie"] || []].flat()) {
|
|
308
|
+
if (typeof sc === "string" && sc.startsWith("ttwid=")) {
|
|
309
|
+
ttwid = sc.split(";")[0].split("=").slice(1).join("=");
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (!ttwid) throw new Error("Failed to obtain session cookie");
|
|
314
|
+
const html = resp.body.toString();
|
|
315
|
+
let roomId = "";
|
|
316
|
+
const sigiMatch = html.match(/id="SIGI_STATE"[^>]*>([^<]+)/);
|
|
317
|
+
if (sigiMatch) {
|
|
318
|
+
try {
|
|
319
|
+
const json = JSON.parse(sigiMatch[1]);
|
|
320
|
+
const jsonStr = JSON.stringify(json);
|
|
321
|
+
const m = jsonStr.match(/"roomId"\s*:\s*"(\d+)"/);
|
|
322
|
+
if (m) roomId = m[1];
|
|
323
|
+
} catch {
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (!roomId) throw new Error(`User @${this.uniqueId} is not currently live`);
|
|
327
|
+
this._roomId = roomId;
|
|
328
|
+
const crMatch = html.match(/"clusterRegion"\s*:\s*"([^"]+)"/);
|
|
329
|
+
const clusterRegion = crMatch ? crMatch[1] : "";
|
|
330
|
+
const wsHost = getWsHost(clusterRegion);
|
|
331
|
+
const wsParams = new URLSearchParams({
|
|
332
|
+
version_code: "270000",
|
|
333
|
+
device_platform: "web",
|
|
334
|
+
cookie_enabled: "true",
|
|
335
|
+
screen_width: "1920",
|
|
336
|
+
screen_height: "1080",
|
|
337
|
+
browser_language: "en-US",
|
|
338
|
+
browser_platform: "Win32",
|
|
339
|
+
browser_name: "Mozilla",
|
|
340
|
+
browser_version: DEFAULT_UA.split("Mozilla/")[1] || "5.0",
|
|
341
|
+
browser_online: "true",
|
|
342
|
+
tz_name: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
343
|
+
app_name: "tiktok_web",
|
|
344
|
+
sup_ws_ds_opt: "1",
|
|
345
|
+
update_version_code: "2.0.0",
|
|
346
|
+
compress: "gzip",
|
|
347
|
+
webcast_language: "en",
|
|
348
|
+
ws_direct: "1",
|
|
349
|
+
aid: "1988",
|
|
350
|
+
live_id: "12",
|
|
351
|
+
app_language: "en",
|
|
352
|
+
client_enter: "1",
|
|
353
|
+
room_id: roomId,
|
|
354
|
+
identity: "audience",
|
|
355
|
+
history_comment_count: "6",
|
|
356
|
+
last_rtt: "0",
|
|
357
|
+
heartbeat_duration: "10000",
|
|
358
|
+
resp_content_type: "protobuf",
|
|
359
|
+
did_rule: "3"
|
|
360
|
+
});
|
|
361
|
+
const rawWsUrl = `https://${wsHost}/webcast/im/ws_proxy/ws_reuse_supplement/?${wsParams}`;
|
|
362
|
+
const signUrl = this.apiKey ? `${this.signServerUrl}/webcast/sign_url?apiKey=${this.apiKey}` : `${this.signServerUrl}/webcast/sign_url`;
|
|
363
|
+
let wsUrl;
|
|
364
|
+
try {
|
|
365
|
+
const signResp = await fetch(signUrl, {
|
|
366
|
+
method: "POST",
|
|
367
|
+
headers: { "Content-Type": "application/json" },
|
|
368
|
+
body: JSON.stringify({ url: rawWsUrl })
|
|
369
|
+
});
|
|
370
|
+
const signData = await signResp.json();
|
|
371
|
+
if (signData.status_code === 0 && signData.data?.signed_url) {
|
|
372
|
+
wsUrl = signData.data.signed_url.replace(/^https:\/\//, "wss://");
|
|
373
|
+
} else {
|
|
374
|
+
wsUrl = rawWsUrl.replace(/^https:\/\//, "wss://");
|
|
375
|
+
}
|
|
376
|
+
} catch {
|
|
377
|
+
wsUrl = rawWsUrl.replace(/^https:\/\//, "wss://");
|
|
378
|
+
}
|
|
379
|
+
return new Promise((resolve, reject) => {
|
|
380
|
+
this.ws = new import_ws.default(wsUrl, {
|
|
381
|
+
headers: {
|
|
382
|
+
"User-Agent": DEFAULT_UA,
|
|
383
|
+
"Cookie": `ttwid=${ttwid}`,
|
|
384
|
+
"Origin": "https://www.tiktok.com"
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
this.ws.on("open", () => {
|
|
388
|
+
this._connected = true;
|
|
389
|
+
this.reconnectAttempts = 0;
|
|
390
|
+
this.ws.send(buildHeartbeat(roomId));
|
|
391
|
+
this.ws.send(buildImEnterRoom(roomId));
|
|
392
|
+
this.startHeartbeat(roomId);
|
|
393
|
+
const roomInfo = {
|
|
394
|
+
roomId,
|
|
395
|
+
wsHost,
|
|
396
|
+
clusterRegion,
|
|
397
|
+
connectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
398
|
+
};
|
|
399
|
+
this.emit("connected");
|
|
400
|
+
this.emit("roomInfo", roomInfo);
|
|
401
|
+
resolve();
|
|
402
|
+
});
|
|
403
|
+
this.ws.on("message", (rawData) => {
|
|
404
|
+
this.handleFrame(Buffer.from(rawData));
|
|
405
|
+
});
|
|
406
|
+
this.ws.on("close", (code, reason) => {
|
|
407
|
+
this._connected = false;
|
|
408
|
+
this.stopHeartbeat();
|
|
409
|
+
const reasonStr = reason?.toString() || "";
|
|
410
|
+
this.emit("disconnected", code, reasonStr);
|
|
411
|
+
if (!this.intentionalClose && this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
412
|
+
this.reconnectAttempts++;
|
|
413
|
+
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts - 1), 3e4);
|
|
414
|
+
setTimeout(() => this.connect().catch((e) => this.emit("error", e)), delay);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
this.ws.on("error", (err) => {
|
|
418
|
+
this.emit("error", err);
|
|
419
|
+
if (!this._connected) reject(err);
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
disconnect() {
|
|
424
|
+
this.intentionalClose = true;
|
|
425
|
+
this.stopHeartbeat();
|
|
426
|
+
if (this.ws) {
|
|
427
|
+
this.ws.close(1e3);
|
|
428
|
+
this.ws = null;
|
|
429
|
+
}
|
|
430
|
+
this._connected = false;
|
|
431
|
+
}
|
|
432
|
+
get connected() {
|
|
433
|
+
return this._connected;
|
|
434
|
+
}
|
|
435
|
+
get eventCount() {
|
|
436
|
+
return this._eventCount;
|
|
437
|
+
}
|
|
438
|
+
get roomId() {
|
|
439
|
+
return this._roomId;
|
|
440
|
+
}
|
|
441
|
+
on(event, listener) {
|
|
442
|
+
return super.on(event, listener);
|
|
443
|
+
}
|
|
444
|
+
once(event, listener) {
|
|
445
|
+
return super.once(event, listener);
|
|
446
|
+
}
|
|
447
|
+
off(event, listener) {
|
|
448
|
+
return super.off(event, listener);
|
|
449
|
+
}
|
|
450
|
+
emit(event, ...args) {
|
|
451
|
+
return super.emit(event, ...args);
|
|
452
|
+
}
|
|
453
|
+
handleFrame(buf) {
|
|
454
|
+
try {
|
|
455
|
+
const fields = decodeProto(buf);
|
|
456
|
+
const idField = fields.find((f) => f.fn === 2 && f.wt === 0);
|
|
457
|
+
const id = idField ? idField.value : 0n;
|
|
458
|
+
const type = getStr(fields, 7);
|
|
459
|
+
const binary = getBytes(fields, 8);
|
|
460
|
+
if (id > 0n && this.ws?.readyState === import_ws.default.OPEN) {
|
|
461
|
+
this.ws.send(buildAck(id));
|
|
462
|
+
}
|
|
463
|
+
if (type === "msg" && binary && binary.length > 0) {
|
|
464
|
+
let inner = binary;
|
|
465
|
+
if (inner.length > 2 && inner[0] === 31 && inner[1] === 139) {
|
|
466
|
+
try {
|
|
467
|
+
inner = zlib.gunzipSync(inner);
|
|
468
|
+
} catch {
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const events = parseWebcastResponse(inner);
|
|
472
|
+
for (const evt of events) {
|
|
473
|
+
this._eventCount++;
|
|
474
|
+
this.emit("event", evt);
|
|
475
|
+
this.emit(evt.type, evt);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
} catch {
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
startHeartbeat(roomId) {
|
|
482
|
+
this.stopHeartbeat();
|
|
483
|
+
this.heartbeatTimer = setInterval(() => {
|
|
484
|
+
if (this.ws?.readyState === import_ws.default.OPEN) {
|
|
485
|
+
this.ws.send(buildHeartbeat(roomId));
|
|
486
|
+
}
|
|
487
|
+
}, this.heartbeatInterval);
|
|
488
|
+
}
|
|
489
|
+
stopHeartbeat() {
|
|
490
|
+
if (this.heartbeatTimer) {
|
|
491
|
+
clearInterval(this.heartbeatTimer);
|
|
492
|
+
this.heartbeatTimer = null;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
497
|
+
0 && (module.exports = {
|
|
498
|
+
TikTokLive
|
|
499
|
+
});
|
|
500
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/proto.ts"],"sourcesContent":["export { TikTokLive } from './client.js';\r\n\r\nexport type {\r\n TikTokLiveOptions,\r\n TikTokLiveEvents,\r\n RoomInfo,\r\n LiveEvent,\r\n BaseEvent,\r\n ChatEvent,\r\n MemberEvent,\r\n LikeEvent,\r\n GiftEvent,\r\n SocialEvent,\r\n RoomUserSeqEvent,\r\n BattleEvent,\r\n BattleArmiesEvent,\r\n SubscribeEvent,\r\n EmoteChatEvent,\r\n EnvelopeEvent,\r\n QuestionEvent,\r\n ControlEvent,\r\n RoomEvent,\r\n LiveIntroEvent,\r\n RankUpdateEvent,\r\n LinkMicEvent,\r\n UnknownEvent,\r\n TikTokUser,\r\n} from './types.js';\r\n","import { EventEmitter } from 'events';\r\nimport * as http from 'http';\r\nimport * as https from 'https';\r\nimport * as zlib from 'zlib';\r\nimport WebSocket from 'ws';\r\nimport type { TikTokLiveOptions, TikTokLiveEvents, RoomInfo, LiveEvent } from './types.js';\r\nimport {\r\n decodeProto,\r\n getStr,\r\n getBytes,\r\n buildHeartbeat,\r\n buildImEnterRoom,\r\n buildAck,\r\n parseWebcastResponse,\r\n} from './proto.js';\r\n\r\nconst DEFAULT_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';\r\nconst DEFAULT_SIGN_SERVER = 'https://api.tik.tools';\r\n\r\nfunction httpGet(url: string, headers: Record<string, string>): Promise<{\r\n status: number;\r\n headers: http.IncomingHttpHeaders;\r\n body: Buffer;\r\n}> {\r\n return new Promise((resolve, reject) => {\r\n const mod = url.startsWith('https') ? https : http;\r\n const req = mod.get(url, { headers }, (res) => {\r\n const chunks: Buffer[] = [];\r\n const enc = res.headers['content-encoding'];\r\n const stream = (enc === 'gzip' || enc === 'br')\r\n ? res.pipe(enc === 'br' ? zlib.createBrotliDecompress() : zlib.createGunzip())\r\n : res;\r\n stream.on('data', (c: Buffer) => chunks.push(c));\r\n stream.on('end', () => resolve({\r\n status: res.statusCode || 0,\r\n headers: res.headers,\r\n body: Buffer.concat(chunks),\r\n }));\r\n stream.on('error', reject);\r\n });\r\n req.on('error', reject);\r\n req.setTimeout(15_000, () => { req.destroy(); reject(new Error('Request timeout')); });\r\n });\r\n}\r\n\r\nfunction getWsHost(clusterRegion: string): string {\r\n if (!clusterRegion) return 'webcast-ws.tiktok.com';\r\n const r = clusterRegion.toLowerCase();\r\n if (r.startsWith('eu') || r.includes('eu')) return 'webcast-ws.eu.tiktok.com';\r\n if (r.startsWith('us') || r.includes('us')) return 'webcast-ws.us.tiktok.com';\r\n return 'webcast-ws.tiktok.com';\r\n}\r\n\r\nexport class TikTokLive extends EventEmitter {\r\n private ws: WebSocket | null = null;\r\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\r\n private reconnectAttempts = 0;\r\n private intentionalClose = false;\r\n private _connected = false;\r\n private _eventCount = 0;\r\n private _roomId = '';\r\n\r\n private readonly uniqueId: string;\r\n private readonly signServerUrl: string;\r\n private readonly apiKey?: string;\r\n private readonly autoReconnect: boolean;\r\n private readonly maxReconnectAttempts: number;\r\n private readonly heartbeatInterval: number;\r\n private readonly debug: boolean;\r\n\r\n constructor(options: TikTokLiveOptions) {\r\n super();\r\n this.uniqueId = options.uniqueId.replace(/^@/, '');\r\n this.signServerUrl = (options.signServerUrl || DEFAULT_SIGN_SERVER).replace(/\\/$/, '');\r\n this.apiKey = options.apiKey;\r\n this.autoReconnect = options.autoReconnect ?? true;\r\n this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;\r\n this.heartbeatInterval = options.heartbeatInterval ?? 10_000;\r\n this.debug = options.debug ?? false;\r\n }\r\n\r\n async connect(): Promise<void> {\r\n this.intentionalClose = false;\r\n\r\n const resp = await httpGet(`https://www.tiktok.com/@${this.uniqueId}/live`, {\r\n 'User-Agent': DEFAULT_UA,\r\n 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\r\n 'Accept-Encoding': 'gzip, deflate, br',\r\n 'Accept-Language': 'en-US,en;q=0.9',\r\n });\r\n\r\n let ttwid = '';\r\n for (const sc of [resp.headers['set-cookie'] || []].flat()) {\r\n if (typeof sc === 'string' && sc.startsWith('ttwid=')) {\r\n ttwid = sc.split(';')[0].split('=').slice(1).join('=');\r\n break;\r\n }\r\n }\r\n if (!ttwid) throw new Error('Failed to obtain session cookie');\r\n\r\n const html = resp.body.toString();\r\n let roomId = '';\r\n const sigiMatch = html.match(/id=\"SIGI_STATE\"[^>]*>([^<]+)/);\r\n if (sigiMatch) {\r\n try {\r\n const json = JSON.parse(sigiMatch[1]);\r\n const jsonStr = JSON.stringify(json);\r\n const m = jsonStr.match(/\"roomId\"\\s*:\\s*\"(\\d+)\"/);\r\n if (m) roomId = m[1];\r\n } catch { }\r\n }\r\n if (!roomId) throw new Error(`User @${this.uniqueId} is not currently live`);\r\n this._roomId = roomId;\r\n\r\n const crMatch = html.match(/\"clusterRegion\"\\s*:\\s*\"([^\"]+)\"/);\r\n const clusterRegion = crMatch ? crMatch[1] : '';\r\n const wsHost = getWsHost(clusterRegion);\r\n\r\n const wsParams = new URLSearchParams({\r\n version_code: '270000', device_platform: 'web', cookie_enabled: 'true',\r\n screen_width: '1920', screen_height: '1080', browser_language: 'en-US',\r\n browser_platform: 'Win32', browser_name: 'Mozilla',\r\n browser_version: DEFAULT_UA.split('Mozilla/')[1] || '5.0',\r\n browser_online: 'true', tz_name: Intl.DateTimeFormat().resolvedOptions().timeZone,\r\n app_name: 'tiktok_web', sup_ws_ds_opt: '1', update_version_code: '2.0.0',\r\n compress: 'gzip', webcast_language: 'en', ws_direct: '1', aid: '1988',\r\n live_id: '12', app_language: 'en', client_enter: '1', room_id: roomId,\r\n identity: 'audience', history_comment_count: '6', last_rtt: '0',\r\n heartbeat_duration: '10000', resp_content_type: 'protobuf', did_rule: '3',\r\n });\r\n\r\n const rawWsUrl = `https://${wsHost}/webcast/im/ws_proxy/ws_reuse_supplement/?${wsParams}`;\r\n\r\n const signUrl = this.apiKey\r\n ? `${this.signServerUrl}/webcast/sign_url?apiKey=${this.apiKey}`\r\n : `${this.signServerUrl}/webcast/sign_url`;\r\n\r\n let wsUrl: string;\r\n try {\r\n const signResp = await fetch(signUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ url: rawWsUrl }),\r\n });\r\n const signData = await signResp.json() as Record<string, any>;\r\n\r\n if (signData.status_code === 0 && signData.data?.signed_url) {\r\n wsUrl = (signData.data.signed_url as string).replace(/^https:\\/\\//, 'wss://');\r\n } else {\r\n wsUrl = rawWsUrl.replace(/^https:\\/\\//, 'wss://');\r\n }\r\n } catch {\r\n wsUrl = rawWsUrl.replace(/^https:\\/\\//, 'wss://');\r\n }\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n this.ws = new WebSocket(wsUrl, {\r\n headers: {\r\n 'User-Agent': DEFAULT_UA,\r\n 'Cookie': `ttwid=${ttwid}`,\r\n 'Origin': 'https://www.tiktok.com',\r\n },\r\n });\r\n\r\n this.ws.on('open', () => {\r\n this._connected = true;\r\n this.reconnectAttempts = 0;\r\n\r\n this.ws!.send(buildHeartbeat(roomId));\r\n this.ws!.send(buildImEnterRoom(roomId));\r\n this.startHeartbeat(roomId);\r\n\r\n const roomInfo: RoomInfo = {\r\n roomId,\r\n wsHost,\r\n clusterRegion,\r\n connectedAt: new Date().toISOString(),\r\n };\r\n\r\n this.emit('connected');\r\n this.emit('roomInfo', roomInfo);\r\n resolve();\r\n });\r\n\r\n this.ws.on('message', (rawData: Buffer) => {\r\n this.handleFrame(Buffer.from(rawData));\r\n });\r\n\r\n this.ws.on('close', (code, reason) => {\r\n this._connected = false;\r\n this.stopHeartbeat();\r\n\r\n const reasonStr = reason?.toString() || '';\r\n this.emit('disconnected', code, reasonStr);\r\n\r\n if (!this.intentionalClose && this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {\r\n this.reconnectAttempts++;\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts - 1), 30_000);\r\n setTimeout(() => this.connect().catch(e => this.emit('error', e)), delay);\r\n }\r\n });\r\n\r\n this.ws.on('error', (err) => {\r\n this.emit('error', err);\r\n if (!this._connected) reject(err);\r\n });\r\n });\r\n }\r\n\r\n disconnect(): void {\r\n this.intentionalClose = true;\r\n this.stopHeartbeat();\r\n if (this.ws) {\r\n this.ws.close(1000);\r\n this.ws = null;\r\n }\r\n this._connected = false;\r\n }\r\n\r\n get connected(): boolean {\r\n return this._connected;\r\n }\r\n\r\n get eventCount(): number {\r\n return this._eventCount;\r\n }\r\n\r\n get roomId(): string {\r\n return this._roomId;\r\n }\r\n\r\n on<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.on(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n once<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.once(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n off<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this {\r\n return super.off(event, listener as (...args: any[]) => void);\r\n }\r\n\r\n emit<K extends keyof TikTokLiveEvents>(event: K, ...args: Parameters<TikTokLiveEvents[K]>): boolean {\r\n return super.emit(event, ...args);\r\n }\r\n\r\n private handleFrame(buf: Buffer): void {\r\n try {\r\n const fields = decodeProto(buf);\r\n const idField = fields.find(f => f.fn === 2 && f.wt === 0);\r\n const id = idField ? idField.value as bigint : 0n;\r\n const type = getStr(fields, 7);\r\n const binary = getBytes(fields, 8);\r\n\r\n if (id > 0n && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(buildAck(id));\r\n }\r\n\r\n if (type === 'msg' && binary && binary.length > 0) {\r\n let inner = binary;\r\n if (inner.length > 2 && inner[0] === 0x1f && inner[1] === 0x8b) {\r\n try { inner = zlib.gunzipSync(inner); } catch { }\r\n }\r\n\r\n const events = parseWebcastResponse(inner);\r\n for (const evt of events) {\r\n this._eventCount++;\r\n this.emit('event', evt);\r\n this.emit(evt.type as keyof TikTokLiveEvents, evt as any);\r\n }\r\n }\r\n } catch { }\r\n }\r\n\r\n private startHeartbeat(roomId: string): void {\r\n this.stopHeartbeat();\r\n this.heartbeatTimer = setInterval(() => {\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(buildHeartbeat(roomId));\r\n }\r\n }, this.heartbeatInterval);\r\n }\r\n\r\n private stopHeartbeat(): void {\r\n if (this.heartbeatTimer) {\r\n clearInterval(this.heartbeatTimer);\r\n this.heartbeatTimer = null;\r\n }\r\n }\r\n}\r\n","import type { LiveEvent, TikTokUser } from './types.js';\r\n\r\nexport function decodeVarint(buf: Buffer, offset: number): { value: number; offset: number } {\r\n let result = 0, shift = 0;\r\n while (offset < buf.length) {\r\n const byte = buf[offset++];\r\n result |= (byte & 0x7F) << shift;\r\n if ((byte & 0x80) === 0) break;\r\n shift += 7;\r\n }\r\n return { value: result >>> 0, offset };\r\n}\r\n\r\nexport function decodeVarint64(buf: Buffer, offset: number): { value: bigint; offset: number } {\r\n let result = 0n, shift = 0n;\r\n while (offset < buf.length) {\r\n const byte = BigInt(buf[offset++]);\r\n result |= (byte & 0x7Fn) << shift;\r\n if ((byte & 0x80n) === 0n) break;\r\n shift += 7n;\r\n }\r\n return { value: result, offset };\r\n}\r\n\r\nexport function encodeVarint(v: number | bigint): Buffer {\r\n const bytes: number[] = [];\r\n let n = typeof v === 'bigint' ? v : BigInt(v);\r\n do {\r\n let b = Number(n & 0x7Fn);\r\n n >>= 7n;\r\n if (n > 0n) b |= 0x80;\r\n bytes.push(b);\r\n } while (n > 0n);\r\n return Buffer.from(bytes);\r\n}\r\n\r\nexport interface ProtoField {\r\n fn: number;\r\n wt: number;\r\n value: Buffer | number | bigint;\r\n}\r\n\r\nexport function encodeField(fn: number, wt: number, value: Buffer | bigint | number | string): Buffer {\r\n const tag = encodeVarint((fn << 3) | wt);\r\n if (wt === 0) {\r\n return Buffer.concat([tag, encodeVarint(typeof value === 'number' ? BigInt(value) : value as bigint)]);\r\n }\r\n const data = typeof value === 'string' ? Buffer.from(value) : value as Buffer;\r\n return Buffer.concat([tag, encodeVarint(data.length), data]);\r\n}\r\n\r\nexport function decodeProto(buf: Buffer): ProtoField[] {\r\n const fields: ProtoField[] = [];\r\n let offset = 0;\r\n while (offset < buf.length) {\r\n const tagResult = decodeVarint(buf, offset);\r\n offset = tagResult.offset;\r\n const fn = tagResult.value >> 3;\r\n const wt = tagResult.value & 7;\r\n if (wt === 0) {\r\n const r = decodeVarint64(buf, offset);\r\n offset = r.offset;\r\n fields.push({ fn, wt, value: r.value });\r\n } else if (wt === 2) {\r\n const lenR = decodeVarint(buf, offset);\r\n offset = lenR.offset;\r\n const data = buf.subarray(offset, offset + lenR.value);\r\n offset += lenR.value;\r\n fields.push({ fn, wt, value: data });\r\n } else if (wt === 1) {\r\n fields.push({ fn, wt, value: buf.readBigInt64LE(offset) });\r\n offset += 8;\r\n } else if (wt === 5) {\r\n fields.push({ fn, wt, value: BigInt(buf.readInt32LE(offset)) });\r\n offset += 4;\r\n } else {\r\n break;\r\n }\r\n }\r\n return fields;\r\n}\r\n\r\nexport function getStr(fields: ProtoField[], fn: number): string {\r\n const f = fields.find(x => x.fn === fn && x.wt === 2);\r\n return f ? (f.value as Buffer).toString('utf-8') : '';\r\n}\r\n\r\nexport function getBytes(fields: ProtoField[], fn: number): Buffer | null {\r\n const f = fields.find(x => x.fn === fn && x.wt === 2);\r\n return f ? f.value as Buffer : null;\r\n}\r\n\r\nexport function getInt(fields: ProtoField[], fn: number): number {\r\n const f = fields.find(x => x.fn === fn && x.wt === 0);\r\n return f ? Number(f.value) : 0;\r\n}\r\n\r\nexport function buildHeartbeat(roomId: string): Buffer {\r\n const payload = encodeField(1, 0, BigInt(roomId));\r\n return Buffer.concat([\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'hb'),\r\n encodeField(8, 2, payload),\r\n ]);\r\n}\r\n\r\nexport function buildImEnterRoom(roomId: string): Buffer {\r\n const inner = Buffer.concat([\r\n encodeField(1, 0, BigInt(roomId)),\r\n encodeField(4, 0, 12n),\r\n encodeField(5, 2, 'audience'),\r\n encodeField(6, 2, ''),\r\n encodeField(9, 2, ''),\r\n encodeField(10, 2, ''),\r\n ]);\r\n return Buffer.concat([\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'im_enter_room'),\r\n encodeField(8, 2, inner),\r\n ]);\r\n}\r\n\r\nexport function buildAck(id: bigint): Buffer {\r\n return Buffer.concat([\r\n encodeField(2, 0, id),\r\n encodeField(6, 2, 'pb'),\r\n encodeField(7, 2, 'ack'),\r\n ]);\r\n}\r\n\r\nfunction parseUser(data: Buffer): TikTokUser {\r\n const f = decodeProto(data);\r\n const id = String(getInt(f, 1) || getStr(f, 1));\r\n const nickname = getStr(f, 3) || getStr(f, 5);\r\n const uniqueId = getStr(f, 38) || getStr(f, 4) || getStr(f, 2);\r\n return { id, nickname, uniqueId: uniqueId || nickname || id };\r\n}\r\n\r\nexport function parseWebcastMessage(method: string, payload: Buffer): LiveEvent {\r\n const f = decodeProto(payload);\r\n const userBuf = getBytes(f, 2);\r\n const user = userBuf ? parseUser(userBuf) : { id: '0', nickname: '', uniqueId: '' };\r\n const base = { timestamp: Date.now(), msgId: String(getInt(f, 1) || '') };\r\n\r\n switch (method) {\r\n case 'WebcastChatMessage':\r\n return { ...base, type: 'chat' as const, user, comment: getStr(f, 3) };\r\n\r\n case 'WebcastMemberMessage':\r\n return { ...base, type: 'member' as const, user, action: getInt(f, 3) };\r\n\r\n case 'WebcastLikeMessage':\r\n return { ...base, type: 'like' as const, user, likeCount: getInt(f, 5), totalLikes: getInt(f, 6) || getInt(f, 7) };\r\n\r\n case 'WebcastGiftMessage': {\r\n const giftBuf = getBytes(f, 3);\r\n let giftName = '', giftId = 0, diamondCount = 0;\r\n if (giftBuf) {\r\n const gf = decodeProto(giftBuf);\r\n giftId = getInt(gf, 1);\r\n giftName = getStr(gf, 2);\r\n diamondCount = getInt(gf, 5);\r\n }\r\n const repeatCount = getInt(f, 5);\r\n const repeatEnd = getInt(f, 9) === 1;\r\n return { ...base, type: 'gift' as const, user, giftId, giftName, diamondCount, repeatCount, repeatEnd, combo: repeatCount > 1 && !repeatEnd };\r\n }\r\n\r\n case 'WebcastSocialMessage': {\r\n const action = getInt(f, 3);\r\n const actionMap: Record<number, string> = { 1: 'follow', 2: 'share', 3: 'like' };\r\n return { ...base, type: 'social' as const, user, action: actionMap[action] || `action_${action}` };\r\n }\r\n\r\n case 'WebcastRoomUserSeqMessage':\r\n return { ...base, type: 'roomUserSeq' as const, viewerCount: getInt(f, 3), totalViewers: getInt(f, 4) };\r\n\r\n case 'WebcastLinkMicBattle':\r\n return { ...base, type: 'battle' as const, status: getInt(f, 3) };\r\n\r\n case 'WebcastLinkMicArmies':\r\n return { ...base, type: 'battleArmies' as const };\r\n\r\n case 'WebcastSubNotifyMessage':\r\n return { ...base, type: 'subscribe' as const, user, subMonth: getInt(f, 8) };\r\n\r\n case 'WebcastEmoteChatMessage':\r\n return { ...base, type: 'emoteChat' as const, user, emoteId: getStr(f, 3) };\r\n\r\n case 'WebcastEnvelopeMessage':\r\n return { ...base, type: 'envelope' as const, diamondCount: getInt(f, 3) };\r\n\r\n case 'WebcastQuestionNewMessage':\r\n return { ...base, type: 'question' as const, user, questionText: getStr(f, 3) };\r\n\r\n case 'WebcastRankUpdateMessage':\r\n case 'WebcastHourlyRankMessage':\r\n return { ...base, type: 'rankUpdate' as const, rankType: getStr(f, 3) };\r\n\r\n case 'WebcastControlMessage':\r\n return { ...base, type: 'control' as const, action: getInt(f, 2) };\r\n\r\n case 'WebcastRoomMessage':\r\n case 'RoomMessage':\r\n return { ...base, type: 'room' as const, status: getStr(f, 3) };\r\n\r\n case 'WebcastLiveIntroMessage':\r\n return { ...base, type: 'liveIntro' as const, title: getStr(f, 3) };\r\n\r\n case 'WebcastLinkMicMethod':\r\n case 'WebcastLinkmicBattleTaskMessage':\r\n return { ...base, type: 'linkMic' as const, action: getInt(f, 3) };\r\n\r\n default:\r\n return { ...base, type: 'unknown' as const, method };\r\n }\r\n}\r\n\r\nexport function parseWebcastResponse(payload: Buffer): LiveEvent[] {\r\n const events: LiveEvent[] = [];\r\n const respFields = decodeProto(payload);\r\n\r\n for (const mf of respFields.filter(f => f.fn === 1 && f.wt === 2)) {\r\n const msgBuf = mf.value as Buffer;\r\n const msgFields = decodeProto(msgBuf);\r\n const method = getStr(msgFields, 1);\r\n const innerPayload = getBytes(msgFields, 2);\r\n if (!method || !innerPayload) continue;\r\n\r\n try {\r\n events.push(parseWebcastMessage(method, innerPayload));\r\n } catch { }\r\n }\r\n\r\n return events;\r\n}\r\n"],"mappings":"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","names":["WebSocket"]}
|