UNPKG

igdb-ts

Version:

Unofficial IGDB API TypeScript wrapper.

767 lines (619 loc) 25.7 kB
import axios from "axios" import axiosRateLimit, { rateLimitOptions } from "axios-rate-limit" import { AgeRating, AgeRatingContentDescription, AlternativeName, Artwork, Character, CharacterMugShot, Collection, Company, CompanyLogo, CompanyWebsite, Cover, ExternalGame, Franchise, Game, GameEngine, TwitchAuthResponse, IGDBOptions, Query, ImageOptions, GameEngineLogo, GameMode, GameVersion, GameVersionFeature, GameVersionFeatureValue, GameVideo, Genre, InvolvedCompany, Keyword, MultiplayerMode, Platform, PlatformFamily, PlatformLogo, PlatformVersion, PlatformVersionReleaseDate, PlatformWebsite, PlayerPerspective, ReleaseDate, Screenshot, Search, Theme, Website, PlatformVersionCompany, UntypedIGDBOptions, DefaultIGDBOptions, SearchableIGDBOptions, Filter, CombinedFilter, Endpoints, } from './types' export class IGDB { private clientId: string private clientToken: string private clientSecret: string private API_URL = "https://api.igdb.com/v4" private IMAGE_URL = "https://images.igdb.com/igdb/image/upload" public _axios = axiosRateLimit(axios.create(), { maxRPS: 4, perMilliseconds: 1000, maxRequests: 4 }) private onAccessTokenRetrieved: (clientToken: string, expiresAt: number) => void private tokenExpiry: number /** * Use function init() before calling endpoints. */ constructor(){} /** * Initialises wrapper and generates access token (if needed) to call API. * @param clientId - Twitch Client Id * @param clientSecret - Twitch Client Secret * @param clientToken - Twitch App Access Token * @param rateLimitOptions - Axios Rate Limit Options. Default is 4 requests/s as per IGDB documentation. * @param onAccessTokenRetrieved - Callback which can be used to save token to storage. Includes timestamp of when the token will expire. */ public async init(clientId: string, clientSecret: string, clientToken?: { token: string, tokenExpiry: number }, onAccessTokenRetrieved?: (token: string, tokenExpiry: number) => void, rateLimitOptions?: rateLimitOptions){ this.clientId = clientId this.clientSecret = clientSecret this.onAccessTokenRetrieved = onAccessTokenRetrieved if (!clientToken) { await this.getToken() } else { this.clientToken = clientToken.token this.tokenExpiry = clientToken.tokenExpiry } if (rateLimitOptions){ this._axios.setRateLimitOptions(rateLimitOptions) } } /** * Ensures token is valid and has not expired. */ private async validateToken(){ let currentTime = new Date().getTime() if (currentTime >= this.tokenExpiry) { await this.getToken() } } /** * Can be used to generate a new access token. * * As per documentation, the access token cannot be refreshed. Therefore, when an access token expires you need create a new one. * * https://dev.twitch.tv/docs/authentication/refresh-tokens */ private async getToken(){ let response = await axios.post<TwitchAuthResponse>(`https://id.twitch.tv/oauth2/token?client_id=${this.clientId}&client_secret=${this.clientSecret}&grant_type=client_credentials`) .then((response) => { if (response.data) { return response.data } }) //expires_in is expressed in seconds not milliseconds therefore, *1000 const expiry = new Date().getTime() + (response.expires_in * 1000) this.clientToken = response.access_token this.tokenExpiry = expiry this.onAccessTokenRetrieved(response.access_token, expiry) } private buildFilter({ filters, operators }: Filter){ //For every 2, filters there must be 1 operator 8 querys = 4 operators if (filters.length === 0) { throw Error("You need to provide at least one filter.") } if (filters.length > 1){ if (!operators || (operators.length != filters.length / 2) ) { throw Error("You must provide 1 operator for every two filters.") } } let _filter = "" //if 1 then add for (let i = 0; i < filters.length; i++){ if (i % 2 !== 0) { _filter += ` ${operators[i - 1]} ` } let f = filters[i] _filter += `${f.field} ${f.postfix} ${f.value}` } return _filter } private buildCombinedFilter({ filters, operators }: CombinedFilter) { if (filters.length <= 1) { throw Error("A combined filter required at least two filters.") } else { if (!operators || (operators.length != filters.length / 2) ) { throw Error("You must provide 1 operator for every two filters.") } } let _combinedFilter = "" for (let i = 0; i < filters.length; i++){ if (i % 2 !== 0) { _combinedFilter += ` ${operators[i - 1]} ` } let f = filters[i] _combinedFilter += `(${this.buildFilter(f)})` } return _combinedFilter } private buildOptions<T>(options?: IGDBOptions<T>){ //If no options, get all fields if (!options) { return `fields *;` } let result = "" if (options.fields){ result += `fields ${options.fields.join(",")};` } else { result += `fields *;` } if (options.exclude){ result += `exclude ${options.exclude.join(",")};` } if (options.filter){ let filtersAsString = this.buildFilter(options.filter) result += `where ${filtersAsString};` } if (options.combinedFilter) { let filtersAsString = this.buildCombinedFilter(options.combinedFilter) result += `where ${filtersAsString};` } if (options.sortBy){ result += `sort ${options.sortBy.field} ${options.sortBy.order};` } if (options.search){ result += `search "${options.search}";` } if (options.limit){ result += `limit ${options.limit};` } if (options.offset){ result += `offset ${options.offset};` } return result } private buildUntypedOptions(options?: UntypedIGDBOptions){ //If no options, get all fields if (!options) { return `fields *;` } let result = "" if (options.fields){ result += `fields ${options.fields.join(",")};` } else { result += `fields *;` } if (options.exclude){ result += `exclude ${options.exclude.join(",")};` } if (options.filter){ let filtersAsString = this.buildFilter(options.filter) result += `where ${filtersAsString};` } if (options.combinedFilter) { let filtersAsString = this.buildCombinedFilter(options.combinedFilter) result += `filters ${filtersAsString};` } if (options.sortBy){ result += `sort ${options.sortBy.field} ${options.sortBy.order};` } if (options.search){ result += `search "${options.search}";` } if (options.limit){ result += `limit ${options.limit};` } if (options.offset){ result += `offset ${options.offset};` } return result } private async request<T>(endpoint: string, options?: IGDBOptions<T>){ if (!this.clientToken) { throw Error("Client token not found. Make sure to init() before requesting an endpoint.") } return this.validateToken().then(() => this._axios.post<T[]>(`${this.API_URL}/${endpoint}`, this.buildOptions(options), { headers: { 'Client-ID': this.clientId, 'Authorization': `Bearer ${this.clientToken}`, 'Accept': 'application/json', }, })) .then((response) => { return response.data }) } private buildMultiQuery(queries: Query[]){ if (queries.length < 2) { throw Error("You need at least two queries to multiquery.") } if (queries.length > 10){ throw Error("You can only run a maxiumum of 10 queries.") } let query = "" queries.forEach((q) => { query += `query ${q.endpoint} "${q.resultName}" {${q.options ? this.buildUntypedOptions(q.options) : ""}};` }) return query } /** * Get a multiquery. * * * Maximum of 10 Queries. * * {@link https://api-docs.igdb.com/#multi-query} * @param {Array} queries - an array of [Query]({@link Query}) * @returns any[] **/ public async multiQuery(queries: Query[]){ if (!this.clientToken) { throw Error("Client token not found. Make sure to init() before requesting an endpoint.") } return this.validateToken().then(() => this._axios.post<any[]>(`${this.API_URL}/multiquery`, this.buildMultiQuery(queries), { headers: { 'Client-ID': this.clientId, 'Authorization': `Bearer ${this.clientToken}`, 'Accept': 'application/json', } })).then((response) => { return response.data }) } /** * Get an Image URL. * {@link https://api-docs.igdb.com/#images} * @param {Object} imageOptions - [Image Options]({@link ImageOptions}) * **/ public getImageUrl({ imageId, size, retina }: ImageOptions){ return `${this.IMAGE_URL}/t_${size}${retina && "_2x"}/${imageId}.jpg` } /** * Generic Endpoint Call. * * Provide your own endpoint and response type. * * {@link https://api-docs.igdb.com/#about} * @param {Object} options - [Untyped Endpoint Options]({@link UntypedIGDBOptions}) * **/ public async get<T>(endpoint: string, options?: UntypedIGDBOptions){ if (!this.clientToken) { throw Error("Client token not found. Make sure to init() before requesting an endpoint.") } return this.validateToken().then(() => this._axios.post<T>(`${this.API_URL}/${endpoint}`, this.buildUntypedOptions(options), { headers: { 'Client-ID': this.clientId, 'Authorization': `Bearer ${this.clientToken}`, 'Accept': 'application/json', }, })) .then((response) => { return response.data }) } /** * Get Age Ratings. * {@link https://api-docs.igdb.com/#age-rating} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getAgeRatings(options?: DefaultIGDBOptions<AgeRating>) { return this.request<AgeRating>(Endpoints.AGE_RATING, options) } /** * Get Age Rating Content Descriptions. * {@link https://api-docs.igdb.com/#age-rating-content-description} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getAgeRatingContentDescriptions(options?: DefaultIGDBOptions<AgeRatingContentDescription>){ return this.request<AgeRatingContentDescription>(Endpoints.AGE_RATING_CONTENT_DESCRIPTION, options) } /** * Get Alternative Names. * {@link https://api-docs.igdb.com/#alternative-name} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getAlternativeNames(options?: DefaultIGDBOptions<AlternativeName>) { return this.request<AlternativeName>(Endpoints.ALTERNATIVE_NAME, options) } /** * Get Artworks. * {@link https://api-docs.igdb.com/#artwork} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getArtworks(options?: DefaultIGDBOptions<Artwork>) { return this.request<Artwork>(Endpoints.ARTWORK, options) } /** * Get Characters. * {@link https://api-docs.igdb.com/#character} * @param {Object} options - [Searchable Endpoint Options]({@link SearchableIGDBOptions}) * **/ public getCharacters(options?: SearchableIGDBOptions<Character>) { return this.request<Character>(Endpoints.CHARACTER, options) } /** * Get Character Mug Shot. * {@link https://api-docs.igdb.com/#character-mug-shot} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getCharacterMugShots(options?: DefaultIGDBOptions<CharacterMugShot>) { return this.request<CharacterMugShot>(Endpoints.CHARACTER_MUG_SHOT, options) } /** * Get Collections. * {@link https://api-docs.igdb.com/#collection} * @param {Object} options - [Searchable Endpoint Options]({@link SearchableIGDBOptions}) * **/ public getCollections(options?: SearchableIGDBOptions<Collection>) { return this.request<Collection>(Endpoints.COLLECTION, options) } /** * Get Companies. * {@link https://api-docs.igdb.com/#company} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getCompanies(options?: DefaultIGDBOptions<Company>) { return this.request<Company>(Endpoints.COMPANY, options) } /** * Get Company Logos. * {@link https://api-docs.igdb.com/#company-logo} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getCompanyLogos(options?: DefaultIGDBOptions<CompanyLogo>) { return this.request<CompanyLogo>(Endpoints.COMPANY_LOGO, options) } /** * Get Company Website. * {@link https://api-docs.igdb.com/#company-website} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getCompanyWebsite(options?: DefaultIGDBOptions<CompanyWebsite>) { return this.request<CompanyWebsite>(Endpoints.COMPANY_WEBSITE, options) } /** * Get Covers. * {@link https://api-docs.igdb.com/#cover} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getCovers(options?: DefaultIGDBOptions<Cover>) { return this.request<Cover>(Endpoints.COVER, options) } /** * Get External Games. * {@link https://api-docs.igdb.com/#external-game} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getExternalGames(options?: DefaultIGDBOptions<ExternalGame>) { return this.request<ExternalGame>(Endpoints.EXTERNAL_GAME, options) } /** * Get Franchises. * {@link https://api-docs.igdb.com/#franchise} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getFranchises(options?: DefaultIGDBOptions<Franchise>) { return this.request<Franchise>(Endpoints.FRANCHISE, options) } /** * Get Games. * {@link https://api-docs.igdb.com/#game} * @param {Object} options - [Searchable Endpoint Options]({@link SearchableIGDBOptions}) * **/ public getGames(options?: SearchableIGDBOptions<Game>) { return this.request<Game>(Endpoints.GAME, options) } /** * Get Game Engines. * {@link https://api-docs.igdb.com/#game-engine} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getGameEngines(options?: DefaultIGDBOptions<GameEngine>) { return this.request<GameEngine>(Endpoints.GAME_ENGINE, options) } /** * Get Game Engine Logos. * {@link https://api-docs.igdb.com/#game-engine-logo} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getGameEngineLogos(options?: DefaultIGDBOptions<GameEngineLogo>) { return this.request<GameEngineLogo>(Endpoints.GAME_ENGINE_LOGO, options) } /** * Get Game Modes. * {@link https://api-docs.igdb.com/#game-mode} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getGameModes(options?: DefaultIGDBOptions<GameMode>) { return this.request<GameMode>(Endpoints.GAME_MODE, options) } /** * Get Game Versions. * {@link https://api-docs.igdb.com/#game-version} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getGameVersions(options?: DefaultIGDBOptions<GameVersion>) { return this.request<GameVersion>(Endpoints.GAME_VERSION, options) } /** * Get Game Version Features. * {@link https://api-docs.igdb.com/#game-version-feature} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getGameVersionFeatures(options?: DefaultIGDBOptions<GameVersionFeature>) { return this.request<GameVersionFeature>(Endpoints.GAME_VERSION_FEATURE, options) } /** * Get Game Version Feature Values. * {@link https://api-docs.igdb.com/#game-engine-feature-value} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getGameVersionFeatureValues(options?: DefaultIGDBOptions<GameVersionFeatureValue>) { return this.request<GameVersionFeatureValue>(Endpoints.GAME_VERSION_FEATURE_VALUE, options) } /** * Get Game Videos. * {@link https://api-docs.igdb.com/#game-video} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getGameVideos(options?: DefaultIGDBOptions<GameVideo>) { return this.request<GameVideo>(Endpoints.GAME_VIDEO, options) } /** * Get Genres. * {@link https://api-docs.igdb.com/#genre} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getGenres(options?: DefaultIGDBOptions<Genre>) { return this.request<Genre>(Endpoints.GENRE, options) } /** * Get Involved Companies. * {@link https://api-docs.igdb.com/#involved-company} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getInvolvedCompanies(options?: DefaultIGDBOptions<InvolvedCompany>) { return this.request<InvolvedCompany>(Endpoints.INVOLVED_COMPANY, options) } /** * Get Keywords. * {@link https://api-docs.igdb.com/#keyword} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getKeywords(options?: DefaultIGDBOptions<Keyword>) { return this.request<Keyword>(Endpoints.KEYWORD, options) } /** * Get Multiplayer Modes. * {@link https://api-docs.igdb.com/#multiplayer-mode} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getMultiplayerModes(options?: DefaultIGDBOptions<MultiplayerMode>) { return this.request<MultiplayerMode>(Endpoints.MULTIPLAYER_MODE, options) } /** * Get Platform. * {@link https://api-docs.igdb.com/#platform} * @param {Object} options - [Searchable Endpoint Options]({@link SearchableIGDBOptions}) * **/ public getPlatforms(options?: SearchableIGDBOptions<Platform>) { return this.request<Platform>(Endpoints.PLATFORM, options) } /** * Get Platform Families. * {@link https://api-docs.igdb.com/#platform-family} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getPlatformFamilies(options?: DefaultIGDBOptions<PlatformFamily>) { return this.request<PlatformFamily>(Endpoints.PLATFORM_FAMILY, options) } /** * Get Platform Logos. * {@link https://api-docs.igdb.com/#platform-logo} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getPlatformLogos(options?: DefaultIGDBOptions<PlatformLogo>) { return this.request<PlatformLogo>(Endpoints.PLATFORM_LOGO, options) } /** * Get Platform Versions. * {@link https://api-docs.igdb.com/#platform-version} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getPlatformVersion(options?: DefaultIGDBOptions<PlatformVersion>) { return this.request<PlatformVersion>(Endpoints.PLATFORM_VERSION, options) } /** * Get Platform Version Companies. * {@link https://api-docs.igdb.com/#platform-version-company} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getPlatformVersionCompanies(options?: DefaultIGDBOptions<PlatformVersionCompany>) { return this.request<PlatformVersionCompany>(Endpoints.PLATFORM_VERSION_COMPANY, options) } /** * Get Platform Version Release Dates. * {@link https://api-docs.igdb.com/#platform-version-release_date} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getPlatformVersionReleaseDates(options?: DefaultIGDBOptions<PlatformVersionReleaseDate>) { return this.request<PlatformVersionReleaseDate>(Endpoints.PLATFORM_VERSION_RELEASE_DATE, options) } /** * Get Platform Websites. * {@link https://api-docs.igdb.com/#platform-website} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getPlatformWebsites(options?: DefaultIGDBOptions<PlatformWebsite>) { return this.request<PlatformWebsite>(Endpoints.PLATFORM_WEBSITE, options) } /** * Get Player Perspectives. * {@link https://api-docs.igdb.com/#player-perspective} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getPlayerPerspectives(options?: DefaultIGDBOptions<PlayerPerspective>) { return this.request<PlayerPerspective>(Endpoints.PLAYER_PERSPECTIVE, options) } /** * Get Release Dates. * {@link https://api-docs.igdb.com/#release-date} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getReleaseDates(options?: DefaultIGDBOptions<ReleaseDate>) { return this.request<ReleaseDate>(Endpoints.RELEASE_DATE, options) } /** * Get Screenshots. * {@link https://api-docs.igdb.com/#screenshot} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getScreenshots(options?: DefaultIGDBOptions<Screenshot>) { return this.request<Screenshot>(Endpoints.SCREENSHOT, options) } /** * Search IGDB. * {@link https://api-docs.igdb.com/#search} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public search(options?: DefaultIGDBOptions<Search>) { return this.request<Search>(Endpoints.SEARCH, options) } /** * Get Themes. * {@link https://api-docs.igdb.com/#theme} * @param {Object} options - [Searchable Endpoint Options]({@link SearchableIGDBOptions}) * **/ public getThemes(options?: SearchableIGDBOptions<Theme>) { return this.request<Theme>(Endpoints.THEME, options) } /** * Get Websites. * {@link https://api-docs.igdb.com/#website} * @param {Object} options - [Default Endpoint Options]({@link DefaultIGDBOptions}) * **/ public getWebsites(options?: DefaultIGDBOptions<Website>) { return this.request<Website>(Endpoints.WEBSITE, options) } }