@tiktool/live 2.9.0 → 2.10.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 CHANGED
@@ -291,6 +291,20 @@ interface TikTokLiveOptions {
291
291
  * proxy: 'http://USER:PASS@p.webshare.io:80'
292
292
  */
293
293
  proxy?: string;
294
+ /**
295
+ * Connection mode.
296
+ * - `'direct'` (default): SDK opens WebSocket directly to TikTok. TikTok
297
+ * sees your IP. Cheapest on API quota. Limited to ~30 concurrent
298
+ * streams per container before TikTok rate-limits.
299
+ * - `'relayed'`: SDK connects to TikTools' managed relay
300
+ * (`wss://api.tik.tools/?...`). We connect to TikTok on your behalf
301
+ * from our rotated proxy pool. TikTok never sees your IP. No rate-
302
+ * limit risk. Scales to thousands of concurrent streams.
303
+ *
304
+ * The decoded events emitted are identical in both modes — code that
305
+ * uses `client.on('chat', e => ...)` works without changes.
306
+ */
307
+ mode?: 'direct' | 'relayed';
294
308
  }
295
309
 
296
310
  declare class TikTokLive extends EventEmitter {
@@ -315,6 +329,7 @@ declare class TikTokLive extends EventEmitter {
315
329
  private readonly _presetRoomId;
316
330
  private readonly _presetSessionId;
317
331
  private readonly proxyUrl;
332
+ private readonly mode;
318
333
  constructor(options: TikTokLiveOptions);
319
334
  /**
320
335
  * Build an HttpsProxyAgent when `proxy` option is set, else undefined.
@@ -367,6 +382,13 @@ declare class TikTokLive extends EventEmitter {
367
382
  private resolveHostUsers;
368
383
  private startHeartbeat;
369
384
  private stopHeartbeat;
385
+ /**
386
+ * Relayed-mode connect. Opens a plain WebSocket to TikTools' managed relay
387
+ * endpoint. Events arrive pre-decoded as `{event, data}` JSON envelopes.
388
+ * The SDK re-emits them on the same event names as Direct mode so user
389
+ * code is identical regardless of mode.
390
+ */
391
+ private _connectRelayed;
370
392
  }
371
393
 
372
394
  export { type BaseEvent, type BattleArmiesEvent, type BattleContributor, type BattleEvent, type BattleHost, type BattleItemCardEvent, type BattleTeam, type BattleTeamUser, type ChatEvent, type ControlEvent, type EmoteChatEvent, type EnvelopeEvent, type GiftEvent, type LikeEvent, type LinkMicEvent, type LiveEvent, type LiveIntroEvent, type MemberEvent, type QuestionEvent, type RankUpdateEvent, type RoomEvent, type RoomInfo, type RoomUserSeqEvent, type SocialEvent, type StreamInfo, type StreamQuality, type StreamUrls, type SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent };
package/dist/index.d.ts CHANGED
@@ -291,6 +291,20 @@ interface TikTokLiveOptions {
291
291
  * proxy: 'http://USER:PASS@p.webshare.io:80'
292
292
  */
293
293
  proxy?: string;
294
+ /**
295
+ * Connection mode.
296
+ * - `'direct'` (default): SDK opens WebSocket directly to TikTok. TikTok
297
+ * sees your IP. Cheapest on API quota. Limited to ~30 concurrent
298
+ * streams per container before TikTok rate-limits.
299
+ * - `'relayed'`: SDK connects to TikTools' managed relay
300
+ * (`wss://api.tik.tools/?...`). We connect to TikTok on your behalf
301
+ * from our rotated proxy pool. TikTok never sees your IP. No rate-
302
+ * limit risk. Scales to thousands of concurrent streams.
303
+ *
304
+ * The decoded events emitted are identical in both modes — code that
305
+ * uses `client.on('chat', e => ...)` works without changes.
306
+ */
307
+ mode?: 'direct' | 'relayed';
294
308
  }
295
309
 
296
310
  declare class TikTokLive extends EventEmitter {
@@ -315,6 +329,7 @@ declare class TikTokLive extends EventEmitter {
315
329
  private readonly _presetRoomId;
316
330
  private readonly _presetSessionId;
317
331
  private readonly proxyUrl;
332
+ private readonly mode;
318
333
  constructor(options: TikTokLiveOptions);
319
334
  /**
320
335
  * Build an HttpsProxyAgent when `proxy` option is set, else undefined.
@@ -367,6 +382,13 @@ declare class TikTokLive extends EventEmitter {
367
382
  private resolveHostUsers;
368
383
  private startHeartbeat;
369
384
  private stopHeartbeat;
385
+ /**
386
+ * Relayed-mode connect. Opens a plain WebSocket to TikTools' managed relay
387
+ * endpoint. Events arrive pre-decoded as `{event, data}` JSON envelopes.
388
+ * The SDK re-emits them on the same event names as Direct mode so user
389
+ * code is identical regardless of mode.
390
+ */
391
+ private _connectRelayed;
370
392
  }
371
393
 
372
394
  export { type BaseEvent, type BattleArmiesEvent, type BattleContributor, type BattleEvent, type BattleHost, type BattleItemCardEvent, type BattleTeam, type BattleTeamUser, type ChatEvent, type ControlEvent, type EmoteChatEvent, type EnvelopeEvent, type GiftEvent, type LikeEvent, type LinkMicEvent, type LiveEvent, type LiveIntroEvent, type MemberEvent, type QuestionEvent, type RankUpdateEvent, type RoomEvent, type RoomInfo, type RoomUserSeqEvent, type SocialEvent, type StreamInfo, type StreamQuality, type StreamUrls, type SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent };
package/dist/index.js CHANGED
@@ -2147,6 +2147,7 @@ var TikTokLive = class _TikTokLive extends import_events.EventEmitter {
2147
2147
  _presetRoomId;
2148
2148
  _presetSessionId;
2149
2149
  proxyUrl;
2150
+ mode;
2150
2151
  constructor(options) {
2151
2152
  super();
2152
2153
  this.setMaxListeners(20);
@@ -2161,6 +2162,7 @@ var TikTokLive = class _TikTokLive extends import_events.EventEmitter {
2161
2162
  this._presetRoomId = options.roomId || "";
2162
2163
  this._presetSessionId = options.sessionId || "";
2163
2164
  this.proxyUrl = options.proxy || "";
2165
+ this.mode = options.mode || "direct";
2164
2166
  }
2165
2167
  /**
2166
2168
  * Build an HttpsProxyAgent when `proxy` option is set, else undefined.
@@ -2180,6 +2182,9 @@ var TikTokLive = class _TikTokLive extends import_events.EventEmitter {
2180
2182
  async connect() {
2181
2183
  if (this._destroyed) throw new Error("Client has been destroyed. Create a new instance.");
2182
2184
  this.intentionalClose = false;
2185
+ if (this.mode === "relayed") {
2186
+ return this._connectRelayed();
2187
+ }
2183
2188
  let ttwid = this._presetSessionId;
2184
2189
  let roomId = this._presetRoomId;
2185
2190
  let clusterRegion = "";
@@ -2635,6 +2640,73 @@ var TikTokLive = class _TikTokLive extends import_events.EventEmitter {
2635
2640
  this.heartbeatTimer = null;
2636
2641
  }
2637
2642
  }
2643
+ /**
2644
+ * Relayed-mode connect. Opens a plain WebSocket to TikTools' managed relay
2645
+ * endpoint. Events arrive pre-decoded as `{event, data}` JSON envelopes.
2646
+ * The SDK re-emits them on the same event names as Direct mode so user
2647
+ * code is identical regardless of mode.
2648
+ */
2649
+ async _connectRelayed() {
2650
+ const host = this.signServerUrl.replace(/^https:\/\//, "wss://").replace(/^http:\/\//, "ws://");
2651
+ const wsUrl = `${host}/?uniqueId=${encodeURIComponent(this.uniqueId)}&apiKey=${encodeURIComponent(this.apiKey)}`;
2652
+ if (this.debug) console.log(`[TikTokLive] Relayed mode: ${wsUrl.replace(this.apiKey, "***")}`);
2653
+ return new Promise((resolve, reject) => {
2654
+ this.ws = new import_ws.default(wsUrl);
2655
+ let firstOpen = true;
2656
+ this.ws.on("open", () => {
2657
+ this._connected = true;
2658
+ this.reconnectAttempts = 0;
2659
+ if (firstOpen) {
2660
+ firstOpen = false;
2661
+ resolve();
2662
+ }
2663
+ this.emit("connected");
2664
+ });
2665
+ this.ws.on("message", (raw) => {
2666
+ try {
2667
+ const msg = JSON.parse(raw.toString());
2668
+ if (typeof msg !== "object" || !msg) return;
2669
+ const evName = msg.event;
2670
+ const evData = msg.data ?? msg;
2671
+ if (!evName) return;
2672
+ if (evName === "connected" || evName === "_journal" || evName === "ping" || evName === "pong") return;
2673
+ if (evName === "roomInfo") {
2674
+ const ri = {
2675
+ roomId: evData.roomId || "",
2676
+ wsHost: evData.wsHost || "",
2677
+ clusterRegion: evData.clusterRegion || "",
2678
+ connectedAt: evData.connectedAt || (/* @__PURE__ */ new Date()).toISOString()
2679
+ };
2680
+ this.emit("roomInfo", ri);
2681
+ return;
2682
+ }
2683
+ this.emit(evName, evData);
2684
+ this.emit("event", evData);
2685
+ } catch {
2686
+ }
2687
+ });
2688
+ this.ws.on("close", (code, reason) => {
2689
+ this._connected = false;
2690
+ const reasonStr = reason?.toString() || "";
2691
+ this.emit("disconnected", code, reasonStr);
2692
+ if (!this.intentionalClose && this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
2693
+ this.reconnectAttempts++;
2694
+ const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 3e4);
2695
+ if (this.debug) console.log(`[TikTokLive] Relayed reconnect in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
2696
+ setTimeout(() => {
2697
+ this._connectRelayed().catch(() => {
2698
+ });
2699
+ }, delay);
2700
+ }
2701
+ });
2702
+ this.ws.on("error", (err) => {
2703
+ if (firstOpen) {
2704
+ firstOpen = false;
2705
+ reject(err);
2706
+ } else this.emit("error", err);
2707
+ });
2708
+ });
2709
+ }
2638
2710
  };
2639
2711
  // Annotate the CommonJS export names for ESM import in node:
2640
2712
  0 && (module.exports = {