@tiktool/live 2.4.4 → 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/dist/index.d.ts CHANGED
@@ -1,28 +1,22 @@
1
+ import { EventEmitter } from 'events';
2
+
1
3
  interface TikTokUser {
2
4
  id: string;
3
5
  nickname: string;
4
6
  uniqueId: string;
5
- avatarUrl?: string;
7
+ profilePicture?: string;
6
8
  profilePictureUrl?: string;
7
- followRole?: number;
8
9
  badges?: string[];
9
- level?: number;
10
10
  }
11
11
  interface BaseEvent {
12
12
  type: string;
13
13
  timestamp: number;
14
14
  msgId: string;
15
15
  }
16
- interface ChatEmote {
17
- id: string;
18
- url: string;
19
- name: string;
20
- }
21
16
  interface ChatEvent extends BaseEvent {
22
17
  type: 'chat';
23
18
  user: TikTokUser;
24
19
  comment: string;
25
- emotes?: ChatEmote[];
26
20
  }
27
21
  interface MemberEvent extends BaseEvent {
28
22
  type: 'member';
@@ -64,9 +58,9 @@ interface BattleTeamUser {
64
58
  }
65
59
  interface BattleTeam {
66
60
  hostUserId: string;
67
- hostUser?: TikTokUser;
68
61
  score: number;
69
62
  users: BattleTeamUser[];
63
+ hostUser?: TikTokUser;
70
64
  }
71
65
  interface BattleEvent extends BaseEvent {
72
66
  type: 'battle';
@@ -74,12 +68,43 @@ interface BattleEvent extends BaseEvent {
74
68
  status: number;
75
69
  battleDuration: number;
76
70
  teams: BattleTeam[];
71
+ battleSettings?: {
72
+ startTimeMs?: number;
73
+ duration?: number;
74
+ endTimeMs?: number;
75
+ };
77
76
  }
78
77
  interface BattleArmiesEvent extends BaseEvent {
79
78
  type: 'battleArmies';
80
79
  battleId: string;
81
80
  status: number;
82
81
  teams: BattleTeam[];
82
+ battleSettings?: {
83
+ startTimeMs?: number;
84
+ duration?: number;
85
+ endTimeMs?: number;
86
+ };
87
+ }
88
+ interface BattleTaskEvent extends BaseEvent {
89
+ type: 'battleTask';
90
+ taskAction: number;
91
+ battleRefId: string;
92
+ missionType: string;
93
+ multiplier: number;
94
+ missionDuration: number;
95
+ missionTarget: number;
96
+ remainingSeconds: number;
97
+ endTimestampS: number;
98
+ timerType: number;
99
+ }
100
+ interface BarrageEvent extends BaseEvent {
101
+ type: 'barrage';
102
+ msgType: number;
103
+ subType: number;
104
+ displayType: number;
105
+ duration: number;
106
+ defaultPattern: string;
107
+ content: string;
83
108
  }
84
109
  interface SubscribeEvent extends BaseEvent {
85
110
  type: 'subscribe';
@@ -91,6 +116,7 @@ interface EmoteChatEvent extends BaseEvent {
91
116
  user: TikTokUser;
92
117
  emoteId: string;
93
118
  emoteUrl: string;
119
+ emoteName?: string;
94
120
  }
95
121
  interface EnvelopeEvent extends BaseEvent {
96
122
  type: 'envelope';
@@ -133,7 +159,7 @@ interface UnknownEvent extends BaseEvent {
133
159
  type: 'unknown';
134
160
  method: string;
135
161
  }
136
- type LiveEvent = ChatEvent | MemberEvent | LikeEvent | GiftEvent | SocialEvent | RoomUserSeqEvent | BattleEvent | BattleArmiesEvent | SubscribeEvent | EmoteChatEvent | EnvelopeEvent | QuestionEvent | ControlEvent | RoomEvent | LiveIntroEvent | RankUpdateEvent | LinkMicEvent | UnknownEvent;
162
+ type LiveEvent = ChatEvent | MemberEvent | LikeEvent | GiftEvent | SocialEvent | RoomUserSeqEvent | BattleEvent | BattleArmiesEvent | BattleTaskEvent | BarrageEvent | SubscribeEvent | EmoteChatEvent | EnvelopeEvent | QuestionEvent | ControlEvent | RoomEvent | LiveIntroEvent | RankUpdateEvent | LinkMicEvent | UnknownEvent;
137
163
  interface TikTokLiveEvents {
138
164
  connected: () => void;
139
165
  disconnected: (code: number, reason: string) => void;
@@ -147,6 +173,8 @@ interface TikTokLiveEvents {
147
173
  roomUserSeq: (event: RoomUserSeqEvent) => void;
148
174
  battle: (event: BattleEvent) => void;
149
175
  battleArmies: (event: BattleArmiesEvent) => void;
176
+ battleTask: (event: BattleTaskEvent) => void;
177
+ barrage: (event: BarrageEvent) => void;
150
178
  subscribe: (event: SubscribeEvent) => void;
151
179
  emoteChat: (event: EmoteChatEvent) => void;
152
180
  envelope: (event: EnvelopeEvent) => void;
@@ -164,6 +192,7 @@ interface RoomInfo {
164
192
  wsHost: string;
165
193
  clusterRegion: string;
166
194
  connectedAt: string;
195
+ ownerUserId?: string;
167
196
  }
168
197
  interface TikTokLiveOptions {
169
198
  uniqueId: string;
@@ -175,48 +204,143 @@ interface TikTokLiveOptions {
175
204
  debug?: boolean;
176
205
  webSocketImpl?: any;
177
206
  }
178
- /** Result from solving a puzzle (slider) CAPTCHA */
179
- interface PuzzleSolveResult {
180
- /** X position as proportion (0.0–1.0) of background width */
181
- slide_x_proportion: number;
182
- /** Absolute pixel X offset */
183
- slide_x_px: number;
184
- /** Confidence score (0.0–1.0) */
185
- confidence: number;
186
- }
187
- /** Result from solving a rotate (whirl) CAPTCHA */
188
- interface RotateSolveResult {
189
- /** Rotation angle in degrees (0–360) */
190
- angle: number;
191
- /** Confidence score (0.0–1.0) */
192
- confidence: number;
193
- }
194
- /** Result from solving a shapes (3D matching) CAPTCHA */
195
- interface ShapesSolveResult {
196
- /** First matching shape center */
197
- point1: {
198
- x: number;
199
- y: number;
207
+ interface RanklistUser {
208
+ /** TikTok user ID */
209
+ id_str: string;
210
+ /** Display name */
211
+ nickname: string;
212
+ /** Username (@handle) */
213
+ display_id?: string;
214
+ unique_id?: string;
215
+ /** Avatar thumbnail URLs */
216
+ avatar_thumb?: {
217
+ url_list: string[];
200
218
  };
201
- /** Second matching shape center */
202
- point2: {
203
- x: number;
204
- y: number;
219
+ /** Full avatar URLs */
220
+ avatar_medium?: {
221
+ url_list: string[];
205
222
  };
206
- /** Confidence score (0.0–1.0) */
207
- confidence: number;
223
+ /** Follower info */
224
+ follow_info?: {
225
+ follow_status: number;
226
+ follower_count: number;
227
+ following_count: number;
228
+ };
229
+ }
230
+ /**
231
+ * Entry from online_audience endpoint (in-room top gifters).
232
+ * Has explicit `rank` (1-based) and `score` fields.
233
+ */
234
+ interface OnlineAudienceEntry {
235
+ rank: number;
236
+ score: number;
237
+ user: RanklistUser;
208
238
  }
239
+ /**
240
+ * Entry from anchor/rank_list endpoint (leaderboard/gifter rankings).
241
+ * Position is the array index. Uses `value` (not `score`).
242
+ */
243
+ interface AnchorRankListEntry {
244
+ /** Gift value (diamonds/coins) */
245
+ value: number;
246
+ /** Rank category type (e.g. 3 = gifter) */
247
+ rank_type: number;
248
+ /** Time period type (e.g. 1 = hourly) */
249
+ rank_time_type: number;
250
+ /** Auto-thanks message configured by anchor */
251
+ auto_thanks_message?: string;
252
+ /** Schema URL for navigation */
253
+ schema_url?: string;
254
+ /** Highlight DM share status */
255
+ highlight_dm_share_status?: number;
256
+ /** Ranked user data */
257
+ user: RanklistUser;
258
+ }
259
+ interface RanklistSelfInfo {
260
+ rank: number;
261
+ score: number;
262
+ gap_description?: string;
263
+ }
264
+ /** Response from "online_audience" sub-endpoint — in-room top gifters */
265
+ interface OnlineAudienceResponse {
266
+ status_code: number;
267
+ data: {
268
+ ranks: OnlineAudienceEntry[];
269
+ self_info?: RanklistSelfInfo;
270
+ currency?: string;
271
+ total?: number;
272
+ };
273
+ }
274
+ /** Response from "anchor_rank_list" sub-endpoint — gifter leaderboard */
275
+ interface AnchorRankListResponse {
276
+ status_code: number;
277
+ data: {
278
+ rank_list: AnchorRankListEntry[];
279
+ /** Room where ranks were accumulated */
280
+ latest_room_id_str?: string;
281
+ /** Rank period start (unix timestamp) */
282
+ rank_time_begin?: number;
283
+ /** Rank period end (unix timestamp) */
284
+ rank_time_end?: number;
285
+ /** Whether to show rank summary */
286
+ show_rank_summary?: boolean;
287
+ /** Default rank type for this anchor */
288
+ default_rank_type?: number;
289
+ };
290
+ }
291
+ /** Entrance tab info from "entrance" sub-endpoint */
292
+ interface EntranceTab {
293
+ title: string;
294
+ rank_type: number;
295
+ list_lynx_type?: number;
296
+ }
297
+ /** Entrance config for a single rank_type */
298
+ interface EntranceInfo {
299
+ rank_type: number;
300
+ /** Whether the anchor appears in this ranking */
301
+ owner_on_rank: boolean;
302
+ /** Anchor's position in this ranking (0-based) */
303
+ owner_rank_idx?: number;
304
+ /** Current score in this ranking */
305
+ current_score?: number;
306
+ /** Countdown in seconds until ranking resets */
307
+ countdown?: number;
308
+ /** Window size in seconds (86400 = daily) */
309
+ window_size?: number;
310
+ /** Unix timestamp when ranking resets */
311
+ reset_time?: number;
312
+ /** Related tab to show when clicking this entrance */
313
+ related_tab_rank_type?: number;
314
+ /** Gap description with points needed to reach a rank */
315
+ affiliated_content?: {
316
+ gap_desc?: {
317
+ default_pattern: string;
318
+ pieces?: Array<{
319
+ string_value: string;
320
+ type: number;
321
+ }>;
322
+ };
323
+ };
324
+ /** Class/League info */
325
+ class_info?: {
326
+ class_type: number;
327
+ star_count: number;
328
+ };
329
+ }
330
+ interface EntranceResponse {
331
+ status_code: number;
332
+ data: Array<{
333
+ group_type?: number;
334
+ Priority?: number;
335
+ data?: {
336
+ tabs?: EntranceTab[];
337
+ entrances?: EntranceInfo[];
338
+ };
339
+ }>;
340
+ }
341
+ type RanklistResponse = OnlineAudienceResponse | AnchorRankListResponse | EntranceResponse;
209
342
 
210
- type Listener = (...args: any[]) => void;
211
- declare class TypedEmitter {
212
- private _listeners;
213
- on(event: string, fn: Listener): this;
214
- once(event: string, fn: Listener): this;
215
- off(event: string, fn: Listener): this;
216
- emit(event: string, ...args: any[]): boolean;
217
- removeAllListeners(event?: string): this;
218
- }
219
- declare class TikTokLive extends TypedEmitter {
343
+ declare class TikTokLive extends EventEmitter {
220
344
  private ws;
221
345
  private heartbeatTimer;
222
346
  private reconnectAttempts;
@@ -224,7 +348,7 @@ declare class TikTokLive extends TypedEmitter {
224
348
  private _connected;
225
349
  private _eventCount;
226
350
  private _roomId;
227
- private _battleHosts;
351
+ private _ownerUserId;
228
352
  private readonly uniqueId;
229
353
  private readonly signServerUrl;
230
354
  private readonly apiKey;
@@ -232,8 +356,6 @@ declare class TikTokLive extends TypedEmitter {
232
356
  private readonly maxReconnectAttempts;
233
357
  private readonly heartbeatInterval;
234
358
  private readonly debug;
235
- private readonly webSocketImpl?;
236
- private WS;
237
359
  constructor(options: TikTokLiveOptions);
238
360
  connect(): Promise<void>;
239
361
  disconnect(): void;
@@ -244,107 +366,138 @@ declare class TikTokLive extends TypedEmitter {
244
366
  once<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this;
245
367
  off<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this;
246
368
  emit<K extends keyof TikTokLiveEvents>(event: K, ...args: Parameters<TikTokLiveEvents[K]>): boolean;
247
- private handleMessage;
248
369
  private handleFrame;
249
370
  private startHeartbeat;
250
371
  private stopHeartbeat;
251
372
  }
252
373
 
374
+ interface GetRanklistOptions {
375
+ /** API server URL (default: https://api.tik.tools) */
376
+ serverUrl?: string;
377
+ /** API key for authentication */
378
+ apiKey: string;
379
+ /** TikTok username to look up (auto-resolves room_id and anchor_id) */
380
+ uniqueId?: string;
381
+ /** Direct room ID (skip resolution) */
382
+ roomId?: string;
383
+ /** Direct anchor/owner ID (skip resolution) */
384
+ anchorId?: string;
385
+ /**
386
+ * TikTok session cookie string for authentication.
387
+ * Required — ranklist endpoints return 20003 without login.
388
+ * Example: "sessionid=abc123; sid_guard=def456"
389
+ */
390
+ sessionCookie?: string;
391
+ /**
392
+ * Which ranklist sub-endpoint to call:
393
+ * - "online_audience" (default) — top gifters with scores
394
+ * - "anchor_rank_list" — gifter ranking by rank_type
395
+ * - "entrance" — entrance UI metadata, tabs, gap-to-rank
396
+ */
397
+ type?: 'online_audience' | 'anchor_rank_list' | 'entrance';
398
+ /**
399
+ * For "anchor_rank_list" type only:
400
+ * - "1" = hourly ranking (default)
401
+ * - "8" = daily ranking
402
+ */
403
+ rankType?: string;
404
+ }
253
405
  /**
254
- * TikTool API utilities for the sign-and-return API flow.
406
+ * Fetch ranked user lists from TikTok via the sign server.
255
407
  *
256
- * The API server returns signed URLs instead of fetching TikTok data directly.
257
- * These utilities handle the two-step flow:
258
- * 1. resolve_required — scrape TikTok HTML to get room_id
259
- * 2. fetch_signed_url — use the signed URL to get actual TikTok data
408
+ * Requires `sessionCookie` for authentication without it, TikTok
409
+ * returns status 20003 ("Please login first").
260
410
  *
261
- * @module
262
- */
263
- /**
264
- * Resolved live page metadata from a TikTok live page.
411
+ * @example
412
+ * ```ts
413
+ * const data = await getRanklist({
414
+ * apiKey: 'your-key',
415
+ * uniqueId: 'katarina.live',
416
+ * sessionCookie: 'sessionid=abc; sid_guard=def',
417
+ * type: 'online_audience',
418
+ * });
419
+ * console.log(data.data.ranks); // top gifters
420
+ * ```
265
421
  */
266
- interface LivePageInfo {
267
- /** Active room ID for the livestream */
268
- roomId: string;
269
- /** Session cookie required for WebSocket authentication */
270
- ttwid: string;
271
- /** Server cluster region (e.g. 'us', 'eu') */
272
- clusterRegion: string;
273
- }
274
- /**
275
- * Scrape a TikTok live page to extract room metadata.
276
- * Returns the room ID, session cookie, and cluster region.
277
- * Results are cached for 5 minutes. Returns `null` if the user is not live.
278
- */
279
- declare function resolveLivePage(uniqueId: string): Promise<LivePageInfo | null>;
280
- /**
281
- * Resolve a TikTok username to a room ID.
282
- * Returns `null` if the user is not currently live.
283
- * Results are cached for 5 minutes.
284
- */
285
- declare function resolveRoomId(uniqueId: string): Promise<string | null>;
286
- interface SignedUrlResponse {
422
+ declare function getRanklist(opts: GetRanklistOptions): Promise<RanklistResponse>;
423
+
424
+ interface GetRegionalRanklistOptions {
425
+ /** API server URL (default: https://api.tik.tools) */
426
+ serverUrl?: string;
427
+ /** API key for authentication (Pro or Ultra tier required) */
428
+ apiKey: string;
429
+ /** TikTok username to look up (auto-resolves room_id and anchor_id) */
430
+ uniqueId?: string;
431
+ /** Direct room ID (skip resolution) */
432
+ roomId?: string;
433
+ /** Direct anchor/owner ID (skip resolution) */
434
+ anchorId?: string;
435
+ /**
436
+ * Ranking period:
437
+ * - "1" = Hourly
438
+ * - "8" = Daily (default)
439
+ * - "15" = Popular LIVE
440
+ * - "16" = League
441
+ */
442
+ rankType?: '1' | '8' | '15' | '16';
443
+ /**
444
+ * Sub-endpoint type:
445
+ * - "list" (default) — ranked users with scores
446
+ * - "entrance" — available ranking tabs/metadata
447
+ */
448
+ type?: 'list' | 'entrance';
449
+ /** Gap interval filter (default: "0") */
450
+ gapInterval?: string;
451
+ }
452
+ interface RegionalRanklistSignedResponse {
453
+ /** Always 0 on success */
287
454
  status_code: number;
288
- action?: string;
455
+ /** Always "fetch_signed_url" */
456
+ action: string;
457
+ /** The signed TikTok URL to POST */
289
458
  signed_url: string;
459
+ /** HTTP method (always POST) */
460
+ method: string;
461
+ /** Required headers for the fetch */
290
462
  headers: Record<string, string>;
463
+ /** URL-encoded POST body */
464
+ body: string;
465
+ /** Cookies to include (ttwid etc.) — append your sessionid */
291
466
  cookies: string;
467
+ /** Human-readable note */
468
+ note: string;
292
469
  }
293
470
  /**
294
- * Execute a signed-URL API response: fetch the signed URL and return the
295
- * parsed JSON data from TikTok.
296
- */
297
- declare function fetchSignedUrl(response: SignedUrlResponse): Promise<any>;
298
- interface CallApiOptions {
299
- /** API server URL (default: https://api.tik.tools) */
300
- serverUrl?: string;
301
- /** API key for authentication */
302
- apiKey: string;
303
- /** API endpoint path (e.g. '/webcast/room_video') */
304
- endpoint: string;
305
- /** TikTok unique_id to resolve */
306
- uniqueId: string;
307
- /** HTTP method (default: POST) */
308
- method?: 'GET' | 'POST';
309
- /** Additional body fields for POST requests */
310
- extraBody?: Record<string, any>;
311
- }
312
- /**
313
- * Call a TikTool API endpoint, handling the full
314
- * resolve_required → room_id → signed_url → fetch flow automatically.
471
+ * Get a signed URL for fetching regional LIVE leaderboard data.
315
472
  *
316
- * Returns the actual TikTok data, or `null` if the user is not live.
317
- */
318
- declare function callApi(opts: CallApiOptions): Promise<any>;
319
-
320
- /**
321
- * Solve a puzzle (slider) CAPTCHA using the TikTool solver.
473
+ * **Two-step pattern**: TikTok sessions are IP-bound, so instead of
474
+ * server-side fetching, this returns a signed URL with headers/body
475
+ * that you POST from your own IP with your session cookie.
322
476
  *
323
- * @param apiKey - API key for authentication
324
- * @param puzzleB64 - Base64-encoded background image (PNG/JPEG)
325
- * @param pieceB64 - Base64-encoded puzzle piece image (PNG/JPEG)
326
- * @param serverUrl - Custom server URL (default: https://api.tik.tools)
327
- * @returns Solve result with slide position and confidence
328
- */
329
- declare function solvePuzzle(apiKey: string, puzzleB64: string, pieceB64: string, serverUrl?: string): Promise<PuzzleSolveResult>;
330
- /**
331
- * Solve a rotate (whirl) CAPTCHA using the TikTool solver.
477
+ * Requires **Pro** or **Ultra** API key tier.
332
478
  *
333
- * @param apiKey - API key for authentication
334
- * @param outerB64 - Base64-encoded outer ring image (PNG/JPEG)
335
- * @param innerB64 - Base64-encoded inner rotated image (PNG/JPEG)
336
- * @param serverUrl - Custom server URL (default: https://api.tik.tools)
337
- * @returns Solve result with rotation angle and confidence
338
- */
339
- declare function solveRotate(apiKey: string, outerB64: string, innerB64: string, serverUrl?: string): Promise<RotateSolveResult>;
340
- /**
341
- * Solve a shapes (3D matching) CAPTCHA using the TikTool solver.
479
+ * @example
480
+ * ```ts
481
+ * // Step 1: Get signed URL
482
+ * const signed = await getRegionalRanklist({
483
+ * apiKey: 'your-pro-key',
484
+ * roomId: '7607695933891218198',
485
+ * anchorId: '7444599004337652758',
486
+ * rankType: '8', // Daily
487
+ * });
342
488
  *
343
- * @param apiKey - API key for authentication
344
- * @param imageB64 - Base64-encoded CAPTCHA image with shape grid (PNG/JPEG)
345
- * @param serverUrl - Custom server URL (default: https://api.tik.tools)
346
- * @returns Solve result with two matching shape coordinates and confidence
489
+ * // Step 2: Fetch from YOUR IP with YOUR session
490
+ * const resp = await fetch(signed.signed_url, {
491
+ * method: signed.method,
492
+ * headers: { ...signed.headers, Cookie: `sessionid=YOUR_SID; ${signed.cookies}` },
493
+ * body: signed.body,
494
+ * });
495
+ * const { data } = await resp.json();
496
+ * data.rank_view.ranks.forEach((r, i) =>
497
+ * console.log(`${i+1}. ${r.user.nickname} — ${r.score} pts`)
498
+ * );
499
+ * ```
347
500
  */
348
- declare function solveShapes(apiKey: string, imageB64: string, serverUrl?: string): Promise<ShapesSolveResult>;
501
+ declare function getRegionalRanklist(opts: GetRegionalRanklistOptions): Promise<RegionalRanklistSignedResponse>;
349
502
 
350
- export { type BaseEvent, type BattleArmiesEvent, type BattleEvent, type BattleTeam, type BattleTeamUser, type CallApiOptions, type ChatEvent, type ControlEvent, type EmoteChatEvent, type EnvelopeEvent, type GiftEvent, type LikeEvent, type LinkMicEvent, type LiveEvent, type LiveIntroEvent, type LivePageInfo, type MemberEvent, type PuzzleSolveResult, type QuestionEvent, type RankUpdateEvent, type RoomEvent, type RoomInfo, type RoomUserSeqEvent, type RotateSolveResult, type ShapesSolveResult, type SignedUrlResponse, type SocialEvent, type SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent, callApi, fetchSignedUrl, resolveLivePage, resolveRoomId, solvePuzzle, solveRotate, solveShapes };
503
+ export { type AnchorRankListEntry, type AnchorRankListResponse, type BarrageEvent, type BaseEvent, type BattleArmiesEvent, type BattleEvent, type BattleTaskEvent, type BattleTeam, type BattleTeamUser, type ChatEvent, type ControlEvent, type EmoteChatEvent, type EntranceInfo, type EntranceResponse, type EntranceTab, type EnvelopeEvent, type GetRanklistOptions, type GetRegionalRanklistOptions, type GiftEvent, type LikeEvent, type LinkMicEvent, type LiveEvent, type LiveIntroEvent, type MemberEvent, type OnlineAudienceEntry, type OnlineAudienceResponse, type QuestionEvent, type RankUpdateEvent, type RanklistResponse, type RanklistSelfInfo, type RanklistUser, type RegionalRanklistSignedResponse, type RoomEvent, type RoomInfo, type RoomUserSeqEvent, type SocialEvent, type SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent, getRanklist, getRegionalRanklist };