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/types.d.ts
ADDED
|
@@ -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,15 @@
|
|
|
1
|
+
// examples/basic-connection.ts
|
|
2
|
+
import { createLightStreamClient } from '../src/index.js'
|
|
3
|
+
|
|
4
|
+
const client = createLightStreamClient({
|
|
5
|
+
baseUrl: 'https://gateway.lightstream.lat',
|
|
6
|
+
wsUrl: 'wss://gateway.lightstream.lat/ws',
|
|
7
|
+
apiKey: process.env.LIGHTSTREAM_API_KEY || 'YOUR_API_KEY',
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
client.stream.on('status', (s) => console.log('Status:', s))
|
|
11
|
+
client.stream.on('connected', (e) => console.log('Stream live:', e))
|
|
12
|
+
client.stream.on('stream:offline', (e) => console.log('Stream offline:', e))
|
|
13
|
+
client.stream.on('chat', (c) => console.log(`[${c.platform}] ${c.user.displayName}: ${c.content}`))
|
|
14
|
+
|
|
15
|
+
await client.stream.connect('twitch', 'shroud')
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// examples/bot-moderation.ts
|
|
2
|
+
import { createLightStreamClient, type ChatMessageEvent } from '../src/index.js'
|
|
3
|
+
|
|
4
|
+
const BANNED_WORDS = ['spamword1', 'badlink.com', 'scam']
|
|
5
|
+
|
|
6
|
+
const client = createLightStreamClient({
|
|
7
|
+
baseUrl: 'https://gateway.lightstream.lat',
|
|
8
|
+
wsUrl: 'wss://gateway.lightstream.lat/ws',
|
|
9
|
+
apiKey: process.env.LIGHTSTREAM_API_KEY!,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
client.stream.on('chat', async (msg: ChatMessageEvent) => {
|
|
13
|
+
const hasBadWord = BANNED_WORDS.some((word) => msg.content.toLowerCase().includes(word))
|
|
14
|
+
|
|
15
|
+
if (hasBadWord) {
|
|
16
|
+
console.log(`🚨 Moderating message from ${msg.user.username} in ${msg.channel}...`)
|
|
17
|
+
|
|
18
|
+
// 1. Delete message
|
|
19
|
+
await client.rest.moderation.deleteMessage(msg.platform, msg.channel, msg.id)
|
|
20
|
+
|
|
21
|
+
// 2. Mute user for 5 minutes
|
|
22
|
+
await client.rest.moderation.mute(msg.platform, msg.channel, msg.user.username, 300)
|
|
23
|
+
console.log(`User ${msg.user.username} muted for 300s.`)
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
await client.stream.connect('kick', 'my_kick_channel')
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// examples/earnings-tracker.ts
|
|
2
|
+
import { createLightStreamClient } from '../src/index.js'
|
|
3
|
+
|
|
4
|
+
const client = createLightStreamClient({
|
|
5
|
+
baseUrl: 'https://gateway.lightstream.lat',
|
|
6
|
+
apiKey: process.env.LIGHTSTREAM_API_KEY!,
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
async function checkEarnings(platform: 'tiktok' | 'kick' | 'twitch', channel: string) {
|
|
10
|
+
const [streamInfo, earnings] = await Promise.all([
|
|
11
|
+
client.rest.getRoomStreamInfo(platform, channel),
|
|
12
|
+
client.rest.getUserEarnings(platform, channel),
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
console.log('Stream Info:', streamInfo)
|
|
16
|
+
console.log('Earnings & Diamonds:', earnings)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
await checkEarnings('tiktok', 'jesusquesillo')
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// examples/react-use-stream.tsx
|
|
2
|
+
import { useEffect, useState, useRef } from 'react'
|
|
3
|
+
import {
|
|
4
|
+
createLightStreamClient,
|
|
5
|
+
type ConnectionStatus,
|
|
6
|
+
type ChatMessageEvent,
|
|
7
|
+
type GiftEvent,
|
|
8
|
+
type Platform
|
|
9
|
+
} from '../src/index.js'
|
|
10
|
+
|
|
11
|
+
export interface UseLightStreamOptions {
|
|
12
|
+
platform: Platform
|
|
13
|
+
channel: string
|
|
14
|
+
token?: string
|
|
15
|
+
apiKey?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function useLightStream({ platform, channel, token, apiKey }: UseLightStreamOptions) {
|
|
19
|
+
const [status, setStatus] = useState<ConnectionStatus>('idle')
|
|
20
|
+
const [isLive, setIsLive] = useState<boolean>(false)
|
|
21
|
+
const [chatMessages, setChatMessages] = useState<ChatMessageEvent[]>([])
|
|
22
|
+
const [gifts, setGifts] = useState<GiftEvent[]>([])
|
|
23
|
+
const clientRef = useRef<any>(null)
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const client = createLightStreamClient({
|
|
27
|
+
baseUrl: 'https://gateway.lightstream.lat',
|
|
28
|
+
wsUrl: 'wss://gateway.lightstream.lat/ws',
|
|
29
|
+
token,
|
|
30
|
+
apiKey,
|
|
31
|
+
})
|
|
32
|
+
clientRef.current = client
|
|
33
|
+
|
|
34
|
+
client.stream.on('status', setStatus)
|
|
35
|
+
client.stream.on('connected', () => setIsLive(true))
|
|
36
|
+
client.stream.on('stream:offline', () => setIsLive(false))
|
|
37
|
+
client.stream.on('chat', (msg) => setChatMessages((prev) => [...prev.slice(-99), msg]))
|
|
38
|
+
client.stream.on('gift', (gift) => setGifts((prev) => [...prev.slice(-49), gift]))
|
|
39
|
+
|
|
40
|
+
client.stream.connect(platform, channel)
|
|
41
|
+
|
|
42
|
+
return () => {
|
|
43
|
+
client.stream.disconnect()
|
|
44
|
+
}
|
|
45
|
+
}, [platform, channel, token, apiKey])
|
|
46
|
+
|
|
47
|
+
return { status, isLive, chatMessages, gifts, client: clientRef.current }
|
|
48
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lightstream-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official TypeScript/JavaScript SDK for LightStream Gateway (Unified TikTok, Kick, and Twitch Real-time Streaming, REST APIs, and Moderation)",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src",
|
|
18
|
+
"README.md",
|
|
19
|
+
"QUICKSTART.md",
|
|
20
|
+
"LICENSE",
|
|
21
|
+
"examples"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "bun build ./src/index.ts --outfile ./dist/index.mjs --target node --external ws && bun build ./src/index.ts --outfile ./dist/index.cjs --target node --format cjs --external ws && bun x tsc --emitDeclarationOnly",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test": "bun test"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"ws": "^8.18.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^20.0.0",
|
|
33
|
+
"@types/ws": "^8.5.10",
|
|
34
|
+
"typescript": "^5.0.0"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"lightstream",
|
|
38
|
+
"tiktok",
|
|
39
|
+
"kick",
|
|
40
|
+
"twitch",
|
|
41
|
+
"livestream",
|
|
42
|
+
"realtime",
|
|
43
|
+
"websocket",
|
|
44
|
+
"moderation",
|
|
45
|
+
"sdk"
|
|
46
|
+
],
|
|
47
|
+
"author": "LightStream Team",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/LightCode-ctrl/gateway-lightstream.git"
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
// Canonical LightStream Domain Error Taxonomy and helpers.
|
|
3
|
+
|
|
4
|
+
export const LIGHTSTREAM_ERROR_CODES = {
|
|
5
|
+
STREAMER_OFFLINE: 'STREAMER_OFFLINE',
|
|
6
|
+
ROOM_NOT_FOUND: 'ROOM_NOT_FOUND',
|
|
7
|
+
AUTHENTICATION_FAILED: 'AUTHENTICATION_FAILED',
|
|
8
|
+
FORBIDDEN_CHANNEL: 'FORBIDDEN_CHANNEL',
|
|
9
|
+
RATE_LIMITED: 'RATE_LIMITED',
|
|
10
|
+
UPSTREAM_ERROR: 'UPSTREAM_ERROR',
|
|
11
|
+
TIMEOUT: 'TIMEOUT',
|
|
12
|
+
INTERNAL_ERROR: 'INTERNAL_ERROR',
|
|
13
|
+
} as const
|
|
14
|
+
|
|
15
|
+
export type LightStreamErrorCode = keyof typeof LIGHTSTREAM_ERROR_CODES
|
|
16
|
+
|
|
17
|
+
export interface LightStreamErrorPayload {
|
|
18
|
+
code: LightStreamErrorCode
|
|
19
|
+
message: string
|
|
20
|
+
details?: unknown
|
|
21
|
+
retryable?: boolean
|
|
22
|
+
timestamp: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class LightStreamError extends Error {
|
|
26
|
+
readonly code: LightStreamErrorCode
|
|
27
|
+
readonly details?: unknown
|
|
28
|
+
readonly retryable: boolean
|
|
29
|
+
readonly timestamp: string
|
|
30
|
+
|
|
31
|
+
constructor(payload: LightStreamErrorPayload) {
|
|
32
|
+
super(payload.message)
|
|
33
|
+
this.name = 'LightStreamError'
|
|
34
|
+
this.code = payload.code
|
|
35
|
+
this.details = payload.details
|
|
36
|
+
this.retryable = payload.retryable ?? false
|
|
37
|
+
this.timestamp = payload.timestamp
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
toJSON(): LightStreamErrorPayload {
|
|
41
|
+
return {
|
|
42
|
+
code: this.code,
|
|
43
|
+
message: this.message,
|
|
44
|
+
details: this.details,
|
|
45
|
+
retryable: this.retryable,
|
|
46
|
+
timestamp: this.timestamp,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
// Entrypoint for the official LightStream TypeScript SDK.
|
|
3
|
+
|
|
4
|
+
import { LightStreamRestClient } from './rest-client.js'
|
|
5
|
+
import { LightStreamStreamClient } from './stream-client.js'
|
|
6
|
+
import type { ClientOptions } from './types.js'
|
|
7
|
+
|
|
8
|
+
export class LightStreamClient {
|
|
9
|
+
readonly rest: LightStreamRestClient
|
|
10
|
+
readonly stream: LightStreamStreamClient
|
|
11
|
+
|
|
12
|
+
constructor(options: ClientOptions) {
|
|
13
|
+
this.rest = new LightStreamRestClient({
|
|
14
|
+
baseUrl: options.baseUrl || 'https://gateway.lightstream.lat',
|
|
15
|
+
apiKey: options.apiKey,
|
|
16
|
+
jwtKey: options.jwtKey || options.token,
|
|
17
|
+
})
|
|
18
|
+
this.stream = new LightStreamStreamClient(options)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createLightStreamClient(options: ClientOptions): LightStreamClient {
|
|
23
|
+
return new LightStreamClient(options)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export * from './types.js'
|
|
27
|
+
export * from './errors.js'
|
|
28
|
+
export * from './rest-client.js'
|
|
29
|
+
export * from './stream-client.js'
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// src/rest-client.ts
|
|
2
|
+
// Typed HTTP REST client for LightStream Gateway Developer APIs.
|
|
3
|
+
|
|
4
|
+
import type { Platform, Timeframe } from './types.js'
|
|
5
|
+
|
|
6
|
+
export interface RestClientConfig {
|
|
7
|
+
baseUrl: string
|
|
8
|
+
apiKey?: string
|
|
9
|
+
jwtKey?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class LightStreamRestClient {
|
|
13
|
+
constructor(private config: RestClientConfig) {}
|
|
14
|
+
|
|
15
|
+
private async fetchJson<T>(path: string, options: RequestInit = {}): Promise<T> {
|
|
16
|
+
const url = `${this.config.baseUrl.replace(/\/+$/, '')}${path}`
|
|
17
|
+
const headers = new Headers(options.headers || {})
|
|
18
|
+
if (!headers.has('Accept')) headers.set('Accept', 'application/json')
|
|
19
|
+
if (this.config.apiKey) {
|
|
20
|
+
headers.set('x-api-key', this.config.apiKey)
|
|
21
|
+
} else if (this.config.jwtKey) {
|
|
22
|
+
headers.set('Authorization', `Bearer ${this.config.jwtKey}`)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const res = await fetch(url, { ...options, headers })
|
|
26
|
+
const data = await res.json()
|
|
27
|
+
if (!res.ok) {
|
|
28
|
+
throw new Error((data as { error?: string })?.error || `HTTP ${res.status}`)
|
|
29
|
+
}
|
|
30
|
+
return data as T
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Auth & Scoped Ephemeral Tokens
|
|
34
|
+
async issueToken(options: {
|
|
35
|
+
expireAfterSeconds?: number
|
|
36
|
+
allowedPlatforms?: Platform[]
|
|
37
|
+
allowedChannels?: string[]
|
|
38
|
+
maxWebSockets?: number
|
|
39
|
+
}): Promise<{ token: string; expiresAt: number; maxWebSockets: number }> {
|
|
40
|
+
const res = await this.fetchJson<{ ok: boolean; data: { token: string; expiresAt: number; maxWebSockets: number } }>(
|
|
41
|
+
'/api/v1/auth/tokens',
|
|
42
|
+
{
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: { 'Content-Type': 'application/json' },
|
|
45
|
+
body: JSON.stringify(options),
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
return res.data
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Streams & Rooms
|
|
52
|
+
async bulkCheckStreams(requests: Array<{ platform: Platform; channel: string }>): Promise<Array<{
|
|
53
|
+
platform: Platform
|
|
54
|
+
channel: string
|
|
55
|
+
isLive: boolean
|
|
56
|
+
viewerCount: number
|
|
57
|
+
title?: string
|
|
58
|
+
cached?: boolean
|
|
59
|
+
}>> {
|
|
60
|
+
const res = await this.fetchJson<{ ok: boolean; results: any[] }>('/api/v1/streams/bulk-check', {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: { 'Content-Type': 'application/json' },
|
|
63
|
+
body: JSON.stringify({ requests }),
|
|
64
|
+
})
|
|
65
|
+
return res.results
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async getRoomStreamInfo(platform: Platform, channel: string): Promise<Record<string, unknown>> {
|
|
69
|
+
const res = await this.fetchJson<{ ok: boolean; data: Record<string, unknown> }>(
|
|
70
|
+
`/api/v1/rooms/${encodeURIComponent(platform)}/${encodeURIComponent(channel)}/stream-info`,
|
|
71
|
+
)
|
|
72
|
+
return res.data
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async getUserEarnings(platform: Platform, channel: string): Promise<Record<string, unknown>> {
|
|
76
|
+
const res = await this.fetchJson<{ ok: boolean; data: Record<string, unknown> }>(
|
|
77
|
+
`/api/v1/users/${encodeURIComponent(platform)}/${encodeURIComponent(channel)}/earnings`,
|
|
78
|
+
)
|
|
79
|
+
return res.data
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async getGiftsCatalog(platform: Platform = 'tiktok'): Promise<any[]> {
|
|
83
|
+
const res = await this.fetchJson<{ ok: boolean; data: any[] }>(
|
|
84
|
+
`/api/v1/gifts/catalog?platform=${encodeURIComponent(platform)}`,
|
|
85
|
+
)
|
|
86
|
+
return res.data
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Rankings
|
|
90
|
+
async getLeaderboard(platform: Platform, timeframe: Timeframe = 'daily', limit = 50): Promise<any[]> {
|
|
91
|
+
const res = await this.fetchJson<{ ok: boolean; entries: any[] }>(
|
|
92
|
+
`/api/v1/rankings/leaderboard?platform=${encodeURIComponent(platform)}&timeframe=${encodeURIComponent(timeframe)}&limit=${limit}`,
|
|
93
|
+
)
|
|
94
|
+
return res.entries
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async searchRankings(channel: string, platform: Platform = 'tiktok', timeframe: Timeframe = 'daily'): Promise<any> {
|
|
98
|
+
const res = await this.fetchJson<{ ok: boolean; data: any }>(
|
|
99
|
+
`/api/v1/rankings/search?channel=${encodeURIComponent(channel)}&platform=${encodeURIComponent(platform)}&timeframe=${encodeURIComponent(timeframe)}`,
|
|
100
|
+
)
|
|
101
|
+
return res.data
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Moderation
|
|
105
|
+
readonly moderation = {
|
|
106
|
+
mute: (platform: Platform, channel: string, targetUser: string, duration?: number) =>
|
|
107
|
+
this.fetchJson('/api/v1/moderation/mutes', {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: { 'Content-Type': 'application/json' },
|
|
110
|
+
body: JSON.stringify({ platform, channel, targetUser, duration }),
|
|
111
|
+
}),
|
|
112
|
+
unmute: (platform: Platform, channel: string, targetUser: string) =>
|
|
113
|
+
this.fetchJson('/api/v1/moderation/mutes', {
|
|
114
|
+
method: 'DELETE',
|
|
115
|
+
headers: { 'Content-Type': 'application/json' },
|
|
116
|
+
body: JSON.stringify({ platform, channel, targetUser }),
|
|
117
|
+
}),
|
|
118
|
+
ban: (platform: Platform, channel: string, targetUser: string, reason?: string) =>
|
|
119
|
+
this.fetchJson('/api/v1/moderation/bans', {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
headers: { 'Content-Type': 'application/json' },
|
|
122
|
+
body: JSON.stringify({ platform, channel, targetUser, reason }),
|
|
123
|
+
}),
|
|
124
|
+
unban: (platform: Platform, channel: string, targetUser: string) =>
|
|
125
|
+
this.fetchJson('/api/v1/moderation/bans', {
|
|
126
|
+
method: 'DELETE',
|
|
127
|
+
headers: { 'Content-Type': 'application/json' },
|
|
128
|
+
body: JSON.stringify({ platform, channel, targetUser }),
|
|
129
|
+
}),
|
|
130
|
+
deleteMessage: (platform: Platform, channel: string, messageId: string) =>
|
|
131
|
+
this.fetchJson('/api/v1/moderation/messages', {
|
|
132
|
+
method: 'DELETE',
|
|
133
|
+
headers: { 'Content-Type': 'application/json' },
|
|
134
|
+
body: JSON.stringify({ platform, channel, messageId }),
|
|
135
|
+
}),
|
|
136
|
+
toggleComments: (platform: Platform, channel: string, enabled: boolean) =>
|
|
137
|
+
this.fetchJson('/api/v1/moderation/comments/toggle', {
|
|
138
|
+
method: 'POST',
|
|
139
|
+
headers: { 'Content-Type': 'application/json' },
|
|
140
|
+
body: JSON.stringify({ platform, channel, enabled }),
|
|
141
|
+
}),
|
|
142
|
+
}
|
|
143
|
+
}
|