@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/README.md CHANGED
@@ -2,52 +2,40 @@
2
2
 
3
3
  # @tiktool/live
4
4
 
5
- ### TikTok LIVE API SDK for Node.js, Bun, Deno, and Cloudflare Workers
5
+ ### Connect to any TikTok LIVE stream in 4 lines of code.
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/@tiktool/live?color=%23ff0050&label=npm&logo=npm)](https://www.npmjs.com/package/@tiktool/live)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
9
  [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-green?logo=node.js)](https://nodejs.org)
10
- [![TypeScript](https://img.shields.io/badge/TypeScript-First--Class-blue?logo=typescript)](https://www.typescriptlang.org)
11
- [![Downloads](https://img.shields.io/npm/dm/@tiktool/live?color=%2300c853)](https://www.npmjs.com/package/@tiktool/live)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue?logo=typescript)](https://www.typescriptlang.org)
12
11
 
13
- **Real-time TikTok LIVE events — chat messages, gifts, follows, likes, battles, viewer counts, and 18+ event types streamed directly to your application via WebSocket.**
12
+ Real-time chat, gifts, viewers, battles, follows & 18+ event types from any TikTok livestream.
14
13
 
15
- [Quick Start](#-quick-start) · [Events](#-events) · [API Utilities](#-api-utilities) · [REST API](#-rest-api-endpoints) · [Pricing](#-pricing) · [Get API Key](https://tik.tools)
14
+ [Quick Start](#-quick-start) · [Events](#-events) · [API](#-api-reference) · [Rate Limits](#-rate-limits) · [Get API Key](https://tik.tools)
16
15
 
17
16
  </div>
18
17
 
19
18
  ---
20
19
 
21
- ## Why @tiktool/live?
22
-
23
- - **Direct WebSocket connection** to TikTok from your own IP — events are never proxied through third-party servers
24
- - **Zero dependencies** — built-in protobuf parser, universal WebSocket resolution, gzip decompression
25
- - **Universal runtime support** — Node.js 18+, Bun, Deno, Cloudflare Workers, Durable Objects, and browsers
26
- - **Full TypeScript** — complete type definitions for all 18+ event types with IDE autocompletion
27
- - **Battle-tested** — production-ready with auto-reconnect, heartbeat management, and error recovery
28
- - **Generous free tier** — 2,500 requests/day, 1 WebSocket connection, no credit card required
29
-
30
- ---
31
-
32
20
  ## ⚡ Quick Start
33
21
 
34
22
  ```bash
35
23
  npm install @tiktool/live
36
24
  ```
37
25
 
38
- Get your free API key at **[tik.tools](https://tik.tools)** — no credit card required. The free tier includes WebSocket signing, live status checks, and 2,500 requests/day.
26
+ Get your free API key at [tik.tools](https://tik.tools)
39
27
 
40
28
  ```typescript
41
29
  import { TikTokLive } from '@tiktool/live';
42
30
 
43
31
  const live = new TikTokLive({
44
- uniqueId: 'username',
32
+ uniqueId: 'tv_asahi_news',
45
33
  apiKey: 'YOUR_API_KEY',
46
34
  });
47
35
 
48
36
  live.on('chat', e => console.log(`${e.user.uniqueId}: ${e.comment}`));
49
- live.on('gift', e => console.log(`${e.user.uniqueId} sent ${e.giftName} (${e.diamondCount}💎)`));
50
- live.on('member', e => console.log(`${e.user.uniqueId} joined the stream`));
37
+ live.on('gift', e => console.log(`${e.user.uniqueId} sent ${e.giftName} (${e.diamondCount} diamonds)`));
38
+ live.on('member', e => console.log(`${e.user.uniqueId} joined`));
51
39
  live.on('roomUserSeq', e => console.log(`Viewers: ${e.viewerCount}`));
52
40
 
53
41
  await live.connect();
@@ -55,32 +43,36 @@ await live.connect();
55
43
 
56
44
  ---
57
45
 
58
- ## Architecture
46
+ ## How It Works
59
47
 
60
48
  ```
61
- Your App api.tik.tools TikTok
49
+ Your App tik.tools TikTok
62
50
  +-----------+ +--------------+ +--------------+
63
- | |-- sign_url --> Signs URL | | |
64
- | SDK |<-- X-Bogus --| (1 req) | | WebSocket |
65
- | | | | | Server |
66
- | |------------- Connect directly --------->| |
67
- | |<------------ Live events (protobuf) <---| |
51
+ | -+-- sign_url --> Signs URL | | |
52
+ | Your <-+-- X-Bogus --| with params | | TikTok |
53
+ | Code | | | | WebSocket |
54
+ | -+------------ Connect directly ---------->| Server |
55
+ | <-+------------ Live events (protobuf) <---| |
68
56
  +-----------+ +--------------+ +--------------+
69
- ^ Only signing ^ Direct from
70
- touches our API YOUR IP
57
+ ^ Only interaction ^ Direct from
58
+ with our server YOUR IP
71
59
  ```
72
60
 
73
- The SDK calls the sign server **once** per connection to generate a cryptographic signature, then connects **directly** to TikTok's WebSocket server from your IP address. All event data flows directly between your app and TikTok — never through our infrastructure.
61
+ - Your app connects directly to TikTok from your IP address
62
+ - The sign server only generates cryptographic signatures (requires API key)
63
+ - TikTok never sees the sign server
64
+ - Built-in protobuf parser, no external dependencies
74
65
 
75
66
  ---
76
67
 
77
- ## 📡 Events
68
+ ## Events
69
+
70
+ ### Listening
78
71
 
79
72
  ```typescript
80
73
  live.on('chat', (event) => {
81
- event.user.uniqueId; // string
82
- event.user.nickname; // string
83
- event.comment; // string
74
+ event.user.uniqueId // string
75
+ event.comment // string
84
76
  });
85
77
 
86
78
  live.on('event', (event) => {
@@ -88,238 +80,129 @@ live.on('event', (event) => {
88
80
  });
89
81
  ```
90
82
 
91
- ### Event Reference
92
-
93
- | Event | Type | Description | Key Fields |
94
- |-------|------|-------------|------------|
95
- | `chat` | `ChatEvent` | Chat message received | `user`, `comment`, `emotes` |
96
- | `member` | `MemberEvent` | User joined the stream | `user`, `action` |
97
- | `like` | `LikeEvent` | User liked the stream | `user`, `likeCount`, `totalLikes` |
98
- | `gift` | `GiftEvent` | Gift sent to streamer | `user`, `giftName`, `diamondCount`, `repeatCount`, `combo` |
99
- | `social` | `SocialEvent` | Follow or share event | `user`, `action` (`'follow'` / `'share'`) |
100
- | `roomUserSeq` | `RoomUserSeqEvent` | Viewer count update | `viewerCount`, `totalViewers` |
101
- | `battle` | `BattleEvent` | Battle started/ended | `battleId`, `status`, `teams` |
102
- | `battleArmies` | `BattleArmiesEvent` | Battle scores update | `battleId`, `teams` (with scores) |
83
+ ### Reference
84
+
85
+ | Event | Type | Description | Fields |
86
+ |-------|------|-------------|--------|
87
+ | `chat` | `ChatEvent` | Chat message | `user`, `comment` |
88
+ | `member` | `MemberEvent` | User joined | `user`, `action` |
89
+ | `like` | `LikeEvent` | User liked | `user`, `likeCount`, `totalLikes` |
90
+ | `gift` | `GiftEvent` | Gift sent | `user`, `giftName`, `diamondCount`, `repeatCount`, `combo` |
91
+ | `social` | `SocialEvent` | Follow / Share | `user`, `action` |
92
+ | `roomUserSeq` | `RoomUserSeqEvent` | Viewer count | `viewerCount`, `totalViewers` |
93
+ | `battle` | `BattleEvent` | Link Mic battle | `status` |
94
+ | `battleArmies` | `BattleArmiesEvent` | Battle teams | |
103
95
  | `subscribe` | `SubscribeEvent` | New subscriber | `user`, `subMonth` |
104
- | `emoteChat` | `EmoteChatEvent` | Emote message | `user`, `emoteId`, `emoteUrl` |
105
- | `envelope` | `EnvelopeEvent` | Treasure chest | `envelopeId`, `diamondCount` |
96
+ | `emoteChat` | `EmoteChatEvent` | Emote in chat | `user`, `emoteId` |
97
+ | `envelope` | `EnvelopeEvent` | Treasure chest | `diamondCount` |
106
98
  | `question` | `QuestionEvent` | Q&A question | `user`, `questionText` |
107
- | `control` | `ControlEvent` | Stream control signal | `action` (3 = stream ended) |
108
- | `room` | `RoomEvent` | Room status change | `status` |
109
- | `liveIntro` | `LiveIntroEvent` | Stream intro displayed | `roomId`, `title` |
110
- | `rankUpdate` | `RankUpdateEvent` | Ranking update | `rankType`, `rankList` |
111
- | `linkMic` | `LinkMicEvent` | Link Mic event | `action`, `users` |
112
- | `unknown` | `UnknownEvent` | Unrecognized message | `method` |
99
+ | `control` | `ControlEvent` | Stream control | `action` (3 = ended) |
100
+ | `room` | `RoomEvent` | Room status | `status` |
101
+ | `liveIntro` | `LiveIntroEvent` | Stream intro | `title` |
102
+ | `rankUpdate` | `RankUpdateEvent` | Rank update | `rankType` |
103
+ | `linkMic` | `LinkMicEvent` | Link Mic | `action` |
104
+ | `unknown` | `UnknownEvent` | Unrecognized | `method` |
113
105
 
114
106
  ### Connection Events
115
107
 
116
108
  | Event | Callback | Description |
117
109
  |-------|----------|-------------|
118
- | `connected` | `() => void` | Successfully connected to the livestream |
119
- | `disconnected` | `(code, reason) => void` | Disconnected from the livestream |
120
- | `roomInfo` | `(info: RoomInfo) => void` | Room metadata received |
121
- | `error` | `(error: Error) => void` | Connection or parsing error |
110
+ | `connected` | `() => void` | Connected to stream |
111
+ | `disconnected` | `(code, reason) => void` | Disconnected |
112
+ | `roomInfo` | `(info: RoomInfo) => void` | Room info |
113
+ | `error` | `(error: Error) => void` | Error |
122
114
 
123
115
  ---
124
116
 
125
- ## 🛠️ API Utilities
126
-
127
- The SDK includes server-side utilities for calling TikTool REST endpoints. These handle the **sign-and-return** flow automatically — resolving usernames to room IDs, fetching signed URLs, and returning actual TikTok data.
128
-
129
- ### `callApi(options)` — High-Level Helper
117
+ ## API Reference
130
118
 
131
- The easiest way to call any TikTool endpoint. Handles the full flow automatically:
132
-
133
- ```typescript
134
- import { callApi } from '@tiktool/live';
135
-
136
- // Get stream video URLs for a user
137
- const streamData = await callApi({
138
- apiKey: 'YOUR_API_KEY',
139
- endpoint: '/webcast/room_video',
140
- uniqueId: 'streamer_name',
141
- });
142
-
143
- // Get room info
144
- const roomInfo = await callApi({
145
- apiKey: 'YOUR_API_KEY',
146
- endpoint: '/webcast/room_info',
147
- uniqueId: 'streamer_name',
148
- });
149
-
150
- // Use a custom server URL
151
- const data = await callApi({
152
- serverUrl: 'https://your-server.com',
153
- apiKey: 'YOUR_API_KEY',
154
- endpoint: '/webcast/rankings',
155
- uniqueId: 'streamer_name',
156
- method: 'GET',
157
- });
158
- ```
159
-
160
- #### `CallApiOptions`
119
+ ### `new TikTokLive(options)`
161
120
 
162
121
  | Option | Type | Default | Description |
163
122
  |--------|------|---------|-------------|
164
- | `apiKey` | `string` | — | API key from [tik.tools](https://tik.tools) |
165
- | `endpoint` | `string` | — | API path (e.g. `'/webcast/room_video'`) |
166
- | `uniqueId` | `string` | | TikTok username to resolve |
167
- | `serverUrl` | `string` | `https://api.tik.tools` | Custom API server URL |
168
- | `method` | `'GET' \| 'POST'` | `'POST'` | HTTP method |
169
- | `extraBody` | `object` | | Additional POST body fields |
170
-
171
- ### `resolveRoomId(uniqueId)` — Username to Room ID
172
-
173
- Scrapes a TikTok live page to extract the `room_id`. Results are cached for 5 minutes. Returns `null` if the user is not live.
174
-
175
- ```typescript
176
- import { resolveRoomId } from '@tiktool/live';
177
-
178
- const roomId = await resolveRoomId('streamer_name');
179
- if (roomId) {
180
- console.log(`Room ID: ${roomId}`);
181
- }
182
- ```
183
-
184
- ### `resolveLivePage(uniqueId)` — Full Live Page Resolution
185
-
186
- Returns all live page metadata including the room ID, session cookie (`ttwid`), and cluster region. Used internally by `TikTokLive.connect()`. Returns `null` if the user is not live.
187
-
188
- ```typescript
189
- import { resolveLivePage } from '@tiktool/live';
123
+ | `uniqueId` | `string` | — | TikTok username (without @) |
124
+ | `apiKey` | `string` | — | **Required.** API key from [tik.tools](https://tik.tools) |
125
+ | `signServerUrl` | `string` | `https://api.tik.tools` | Sign server URL |
126
+ | `autoReconnect` | `boolean` | `true` | Auto-reconnect on disconnect |
127
+ | `maxReconnectAttempts` | `number` | `5` | Max reconnect attempts |
128
+ | `heartbeatInterval` | `number` | `10000` | Heartbeat interval (ms) |
129
+ | `debug` | `boolean` | `false` | Debug logging |
190
130
 
191
- const info = await resolveLivePage('streamer_name');
192
- if (info) {
193
- console.log(`Room: ${info.roomId}, Region: ${info.clusterRegion}`);
194
- }
195
- ```
131
+ ### Methods
196
132
 
197
- #### `LivePageInfo`
133
+ | Method | Returns | Description |
134
+ |--------|---------|-------------|
135
+ | `connect()` | `Promise<void>` | Connect to livestream |
136
+ | `disconnect()` | `void` | Disconnect |
137
+ | `connected` | `boolean` | Connection status |
138
+ | `eventCount` | `number` | Total events received |
139
+ | `roomId` | `string` | Current room ID |
198
140
 
199
- | Field | Type | Description |
200
- |-------|------|-------------|
201
- | `roomId` | `string` | Active room ID for the livestream |
202
- | `ttwid` | `string` | Session cookie for WebSocket auth |
203
- | `clusterRegion` | `string` | Server cluster region (e.g. `'us'`, `'eu'`) |
141
+ ---
204
142
 
205
- ### `fetchSignedUrl(response)` — Execute Signed URL Response
143
+ ## Rate Limits
206
144
 
207
- Takes a sign-and-return API response and fetches the actual TikTok data from the signed URL.
145
+ All API requests require an API key. Get yours at [tik.tools](https://tik.tools).
208
146
 
209
- ```typescript
210
- import { fetchSignedUrl } from '@tiktool/live';
147
+ | Tier | Rate Limit | Endpoints | Price |
148
+ |------|-----------|-----------|-------|
149
+ | **Free** | 5/min | `sign_url`, `check_alive` | Free |
150
+ | **Basic** | 30/min | + `fetch`, `room_info`, `bulk_live_check`, WS relay | Free (with key) |
151
+ | **Pro** | 120/min | + `room_video`, `ranklist/regional`, CAPTCHA solver, all endpoints | Coming soon |
211
152
 
212
- // After calling an API endpoint that returns action: 'fetch_signed_url'
213
- const tikTokData = await fetchSignedUrl(apiResponse);
214
- ```
153
+ The SDK calls the sign server **once per connection**, then stays connected via WebSocket. A free key is sufficient for most use cases.
215
154
 
216
155
  ---
217
156
 
218
- ## 🧩 CAPTCHA Solver (Ultra)
157
+ ## 🏆 Regional Leaderboard
219
158
 
220
- Solve TikTok CAPTCHAs programmatically. Requires an **Ultra** tier API key.
159
+ Get daily, hourly, popular, or league LIVE rankings for any streamer. **Requires Pro or Ultra tier.**
221
160
 
222
- ### `solvePuzzle(apiKey, puzzleB64, pieceB64, serverUrl?)` Puzzle Slider
223
-
224
- ```typescript
225
- import { solvePuzzle } from '@tiktool/live';
226
-
227
- const result = await solvePuzzle('YOUR_API_KEY', backgroundBase64, pieceBase64);
228
- console.log(`Slide to X=${result.slideX}px (confidence: ${result.confidence})`);
229
- ```
161
+ This endpoint uses a **two-step sign-and-return pattern** because TikTok sessions are IP-bound:
230
162
 
231
- | Field | Type | Description |
232
- |-------|------|-------------|
233
- | `slideX` | `number` | Pixel X position for the slider |
234
- | `confidence` | `number` | Confidence score (0.0–1.0) |
235
- | `solveTimeMs` | `number` | Time taken to solve in ms |
236
-
237
- ### `solveRotate(apiKey, outerB64, innerB64, serverUrl?)` — Rotate Whirl
163
+ 1. Call `getRegionalRanklist()` to get a signed URL from the server
164
+ 2. POST the signed URL from **your own IP** with your TikTok session cookie
238
165
 
239
166
  ```typescript
240
- import { solveRotate } from '@tiktool/live';
241
-
242
- const result = await solveRotate('YOUR_API_KEY', outerBase64, innerBase64);
243
- console.log(`Rotate ${result.angle}° (confidence: ${result.confidence})`);
244
- ```
245
-
246
- | Field | Type | Description |
247
- |-------|------|-------------|
248
- | `angle` | `number` | Rotation angle in degrees (0–360) |
249
- | `confidence` | `number` | Confidence score (0.0–1.0) |
250
- | `solveTimeMs` | `number` | Time taken to solve in ms |
251
-
252
- ### `solveShapes(apiKey, imageB64, serverUrl?)` — 3D Shape Matching
167
+ import { getRegionalRanklist } from '@tiktool/live';
168
+
169
+ // Step 1: Get signed URL from the API
170
+ const signed = await getRegionalRanklist({
171
+ apiKey: 'YOUR_PRO_KEY',
172
+ roomId: '7607695933891218198',
173
+ anchorId: '7444599004337652758',
174
+ rankType: '8', // Daily
175
+ });
253
176
 
254
- ```typescript
255
- import { solveShapes } from '@tiktool/live';
177
+ // Step 2: Fetch from YOUR IP with YOUR session cookie
178
+ const resp = await fetch(signed.signed_url, {
179
+ method: signed.method,
180
+ headers: { ...signed.headers, Cookie: `sessionid=YOUR_SID; ${signed.cookies}` },
181
+ body: signed.body,
182
+ });
256
183
 
257
- const result = await solveShapes('YOUR_API_KEY', imageBase64);
258
- console.log(`Match: (${result.point1.x},${result.point1.y}) (${result.point2.x},${result.point2.y})`);
184
+ const { data } = await resp.json();
185
+ data.rank_view.ranks.forEach((r: any, i: number) =>
186
+ console.log(`${i+1}. ${r.user.nickname} — ${r.score} pts`)
187
+ );
259
188
  ```
260
189
 
261
- | Field | Type | Description |
262
- |-------|------|-------------|
263
- | `point1` | `{x, y}` | First matching shape center |
264
- | `point2` | `{x, y}` | Second matching shape center |
265
- | `confidence` | `number` | Confidence score (0.0–1.0) |
266
- | `solveTimeMs` | `number` | Time taken to solve in ms |
190
+ ### Rank Types
267
191
 
268
- ---
269
-
270
- ## 🌐 REST API Endpoints
271
-
272
- The sign server at `api.tik.tools` exposes a full REST API alongside WebSocket signing. Endpoints use a **sign-and-return** pattern — when called with a `unique_id`, they return instructions for resolving the room ID; when called with a `room_id`, they return a signed URL to fetch data directly from TikTok. Use `callApi()` to handle this automatically.
273
-
274
- | Endpoint | Description |
275
- |----------|-------------|
276
- | `POST /webcast/sign_url` | Generate X-Bogus signed URL |
277
- | `GET /webcast/check_alive` | Check if a user is currently live |
278
- | `GET /webcast/room_info` | Detailed room metadata (title, viewers, streamer info) |
279
- | `GET /webcast/room_id` | Resolve username to room ID |
280
- | `GET /webcast/room_cover` | Get stream cover image URL |
281
- | `GET /webcast/room_video` | Get HLS/FLV stream URLs |
282
- | `GET /webcast/rankings` | In-stream contribution rankings |
283
- | `GET /webcast/gift_info` | Available gifts and diamond values |
284
- | `GET /webcast/hashtag_list` | Trending live hashtags |
285
- | `GET /webcast/bulk_live_check` | Check multiple users' live status in one request |
286
- | `POST /webcast/fetch` | Fetch protobuf messages via HTTP polling |
287
- | `GET /webcast/ws_credentials` | Get WebSocket connection credentials |
288
- | `POST /webcast/sign_websocket` | Sign a WebSocket URL |
289
- | `GET /webcast/resolve_user_ids` | Resolve display names to user IDs |
290
- | `POST /webcast/chat` | Send chat messages (requires session cookies) |
291
- | `GET /webcast/user_earnings` | Streamer earnings data (requires session cookies) |
292
- | `GET /webcast/feed` | Live feed discovery |
293
- | `GET /webcast/live_analytics/*` | Video list, video detail, user interactions |
294
- | `GET /webcast/moderation/*` | Mute and ban management |
295
- | `POST /authentication/jwt` | Generate scoped JWT for frontend WebSocket access |
296
- | `POST /captcha/solve/puzzle` | Solve puzzle slider CAPTCHA (**Pro+**) |
297
- | `POST /captcha/solve/rotate` | Solve rotate whirl CAPTCHA (**Pro+**) |
298
- | `POST /captcha/solve/shapes` | Solve 3D shape matching CAPTCHA (**Pro+**) |
299
-
300
- Full API documentation: **[tik.tools/docs](https://tik.tools/docs)**
301
-
302
- ---
192
+ | Value | Period |
193
+ |-------|--------|
194
+ | `"1"` | Hourly |
195
+ | `"8"` | Daily (default) |
196
+ | `"15"` | Popular LIVE |
197
+ | `"16"` | League |
303
198
 
304
- ## 💰 Pricing
199
+ ### Response Shape
305
200
 
306
- All plans include signatures and full WebSocket support. Higher tiers unlock priority routing, CAPTCHA solving, and increased throughput.
307
-
308
- | | **Free** | **Pro** | **Ultra** |
309
- |---|---|---|---|
310
- | **Weekly** | Free | $15/wk | $45/wk |
311
- | **Monthly** | Free | $48/mo | $169/mo |
312
- | **Yearly** | Free | $39/mo | $149/mo |
313
- | **Daily Requests** | 2,500 | 50,000 | 250,000 |
314
- | **WebSocket Connections** | 3 | 50 | 1,000 |
315
- | **Endpoints** | All | All | All |
316
- | **CAPTCHA Solver** | — | 50/day | 500/day |
317
- | **Uptime SLA** | — | — | 99.5% |
318
- | **Priority Routing** | — | ✓ | ✓ |
319
- | **Overage** | — | $0.001/req | $0.0005/req |
320
- | **Support** | Community | Email | Dedicated |
321
-
322
- Get your Free API key at **[tik.tools](https://tik.tools)**.
201
+ The TikTok response contains `data.rank_view` with:
202
+ - **`ranks`** — Array of ranked users with `user.nickname`, `user.display_id`, `score`
203
+ - **`owner_rank`** The streamer's own position and gap info
204
+ - **`countdown`** — Seconds until the ranking period ends
205
+ - **`cut_off_line`** Minimum score to appear on the leaderboard
323
206
 
324
207
  ---
325
208
 
@@ -343,15 +226,14 @@ live.on('chat', (e) => {
343
226
 
344
227
  live.on('gift', (e) => {
345
228
  if (e.repeatEnd) {
346
- const total = e.diamondCount * e.repeatCount;
347
- console.log(`${e.user.uniqueId} sent ${e.repeatCount}x ${e.giftName} (${total} diamonds)`);
229
+ console.log(`${e.user.uniqueId} sent ${e.repeatCount}x ${e.giftName} (${e.diamondCount * e.repeatCount} diamonds)`);
348
230
  }
349
231
  });
350
232
 
351
233
  await live.connect();
352
234
  ```
353
235
 
354
- ### Real-Time OBS Overlay
236
+ ### OBS Overlay
355
237
 
356
238
  ```typescript
357
239
  import { TikTokLive } from '@tiktool/live';
@@ -370,123 +252,17 @@ live.on('event', (event) => {
370
252
  });
371
253
 
372
254
  await live.connect();
373
- console.log('Forwarding TikTok LIVE events ws://localhost:8080');
374
- ```
375
-
376
- ### Gift Leaderboard
377
-
378
- ```typescript
379
- import { TikTokLive, GiftEvent } from '@tiktool/live';
380
-
381
- const live = new TikTokLive({
382
- uniqueId: 'streamer_name',
383
- apiKey: 'YOUR_API_KEY',
384
- });
385
-
386
- const leaderboard = new Map<string, number>();
387
-
388
- live.on('gift', (e: GiftEvent) => {
389
- if (e.repeatEnd) {
390
- const total = e.diamondCount * e.repeatCount;
391
- const current = leaderboard.get(e.user.uniqueId) || 0;
392
- leaderboard.set(e.user.uniqueId, current + total);
393
-
394
- const sorted = [...leaderboard.entries()].sort((a, b) => b[1] - a[1]);
395
- console.clear();
396
- console.log('🏆 Gift Leaderboard');
397
- sorted.slice(0, 10).forEach(([user, diamonds], i) => {
398
- console.log(`${i + 1}. ${user}: ${diamonds} 💎`);
399
- });
400
- }
401
- });
402
-
403
- await live.connect();
404
- ```
405
-
406
- ### Battle Monitor
407
-
408
- ```typescript
409
- import { TikTokLive } from '@tiktool/live';
410
-
411
- const live = new TikTokLive({
412
- uniqueId: 'streamer_name',
413
- apiKey: 'YOUR_API_KEY',
414
- });
415
-
416
- live.on('battle', (e) => {
417
- console.log(`Battle ${e.battleId} — Status: ${e.status}`);
418
- for (const team of e.teams) {
419
- console.log(` Team ${team.hostUserId}: ${team.score} points (${team.users.length} members)`);
420
- }
421
- });
422
-
423
- live.on('battleArmies', (e) => {
424
- for (const team of e.teams) {
425
- const host = team.hostUser?.uniqueId || team.hostUserId;
426
- console.log(` ${host}: ${team.score} points`);
427
- }
428
- });
429
-
430
- await live.connect();
431
- ```
432
-
433
- ### Frontend JWT Authentication
434
-
435
- ```typescript
436
- const data = await fetch('https://tik.tools/api/live/connect?uniqueId=streamer')
437
- .then(r => r.json());
438
-
439
- const ws = new WebSocket(`${data.wsUrl}?uniqueId=streamer&jwtKey=${data.token}`);
440
-
441
- ws.onmessage = (event) => {
442
- const parsed = JSON.parse(event.data);
443
- console.log(parsed.type, parsed);
444
- };
255
+ console.log('Forwarding events to ws://localhost:8080');
445
256
  ```
446
257
 
447
258
  ---
448
259
 
449
- ## 🔧 Configuration
450
-
451
- ### `new TikTokLive(options)`
452
-
453
- | Option | Type | Default | Description |
454
- |--------|------|---------|-------------|
455
- | `uniqueId` | `string` | — | TikTok username (without `@`) |
456
- | `apiKey` | `string` | — | API key from [tik.tools](https://tik.tools) |
457
- | `signServerUrl` | `string` | `https://api.tik.tools` | Custom sign server URL |
458
- | `autoReconnect` | `boolean` | `true` | Auto-reconnect on disconnect |
459
- | `maxReconnectAttempts` | `number` | `5` | Maximum reconnect attempts |
460
- | `heartbeatInterval` | `number` | `10000` | Heartbeat interval in milliseconds |
461
- | `debug` | `boolean` | `false` | Enable debug logging |
462
- | `webSocketImpl` | `any` | Auto-detected | Custom WebSocket implementation |
463
-
464
- ### Instance Properties
465
-
466
- | Property | Type | Description |
467
- |----------|------|-------------|
468
- | `connected` | `boolean` | Current connection status |
469
- | `eventCount` | `number` | Total events received |
470
- | `roomId` | `string` | Active room ID |
471
-
472
- ### Instance Methods
473
-
474
- | Method | Returns | Description |
475
- |--------|---------|-------------|
476
- | `connect()` | `Promise<void>` | Connect to a livestream |
477
- | `disconnect()` | `void` | Gracefully disconnect |
478
- | `on(event, handler)` | `this` | Listen for events |
479
- | `once(event, handler)` | `this` | Listen for a single event |
480
- | `off(event, handler)` | `this` | Remove event listener |
481
-
482
- ---
483
-
484
260
  ## TypeScript
485
261
 
486
- Full TypeScript support with generic type inference on all event handlers:
262
+ Full TypeScript support with type inference:
487
263
 
488
264
  ```typescript
489
- import { TikTokLive, ChatEvent, GiftEvent, BattleEvent } from '@tiktool/live';
265
+ import { TikTokLive, ChatEvent, GiftEvent } from '@tiktool/live';
490
266
 
491
267
  const live = new TikTokLive({
492
268
  uniqueId: 'username',
@@ -502,30 +278,10 @@ live.on('gift', (event: GiftEvent) => {
502
278
  const diamonds: number = event.diamondCount;
503
279
  const isCombo: boolean = event.combo;
504
280
  });
505
-
506
- live.on('battle', (event: BattleEvent) => {
507
- const teams: typeof event.teams = event.teams;
508
- const score: number = teams[0].score;
509
- });
510
281
  ```
511
282
 
512
- All event types, user interfaces, and configuration options are fully exported for use in your application's type system.
513
-
514
- ---
515
-
516
- ## Runtime Compatibility
517
-
518
- | Runtime | Version | WebSocket | Notes |
519
- |---------|---------|-----------|-------|
520
- | **Node.js** | ≥ 22 | Native | No additional packages needed |
521
- | **Node.js** | 18–21 | `ws` package | `npm i ws` |
522
- | **Bun** | ≥ 1.0 | Native | Full support |
523
- | **Deno** | ≥ 1.x | Native | Full support |
524
- | **Cloudflare Workers** | — | Native | Full support |
525
- | **Browsers** | Modern | Native | Via bundled connect endpoint |
526
-
527
283
  ---
528
284
 
529
285
  ## License
530
286
 
531
- MIT © [TikTool](https://tik.tools)
287
+ MIT © [tiktool](https://tik.tools)