lightstream-sdk 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.
- package/LICENSE +21 -0
- package/QUICKSTART.md +150 -0
- package/README.md +178 -0
- package/dist/errors.d.ts +26 -0
- package/dist/index.cjs +404 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.mjs +365 -0
- package/dist/rest-client.d.ts +45 -0
- package/dist/src/errors.d.ts +26 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/rest-client.d.ts +45 -0
- package/dist/src/stream-client.d.ts +26 -0
- package/dist/src/types.d.ts +86 -0
- package/dist/stream-client.d.ts +26 -0
- package/dist/types.d.ts +86 -0
- package/examples/basic-connection.ts +15 -0
- package/examples/bot-moderation.ts +27 -0
- package/examples/earnings-tracker.ts +19 -0
- package/examples/react-use-stream.tsx +48 -0
- package/package.json +53 -0
- package/src/errors.ts +49 -0
- package/src/index.ts +29 -0
- package/src/rest-client.ts +143 -0
- package/src/stream-client.ts +250 -0
- package/src/types.ts +102 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// src/rest-client.ts
|
|
5
|
+
class LightStreamRestClient {
|
|
6
|
+
config;
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
}
|
|
10
|
+
async fetchJson(path, options = {}) {
|
|
11
|
+
const url = `${this.config.baseUrl.replace(/\/+$/, "")}${path}`;
|
|
12
|
+
const headers = new Headers(options.headers || {});
|
|
13
|
+
if (!headers.has("Accept"))
|
|
14
|
+
headers.set("Accept", "application/json");
|
|
15
|
+
if (this.config.apiKey) {
|
|
16
|
+
headers.set("x-api-key", this.config.apiKey);
|
|
17
|
+
} else if (this.config.jwtKey) {
|
|
18
|
+
headers.set("Authorization", `Bearer ${this.config.jwtKey}`);
|
|
19
|
+
}
|
|
20
|
+
const res = await fetch(url, { ...options, headers });
|
|
21
|
+
const data = await res.json();
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
throw new Error(data?.error || `HTTP ${res.status}`);
|
|
24
|
+
}
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
27
|
+
async issueToken(options) {
|
|
28
|
+
const res = await this.fetchJson("/api/v1/auth/tokens", {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: { "Content-Type": "application/json" },
|
|
31
|
+
body: JSON.stringify(options)
|
|
32
|
+
});
|
|
33
|
+
return res.data;
|
|
34
|
+
}
|
|
35
|
+
async bulkCheckStreams(requests) {
|
|
36
|
+
const res = await this.fetchJson("/api/v1/streams/bulk-check", {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: { "Content-Type": "application/json" },
|
|
39
|
+
body: JSON.stringify({ requests })
|
|
40
|
+
});
|
|
41
|
+
return res.results;
|
|
42
|
+
}
|
|
43
|
+
async getRoomStreamInfo(platform, channel) {
|
|
44
|
+
const res = await this.fetchJson(`/api/v1/rooms/${encodeURIComponent(platform)}/${encodeURIComponent(channel)}/stream-info`);
|
|
45
|
+
return res.data;
|
|
46
|
+
}
|
|
47
|
+
async getUserEarnings(platform, channel) {
|
|
48
|
+
const res = await this.fetchJson(`/api/v1/users/${encodeURIComponent(platform)}/${encodeURIComponent(channel)}/earnings`);
|
|
49
|
+
return res.data;
|
|
50
|
+
}
|
|
51
|
+
async getGiftsCatalog(platform = "tiktok") {
|
|
52
|
+
const res = await this.fetchJson(`/api/v1/gifts/catalog?platform=${encodeURIComponent(platform)}`);
|
|
53
|
+
return res.data;
|
|
54
|
+
}
|
|
55
|
+
async getLeaderboard(platform, timeframe = "daily", limit = 50) {
|
|
56
|
+
const res = await this.fetchJson(`/api/v1/rankings/leaderboard?platform=${encodeURIComponent(platform)}&timeframe=${encodeURIComponent(timeframe)}&limit=${limit}`);
|
|
57
|
+
return res.entries;
|
|
58
|
+
}
|
|
59
|
+
async searchRankings(channel, platform = "tiktok", timeframe = "daily") {
|
|
60
|
+
const res = await this.fetchJson(`/api/v1/rankings/search?channel=${encodeURIComponent(channel)}&platform=${encodeURIComponent(platform)}&timeframe=${encodeURIComponent(timeframe)}`);
|
|
61
|
+
return res.data;
|
|
62
|
+
}
|
|
63
|
+
moderation = {
|
|
64
|
+
mute: (platform, channel, targetUser, duration) => this.fetchJson("/api/v1/moderation/mutes", {
|
|
65
|
+
method: "POST",
|
|
66
|
+
headers: { "Content-Type": "application/json" },
|
|
67
|
+
body: JSON.stringify({ platform, channel, targetUser, duration })
|
|
68
|
+
}),
|
|
69
|
+
unmute: (platform, channel, targetUser) => this.fetchJson("/api/v1/moderation/mutes", {
|
|
70
|
+
method: "DELETE",
|
|
71
|
+
headers: { "Content-Type": "application/json" },
|
|
72
|
+
body: JSON.stringify({ platform, channel, targetUser })
|
|
73
|
+
}),
|
|
74
|
+
ban: (platform, channel, targetUser, reason) => this.fetchJson("/api/v1/moderation/bans", {
|
|
75
|
+
method: "POST",
|
|
76
|
+
headers: { "Content-Type": "application/json" },
|
|
77
|
+
body: JSON.stringify({ platform, channel, targetUser, reason })
|
|
78
|
+
}),
|
|
79
|
+
unban: (platform, channel, targetUser) => this.fetchJson("/api/v1/moderation/bans", {
|
|
80
|
+
method: "DELETE",
|
|
81
|
+
headers: { "Content-Type": "application/json" },
|
|
82
|
+
body: JSON.stringify({ platform, channel, targetUser })
|
|
83
|
+
}),
|
|
84
|
+
deleteMessage: (platform, channel, messageId) => this.fetchJson("/api/v1/moderation/messages", {
|
|
85
|
+
method: "DELETE",
|
|
86
|
+
headers: { "Content-Type": "application/json" },
|
|
87
|
+
body: JSON.stringify({ platform, channel, messageId })
|
|
88
|
+
}),
|
|
89
|
+
toggleComments: (platform, channel, enabled) => this.fetchJson("/api/v1/moderation/comments/toggle", {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: { "Content-Type": "application/json" },
|
|
92
|
+
body: JSON.stringify({ platform, channel, enabled })
|
|
93
|
+
})
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/errors.ts
|
|
98
|
+
var LIGHTSTREAM_ERROR_CODES = {
|
|
99
|
+
STREAMER_OFFLINE: "STREAMER_OFFLINE",
|
|
100
|
+
ROOM_NOT_FOUND: "ROOM_NOT_FOUND",
|
|
101
|
+
AUTHENTICATION_FAILED: "AUTHENTICATION_FAILED",
|
|
102
|
+
FORBIDDEN_CHANNEL: "FORBIDDEN_CHANNEL",
|
|
103
|
+
RATE_LIMITED: "RATE_LIMITED",
|
|
104
|
+
UPSTREAM_ERROR: "UPSTREAM_ERROR",
|
|
105
|
+
TIMEOUT: "TIMEOUT",
|
|
106
|
+
INTERNAL_ERROR: "INTERNAL_ERROR"
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
class LightStreamError extends Error {
|
|
110
|
+
code;
|
|
111
|
+
details;
|
|
112
|
+
retryable;
|
|
113
|
+
timestamp;
|
|
114
|
+
constructor(payload) {
|
|
115
|
+
super(payload.message);
|
|
116
|
+
this.name = "LightStreamError";
|
|
117
|
+
this.code = payload.code;
|
|
118
|
+
this.details = payload.details;
|
|
119
|
+
this.retryable = payload.retryable ?? false;
|
|
120
|
+
this.timestamp = payload.timestamp;
|
|
121
|
+
}
|
|
122
|
+
toJSON() {
|
|
123
|
+
return {
|
|
124
|
+
code: this.code,
|
|
125
|
+
message: this.message,
|
|
126
|
+
details: this.details,
|
|
127
|
+
retryable: this.retryable,
|
|
128
|
+
timestamp: this.timestamp
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/stream-client.ts
|
|
134
|
+
class LightStreamStreamClient {
|
|
135
|
+
options;
|
|
136
|
+
ws = null;
|
|
137
|
+
_status = "idle";
|
|
138
|
+
listeners = new Map;
|
|
139
|
+
pingTimer = null;
|
|
140
|
+
reconnectTimer = null;
|
|
141
|
+
reconnectAttempts = 0;
|
|
142
|
+
currentPlatform = null;
|
|
143
|
+
currentChannel = null;
|
|
144
|
+
constructor(options) {
|
|
145
|
+
this.options = options;
|
|
146
|
+
}
|
|
147
|
+
get status() {
|
|
148
|
+
return this._status;
|
|
149
|
+
}
|
|
150
|
+
setStatus(newStatus) {
|
|
151
|
+
if (this._status === newStatus)
|
|
152
|
+
return;
|
|
153
|
+
this._status = newStatus;
|
|
154
|
+
this.emit("status", newStatus);
|
|
155
|
+
}
|
|
156
|
+
connect(platform, channel) {
|
|
157
|
+
this.currentPlatform = platform;
|
|
158
|
+
this.currentChannel = channel.trim().toLowerCase();
|
|
159
|
+
return this.initSocket();
|
|
160
|
+
}
|
|
161
|
+
async initSocket() {
|
|
162
|
+
if (!this.currentPlatform || !this.currentChannel) {
|
|
163
|
+
throw new Error("Platform and channel must be specified to connect");
|
|
164
|
+
}
|
|
165
|
+
this.cleanupSocket();
|
|
166
|
+
this.setStatus("connecting");
|
|
167
|
+
const baseWs = (this.options.wsUrl || "wss://gateway.lightstream.lat/ws").replace(/\/+$/, "");
|
|
168
|
+
const url = new URL(baseWs);
|
|
169
|
+
url.searchParams.set("platform", this.currentPlatform);
|
|
170
|
+
url.searchParams.set("channel", this.currentChannel);
|
|
171
|
+
if (this.options.token) {
|
|
172
|
+
url.searchParams.set("token", this.options.token);
|
|
173
|
+
} else if (this.options.apiKey) {
|
|
174
|
+
url.searchParams.set("apiKey", this.options.apiKey);
|
|
175
|
+
}
|
|
176
|
+
const SocketCtor = typeof WebSocket !== "undefined" ? WebSocket : (await import("ws")).default;
|
|
177
|
+
return new Promise((resolve) => {
|
|
178
|
+
try {
|
|
179
|
+
const socket = new SocketCtor(url.toString());
|
|
180
|
+
this.ws = socket;
|
|
181
|
+
socket.onopen = () => {
|
|
182
|
+
this.reconnectAttempts = 0;
|
|
183
|
+
this.startHeartbeat();
|
|
184
|
+
resolve();
|
|
185
|
+
};
|
|
186
|
+
socket.onmessage = (event) => {
|
|
187
|
+
this.handleIncoming(typeof event.data === "string" ? event.data : event.data?.toString());
|
|
188
|
+
};
|
|
189
|
+
socket.onerror = (err) => {
|
|
190
|
+
this.handleFailure(LIGHTSTREAM_ERROR_CODES.UPSTREAM_ERROR, err?.message || "WebSocket connection error");
|
|
191
|
+
};
|
|
192
|
+
socket.onclose = () => {
|
|
193
|
+
this.cleanupSocket();
|
|
194
|
+
if (this._status !== "disconnected" && this._status !== "offline") {
|
|
195
|
+
this.setStatus("disconnected");
|
|
196
|
+
this.emit("disconnected", "Socket connection closed");
|
|
197
|
+
this.scheduleReconnect();
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
} catch (err) {
|
|
201
|
+
this.handleFailure(LIGHTSTREAM_ERROR_CODES.INTERNAL_ERROR, err.message);
|
|
202
|
+
resolve();
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
handleIncoming(rawData) {
|
|
207
|
+
if (!rawData)
|
|
208
|
+
return;
|
|
209
|
+
let parsed;
|
|
210
|
+
try {
|
|
211
|
+
parsed = JSON.parse(rawData);
|
|
212
|
+
} catch {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (parsed.type === "pong")
|
|
216
|
+
return;
|
|
217
|
+
if (parsed.type === "connected") {
|
|
218
|
+
this.setStatus("connected");
|
|
219
|
+
const evt = {
|
|
220
|
+
channel: parsed.channel || this.currentChannel,
|
|
221
|
+
platform: parsed.platform || this.currentPlatform,
|
|
222
|
+
isLive: true,
|
|
223
|
+
title: parsed.title,
|
|
224
|
+
viewerCount: parsed.viewerCount,
|
|
225
|
+
startedAt: parsed.startedAt
|
|
226
|
+
};
|
|
227
|
+
this.emit("connected", evt);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (parsed.type === "offline") {
|
|
231
|
+
this.setStatus("offline");
|
|
232
|
+
const offlineEvt = {
|
|
233
|
+
channel: parsed.channel || this.currentChannel,
|
|
234
|
+
platform: parsed.platform || this.currentPlatform,
|
|
235
|
+
isLive: false,
|
|
236
|
+
code: parsed.code || LIGHTSTREAM_ERROR_CODES.STREAMER_OFFLINE,
|
|
237
|
+
message: parsed.message || "The streamer is currently offline"
|
|
238
|
+
};
|
|
239
|
+
this.emit("stream:offline", offlineEvt);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (parsed.type === "error") {
|
|
243
|
+
this.handleFailure(parsed.code || LIGHTSTREAM_ERROR_CODES.INTERNAL_ERROR, parsed.message);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (parsed.event === "chat" || parsed.type === "chat") {
|
|
247
|
+
this.emit("chat", parsed.data);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (parsed.event === "gift" || parsed.type === "gift") {
|
|
251
|
+
this.emit("gift", parsed.data);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
this.emit("raw", parsed);
|
|
255
|
+
}
|
|
256
|
+
handleFailure(code, message) {
|
|
257
|
+
this.setStatus("error");
|
|
258
|
+
const errPayload = {
|
|
259
|
+
channel: this.currentChannel || undefined,
|
|
260
|
+
platform: this.currentPlatform || undefined,
|
|
261
|
+
code,
|
|
262
|
+
message,
|
|
263
|
+
retryable: code !== LIGHTSTREAM_ERROR_CODES.FORBIDDEN_CHANNEL && code !== LIGHTSTREAM_ERROR_CODES.AUTHENTICATION_FAILED
|
|
264
|
+
};
|
|
265
|
+
this.emit("error", errPayload);
|
|
266
|
+
if (errPayload.retryable) {
|
|
267
|
+
this.scheduleReconnect();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
scheduleReconnect() {
|
|
271
|
+
if (this.options.autoReconnect === false)
|
|
272
|
+
return;
|
|
273
|
+
const maxAttempts = this.options.maxReconnectAttempts ?? 5;
|
|
274
|
+
if (this.reconnectAttempts >= maxAttempts)
|
|
275
|
+
return;
|
|
276
|
+
this.reconnectAttempts++;
|
|
277
|
+
this.setStatus("reconnecting");
|
|
278
|
+
const baseDelay = this.options.reconnectIntervalMs ?? 2000;
|
|
279
|
+
const delay = Math.min(baseDelay * Math.pow(1.5, this.reconnectAttempts - 1), 30000);
|
|
280
|
+
this.reconnectTimer = setTimeout(() => {
|
|
281
|
+
this.initSocket().catch(() => {});
|
|
282
|
+
}, delay);
|
|
283
|
+
}
|
|
284
|
+
startHeartbeat() {
|
|
285
|
+
const interval = this.options.pingIntervalMs ?? 30000;
|
|
286
|
+
this.pingTimer = setInterval(() => {
|
|
287
|
+
if (this.ws && this.ws.readyState === 1) {
|
|
288
|
+
this.ws.send(JSON.stringify({ type: "ping" }));
|
|
289
|
+
}
|
|
290
|
+
}, interval);
|
|
291
|
+
}
|
|
292
|
+
cleanupSocket() {
|
|
293
|
+
if (this.pingTimer) {
|
|
294
|
+
clearInterval(this.pingTimer);
|
|
295
|
+
this.pingTimer = null;
|
|
296
|
+
}
|
|
297
|
+
if (this.reconnectTimer) {
|
|
298
|
+
clearTimeout(this.reconnectTimer);
|
|
299
|
+
this.reconnectTimer = null;
|
|
300
|
+
}
|
|
301
|
+
if (this.ws) {
|
|
302
|
+
try {
|
|
303
|
+
this.ws.onclose = null;
|
|
304
|
+
this.ws.onerror = null;
|
|
305
|
+
this.ws.close();
|
|
306
|
+
} catch {}
|
|
307
|
+
this.ws = null;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
disconnect() {
|
|
311
|
+
this.cleanupSocket();
|
|
312
|
+
this.setStatus("disconnected");
|
|
313
|
+
this.currentChannel = null;
|
|
314
|
+
this.currentPlatform = null;
|
|
315
|
+
}
|
|
316
|
+
on(event, handler) {
|
|
317
|
+
if (!this.listeners.has(event)) {
|
|
318
|
+
this.listeners.set(event, new Set);
|
|
319
|
+
}
|
|
320
|
+
this.listeners.get(event).add(handler);
|
|
321
|
+
}
|
|
322
|
+
off(event, handler) {
|
|
323
|
+
const handlers = this.listeners.get(event);
|
|
324
|
+
if (handlers) {
|
|
325
|
+
handlers.delete(handler);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
emit(event, ...args) {
|
|
329
|
+
const handlers = this.listeners.get(event);
|
|
330
|
+
if (handlers) {
|
|
331
|
+
for (const handler of handlers) {
|
|
332
|
+
try {
|
|
333
|
+
handler(...args);
|
|
334
|
+
} catch (e) {
|
|
335
|
+
console.error(`Error in LightStream listener [${event}]:`, e);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/index.ts
|
|
343
|
+
class LightStreamClient {
|
|
344
|
+
rest;
|
|
345
|
+
stream;
|
|
346
|
+
constructor(options) {
|
|
347
|
+
this.rest = new LightStreamRestClient({
|
|
348
|
+
baseUrl: options.baseUrl || "https://gateway.lightstream.lat",
|
|
349
|
+
apiKey: options.apiKey,
|
|
350
|
+
jwtKey: options.jwtKey || options.token
|
|
351
|
+
});
|
|
352
|
+
this.stream = new LightStreamStreamClient(options);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function createLightStreamClient(options) {
|
|
356
|
+
return new LightStreamClient(options);
|
|
357
|
+
}
|
|
358
|
+
export {
|
|
359
|
+
LIGHTSTREAM_ERROR_CODES,
|
|
360
|
+
LightStreamClient,
|
|
361
|
+
LightStreamError,
|
|
362
|
+
LightStreamRestClient,
|
|
363
|
+
LightStreamStreamClient,
|
|
364
|
+
createLightStreamClient
|
|
365
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Platform, Timeframe } from './types.js';
|
|
2
|
+
export interface RestClientConfig {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
jwtKey?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class LightStreamRestClient {
|
|
8
|
+
private config;
|
|
9
|
+
constructor(config: RestClientConfig);
|
|
10
|
+
private fetchJson;
|
|
11
|
+
issueToken(options: {
|
|
12
|
+
expireAfterSeconds?: number;
|
|
13
|
+
allowedPlatforms?: Platform[];
|
|
14
|
+
allowedChannels?: string[];
|
|
15
|
+
maxWebSockets?: number;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
token: string;
|
|
18
|
+
expiresAt: number;
|
|
19
|
+
maxWebSockets: number;
|
|
20
|
+
}>;
|
|
21
|
+
bulkCheckStreams(requests: Array<{
|
|
22
|
+
platform: Platform;
|
|
23
|
+
channel: string;
|
|
24
|
+
}>): Promise<Array<{
|
|
25
|
+
platform: Platform;
|
|
26
|
+
channel: string;
|
|
27
|
+
isLive: boolean;
|
|
28
|
+
viewerCount: number;
|
|
29
|
+
title?: string;
|
|
30
|
+
cached?: boolean;
|
|
31
|
+
}>>;
|
|
32
|
+
getRoomStreamInfo(platform: Platform, channel: string): Promise<Record<string, unknown>>;
|
|
33
|
+
getUserEarnings(platform: Platform, channel: string): Promise<Record<string, unknown>>;
|
|
34
|
+
getGiftsCatalog(platform?: Platform): Promise<any[]>;
|
|
35
|
+
getLeaderboard(platform: Platform, timeframe?: Timeframe, limit?: number): Promise<any[]>;
|
|
36
|
+
searchRankings(channel: string, platform?: Platform, timeframe?: Timeframe): Promise<any>;
|
|
37
|
+
readonly moderation: {
|
|
38
|
+
mute: (platform: Platform, channel: string, targetUser: string, duration?: number) => Promise<unknown>;
|
|
39
|
+
unmute: (platform: Platform, channel: string, targetUser: string) => Promise<unknown>;
|
|
40
|
+
ban: (platform: Platform, channel: string, targetUser: string, reason?: string) => Promise<unknown>;
|
|
41
|
+
unban: (platform: Platform, channel: string, targetUser: string) => Promise<unknown>;
|
|
42
|
+
deleteMessage: (platform: Platform, channel: string, messageId: string) => Promise<unknown>;
|
|
43
|
+
toggleComments: (platform: Platform, channel: string, enabled: boolean) => Promise<unknown>;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const LIGHTSTREAM_ERROR_CODES: {
|
|
2
|
+
readonly STREAMER_OFFLINE: 'STREAMER_OFFLINE';
|
|
3
|
+
readonly ROOM_NOT_FOUND: 'ROOM_NOT_FOUND';
|
|
4
|
+
readonly AUTHENTICATION_FAILED: 'AUTHENTICATION_FAILED';
|
|
5
|
+
readonly FORBIDDEN_CHANNEL: 'FORBIDDEN_CHANNEL';
|
|
6
|
+
readonly RATE_LIMITED: 'RATE_LIMITED';
|
|
7
|
+
readonly UPSTREAM_ERROR: 'UPSTREAM_ERROR';
|
|
8
|
+
readonly TIMEOUT: 'TIMEOUT';
|
|
9
|
+
readonly INTERNAL_ERROR: 'INTERNAL_ERROR';
|
|
10
|
+
};
|
|
11
|
+
export type LightStreamErrorCode = keyof typeof LIGHTSTREAM_ERROR_CODES;
|
|
12
|
+
export interface LightStreamErrorPayload {
|
|
13
|
+
code: LightStreamErrorCode;
|
|
14
|
+
message: string;
|
|
15
|
+
details?: unknown;
|
|
16
|
+
retryable?: boolean;
|
|
17
|
+
timestamp: string;
|
|
18
|
+
}
|
|
19
|
+
export declare class LightStreamError extends Error {
|
|
20
|
+
readonly code: LightStreamErrorCode;
|
|
21
|
+
readonly details?: unknown;
|
|
22
|
+
readonly retryable: boolean;
|
|
23
|
+
readonly timestamp: string;
|
|
24
|
+
constructor(payload: LightStreamErrorPayload);
|
|
25
|
+
toJSON(): LightStreamErrorPayload;
|
|
26
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { LightStreamRestClient } from './rest-client.js';
|
|
2
|
+
import { LightStreamStreamClient } from './stream-client.js';
|
|
3
|
+
import type { ClientOptions } from './types.js';
|
|
4
|
+
export declare class LightStreamClient {
|
|
5
|
+
readonly rest: LightStreamRestClient;
|
|
6
|
+
readonly stream: LightStreamStreamClient;
|
|
7
|
+
constructor(options: ClientOptions);
|
|
8
|
+
}
|
|
9
|
+
export declare function createLightStreamClient(options: ClientOptions): LightStreamClient;
|
|
10
|
+
export * from './types.js';
|
|
11
|
+
export * from './errors.js';
|
|
12
|
+
export * from './rest-client.js';
|
|
13
|
+
export * from './stream-client.js';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Platform, Timeframe } from './types.js';
|
|
2
|
+
export interface RestClientConfig {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
jwtKey?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class LightStreamRestClient {
|
|
8
|
+
private config;
|
|
9
|
+
constructor(config: RestClientConfig);
|
|
10
|
+
private fetchJson;
|
|
11
|
+
issueToken(options: {
|
|
12
|
+
expireAfterSeconds?: number;
|
|
13
|
+
allowedPlatforms?: Platform[];
|
|
14
|
+
allowedChannels?: string[];
|
|
15
|
+
maxWebSockets?: number;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
token: string;
|
|
18
|
+
expiresAt: number;
|
|
19
|
+
maxWebSockets: number;
|
|
20
|
+
}>;
|
|
21
|
+
bulkCheckStreams(requests: Array<{
|
|
22
|
+
platform: Platform;
|
|
23
|
+
channel: string;
|
|
24
|
+
}>): Promise<Array<{
|
|
25
|
+
platform: Platform;
|
|
26
|
+
channel: string;
|
|
27
|
+
isLive: boolean;
|
|
28
|
+
viewerCount: number;
|
|
29
|
+
title?: string;
|
|
30
|
+
cached?: boolean;
|
|
31
|
+
}>>;
|
|
32
|
+
getRoomStreamInfo(platform: Platform, channel: string): Promise<Record<string, unknown>>;
|
|
33
|
+
getUserEarnings(platform: Platform, channel: string): Promise<Record<string, unknown>>;
|
|
34
|
+
getGiftsCatalog(platform?: Platform): Promise<any[]>;
|
|
35
|
+
getLeaderboard(platform: Platform, timeframe?: Timeframe, limit?: number): Promise<any[]>;
|
|
36
|
+
searchRankings(channel: string, platform?: Platform, timeframe?: Timeframe): Promise<any>;
|
|
37
|
+
readonly moderation: {
|
|
38
|
+
mute: (platform: Platform, channel: string, targetUser: string, duration?: number) => Promise<unknown>;
|
|
39
|
+
unmute: (platform: Platform, channel: string, targetUser: string) => Promise<unknown>;
|
|
40
|
+
ban: (platform: Platform, channel: string, targetUser: string, reason?: string) => Promise<unknown>;
|
|
41
|
+
unban: (platform: Platform, channel: string, targetUser: string) => Promise<unknown>;
|
|
42
|
+
deleteMessage: (platform: Platform, channel: string, messageId: string) => Promise<unknown>;
|
|
43
|
+
toggleComments: (platform: Platform, channel: string, enabled: boolean) => Promise<unknown>;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ClientOptions, ConnectionStatus, LightStreamEventMap, Platform } from './types.js';
|
|
2
|
+
export declare class LightStreamStreamClient {
|
|
3
|
+
private options;
|
|
4
|
+
private ws;
|
|
5
|
+
private _status;
|
|
6
|
+
private listeners;
|
|
7
|
+
private pingTimer;
|
|
8
|
+
private reconnectTimer;
|
|
9
|
+
private reconnectAttempts;
|
|
10
|
+
private currentPlatform;
|
|
11
|
+
private currentChannel;
|
|
12
|
+
constructor(options: ClientOptions);
|
|
13
|
+
get status(): ConnectionStatus;
|
|
14
|
+
private setStatus;
|
|
15
|
+
connect(platform: Platform, channel: string): Promise<void>;
|
|
16
|
+
private initSocket;
|
|
17
|
+
private handleIncoming;
|
|
18
|
+
private handleFailure;
|
|
19
|
+
private scheduleReconnect;
|
|
20
|
+
private startHeartbeat;
|
|
21
|
+
private cleanupSocket;
|
|
22
|
+
disconnect(): void;
|
|
23
|
+
on<K extends keyof LightStreamEventMap>(event: K, handler: LightStreamEventMap[K]): void;
|
|
24
|
+
off<K extends keyof LightStreamEventMap>(event: K, handler: LightStreamEventMap[K]): void;
|
|
25
|
+
private emit;
|
|
26
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { LightStreamErrorCode } from './errors.js';
|
|
2
|
+
export type Platform = 'tiktok' | 'kick' | 'twitch';
|
|
3
|
+
export type Timeframe = 'hourly' | 'daily' | 'weekly' | 'all';
|
|
4
|
+
export type ConnectionStatus = 'idle' | 'connecting' | 'connected' | 'offline' | 'reconnecting' | 'disconnected' | 'error';
|
|
5
|
+
export interface ClientOptions {
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
wsUrl?: string;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
token?: string;
|
|
10
|
+
jwtKey?: string;
|
|
11
|
+
autoReconnect?: boolean;
|
|
12
|
+
maxReconnectAttempts?: number;
|
|
13
|
+
reconnectIntervalMs?: number;
|
|
14
|
+
pingIntervalMs?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface StreamConnectedEvent {
|
|
17
|
+
channel: string;
|
|
18
|
+
platform: Platform;
|
|
19
|
+
isLive: true;
|
|
20
|
+
title?: string;
|
|
21
|
+
viewerCount?: number;
|
|
22
|
+
startedAt?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface StreamOfflineEvent {
|
|
25
|
+
channel: string;
|
|
26
|
+
platform: Platform;
|
|
27
|
+
isLive: false;
|
|
28
|
+
code: LightStreamErrorCode;
|
|
29
|
+
message: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ConnectionErrorEvent {
|
|
32
|
+
channel?: string;
|
|
33
|
+
platform?: Platform;
|
|
34
|
+
code: LightStreamErrorCode;
|
|
35
|
+
message: string;
|
|
36
|
+
details?: unknown;
|
|
37
|
+
retryable: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface ChatMessageEvent {
|
|
40
|
+
id: string;
|
|
41
|
+
platform: Platform;
|
|
42
|
+
channel: string;
|
|
43
|
+
user: {
|
|
44
|
+
id: string;
|
|
45
|
+
username: string;
|
|
46
|
+
displayName: string;
|
|
47
|
+
avatar?: string;
|
|
48
|
+
badges?: string[];
|
|
49
|
+
};
|
|
50
|
+
content: string;
|
|
51
|
+
timestamp: number;
|
|
52
|
+
emotes?: Array<{
|
|
53
|
+
name: string;
|
|
54
|
+
url: string;
|
|
55
|
+
}>;
|
|
56
|
+
}
|
|
57
|
+
export interface GiftEvent {
|
|
58
|
+
id: string;
|
|
59
|
+
platform: Platform;
|
|
60
|
+
channel: string;
|
|
61
|
+
user: {
|
|
62
|
+
id: string;
|
|
63
|
+
username: string;
|
|
64
|
+
displayName: string;
|
|
65
|
+
avatar?: string;
|
|
66
|
+
};
|
|
67
|
+
gift: {
|
|
68
|
+
id: string;
|
|
69
|
+
name: string;
|
|
70
|
+
value: number;
|
|
71
|
+
repeatCount: number;
|
|
72
|
+
repeatEnd: boolean;
|
|
73
|
+
iconUrl?: string;
|
|
74
|
+
};
|
|
75
|
+
timestamp: number;
|
|
76
|
+
}
|
|
77
|
+
export interface LightStreamEventMap {
|
|
78
|
+
status: (status: ConnectionStatus) => void;
|
|
79
|
+
connected: (event: StreamConnectedEvent) => void;
|
|
80
|
+
'stream:offline': (event: StreamOfflineEvent) => void;
|
|
81
|
+
error: (event: ConnectionErrorEvent) => void;
|
|
82
|
+
disconnected: (reason: string) => void;
|
|
83
|
+
chat: (event: ChatMessageEvent) => void;
|
|
84
|
+
gift: (event: GiftEvent) => void;
|
|
85
|
+
raw: (data: unknown) => void;
|
|
86
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ClientOptions, ConnectionStatus, LightStreamEventMap, Platform } from './types.js';
|
|
2
|
+
export declare class LightStreamStreamClient {
|
|
3
|
+
private options;
|
|
4
|
+
private ws;
|
|
5
|
+
private _status;
|
|
6
|
+
private listeners;
|
|
7
|
+
private pingTimer;
|
|
8
|
+
private reconnectTimer;
|
|
9
|
+
private reconnectAttempts;
|
|
10
|
+
private currentPlatform;
|
|
11
|
+
private currentChannel;
|
|
12
|
+
constructor(options: ClientOptions);
|
|
13
|
+
get status(): ConnectionStatus;
|
|
14
|
+
private setStatus;
|
|
15
|
+
connect(platform: Platform, channel: string): Promise<void>;
|
|
16
|
+
private initSocket;
|
|
17
|
+
private handleIncoming;
|
|
18
|
+
private handleFailure;
|
|
19
|
+
private scheduleReconnect;
|
|
20
|
+
private startHeartbeat;
|
|
21
|
+
private cleanupSocket;
|
|
22
|
+
disconnect(): void;
|
|
23
|
+
on<K extends keyof LightStreamEventMap>(event: K, handler: LightStreamEventMap[K]): void;
|
|
24
|
+
off<K extends keyof LightStreamEventMap>(event: K, handler: LightStreamEventMap[K]): void;
|
|
25
|
+
private emit;
|
|
26
|
+
}
|