@topstats/sdk
Version:
Official Node.js client for the topstats.gg API
205 lines (204 loc) • 7.53 kB
JavaScript
;
/**
* @packageDocumentation
* TopStats - Community maintained Node.js client for the topstats.gg API
* @version 1.0.0
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Client = void 0;
const rankings_1 = require("./types/discord/rankings");
const error_1 = require("./types/error");
/**
* Main client class for interacting with the TopStats API
* @example
* ```typescript
* const client = new Client({ token: 'your-token' });
* // or
* const client = new Client('your-token');
*
* // Get bot info
* const bot = await client.getBot('123456789');
* ```
*/
class Client {
/**
* Creates a new TopStats API client
* @param options - Token string or client options object
*/
constructor(options) {
if (typeof options === 'string') {
this.token = options;
this.BASE_URL = 'https://api.topstats.gg';
}
else {
this.token = options.token;
this.BASE_URL = options.baseUrl ?? 'https://api.topstats.gg';
}
if (!this.token) {
throw new error_1.TopStatsError('No API token provided');
}
}
/**
* Validates a Discord bot ID format
* @throws {TopStatsError} If ID format is invalid
*/
validateBotId(botId) {
if (!/^\d{17,19}$/.test(botId)) {
throw new error_1.TopStatsError('Invalid Discord bot ID format');
}
}
/**
* Makes an HTTP request to the API
* @throws {RateLimitError} When rate limited
* @throws {TopStatsError} For other API errors
*/
async _request(method, path, query) {
try {
const url = new URL(`${this.BASE_URL}${path}`);
if (query && method === 'GET') {
Object.entries(query).forEach(([key, value]) => {
if (value !== undefined) {
url.searchParams.append(key, String(value));
}
});
}
const response = await fetch(url.toString(), {
method,
headers: {
Authorization: this.token,
'Content-Type': 'application/json',
'User-Agent': 'TopStats/1.0.0',
},
body: method !== 'GET' && query ? JSON.stringify(query) : undefined,
});
const data = await response.json();
if (response.status === 429) {
const rateLimitData = data;
throw new error_1.RateLimitError(rateLimitData.message, rateLimitData.expiresIn);
}
if (!response.ok) {
throw new error_1.TopStatsError(`API Error ${response.status}: ${response.statusText}`);
}
return data;
}
catch (error) {
if (error instanceof error_1.TopStatsError)
throw error;
throw new error_1.TopStatsError(error instanceof Error ? error.message : 'Unknown error occurred');
}
}
/**
* Fetches detailed information about a specific bot
* @param botId - The Discord bot ID
* @throws {TopStatsError} If bot ID is invalid
*/
async getBot(botId) {
this.validateBotId(botId);
return this._request('GET', `/discord/bots/${botId}`);
}
/**
* Fetch historical data for a specific bot
* @param botId - The Discord bot ID
* @param timeFrame - Time frame for historical data
* @param type - Type of data to fetch
*/
async getBotHistorical(botId, timeFrame, type) {
this.validateBotId(botId);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const response = await this._request('GET', `/discord/bots/${botId}/historical`, { timeFrame, type });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const transformedData = response.data.map((item) => ({
time: item.time,
id: item.id,
type: type,
value: item[type],
}));
return { data: transformedData };
}
/**
* Fetches recent data for a specific bot
* @param botId - The Discord bot ID
*/
async getBotRecent(botId) {
this.validateBotId(botId);
return this._request('GET', `/discord/bots/${botId}/recent`);
}
/**
* Fetches bot rankings based on specified criteria
* @param options - Ranking options
* @throws {TopStatsError} If limit is invalid
*/
async getRankings(options) {
const limit = options.limit ?? rankings_1.DEFAULT_RANKINGS_LIMIT;
if (!(0, rankings_1.isValidRankingsLimit)(limit)) {
throw new error_1.TopStatsError('Rankings limit must be between 1 and 500');
}
return this._request('GET', '/discord/rankings/bots', {
...options,
limit,
});
}
/**
* Fetches a user's bots
* @param id - The Discord user ID
*/
async getUsersBots(id) {
return this._request('GET', `/discord/users/${id}/bots`);
}
/**
* Compare multiple Discord bots.
*
* This method fetches the latest bot stats across multiple bots.
*
* @param ids - Array of Discord bot IDs
* @returns A promise with the comparison data
* @throws {TopStatsError} If any provided bot ID is invalid
*/
async compareBots(ids) {
// Validate each ID in the request
ids.forEach((id) => this.validateBotId(id));
// Construct the endpoint by joining the ids with a slash
const path = `/discord/compare/${ids.join('/')}`;
return this._request('GET', path);
}
/**
* Compare historical data for multiple Discord bots.
*
* This method fetches historical data (based on the provided timeframe and data type)
* for a set of bots.
*
* @param request - Object containing an array of bot IDs, a timeFrame, and a historical data type
* @returns A promise with the historical compare data
* @throws {TopStatsError} If any provided bot ID is invalid
*/
async compareBotsHistorical(request) {
const { ids, timeFrame, type } = request;
// Validate each ID in the request
ids.forEach((id) => this.validateBotId(id));
// Construct the endpoint by joining the ids with a slash, then use the "historical" endpoint
const path = `/discord/compare/historical/${ids.join('/')}`;
return this._request('GET', path, {
timeFrame,
type,
});
}
}
exports.Client = Client;
__exportStar(require("./types/discord/bots"), exports);
__exportStar(require("./types/discord/rankings"), exports);
__exportStar(require("./types/discord/users"), exports);
__exportStar(require("./types/error"), exports);