UNPKG

@topstats/sdk

Version:

Official Node.js client for the topstats.gg API

205 lines (204 loc) 7.53 kB
"use strict"; /** * @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);