tiktok-live-api 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.
@@ -0,0 +1,349 @@
1
+ /**
2
+ * Type definitions for TikTok LIVE API events.
3
+ *
4
+ * These types provide IDE autocompletion and type safety
5
+ * when handling events from {@link TikTokLive} and {@link TikTokCaptions}.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ /** User profile attached to most LIVE events. */
10
+ interface TikTokUser {
11
+ userId: string;
12
+ uniqueId: string;
13
+ nickname: string;
14
+ profilePictureUrl: string;
15
+ followRole: number;
16
+ isSubscriber: boolean;
17
+ }
18
+ /** Payload for `chat` events. */
19
+ interface ChatEvent {
20
+ user: TikTokUser;
21
+ comment: string;
22
+ emotes: Array<{
23
+ emoteId: string;
24
+ image: string;
25
+ }>;
26
+ }
27
+ /** Payload for `gift` events. */
28
+ interface GiftEvent {
29
+ user: TikTokUser;
30
+ giftId: number;
31
+ giftName: string;
32
+ diamondCount: number;
33
+ repeatCount: number;
34
+ repeatEnd: boolean;
35
+ }
36
+ /** Payload for `like` events. */
37
+ interface LikeEvent {
38
+ user: TikTokUser;
39
+ likeCount: number;
40
+ totalLikes: number;
41
+ }
42
+ /** Payload for `member` (viewer join) events. */
43
+ interface MemberEvent {
44
+ user: TikTokUser;
45
+ actionId: number;
46
+ }
47
+ /** Payload for `follow` and `share` events. */
48
+ interface SocialEvent {
49
+ user: TikTokUser;
50
+ eventType: string;
51
+ }
52
+ /** Payload for `roomUserSeq` (viewer count) events. */
53
+ interface RoomUserSeqEvent {
54
+ viewerCount: number;
55
+ topViewers: TikTokUser[];
56
+ }
57
+ /** Payload for `battle` events. */
58
+ interface BattleEvent {
59
+ type: string;
60
+ teams: Array<Record<string, unknown>>;
61
+ scores: number[];
62
+ }
63
+ /** Payload for `caption` events from {@link TikTokCaptions}. */
64
+ interface CaptionEvent {
65
+ text: string;
66
+ speaker: string;
67
+ isFinal: boolean;
68
+ language: string;
69
+ }
70
+ /** Payload for `translation` events from {@link TikTokCaptions}. */
71
+ interface TranslationEvent {
72
+ text: string;
73
+ sourceLanguage: string;
74
+ targetLanguage: string;
75
+ }
76
+ /** Payload for `credits` events from {@link TikTokCaptions}. */
77
+ interface CreditsEvent {
78
+ total: number;
79
+ used: number;
80
+ remaining: number;
81
+ }
82
+ /** Connection event payload. */
83
+ interface ConnectedEvent {
84
+ uniqueId: string;
85
+ }
86
+ /** Disconnection event payload. */
87
+ interface DisconnectedEvent {
88
+ uniqueId: string;
89
+ }
90
+ /** Error event payload. */
91
+ interface ErrorEvent {
92
+ error: string;
93
+ }
94
+ /**
95
+ * Map of event names to their payload types for {@link TikTokLive}.
96
+ */
97
+ interface TikTokLiveEventMap {
98
+ chat: ChatEvent;
99
+ gift: GiftEvent;
100
+ like: LikeEvent;
101
+ follow: SocialEvent;
102
+ share: SocialEvent;
103
+ member: MemberEvent;
104
+ subscribe: SocialEvent;
105
+ roomUserSeq: RoomUserSeqEvent;
106
+ battle: BattleEvent;
107
+ envelope: Record<string, unknown>;
108
+ streamEnd: Record<string, unknown>;
109
+ roomInfo: Record<string, unknown>;
110
+ connected: ConnectedEvent;
111
+ disconnected: DisconnectedEvent;
112
+ error: ErrorEvent;
113
+ event: Record<string, unknown>;
114
+ }
115
+ /**
116
+ * Map of event names to their payload types for {@link TikTokCaptions}.
117
+ */
118
+ interface TikTokCaptionsEventMap {
119
+ caption: CaptionEvent;
120
+ translation: TranslationEvent;
121
+ credits: CreditsEvent;
122
+ credits_low: CreditsEvent;
123
+ status: Record<string, unknown>;
124
+ connected: ConnectedEvent;
125
+ disconnected: DisconnectedEvent;
126
+ error: ErrorEvent;
127
+ }
128
+
129
+ /**
130
+ * TikTokLive — Connect to any TikTok LIVE stream via WebSocket.
131
+ *
132
+ * Receives real-time events: chat messages, gifts, likes, follows,
133
+ * viewer counts, battles, and more. Powered by the TikTool managed API.
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * import { TikTokLive } from 'tiktok-live-api';
138
+ *
139
+ * const client = new TikTokLive('streamer_username', { apiKey: 'YOUR_KEY' });
140
+ *
141
+ * client.on('chat', (event) => {
142
+ * console.log(`${event.user.uniqueId}: ${event.comment}`);
143
+ * });
144
+ *
145
+ * client.connect();
146
+ * ```
147
+ *
148
+ * @packageDocumentation
149
+ */
150
+
151
+ /** Options for {@link TikTokLive} constructor. */
152
+ interface TikTokLiveOptions {
153
+ /** Your TikTool API key. Get one free at https://tik.tools */
154
+ apiKey?: string;
155
+ /** Auto-reconnect on disconnect (default: true). */
156
+ autoReconnect?: boolean;
157
+ /** Max reconnection attempts (default: 5). */
158
+ maxReconnectAttempts?: number;
159
+ }
160
+ type EventHandler$1<T> = (data: T) => void | Promise<void>;
161
+ /**
162
+ * Connect to a TikTok LIVE stream and receive real-time events.
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * const client = new TikTokLive('username', { apiKey: 'KEY' });
167
+ * client.on('chat', (e) => console.log(e.comment));
168
+ * client.on('gift', (e) => console.log(`${e.giftName} worth ${e.diamondCount} 💎`));
169
+ * client.connect();
170
+ * ```
171
+ */
172
+ declare class TikTokLive {
173
+ /** TikTok username (without @). */
174
+ readonly uniqueId: string;
175
+ /** Your TikTool API key. */
176
+ readonly apiKey: string;
177
+ /** Whether to auto-reconnect on disconnect. */
178
+ readonly autoReconnect: boolean;
179
+ /** Maximum reconnection attempts. */
180
+ readonly maxReconnectAttempts: number;
181
+ private _handlers;
182
+ private _ws;
183
+ private _connected;
184
+ private _intentionalClose;
185
+ private _reconnectAttempts;
186
+ private _eventCount;
187
+ /**
188
+ * Create a new TikTokLive client.
189
+ *
190
+ * @param uniqueId - TikTok username (without @)
191
+ * @param options - Configuration options
192
+ *
193
+ * @example
194
+ * ```typescript
195
+ * const client = new TikTokLive('streamer', { apiKey: 'YOUR_KEY' });
196
+ * ```
197
+ */
198
+ constructor(uniqueId: string, options?: TikTokLiveOptions);
199
+ /** Whether the client is currently connected. */
200
+ get connected(): boolean;
201
+ /** Total number of events received this session. */
202
+ get eventCount(): number;
203
+ /**
204
+ * Register an event handler.
205
+ *
206
+ * @param event - Event name (chat, gift, like, follow, etc.)
207
+ * @param handler - Callback function
208
+ * @returns this (for chaining)
209
+ *
210
+ * @example
211
+ * ```typescript
212
+ * client.on('chat', (event) => console.log(event.comment));
213
+ * client.on('gift', (event) => console.log(event.giftName));
214
+ * ```
215
+ */
216
+ on<K extends keyof TikTokLiveEventMap>(event: K, handler: EventHandler$1<TikTokLiveEventMap[K]>): this;
217
+ on(event: string, handler: EventHandler$1<any>): this;
218
+ /**
219
+ * Remove an event handler.
220
+ *
221
+ * @param event - Event name
222
+ * @param handler - The handler to remove
223
+ */
224
+ off(event: string, handler: EventHandler$1<any>): this;
225
+ private _emit;
226
+ /**
227
+ * Connect to the TikTok LIVE stream.
228
+ *
229
+ * @returns Promise that resolves when connected, rejects on fatal error.
230
+ *
231
+ * @example
232
+ * ```typescript
233
+ * await client.connect();
234
+ * ```
235
+ */
236
+ connect(): Promise<void>;
237
+ /**
238
+ * Disconnect from the stream.
239
+ */
240
+ disconnect(): void;
241
+ private _maybeReconnect;
242
+ }
243
+
244
+ /**
245
+ * TikTokCaptions — Real-time AI speech-to-text for TikTok LIVE streams.
246
+ *
247
+ * Transcribe and translate any TikTok LIVE stream in real-time.
248
+ * This feature is unique to TikTool Live — no other service offers it.
249
+ *
250
+ * @example
251
+ * ```typescript
252
+ * import { TikTokCaptions } from 'tiktok-live-api';
253
+ *
254
+ * const captions = new TikTokCaptions('streamer', {
255
+ * apiKey: 'YOUR_KEY',
256
+ * translate: 'en',
257
+ * diarization: true,
258
+ * });
259
+ *
260
+ * captions.on('caption', (event) => {
261
+ * console.log(`[${event.speaker}] ${event.text}`);
262
+ * });
263
+ *
264
+ * captions.connect();
265
+ * ```
266
+ *
267
+ * @packageDocumentation
268
+ */
269
+
270
+ /** Options for {@link TikTokCaptions} constructor. */
271
+ interface TikTokCaptionsOptions {
272
+ /** Your TikTool API key. Get one at https://tik.tools */
273
+ apiKey?: string;
274
+ /** Target language code for real-time translation (e.g. "en", "es"). */
275
+ translate?: string;
276
+ /** Enable speaker identification (default: true). */
277
+ diarization?: boolean;
278
+ /** Auto-disconnect after N minutes (default: 60, max: 300). */
279
+ maxDurationMinutes?: number;
280
+ }
281
+ type EventHandler<T> = (data: T) => void | Promise<void>;
282
+ /**
283
+ * Real-time AI speech-to-text for TikTok LIVE streams.
284
+ *
285
+ * @example
286
+ * ```typescript
287
+ * const captions = new TikTokCaptions('streamer', {
288
+ * apiKey: 'KEY',
289
+ * translate: 'en',
290
+ * });
291
+ * captions.on('caption', (e) => console.log(e.text));
292
+ * captions.on('translation', (e) => console.log(`→ ${e.text}`));
293
+ * captions.connect();
294
+ * ```
295
+ */
296
+ declare class TikTokCaptions {
297
+ /** TikTok username (without @). */
298
+ readonly uniqueId: string;
299
+ /** Your TikTool API key. */
300
+ readonly apiKey: string;
301
+ /** Target translation language. */
302
+ readonly translate?: string;
303
+ /** Whether speaker diarization is enabled. */
304
+ readonly diarization: boolean;
305
+ /** Max session duration in minutes. */
306
+ readonly maxDurationMinutes?: number;
307
+ private _handlers;
308
+ private _ws;
309
+ private _connected;
310
+ private _intentionalClose;
311
+ /**
312
+ * Create a new TikTokCaptions client.
313
+ *
314
+ * @param uniqueId - TikTok username (without @)
315
+ * @param options - Configuration options
316
+ */
317
+ constructor(uniqueId: string, options?: TikTokCaptionsOptions);
318
+ /** Whether currently connected and receiving captions. */
319
+ get connected(): boolean;
320
+ /**
321
+ * Register an event handler.
322
+ *
323
+ * @param event - Event name (caption, translation, credits, status, error)
324
+ * @param handler - Callback function
325
+ *
326
+ * @example
327
+ * ```typescript
328
+ * captions.on('caption', (event) => {
329
+ * const prefix = event.speaker ? `[${event.speaker}] ` : '';
330
+ * console.log(`${prefix}${event.text}${event.isFinal ? ' ✓' : '...'}`);
331
+ * });
332
+ * ```
333
+ */
334
+ on<K extends keyof TikTokCaptionsEventMap>(event: K, handler: EventHandler<TikTokCaptionsEventMap[K]>): this;
335
+ on(event: string, handler: EventHandler<any>): this;
336
+ /** Remove an event handler. */
337
+ off(event: string, handler: EventHandler<any>): this;
338
+ private _emit;
339
+ /**
340
+ * Start receiving captions from the stream.
341
+ *
342
+ * @returns Promise that resolves when connected.
343
+ */
344
+ connect(): Promise<void>;
345
+ /** Stop receiving captions. */
346
+ disconnect(): void;
347
+ }
348
+
349
+ export { type BattleEvent, type CaptionEvent, type ChatEvent, type ConnectedEvent, type CreditsEvent, type DisconnectedEvent, type ErrorEvent, type GiftEvent, type LikeEvent, type MemberEvent, type RoomUserSeqEvent, type SocialEvent, TikTokCaptions, type TikTokCaptionsEventMap, type TikTokCaptionsOptions, TikTokLive, type TikTokLiveEventMap, type TikTokLiveOptions, type TikTokUser, type TranslationEvent };
package/dist/index.js ADDED
@@ -0,0 +1,291 @@
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
+ TikTokCaptions: () => TikTokCaptions,
34
+ TikTokLive: () => TikTokLive
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // src/client.ts
39
+ var import_ws = __toESM(require("ws"));
40
+ var WS_BASE = "wss://api.tik.tools";
41
+ var VERSION = "1.0.0";
42
+ var TikTokLive = class {
43
+ /**
44
+ * Create a new TikTokLive client.
45
+ *
46
+ * @param uniqueId - TikTok username (without @)
47
+ * @param options - Configuration options
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const client = new TikTokLive('streamer', { apiKey: 'YOUR_KEY' });
52
+ * ```
53
+ */
54
+ constructor(uniqueId, options = {}) {
55
+ this._handlers = /* @__PURE__ */ new Map();
56
+ this._ws = null;
57
+ this._connected = false;
58
+ this._intentionalClose = false;
59
+ this._reconnectAttempts = 0;
60
+ this._eventCount = 0;
61
+ this.uniqueId = uniqueId.replace(/^@/, "");
62
+ this.apiKey = options.apiKey || process.env.TIKTOOL_API_KEY || "";
63
+ if (!this.apiKey) {
64
+ throw new Error("apiKey is required. Get a free key at https://tik.tools");
65
+ }
66
+ this.autoReconnect = options.autoReconnect ?? true;
67
+ this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
68
+ }
69
+ /** Whether the client is currently connected. */
70
+ get connected() {
71
+ return this._connected;
72
+ }
73
+ /** Total number of events received this session. */
74
+ get eventCount() {
75
+ return this._eventCount;
76
+ }
77
+ on(event, handler) {
78
+ if (!this._handlers.has(event)) {
79
+ this._handlers.set(event, /* @__PURE__ */ new Set());
80
+ }
81
+ this._handlers.get(event).add(handler);
82
+ return this;
83
+ }
84
+ /**
85
+ * Remove an event handler.
86
+ *
87
+ * @param event - Event name
88
+ * @param handler - The handler to remove
89
+ */
90
+ off(event, handler) {
91
+ this._handlers.get(event)?.delete(handler);
92
+ return this;
93
+ }
94
+ _emit(event, data) {
95
+ const handlers = this._handlers.get(event);
96
+ if (!handlers) return;
97
+ for (const handler of handlers) {
98
+ try {
99
+ const result = handler(data);
100
+ if (result instanceof Promise) {
101
+ result.catch(
102
+ (err) => console.error(`Error in '${event}' handler:`, err)
103
+ );
104
+ }
105
+ } catch (err) {
106
+ console.error(`Error in '${event}' handler:`, err);
107
+ }
108
+ }
109
+ }
110
+ /**
111
+ * Connect to the TikTok LIVE stream.
112
+ *
113
+ * @returns Promise that resolves when connected, rejects on fatal error.
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * await client.connect();
118
+ * ```
119
+ */
120
+ async connect() {
121
+ this._intentionalClose = false;
122
+ const uri = `${WS_BASE}?uniqueId=${this.uniqueId}&apiKey=${this.apiKey}`;
123
+ return new Promise((resolve, reject) => {
124
+ this._ws = new import_ws.default(uri, {
125
+ headers: { "User-Agent": `tiktok-live-api/${VERSION}` }
126
+ });
127
+ this._ws.on("open", () => {
128
+ this._connected = true;
129
+ this._reconnectAttempts = 0;
130
+ this._emit("connected", { uniqueId: this.uniqueId });
131
+ resolve();
132
+ });
133
+ this._ws.on("message", (raw) => {
134
+ try {
135
+ const event = JSON.parse(raw.toString());
136
+ this._eventCount++;
137
+ const eventType = event.event || "unknown";
138
+ const data = event.data || event;
139
+ this._emit("event", event);
140
+ this._emit(eventType, data);
141
+ } catch {
142
+ }
143
+ });
144
+ this._ws.on("close", () => {
145
+ this._connected = false;
146
+ this._emit("disconnected", { uniqueId: this.uniqueId });
147
+ this._maybeReconnect();
148
+ });
149
+ this._ws.on("error", (err) => {
150
+ this._emit("error", { error: err.message });
151
+ if (!this._connected) reject(err);
152
+ });
153
+ });
154
+ }
155
+ /**
156
+ * Disconnect from the stream.
157
+ */
158
+ disconnect() {
159
+ this._intentionalClose = true;
160
+ if (this._ws) {
161
+ this._ws.close();
162
+ this._ws = null;
163
+ }
164
+ this._connected = false;
165
+ }
166
+ async _maybeReconnect() {
167
+ if (this._intentionalClose || !this.autoReconnect || this._reconnectAttempts >= this.maxReconnectAttempts) {
168
+ return;
169
+ }
170
+ this._reconnectAttempts++;
171
+ const delay = Math.min(2 ** (this._reconnectAttempts - 1) * 1e3, 3e4);
172
+ await new Promise((r) => setTimeout(r, delay));
173
+ try {
174
+ await this.connect();
175
+ } catch {
176
+ }
177
+ }
178
+ };
179
+
180
+ // src/captions.ts
181
+ var import_ws2 = __toESM(require("ws"));
182
+ var CAPTIONS_BASE = "wss://api.tik.tools/captions";
183
+ var VERSION2 = "1.0.0";
184
+ var TikTokCaptions = class {
185
+ /**
186
+ * Create a new TikTokCaptions client.
187
+ *
188
+ * @param uniqueId - TikTok username (without @)
189
+ * @param options - Configuration options
190
+ */
191
+ constructor(uniqueId, options = {}) {
192
+ this._handlers = /* @__PURE__ */ new Map();
193
+ this._ws = null;
194
+ this._connected = false;
195
+ this._intentionalClose = false;
196
+ this.uniqueId = uniqueId.replace(/^@/, "");
197
+ this.apiKey = options.apiKey || process.env.TIKTOOL_API_KEY || "";
198
+ if (!this.apiKey) {
199
+ throw new Error("apiKey is required. Get a free key at https://tik.tools");
200
+ }
201
+ this.translate = options.translate;
202
+ this.diarization = options.diarization ?? true;
203
+ this.maxDurationMinutes = options.maxDurationMinutes;
204
+ }
205
+ /** Whether currently connected and receiving captions. */
206
+ get connected() {
207
+ return this._connected;
208
+ }
209
+ on(event, handler) {
210
+ if (!this._handlers.has(event)) {
211
+ this._handlers.set(event, /* @__PURE__ */ new Set());
212
+ }
213
+ this._handlers.get(event).add(handler);
214
+ return this;
215
+ }
216
+ /** Remove an event handler. */
217
+ off(event, handler) {
218
+ this._handlers.get(event)?.delete(handler);
219
+ return this;
220
+ }
221
+ _emit(event, data) {
222
+ const handlers = this._handlers.get(event);
223
+ if (!handlers) return;
224
+ for (const handler of handlers) {
225
+ try {
226
+ const result = handler(data);
227
+ if (result instanceof Promise) {
228
+ result.catch(
229
+ (err) => console.error(`Error in '${event}' handler:`, err)
230
+ );
231
+ }
232
+ } catch (err) {
233
+ console.error(`Error in '${event}' handler:`, err);
234
+ }
235
+ }
236
+ }
237
+ /**
238
+ * Start receiving captions from the stream.
239
+ *
240
+ * @returns Promise that resolves when connected.
241
+ */
242
+ async connect() {
243
+ this._intentionalClose = false;
244
+ let params = `uniqueId=${this.uniqueId}&apiKey=${this.apiKey}`;
245
+ if (this.translate) params += `&translate=${this.translate}`;
246
+ if (this.diarization) params += "&diarization=true";
247
+ if (this.maxDurationMinutes)
248
+ params += `&max_duration_minutes=${this.maxDurationMinutes}`;
249
+ const uri = `${CAPTIONS_BASE}?${params}`;
250
+ return new Promise((resolve, reject) => {
251
+ this._ws = new import_ws2.default(uri, {
252
+ headers: { "User-Agent": `tiktok-live-api/${VERSION2}` }
253
+ });
254
+ this._ws.on("open", () => {
255
+ this._connected = true;
256
+ this._emit("connected", { uniqueId: this.uniqueId });
257
+ resolve();
258
+ });
259
+ this._ws.on("message", (raw) => {
260
+ try {
261
+ const event = JSON.parse(raw.toString());
262
+ const msgType = event.type || "unknown";
263
+ this._emit(msgType, event);
264
+ } catch {
265
+ }
266
+ });
267
+ this._ws.on("close", () => {
268
+ this._connected = false;
269
+ this._emit("disconnected", { uniqueId: this.uniqueId });
270
+ });
271
+ this._ws.on("error", (err) => {
272
+ this._emit("error", { error: err.message });
273
+ if (!this._connected) reject(err);
274
+ });
275
+ });
276
+ }
277
+ /** Stop receiving captions. */
278
+ disconnect() {
279
+ this._intentionalClose = true;
280
+ if (this._ws) {
281
+ this._ws.close();
282
+ this._ws = null;
283
+ }
284
+ this._connected = false;
285
+ }
286
+ };
287
+ // Annotate the CommonJS export names for ESM import in node:
288
+ 0 && (module.exports = {
289
+ TikTokCaptions,
290
+ TikTokLive
291
+ });