@tiktool/live 1.6.6 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +32 -6
- package/dist/index.d.ts +32 -6
- package/dist/index.js +195 -84
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +201 -83
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -4
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
7
|
|
|
8
8
|
// src/proto.ts
|
|
9
|
+
var encoder = new TextEncoder();
|
|
10
|
+
var decoder = new TextDecoder();
|
|
11
|
+
function concatBytes(...arrays) {
|
|
12
|
+
let totalLength = 0;
|
|
13
|
+
for (const arr of arrays) totalLength += arr.length;
|
|
14
|
+
const result = new Uint8Array(totalLength);
|
|
15
|
+
let offset = 0;
|
|
16
|
+
for (const arr of arrays) {
|
|
17
|
+
result.set(arr, offset);
|
|
18
|
+
offset += arr.length;
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
function readInt32LE(buf, offset) {
|
|
23
|
+
return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24;
|
|
24
|
+
}
|
|
25
|
+
function readBigInt64LE(buf, offset) {
|
|
26
|
+
const lo = BigInt(buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24 >>> 0);
|
|
27
|
+
const hi = BigInt(buf[offset + 4] | buf[offset + 5] << 8 | buf[offset + 6] << 16 | buf[offset + 7] << 24 >>> 0);
|
|
28
|
+
return hi << 32n | lo & 0xFFFFFFFFn;
|
|
29
|
+
}
|
|
9
30
|
function decodeVarint(buf, offset) {
|
|
10
31
|
let result = 0, shift = 0;
|
|
11
32
|
while (offset < buf.length) {
|
|
@@ -35,15 +56,15 @@ function encodeVarint(v) {
|
|
|
35
56
|
if (n > 0n) b |= 128;
|
|
36
57
|
bytes.push(b);
|
|
37
58
|
} while (n > 0n);
|
|
38
|
-
return
|
|
59
|
+
return new Uint8Array(bytes);
|
|
39
60
|
}
|
|
40
61
|
function encodeField(fn, wt, value) {
|
|
41
62
|
const tag = encodeVarint(fn << 3 | wt);
|
|
42
63
|
if (wt === 0) {
|
|
43
|
-
return
|
|
64
|
+
return concatBytes(tag, encodeVarint(typeof value === "number" ? BigInt(value) : value));
|
|
44
65
|
}
|
|
45
|
-
const data = typeof value === "string" ?
|
|
46
|
-
return
|
|
66
|
+
const data = typeof value === "string" ? encoder.encode(value) : value;
|
|
67
|
+
return concatBytes(tag, encodeVarint(data.length), data);
|
|
47
68
|
}
|
|
48
69
|
function decodeProto(buf) {
|
|
49
70
|
const fields = [];
|
|
@@ -64,10 +85,10 @@ function decodeProto(buf) {
|
|
|
64
85
|
offset += lenR.value;
|
|
65
86
|
fields.push({ fn, wt, value: data });
|
|
66
87
|
} else if (wt === 1) {
|
|
67
|
-
fields.push({ fn, wt, value:
|
|
88
|
+
fields.push({ fn, wt, value: readBigInt64LE(buf, offset) });
|
|
68
89
|
offset += 8;
|
|
69
90
|
} else if (wt === 5) {
|
|
70
|
-
fields.push({ fn, wt, value: BigInt(
|
|
91
|
+
fields.push({ fn, wt, value: BigInt(readInt32LE(buf, offset)) });
|
|
71
92
|
offset += 4;
|
|
72
93
|
} else {
|
|
73
94
|
break;
|
|
@@ -77,7 +98,7 @@ function decodeProto(buf) {
|
|
|
77
98
|
}
|
|
78
99
|
function getStr(fields, fn) {
|
|
79
100
|
const f = fields.find((x) => x.fn === fn && x.wt === 2);
|
|
80
|
-
return f ? f.value
|
|
101
|
+
return f ? decoder.decode(f.value) : "";
|
|
81
102
|
}
|
|
82
103
|
function getBytes(fields, fn) {
|
|
83
104
|
const f = fields.find((x) => x.fn === fn && x.wt === 2);
|
|
@@ -92,33 +113,33 @@ function getAllBytes(fields, fn) {
|
|
|
92
113
|
}
|
|
93
114
|
function buildHeartbeat(roomId) {
|
|
94
115
|
const payload = encodeField(1, 0, BigInt(roomId));
|
|
95
|
-
return
|
|
116
|
+
return concatBytes(
|
|
96
117
|
encodeField(6, 2, "pb"),
|
|
97
118
|
encodeField(7, 2, "hb"),
|
|
98
119
|
encodeField(8, 2, payload)
|
|
99
|
-
|
|
120
|
+
);
|
|
100
121
|
}
|
|
101
122
|
function buildImEnterRoom(roomId) {
|
|
102
|
-
const inner =
|
|
123
|
+
const inner = concatBytes(
|
|
103
124
|
encodeField(1, 0, BigInt(roomId)),
|
|
104
125
|
encodeField(4, 0, 12n),
|
|
105
126
|
encodeField(5, 2, "audience"),
|
|
106
127
|
encodeField(6, 2, ""),
|
|
107
128
|
encodeField(9, 2, ""),
|
|
108
129
|
encodeField(10, 2, "")
|
|
109
|
-
|
|
110
|
-
return
|
|
130
|
+
);
|
|
131
|
+
return concatBytes(
|
|
111
132
|
encodeField(6, 2, "pb"),
|
|
112
133
|
encodeField(7, 2, "im_enter_room"),
|
|
113
134
|
encodeField(8, 2, inner)
|
|
114
|
-
|
|
135
|
+
);
|
|
115
136
|
}
|
|
116
137
|
function buildAck(id) {
|
|
117
|
-
return
|
|
138
|
+
return concatBytes(
|
|
118
139
|
encodeField(2, 0, id),
|
|
119
140
|
encodeField(6, 2, "pb"),
|
|
120
141
|
encodeField(7, 2, "ack")
|
|
121
|
-
|
|
142
|
+
);
|
|
122
143
|
}
|
|
123
144
|
function parseUser(data) {
|
|
124
145
|
const f = decodeProto(data);
|
|
@@ -131,7 +152,7 @@ function parseUser(data) {
|
|
|
131
152
|
try {
|
|
132
153
|
const avatarFields = decodeProto(avatarBuf);
|
|
133
154
|
const urlBufs = getAllBytes(avatarFields, 1);
|
|
134
|
-
if (urlBufs.length > 0) profilePicture = urlBufs[0]
|
|
155
|
+
if (urlBufs.length > 0) profilePicture = decoder.decode(urlBufs[0]);
|
|
135
156
|
} catch {
|
|
136
157
|
}
|
|
137
158
|
}
|
|
@@ -448,27 +469,47 @@ function parseWebcastResponse(payload) {
|
|
|
448
469
|
// src/client.ts
|
|
449
470
|
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";
|
|
450
471
|
var DEFAULT_SIGN_SERVER = "https://api.tik.tools";
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
+
var TypedEmitter = class {
|
|
473
|
+
_listeners = /* @__PURE__ */ new Map();
|
|
474
|
+
on(event, fn) {
|
|
475
|
+
const arr = this._listeners.get(event) || [];
|
|
476
|
+
arr.push(fn);
|
|
477
|
+
this._listeners.set(event, arr);
|
|
478
|
+
return this;
|
|
479
|
+
}
|
|
480
|
+
once(event, fn) {
|
|
481
|
+
const wrapper = (...args) => {
|
|
482
|
+
this.off(event, wrapper);
|
|
483
|
+
fn(...args);
|
|
484
|
+
};
|
|
485
|
+
return this.on(event, wrapper);
|
|
486
|
+
}
|
|
487
|
+
off(event, fn) {
|
|
488
|
+
const arr = this._listeners.get(event);
|
|
489
|
+
if (arr) {
|
|
490
|
+
this._listeners.set(event, arr.filter((l) => l !== fn));
|
|
491
|
+
}
|
|
492
|
+
return this;
|
|
493
|
+
}
|
|
494
|
+
emit(event, ...args) {
|
|
495
|
+
const arr = this._listeners.get(event);
|
|
496
|
+
if (!arr || arr.length === 0) return false;
|
|
497
|
+
for (const fn of [...arr]) fn(...args);
|
|
498
|
+
return true;
|
|
499
|
+
}
|
|
500
|
+
removeAllListeners(event) {
|
|
501
|
+
if (event) this._listeners.delete(event);
|
|
502
|
+
else this._listeners.clear();
|
|
503
|
+
return this;
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
function gunzipSync(data) {
|
|
507
|
+
try {
|
|
508
|
+
const zlib = __require("zlib");
|
|
509
|
+
return new Uint8Array(zlib.gunzipSync(data));
|
|
510
|
+
} catch {
|
|
511
|
+
return data;
|
|
512
|
+
}
|
|
472
513
|
}
|
|
473
514
|
function getWsHost(clusterRegion) {
|
|
474
515
|
if (!clusterRegion) return "webcast-ws.tiktok.com";
|
|
@@ -477,7 +518,21 @@ function getWsHost(clusterRegion) {
|
|
|
477
518
|
if (r.startsWith("us") || r.includes("us")) return "webcast-ws.us.tiktok.com";
|
|
478
519
|
return "webcast-ws.tiktok.com";
|
|
479
520
|
}
|
|
480
|
-
|
|
521
|
+
async function resolveWebSocket(userImpl) {
|
|
522
|
+
if (userImpl) return userImpl;
|
|
523
|
+
if (typeof globalThis.WebSocket !== "undefined") {
|
|
524
|
+
return globalThis.WebSocket;
|
|
525
|
+
}
|
|
526
|
+
try {
|
|
527
|
+
const ws = await import("ws");
|
|
528
|
+
return ws.default || ws;
|
|
529
|
+
} catch {
|
|
530
|
+
throw new Error(
|
|
531
|
+
'No WebSocket implementation found. Either use Node.js 22+ (native WebSocket), Cloudflare Workers, or install the "ws" package: npm i ws'
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
var TikTokLive = class extends TypedEmitter {
|
|
481
536
|
ws = null;
|
|
482
537
|
heartbeatTimer = null;
|
|
483
538
|
reconnectAttempts = 0;
|
|
@@ -485,7 +540,6 @@ var TikTokLive = class extends EventEmitter {
|
|
|
485
540
|
_connected = false;
|
|
486
541
|
_eventCount = 0;
|
|
487
542
|
_roomId = "";
|
|
488
|
-
// Cache host identities from battle events for enriching battleArmies
|
|
489
543
|
_battleHosts = /* @__PURE__ */ new Map();
|
|
490
544
|
uniqueId;
|
|
491
545
|
signServerUrl;
|
|
@@ -494,6 +548,8 @@ var TikTokLive = class extends EventEmitter {
|
|
|
494
548
|
maxReconnectAttempts;
|
|
495
549
|
heartbeatInterval;
|
|
496
550
|
debug;
|
|
551
|
+
webSocketImpl;
|
|
552
|
+
WS;
|
|
497
553
|
constructor(options) {
|
|
498
554
|
super();
|
|
499
555
|
this.uniqueId = options.uniqueId.replace(/^@/, "");
|
|
@@ -504,24 +560,40 @@ var TikTokLive = class extends EventEmitter {
|
|
|
504
560
|
this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
|
|
505
561
|
this.heartbeatInterval = options.heartbeatInterval ?? 1e4;
|
|
506
562
|
this.debug = options.debug ?? false;
|
|
563
|
+
this.webSocketImpl = options.webSocketImpl;
|
|
507
564
|
}
|
|
508
565
|
async connect() {
|
|
509
566
|
this.intentionalClose = false;
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
567
|
+
if (!this.WS) {
|
|
568
|
+
this.WS = await resolveWebSocket(this.webSocketImpl);
|
|
569
|
+
}
|
|
570
|
+
const resp = await fetch(`https://www.tiktok.com/@${this.uniqueId}/live`, {
|
|
571
|
+
headers: {
|
|
572
|
+
"User-Agent": DEFAULT_UA,
|
|
573
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
574
|
+
"Accept-Language": "en-US,en;q=0.9"
|
|
575
|
+
},
|
|
576
|
+
redirect: "follow"
|
|
515
577
|
});
|
|
516
578
|
let ttwid = "";
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
579
|
+
const setCookies = resp.headers.get("set-cookie") || "";
|
|
580
|
+
for (const part of setCookies.split(",")) {
|
|
581
|
+
const trimmed = part.trim();
|
|
582
|
+
if (trimmed.startsWith("ttwid=")) {
|
|
583
|
+
ttwid = trimmed.split(";")[0].split("=").slice(1).join("=");
|
|
520
584
|
break;
|
|
521
585
|
}
|
|
522
586
|
}
|
|
587
|
+
if (!ttwid && typeof resp.headers.getSetCookie === "function") {
|
|
588
|
+
for (const sc of resp.headers.getSetCookie()) {
|
|
589
|
+
if (typeof sc === "string" && sc.startsWith("ttwid=")) {
|
|
590
|
+
ttwid = sc.split(";")[0].split("=").slice(1).join("=");
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
523
595
|
if (!ttwid) throw new Error("Failed to obtain session cookie");
|
|
524
|
-
const html = resp.
|
|
596
|
+
const html = await resp.text();
|
|
525
597
|
let roomId = "";
|
|
526
598
|
const sigiMatch = html.match(/id="SIGI_STATE"[^>]*>([^<]+)/);
|
|
527
599
|
if (sigiMatch) {
|
|
@@ -549,7 +621,7 @@ var TikTokLive = class extends EventEmitter {
|
|
|
549
621
|
browser_name: "Mozilla",
|
|
550
622
|
browser_version: DEFAULT_UA.split("Mozilla/")[1] || "5.0",
|
|
551
623
|
browser_online: "true",
|
|
552
|
-
tz_name:
|
|
624
|
+
tz_name: "Etc/UTC",
|
|
553
625
|
app_name: "tiktok_web",
|
|
554
626
|
sup_ws_ds_opt: "1",
|
|
555
627
|
update_version_code: "2.0.0",
|
|
@@ -590,18 +662,27 @@ var TikTokLive = class extends EventEmitter {
|
|
|
590
662
|
wsUrl = rawWsUrl.replace(/^https:\/\//, "wss://");
|
|
591
663
|
}
|
|
592
664
|
return new Promise((resolve, reject) => {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
665
|
+
const connUrl = wsUrl + (wsUrl.includes("?") ? "&" : "?") + `ttwid=${ttwid}`;
|
|
666
|
+
try {
|
|
667
|
+
this.ws = new this.WS(connUrl, {
|
|
668
|
+
headers: {
|
|
669
|
+
"User-Agent": DEFAULT_UA,
|
|
670
|
+
"Cookie": `ttwid=${ttwid}`,
|
|
671
|
+
"Origin": "https://www.tiktok.com"
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
} catch {
|
|
675
|
+
this.ws = new this.WS(connUrl);
|
|
676
|
+
}
|
|
677
|
+
const ws = this.ws;
|
|
678
|
+
let settled = false;
|
|
679
|
+
ws.onopen = () => {
|
|
601
680
|
this._connected = true;
|
|
602
681
|
this.reconnectAttempts = 0;
|
|
603
|
-
|
|
604
|
-
|
|
682
|
+
const hb = buildHeartbeat(roomId);
|
|
683
|
+
const enter = buildImEnterRoom(roomId);
|
|
684
|
+
ws.send(hb.buffer.byteLength === hb.length ? hb.buffer : hb.buffer.slice(hb.byteOffset, hb.byteOffset + hb.byteLength));
|
|
685
|
+
ws.send(enter.buffer.byteLength === enter.length ? enter.buffer : enter.buffer.slice(enter.byteOffset, enter.byteOffset + enter.byteLength));
|
|
605
686
|
this.startHeartbeat(roomId);
|
|
606
687
|
const roomInfo = {
|
|
607
688
|
roomId,
|
|
@@ -611,26 +692,41 @@ var TikTokLive = class extends EventEmitter {
|
|
|
611
692
|
};
|
|
612
693
|
this.emit("connected");
|
|
613
694
|
this.emit("roomInfo", roomInfo);
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
}
|
|
619
|
-
|
|
695
|
+
if (!settled) {
|
|
696
|
+
settled = true;
|
|
697
|
+
resolve();
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
ws.onmessage = (event) => {
|
|
701
|
+
const raw = event.data !== void 0 ? event.data : event;
|
|
702
|
+
this.handleMessage(raw);
|
|
703
|
+
};
|
|
704
|
+
ws.onclose = (event) => {
|
|
620
705
|
this._connected = false;
|
|
621
706
|
this.stopHeartbeat();
|
|
622
|
-
const
|
|
623
|
-
|
|
707
|
+
const code = event?.code ?? 1006;
|
|
708
|
+
const reason = event?.reason ?? "";
|
|
709
|
+
this.emit("disconnected", code, reason?.toString?.() || "");
|
|
624
710
|
if (!this.intentionalClose && this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
625
711
|
this.reconnectAttempts++;
|
|
626
712
|
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts - 1), 3e4);
|
|
627
713
|
setTimeout(() => this.connect().catch((e) => this.emit("error", e)), delay);
|
|
628
714
|
}
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
this.emit("error", err);
|
|
632
|
-
if (!
|
|
633
|
-
|
|
715
|
+
};
|
|
716
|
+
ws.onerror = (err) => {
|
|
717
|
+
this.emit("error", err instanceof Error ? err : new Error(String(err?.message || err)));
|
|
718
|
+
if (!settled) {
|
|
719
|
+
settled = true;
|
|
720
|
+
reject(err);
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
setTimeout(() => {
|
|
724
|
+
if (!settled) {
|
|
725
|
+
settled = true;
|
|
726
|
+
ws.close();
|
|
727
|
+
reject(new Error("Connection timeout"));
|
|
728
|
+
}
|
|
729
|
+
}, 15e3);
|
|
634
730
|
});
|
|
635
731
|
}
|
|
636
732
|
disconnect() {
|
|
@@ -651,6 +747,7 @@ var TikTokLive = class extends EventEmitter {
|
|
|
651
747
|
get roomId() {
|
|
652
748
|
return this._roomId;
|
|
653
749
|
}
|
|
750
|
+
// Typed event emitter overrides
|
|
654
751
|
on(event, listener) {
|
|
655
752
|
return super.on(event, listener);
|
|
656
753
|
}
|
|
@@ -663,6 +760,25 @@ var TikTokLive = class extends EventEmitter {
|
|
|
663
760
|
emit(event, ...args) {
|
|
664
761
|
return super.emit(event, ...args);
|
|
665
762
|
}
|
|
763
|
+
// ── Message handling ──────────────────────────────────────────────
|
|
764
|
+
async handleMessage(raw) {
|
|
765
|
+
try {
|
|
766
|
+
let bytes;
|
|
767
|
+
if (raw instanceof ArrayBuffer) {
|
|
768
|
+
bytes = new Uint8Array(raw);
|
|
769
|
+
} else if (raw instanceof Uint8Array) {
|
|
770
|
+
bytes = raw;
|
|
771
|
+
} else if (typeof Blob !== "undefined" && raw instanceof Blob) {
|
|
772
|
+
bytes = new Uint8Array(await raw.arrayBuffer());
|
|
773
|
+
} else if (raw?.buffer instanceof ArrayBuffer) {
|
|
774
|
+
bytes = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
|
|
775
|
+
} else {
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
this.handleFrame(bytes);
|
|
779
|
+
} catch {
|
|
780
|
+
}
|
|
781
|
+
}
|
|
666
782
|
handleFrame(buf) {
|
|
667
783
|
try {
|
|
668
784
|
const fields = decodeProto(buf);
|
|
@@ -670,14 +786,15 @@ var TikTokLive = class extends EventEmitter {
|
|
|
670
786
|
const id = idField ? idField.value : 0n;
|
|
671
787
|
const type = getStr(fields, 7);
|
|
672
788
|
const binary = getBytes(fields, 8);
|
|
673
|
-
if (id > 0n && this.ws
|
|
674
|
-
|
|
789
|
+
if (id > 0n && this.ws && this.ws.readyState === 1) {
|
|
790
|
+
const ack = buildAck(id);
|
|
791
|
+
this.ws.send(ack.buffer.byteLength === ack.length ? ack.buffer : ack.buffer.slice(ack.byteOffset, ack.byteOffset + ack.byteLength));
|
|
675
792
|
}
|
|
676
793
|
if (type === "msg" && binary && binary.length > 0) {
|
|
677
794
|
let inner = binary;
|
|
678
795
|
if (inner.length > 2 && inner[0] === 31 && inner[1] === 139) {
|
|
679
796
|
try {
|
|
680
|
-
inner =
|
|
797
|
+
inner = gunzipSync(inner);
|
|
681
798
|
} catch {
|
|
682
799
|
}
|
|
683
800
|
}
|
|
@@ -715,8 +832,9 @@ var TikTokLive = class extends EventEmitter {
|
|
|
715
832
|
startHeartbeat(roomId) {
|
|
716
833
|
this.stopHeartbeat();
|
|
717
834
|
this.heartbeatTimer = setInterval(() => {
|
|
718
|
-
if (this.ws
|
|
719
|
-
|
|
835
|
+
if (this.ws && this.ws.readyState === 1) {
|
|
836
|
+
const hb = buildHeartbeat(roomId);
|
|
837
|
+
this.ws.send(hb.buffer.byteLength === hb.length ? hb.buffer : hb.buffer.slice(hb.byteOffset, hb.byteOffset + hb.byteLength));
|
|
720
838
|
}
|
|
721
839
|
}, this.heartbeatInterval);
|
|
722
840
|
}
|