UNPKG

osu-web.js

Version:

osu.js is an unofficial Javascript and Typescript SDK for the browser-facing portion of osu! with type safety in mind

1,410 lines (1,388 loc) 59.3 kB
var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/classes/Errors.ts var isOsuJSErrorSymbol = Symbol.for("osu.js:OsuJSError"); var errorMessageMap = { invalid_json_syntax: "A syntax error occurred while trying to parse the response as JSON. The API returned invalid JSON", network_error: "A network error occurred while making a request to the API", undefined_fetch: 'Global fetch is undefined. Please provide a polyfill for the fetch API by installing "node-fetch"', unexpected_response: "Received an unexpected response from the API" }; var OsuJSGeneralError = class extends Error { constructor(type) { super(errorMessageMap[type]); this._ = { [isOsuJSErrorSymbol]: true }; this.type = type; } }; var OsuJSUnexpectedResponseError = class extends Error { constructor(response) { super(errorMessageMap.unexpected_response); this._ = { [isOsuJSErrorSymbol]: true }; this.type = "unexpected_response"; this.response1 = response; } response(polyfill) { return this.response1; } }; // src/classes/auth/Base.ts var Base = class { constructor(clientId, clientSecret, redirectUri, options) { if (typeof fetch === "undefined" && !(options == null ? void 0 : options.polyfillFetch)) { throw new OsuJSGeneralError("undefined_fetch"); } this.clientId = clientId; this.clientSecret = clientSecret; this.redirectUri = redirectUri; this.oauthUrl = "https://osu.ppy.sh/oauth/"; this.fetch = (options == null ? void 0 : options.polyfillFetch) || fetch; } }; // src/classes/auth/AuthCodeGrant.ts var AuthCodeGrant = class extends Base { /** * @param clientId OAuth client ID * @param clientSecret OAuth client secret * @param redirectUri OAuth redirect URI * @param scopes An array of OAuth scopes * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(clientId, clientSecret, redirectUri, scopes, options) { super(clientId, clientSecret, redirectUri, options); this.scopes = scopes; } /** * Gets a token * * Documentation: {@link https://osujs.mario564.com/oauth/authorization-code-grant} * @param code The string received after a user authorizes the app * @returns An API token */ requestToken(code) { return __async(this, null, function* () { let resp = new Response(); try { resp = yield this.fetch(`${this.oauthUrl}token`, { method: "POST", body: JSON.stringify({ client_id: this.clientId, client_secret: this.clientSecret, code, grant_type: "authorization_code", redirect_uri: this.redirectUri }), headers: { "Content-Type": "application/json" } }); } catch (err) { if (err instanceof TypeError) { throw new OsuJSGeneralError("network_error"); } } if (!resp.ok) { throw new OsuJSUnexpectedResponseError(resp); } let token; try { token = yield resp.json(); } catch (err) { if (err instanceof SyntaxError) { throw new OsuJSGeneralError("invalid_json_syntax"); } } return token; }); } /** * Refreshes a token * * Documentation: {@link https://osujs.mario564.com/oauth/authorization-code-grant} * @param refreshToken The token used to refresh * @returns An API token */ refreshToken(refreshToken) { return __async(this, null, function* () { let resp = new Response(); try { resp = yield this.fetch(`${this.oauthUrl}token`, { method: "POST", body: JSON.stringify({ client_id: this.clientId, client_secret: this.clientSecret, refresh_token: refreshToken, grant_type: "refresh_token", scope: this.scopes }), headers: { "Content-Type": "application/json" } }); } catch (err) { if (err instanceof TypeError) { throw new OsuJSGeneralError("network_error"); } } if (!resp.ok) { throw new OsuJSUnexpectedResponseError(resp); } let token; try { token = yield resp.json(); } catch (err) { if (err instanceof SyntaxError) { throw new OsuJSGeneralError("invalid_json_syntax"); } } return token; }); } }; // src/classes/auth/Auth.ts var Auth = class extends Base { /** * @param clientId OAuth client ID * @param clientSecret OAuth client secret * @param redirectUri OAuth redirect URI * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(clientId, clientSecret, redirectUri, options) { super(clientId, clientSecret, redirectUri, options); } /** * @param scopes An array of scopes * * Documentation: {@link https://osujs.mario564.com/oauth/authorization-code-grant} */ authorizationCodeGrant(scopes = ["identify"]) { return new AuthCodeGrant(this.clientId, this.clientSecret, this.redirectUri, scopes); } /** * Gets a token * * Documentation: {@link https://osujs.mario564.com/oauth/client-credentials-grant} * @param scopes An array of scopes * @returns An API token */ clientCredentialsGrant() { return __async(this, arguments, function* (scopes = ["public"]) { let resp = new Response(); try { resp = yield this.fetch(`${this.oauthUrl}token`, { method: "POST", body: JSON.stringify({ client_id: this.clientId, client_secret: this.clientSecret, grant_type: "client_credentials", scope: scopes }), headers: { "Content-Type": "application/json" } }); } catch (err) { if (err instanceof TypeError) { throw new OsuJSGeneralError("network_error"); } } if (!resp.ok) { throw new OsuJSUnexpectedResponseError(resp); } let token; try { token = yield resp.json(); } catch (err) { if (err instanceof SyntaxError) { throw new OsuJSGeneralError("invalid_json_syntax"); } } return token; }); } /** * Revokes a token * * Documentation: {@link https://osujs.mario564.com/oauth/revoke-token} * @param accessToken Access token to revoke */ revokeToken(accessToken) { return __async(this, null, function* () { try { yield this.fetch("https://osu.ppy.sh/api/v2/oauth/tokens/current", { method: "DELETE", headers: { Authorization: `Bearer ${accessToken}` } }); } catch (err) { if (err instanceof TypeError) { throw new OsuJSGeneralError("network_error"); } } }); } }; // src/utils/index.ts function formatUrlParams(urlParams) { return Object.entries(urlParams).reduce((prev, [key, value]) => { if (!value) return prev; return Array.isArray(value) ? `${prev}${value.reduce((prev2, value2) => `${prev2}&${key}[]=${value2}`, "")}` : `${prev}&${key}=${value}`; }, ""); } function map(obj) { let entries = Object.entries(obj); entries = entries.map(mapCallback); entries.forEach(([key, value]) => { obj[key] = value; }); return obj; } function mapCallback([key, value]) { let newValue = value; if (Array.isArray(value) && typeof value[0] === "object") { newValue = value.map((v) => map(v)); } else if (typeof value === "object" && value) { newValue = map(value); } else if (!isNaN(Number(value)) && typeof value !== "boolean") { newValue = Number(value); } return [key, newValue]; } // src/utils/enums.ts var ModesEnum = /* @__PURE__ */ ((ModesEnum2) => { ModesEnum2[ModesEnum2["osu"] = 0] = "osu"; ModesEnum2[ModesEnum2["taiko"] = 1] = "taiko"; ModesEnum2[ModesEnum2["fruits"] = 2] = "fruits"; ModesEnum2[ModesEnum2["mania"] = 3] = "mania"; return ModesEnum2; })(ModesEnum || {}); var StatusEnum = /* @__PURE__ */ ((StatusEnum2) => { StatusEnum2[StatusEnum2["graveyard"] = -2] = "graveyard"; StatusEnum2[StatusEnum2["wip"] = -1] = "wip"; StatusEnum2[StatusEnum2["pending"] = 0] = "pending"; StatusEnum2[StatusEnum2["ranked"] = 1] = "ranked"; StatusEnum2[StatusEnum2["approved"] = 2] = "approved"; StatusEnum2[StatusEnum2["qualified"] = 3] = "qualified"; StatusEnum2[StatusEnum2["loved"] = 4] = "loved"; return StatusEnum2; })(StatusEnum || {}); var GenresEnum = /* @__PURE__ */ ((GenresEnum2) => { GenresEnum2[GenresEnum2["Any"] = 0] = "Any"; GenresEnum2[GenresEnum2["Unspecified"] = 1] = "Unspecified"; GenresEnum2[GenresEnum2["Video Game"] = 2] = "Video Game"; GenresEnum2[GenresEnum2["Anime"] = 3] = "Anime"; GenresEnum2[GenresEnum2["Rock"] = 4] = "Rock"; GenresEnum2[GenresEnum2["Pop"] = 5] = "Pop"; GenresEnum2[GenresEnum2["Other"] = 6] = "Other"; GenresEnum2[GenresEnum2["Novelty"] = 7] = "Novelty"; GenresEnum2[GenresEnum2["Hip Hop"] = 9] = "Hip Hop"; GenresEnum2[GenresEnum2["Electronic"] = 10] = "Electronic"; GenresEnum2[GenresEnum2["Metal"] = 11] = "Metal"; GenresEnum2[GenresEnum2["Classical"] = 12] = "Classical"; GenresEnum2[GenresEnum2["Folk"] = 13] = "Folk"; GenresEnum2[GenresEnum2["Jazz"] = 14] = "Jazz"; return GenresEnum2; })(GenresEnum || {}); var LanguagesEnum = /* @__PURE__ */ ((LanguagesEnum2) => { LanguagesEnum2[LanguagesEnum2["Any"] = 0] = "Any"; LanguagesEnum2[LanguagesEnum2["Unspecified"] = 1] = "Unspecified"; LanguagesEnum2[LanguagesEnum2["English"] = 2] = "English"; LanguagesEnum2[LanguagesEnum2["Japanese"] = 3] = "Japanese"; LanguagesEnum2[LanguagesEnum2["Chinese"] = 4] = "Chinese"; LanguagesEnum2[LanguagesEnum2["Instrumental"] = 5] = "Instrumental"; LanguagesEnum2[LanguagesEnum2["Korean"] = 6] = "Korean"; LanguagesEnum2[LanguagesEnum2["French"] = 7] = "French"; LanguagesEnum2[LanguagesEnum2["German"] = 8] = "German"; LanguagesEnum2[LanguagesEnum2["Swedish"] = 9] = "Swedish"; LanguagesEnum2[LanguagesEnum2["Spanish"] = 10] = "Spanish"; LanguagesEnum2[LanguagesEnum2["Italian"] = 11] = "Italian"; LanguagesEnum2[LanguagesEnum2["Russian"] = 12] = "Russian"; LanguagesEnum2[LanguagesEnum2["Polish"] = 13] = "Polish"; LanguagesEnum2[LanguagesEnum2["Other"] = 14] = "Other"; return LanguagesEnum2; })(LanguagesEnum || {}); var ModsEnum = /* @__PURE__ */ ((ModsEnum2) => { ModsEnum2[ModsEnum2["NF"] = 1] = "NF"; ModsEnum2[ModsEnum2["EZ"] = 2] = "EZ"; ModsEnum2[ModsEnum2["TD"] = 4] = "TD"; ModsEnum2[ModsEnum2["HD"] = 8] = "HD"; ModsEnum2[ModsEnum2["HR"] = 16] = "HR"; ModsEnum2[ModsEnum2["SD"] = 32] = "SD"; ModsEnum2[ModsEnum2["DT"] = 64] = "DT"; ModsEnum2[ModsEnum2["RX"] = 128] = "RX"; ModsEnum2[ModsEnum2["HT"] = 256] = "HT"; ModsEnum2[ModsEnum2["NC"] = 512] = "NC"; ModsEnum2[ModsEnum2["FL"] = 1024] = "FL"; ModsEnum2[ModsEnum2["AT"] = 2048] = "AT"; ModsEnum2[ModsEnum2["SO"] = 4096] = "SO"; ModsEnum2[ModsEnum2["AP"] = 8192] = "AP"; ModsEnum2[ModsEnum2["PF"] = 16384] = "PF"; ModsEnum2[ModsEnum2["4K"] = 32768] = "4K"; ModsEnum2[ModsEnum2["5K"] = 65536] = "5K"; ModsEnum2[ModsEnum2["6K"] = 131072] = "6K"; ModsEnum2[ModsEnum2["7K"] = 262144] = "7K"; ModsEnum2[ModsEnum2["8K"] = 524288] = "8K"; ModsEnum2[ModsEnum2["FI"] = 1048576] = "FI"; ModsEnum2[ModsEnum2["RD"] = 2097152] = "RD"; ModsEnum2[ModsEnum2["CN"] = 4194304] = "CN"; ModsEnum2[ModsEnum2["TP"] = 8388608] = "TP"; ModsEnum2[ModsEnum2["K9"] = 16777216] = "K9"; ModsEnum2[ModsEnum2["KC"] = 33554432] = "KC"; ModsEnum2[ModsEnum2["1K"] = 67108864] = "1K"; ModsEnum2[ModsEnum2["3K"] = 134217728] = "3K"; ModsEnum2[ModsEnum2["2K"] = 268435456] = "2K"; ModsEnum2[ModsEnum2["SV2"] = 536870912] = "SV2"; ModsEnum2[ModsEnum2["MR"] = 1073741824] = "MR"; return ModsEnum2; })(ModsEnum || {}); var ScoringTypeEnum = /* @__PURE__ */ ((ScoringTypeEnum2) => { ScoringTypeEnum2[ScoringTypeEnum2["Score"] = 0] = "Score"; ScoringTypeEnum2[ScoringTypeEnum2["Accuracy"] = 1] = "Accuracy"; ScoringTypeEnum2[ScoringTypeEnum2["Combo"] = 2] = "Combo"; ScoringTypeEnum2[ScoringTypeEnum2["Score V2"] = 3] = "Score V2"; return ScoringTypeEnum2; })(ScoringTypeEnum || {}); var TeamTypeEnum = /* @__PURE__ */ ((TeamTypeEnum2) => { TeamTypeEnum2[TeamTypeEnum2["Head To Head"] = 0] = "Head To Head"; TeamTypeEnum2[TeamTypeEnum2["Tag Co-Op"] = 1] = "Tag Co-Op"; TeamTypeEnum2[TeamTypeEnum2["Team VS"] = 2] = "Team VS"; TeamTypeEnum2[TeamTypeEnum2["Tag Team VS"] = 3] = "Tag Team VS"; return TeamTypeEnum2; })(TeamTypeEnum || {}); var TeamColorEnum = /* @__PURE__ */ ((TeamColorEnum2) => { TeamColorEnum2[TeamColorEnum2["Blue"] = 1] = "Blue"; TeamColorEnum2[TeamColorEnum2["Red"] = 2] = "Red"; return TeamColorEnum2; })(TeamColorEnum || {}); // src/utils/exported.ts function getModsEnum(mods, derivativeModsWithOriginal) { return mods.reduce((count, mod) => { if (![ "NF", "EZ", "TD", "HD", "HR", "SD", "DT", "RX", "HT", "NC", "FL", "AT", "SO", "AP", "PF", "4K", "5K", "6K", "7K", "8K", "FI", "RD", "CN", "TP", "K9", "KC", "1K", "2K", "3K", "SV2", "MR" ].includes(mod)) return count; if (mod === "NC" && derivativeModsWithOriginal) { return count + 512 /* NC */ + 64 /* DT */; } if (mod === "PF" && derivativeModsWithOriginal) { return count + 16384 /* PF */ + 32 /* SD */; } return count + ModsEnum[mod]; }, 0); } function getEnumMods(modEnum) { const mods = []; if (modEnum === 0) return mods; let parsedGlobalEnum = Number(modEnum); let modEnums = Object.keys(ModsEnum); modEnums = modEnums.splice(0, modEnums.length / 2); for (let i = modEnums.length; parsedGlobalEnum !== 0; i--) { const parsedEnum = Number(modEnums[i]); if (parsedGlobalEnum - parsedEnum >= 0) { mods.push(ModsEnum[parsedEnum]); parsedGlobalEnum -= parsedEnum; } } if (["DT", "NC"].every((mod) => mods.includes(mod))) { const i = mods.indexOf("DT"); mods.splice(i, 1); } if (["SD", "PF"].every((mod) => mods.includes(mod))) { const i = mods.indexOf("SD"); mods.splice(i, 1); } return mods.reverse(); } function isOsuJSError(value) { var _a; return typeof value === "object" && ((_a = value == null ? void 0 : value._) == null ? void 0 : _a[isOsuJSErrorSymbol]) === true; } // src/classes/Base.ts var Base2 = class { constructor(accessToken, options) { if (typeof fetch === "undefined" && !(options == null ? void 0 : options.polyfillFetch)) { throw new OsuJSGeneralError("undefined_fetch"); } this.accessToken = accessToken; this.fetch = (options == null ? void 0 : options.polyfillFetch) || fetch; this.usingPolyfillFetch = !!(options == null ? void 0 : options.polyfillFetch); } request(endpoint, method, options) { return __async(this, null, function* () { if (options == null ? void 0 : options.query) { const query = formatUrlParams(options.query); endpoint += query.replace("&", "?"); } let resp; const headers = { "Content-Type": "application/json", Authorization: `Bearer ${this.accessToken}` }; if (options == null ? void 0 : options.apiVersion) { headers["X-Api-Version"] = options.apiVersion; } try { resp = yield this.fetch(`https://osu.ppy.sh/api/v2/${endpoint}`, { method, headers, body: (options == null ? void 0 : options.body) ? JSON.stringify(options.body) : void 0 }); } catch (err) { if (err instanceof TypeError) { throw new OsuJSGeneralError("network_error"); } } if (resp.status === 404 && (options == null ? void 0 : options.returnNullOn404)) { return null; } if (!resp.ok) { throw new OsuJSUnexpectedResponseError(resp); } if (options == null ? void 0 : options.dontParseResp) { return void 0; } let data; try { data = yield resp.json(); } catch (err) { if (err instanceof SyntaxError) { throw new OsuJSGeneralError("invalid_json_syntax"); } } return data; }); } /** * Prevents a request done to the current API to throw an `OsuJSUnexpectedResponseError` error. * * Documentation: {@link https://osujs.mario564.com/current/safe-parse} */ safeParse(request) { return __async(this, null, function* () { let response; let data; let success = false; try { data = yield request; success = true; } catch (err) { if (isOsuJSError(err) && err.type === "unexpected_response") { response = err.response(this.usingPolyfillFetch); } else { throw err; } } return { success, response, data }; }); } /** * Set a new access token to be used by the current client. * * Documentation: {@link https://osujs.mario564.com/current/set-access-token} */ setAccessToken(accessToken) { this.accessToken = accessToken; } }; // src/classes/Users.ts var Users = class extends Base2 { /** * @param accessToken OAuth access token * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(accessToken, options) { super(accessToken, options); /** * Makes a GET request to the `/users/{user}/scores/{type}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-user-scores} * @param user ID of the user to get their scores * @param type Score type * @param options * @returns An array of the specified user's scores */ this.getUserScores = this.getUserScoresBase("v1"); /** * Makes a GET request to the `/users/{user}/scores/{type}` endpoint with the `x-api-version` header set to `20220705` * * Documentation: {@link https://osujs.mario564.com/current/get-user-scores} * @param user ID of the user to get their scores * @param type Score type * @param options * @returns An array of the specified user's scores */ this.getUserScoresV2 = this.getUserScoresBase("v2"); } /** * Makes a GET request to the `me` endpoint (requires the `identify` scope) * * Documentation: {@link https://osujs.mario564.com/current/get-self} * @returns The user corresponding to the access token provided in the constructor of this class */ getSelf(options) { return __async(this, null, function* () { var _a; let endpoint = "me"; if ((_a = options == null ? void 0 : options.urlParams) == null ? void 0 : _a.mode) { endpoint += `/${options.urlParams.mode}`; } return yield this.request(endpoint, "GET"); }); } /** * Makes a GET request to the `/users/{user}/kudosu` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-user-kudosu} * @param user ID of the user to get kudosu from * @param options * @returns An array containing the specified user's kudosu history */ getUserKudosu(user, options) { return __async(this, null, function* () { return yield this.request(`users/${user}/kudosu`, "GET", options); }); } /** * Makes a GET request to the `/users/{user}/recent_activity` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-user-recent-activity} * @param user ID of the user to get their recent activity from * @param options * @returns An array containing the specified user's recent activity (each event is a union, to discriminate, use the `type` key) */ getUserRecentActivity(user, options) { return __async(this, null, function* () { return yield this.request(`users/${user}/recent_activity`, "GET", options); }); } getUserScoresBase(version) { return (user, type, options) => __async(this, null, function* () { return yield this.request(`users/${user}/scores/${type}`, "GET", __spreadProps(__spreadValues({}, options), { apiVersion: version === "v2" ? "20220705" : void 0 })); }); } /** * Makes a GET request to the `/users/{user}/beatmapsets/{type}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-user-beatmaps} * @param user ID of the user to get their beatmapsets * @param type Type of beatmapsets to return * @param options * @returns An array of a user's beatmapsets */ getUserBeatmaps(user, type, options) { return __async(this, null, function* () { return yield this.request(`users/${user}/beatmapsets/${type}`, "GET", options); }); } /** * Makes a GET request to the `/users/{user}/{mode?}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-user} * @param user ID or username of the user to get * @param options * @returns A user */ getUser(user, options) { return __async(this, null, function* () { var _a; let endpoint = `users/${user}`; if ((_a = options == null ? void 0 : options.urlParams) == null ? void 0 : _a.mode) { endpoint += `/${options.urlParams.mode}`; } return yield this.request(endpoint, "GET", options); }); } /** * Makes a GET request to the `/users` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-users} * @returns An array of users */ getUsers(options) { return __async(this, null, function* () { const users = yield this.request("users", "GET", options); return users.users; }); } }; // src/classes/Wiki.ts var Wiki = class extends Base2 { /** * @param accessToken OAuth access token * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(accessToken, options) { super(accessToken, options); } /** * Makes a GET request to the `/wiki/{locale}/{path}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-wiki-page} * @param locale Two-letter language code of the wiki page * @param path Path to the wiki page * @returns The wiki page */ getWikiPage(locale, path) { return __async(this, null, function* () { return yield this.request(`wiki/${locale}/${path}`, "GET"); }); } }; // src/classes/Comments.ts var Comments = class extends Base2 { /** * @param accessToken OAuth access token * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(accessToken, options) { super(accessToken, options); } /** * Makes a GET request to the `/comments` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-comments} * @returns An object containing comments, users and other related data */ getComments(options) { return __async(this, null, function* () { var _a, _b, _c, _d; let query = options == null ? void 0 : options.query; if (query == null ? void 0 : query.commentable) { query = __spreadProps(__spreadValues({}, query), { commentable_type: (_b = (_a = options == null ? void 0 : options.query) == null ? void 0 : _a.commentable) == null ? void 0 : _b.type, commentable_id: (_d = (_c = options == null ? void 0 : options.query) == null ? void 0 : _c.commentable) == null ? void 0 : _d.id }); delete query.commentable; } options = { query }; return yield this.request("comments", "GET", options); }); } /** * Makes a GET request to the `/comments/{comment}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-comment} * @param comment ID of the comment to get related data from * @returns An object containing comments, users and other related data to the comment with the specified ID */ getComment(comment) { return __async(this, null, function* () { return yield this.request(`comments/${comment}`, "GET"); }); } }; // src/classes/Multiplayer.ts var Multiplayer = class extends Base2 { /** * @param accessToken OAuth access token * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(accessToken, options) { super(accessToken, options); } /** * Makes a GET request to the `/rooms/{room}/playlist/{playlist}/scores` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-playlist-scores} * @param room ID of the room corresponding to the playlist * @param playlist ID of the playlist to get scores from * @returns An object containing playlist scores and metadata */ getPlaylistScores(room, playlist, options) { return __async(this, null, function* () { return yield this.request(`rooms/${room}/playlist/${playlist}/scores`, "GET", options); }); } }; // src/classes/Ranking.ts var Ranking = class extends Base2 { /** * @param accessToken OAuth access token * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(accessToken, options) { super(accessToken, options); } /** * Makes a GET request to the `/rankings/{mode}/{type}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-ranking} * @param mode Ranking gamemode * @param type Ranking type * @returns An object containing ranking data */ getRanking(mode, type, options) { return __async(this, null, function* () { var _a; if ((_a = options == null ? void 0 : options.query) == null ? void 0 : _a.country) { options.query.country = options.query.country.toUpperCase(); } return yield this.request(`rankings/${mode}/${type}`, "GET", options); }); } /** * Makes a GET request to the `/spotights` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-spotlights} * @returns An array of spotlights */ getSpotlights() { return __async(this, null, function* () { const spotlights = yield this.request("spotlights", "GET"); return spotlights.spotlights; }); } }; // src/classes/News.ts var News = class extends Base2 { /** * @param accessToken OAuth access token * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(accessToken, options) { super(accessToken, options); } /** * Makes a GET request to the `/news` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-news-listing} * @returns An object containing news posts and other additional data */ getNewsListing(options) { return __async(this, null, function* () { return yield this.request("news", "GET", options); }); } /** * Makes a GET request to the `/news/{news}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-news-post} * @param news ID or slug of the news post to get * @returns A news post */ getNewsPost(news, options) { return __async(this, null, function* () { return yield this.request(`news/${news}`, "GET", options); }); } }; // src/classes/Beatmaps.ts var Beatmaps = class extends Base2 { /** * @param accessToken OAuth access token * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(accessToken, options) { super(accessToken, options); /** * Makes a GET request to the `/beatmaps/{beatmap}/scores/users/{user}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-beatmap-user-score} * @param beatmap ID of the beatmap to get scores from * @param user ID of the user to get scores from * @returns A user score on a beatmap */ this.getBeatmapUserScore = this.getBeatmapUserScoreBase("v1"); /** * Makes a GET request to the `/beatmaps/{beatmap}/scores/users/{user}` endpoint with the `x-api-version` header set to `20220705` * * Documentation: {@link https://osujs.mario564.com/current/get-beatmap-user-score} * @param beatmap ID of the beatmap to get scores from * @param user ID of the user to get scores from * @returns A user score on a beatmap */ this.getBeatmapUserScoreV2 = this.getBeatmapUserScoreBase("v2"); /** * Makes a GET request to the `/beatmaps/{beatmap}/scores/users/{user}/all` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-beatmap-user-scores} * @param beatmap ID of the beatmap to get scores from * @param user ID of the user to get scores from * @returns An array of user scores on a beatmap */ this.getBeatmapUserScores = this.getBeatmapUserScoresBase("v1"); /** * Makes a GET request to the `/beatmaps/{beatmap}/scores/users/{user}/all` endpoint with the `x-api-version` header set to `20220705` * * Documentation: {@link https://osujs.mario564.com/current/get-beatmap-user-scores} * @param beatmap ID of the beatmap to get scores from * @param user ID of the user to get scores from * @returns An array of user scores on a beatmap */ this.getBeatmapUserScoresV2 = this.getBeatmapUserScoresBase("v2"); /** * Makes a GET request to the `/beatmaps/{beatmap}/scores` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-beatmap-top-scores} * @param beatmap ID of the beatmap to get top scores from * @returns An array of user scores on a beatmap */ this.getBeatmapTopScores = this.getBeatmapTopScoresBase("v1"); /** * Makes a GET request to the `/beatmaps/{beatmap}/scores` endpoint with the `x-api-version` header set to `20220705` * * Documentation: {@link https://osujs.mario564.com/current/get-beatmap-top-scores} * @param beatmap ID of the beatmap to get top scores from * @returns An array of user scores on a beatmap */ this.getBeatmapTopScoresV2 = this.getBeatmapTopScoresBase("v2"); /** * Makes a GET request to the `/beatmaps/{beatmap}/solo-scores` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-beatmap-top-non-legacy-scores} * @param beatmap ID of the beatmap to get top scores from * @returns An array of user scores on a beatmap */ this.getBeatmapTopNonLegacyScores = this.getBeatmapTopNonLegacyScoresBase("v1"); /** * Makes a GET request to the `/beatmaps/{beatmap}/solo-scores` endpoint with the `x-api-version` header set to `20220705` * * Documentation: {@link https://osujs.mario564.com/current/get-beatmap-top-non-legacy-scores} * @param beatmap ID of the beatmap to get top scores from * @returns An array of user scores on a beatmap */ this.getBeatmapTopNonLegacyScoresV2 = this.getBeatmapTopNonLegacyScoresBase("v2"); } /** * Makes a GET request to the `/beatmaps/lookup` endpoint * * Documentation: {@link https://osujs.mario564.com/current/lookup-beatmap} * @returns A beatmap */ lookupBeatmap(options) { return __async(this, null, function* () { return yield this.request("beatmaps/lookup", "GET", __spreadProps(__spreadValues({}, options), { returnNullOn404: true })); }); } getBeatmapUserScoreBase(version) { return (beatmap, user, options) => __async(this, null, function* () { return yield this.request(`beatmaps/${beatmap}/scores/users/${user}`, "GET", __spreadProps(__spreadValues({}, options), { apiVersion: version === "v2" ? "20220705" : void 0 })); }); } getBeatmapUserScoresBase(version) { return (beatmap, user, options) => __async(this, null, function* () { const scores = yield this.request(`beatmaps/${beatmap}/scores/users/${user}/all`, "GET", __spreadProps(__spreadValues({}, options), { apiVersion: version === "v2" ? "20220705" : void 0 })); return scores.scores; }); } getBeatmapTopScoresBase(version) { return (beatmap, options) => __async(this, null, function* () { const scores = yield this.request(`beatmaps/${beatmap}/scores`, "GET", __spreadProps(__spreadValues({}, options), { apiVersion: version === "v2" ? "20220705" : void 0 })); return scores.scores; }); } getBeatmapTopNonLegacyScoresBase(version) { return (beatmap, options) => __async(this, null, function* () { const scores = yield this.request(`beatmaps/${beatmap}/solo-scores`, "GET", __spreadProps(__spreadValues({}, options), { apiVersion: version === "v2" ? "20220705" : void 0 })); return scores.scores; }); } /** * Makes a GET request to the `/beatmaps` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-beatmaps} * @returns An array of beatmaps */ getBeatmaps(options) { return __async(this, null, function* () { const beatmaps = yield this.request("beatmaps", "GET", options); return beatmaps.beatmaps; }); } /** * Makes a GET request to the `/beatmaps/{beatmap}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-beatmap} * @param beatmap ID of the beatmap to get * @returns A beatmap */ getBeatmap(beatmap) { return __async(this, null, function* () { return yield this.request(`beatmaps/${beatmap}`, "GET"); }); } /** * Makes a POST request to the `/beatmaps/{beatmap}/attributes` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-beatmap-attributes} * @param beatmap ID of the beatmap to get its attributes * @param gamemode Gamemode attributes to get * @returns A beatmap's attributes */ getBeatmapAttributes(beatmap, gamemode, options) { return __async(this, null, function* () { var _a; const remapped = { body: { mods: (_a = options == null ? void 0 : options.body) == null ? void 0 : _a.mods, ruleset: gamemode } }; const beatmap_ = yield this.request(`beatmaps/${beatmap}/attributes`, "POST", remapped); return beatmap_.attributes; }); } }; // src/classes/Changelog.ts var Changelog = class extends Base2 { /** * @param accessToken OAuth access token * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(accessToken, options) { super(accessToken, options); } /** * Makes a GET request to the `/changelog/{stream}/{build}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-changelog-build} * @param stream Update stream name * @param build Build version * @returns A changelog build */ getChangelogBuild(stream, build) { return __async(this, null, function* () { return yield this.request(`changelog/${stream}/${build}`, "GET"); }); } /** * Makes a GET request to the `/changelog` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-changelog-listing} * @returns An object containing a array of builds, update stream and search parameters used */ getChangelogListing(options) { return __async(this, null, function* () { return yield this.request("changelog", "GET", options); }); } /** * Makes a GET request to the `/changelog/{changelog}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/lookup-changelog-build} * @param changelog Build version, update stream name, or build ID * @returns A changelog build */ lookupChangelogBuild(changelog, options) { return __async(this, null, function* () { return yield this.request(`changelog/${changelog}`, "GET", __spreadProps(__spreadValues({}, options), { returnNullOn404: true })); }); } }; // src/classes/Chat.ts var Chat = class extends Base2 { /** * @param accessToken OAuth access token * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(accessToken, options) { super(accessToken, options); } /** * Makes a POST request to the `/chat/new` endpoint * * Documentation: {@link https://osujs.mario564.com/current/create-pm} * @returns An object containing the message sent and the channel it was sent to */ createPM(options) { return __async(this, null, function* () { return yield this.request("chat/new", "POST", options); }); } /** * Makes a POST request to the `/chat/channels` endpoint * * Documentation: {@link https://osujs.mario564.com/current/create-channel} * @param type Channel type to create or rejoin * @returns The created or rejoined channel */ createChannel(type, options) { return __async(this, null, function* () { const remapped = { body: __spreadProps(__spreadValues({}, options.body), { type }) }; return yield this.request("chat/channels", "POST", remapped); }); } }; // src/classes/Forum.ts var Forum = class extends Base2 { /** * @param accessToken OAuth access token * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(accessToken, options) { super(accessToken, options); } /** * Makes a POST request to the `/forums/topics/{topic}/reply` endpoint * * Documentation: {@link https://osujs.mario564.com/current/reply-to-topic} * @param topic ID of the topic to reply to * @returns A forum post */ replyToTopic(topic, options) { return __async(this, null, function* () { return yield this.request(`forums/topics/${topic}/reply`, "POST", options); }); } /** * Makes a POST request to the `/forums/topics` endpoint * * Documentation: {@link https://osujs.mario564.com/current/create-topic} * @returns A forum topic and the post attached to it */ createTopic(options) { return __async(this, null, function* () { const poll = options.body.forum_topic_poll; let parsedPoll; if (poll) { parsedPoll = {}; for (const key in poll) { parsedPoll[`forum_topic_poll[${key}]`] = poll[key]; } } const parsed = { body: __spreadValues(__spreadValues({}, options.body), parsedPoll) }; delete parsed.body.forum_topic_poll; return yield this.request("forums/topics", "POST", parsed); }); } /** * Makes a GET request to the `/forums/topics/{topic}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-topic} * @param topic ID of the topic to get its data and posts from * @returns An object containing the cursor string, posts and the topic itself */ getTopic(topic, options) { return __async(this, null, function* () { return yield this.request(`forums/topics/${topic}`, "GET", options); }); } /** * Makes a PATCH request to the `/forums/topics/{topic}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/update-topic} * @param topic ID of the topic to update * @returns A forum topic */ updateTopic(topic, options) { return __async(this, null, function* () { var _a, _b; const forumTopic = (_a = options == null ? void 0 : options.body) == null ? void 0 : _a.forum_topic; let parsedForumTopic; if (forumTopic) { parsedForumTopic = {}; for (const key in forumTopic) { parsedForumTopic[`forum_topic[${key}]`] = forumTopic[key]; } } const parsed = { body: (options == null ? void 0 : options.body) ? __spreadValues(__spreadValues({}, options.body), parsedForumTopic) : void 0 }; (_b = parsed.body) == null ? true : delete _b.forum_topic; return yield this.request(`forums/topics/${topic}`, "PATCH", parsed); }); } /** * Makes a PATCH request to the `/forums/posts/{post}` endpoint * * Documentation: {@link https://osujs.mario564.com/current/update-post} * @param post ID of the post to update * @returns A forum post */ updatePost(post, options) { return __async(this, null, function* () { return yield this.request(`forums/posts/${post}`, "PATCH", options); }); } }; // src/classes/BeatmapsetDiscussions.ts var BeatmapsetDiscussions = class extends Base2 { /** * @param accessToken OAuth access token * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(accessToken, options) { super(accessToken, options); } /** * Makes a GET request to the `/beatmapsets/discussions/posts` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-discussion-posts} * @returns An object containing a cursor and arrays of beatmapsets, users, discussions and posts */ getDiscussionPosts(options) { return __async(this, null, function* () { return yield this.request("beatmapsets/discussions/posts", "GET", options); }); } /** * Makes a GET request to the `/beatmapsets/discussions/votes` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-discussion-votes} * @returns An object containing a cursor and arrays of discussions, users and votes */ getDiscussionVotes(options) { return __async(this, null, function* () { return yield this.request("beatmapsets/discussions/votes", "GET", options); }); } /** * Makes a GET request to the `/beatmapsets/discussions` endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-discussions} * @returns An object containing a cursor and arrays of beatmaps, discussions and users */ getDiscussions(options) { return __async(this, null, function* () { return yield this.request("beatmapsets/discussions", "GET", options); }); } }; // src/classes/Client.ts var Client = class extends Base2 { /** * @param accessToken OAuth access token * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(accessToken, options) { super(accessToken, options); this.beatmaps = new Beatmaps(accessToken, options); this.beatmapsetDiscussions = new BeatmapsetDiscussions(accessToken, options); this.changelog = new Changelog(accessToken, options); this.chat = new Chat(accessToken, options); this.comments = new Comments(accessToken, options); this.forum = new Forum(accessToken, options); this.multiplayer = new Multiplayer(accessToken, options); this.news = new News(accessToken, options); this.ranking = new Ranking(accessToken, options); this.users = new Users(accessToken, options); this.wiki = new Wiki(accessToken, options); } /** * Set a new access token to be used by the current client. * * Documentation: {@link https://osujs.mario564.com/current/set-access-token} */ setAccessToken(accessToken) { this.accessToken = accessToken; this.beatmaps.setAccessToken(accessToken); this.beatmapsetDiscussions.setAccessToken(accessToken); this.changelog.setAccessToken(accessToken); this.chat.setAccessToken(accessToken); this.comments.setAccessToken(accessToken); this.forum.setAccessToken(accessToken); this.multiplayer.setAccessToken(accessToken); this.news.setAccessToken(accessToken); this.ranking.setAccessToken(accessToken); this.users.setAccessToken(accessToken); this.wiki.setAccessToken(accessToken); } /** * Makes a GET request to the `/search` endpoint * * Documentation: {@link https://osujs.mario564.com/current/search} * @returns Users and wiki pages as results */ search(options) { return __async(this, null, function* () { return yield this.request("search", "GET", options); }); } /** * Makes a DELETE request to the `/oauth/tokens/current` endpoint. Revokes the access token * * Documentation: {@link https://osujs.mario564.com/current/revoke-token} */ revokeToken() { return __async(this, null, function* () { yield this.request("oauth/tokens/current", "DELETE", { dontParseResp: true }); }); } /** * Make a GET request to an undocumented endpoint * * Documentation: {@link https://osujs.mario564.com/current/get-undocumented} * @param endpoint The endpoint to make a request to */ getUndocumented(endpoint, options) { return __async(this, null, function* () { return yield this.request(endpoint, "GET", options); }); } }; // src/classes/LegacyClient.ts var LegacyClient = class { /** * @param apiKey API key * @param options.polyfillFetch In case developing with a Node.js version prior to 18, you need to pass a polyfill for the fetch API. Install `node-fetch` */ constructor(apiKey, options) { if (typeof fetch === "undefined" && !(options == null ? void 0 : options.polyfillFetch)) { throw new OsuJSGeneralError("undefined_fetch"); } this.apiKey = apiKey; this.fetch = (options == null ? void 0 : options.polyfillFetch) || fetch; } request(endpoint, urlParams) { return __async(this, null, function* () { const params = formatUrlParams(urlParams); const url = `https://osu.ppy.sh/api/${endpoint}?k=${this.apiKey}${params}`; let resp = new Response(); try { resp = yield this.fetch(url, { headers: { "Content-Type": "application/json" } }); } catch (err) { if (err instanceof TypeError) { throw new OsuJSGeneralError("network_error"); } } if (!resp.ok) { throw new OsuJSUnexpectedResponseError(resp); } let data; try { data = yield resp.json(); } catch (err) { if (err instanceof SyntaxError) { throw new OsuJSGeneralError("invalid_json_syntax"); } } return data; }); } /** * Makes a GET request to the `get_beatmaps` endpoint * * Documentation: {@link https://osujs.mario564.com/legacy/get-beatmaps} * @returns An array of beatmaps */ getBeatmaps(params) { return __async(this, null, function* () { var _a; const mods = params.mods ? params.mods : []; const diffIncreaseMods = mods.filter((mod) => { return ["HD", "HR", "DT", "FL", "FI"].includes(mod); }); const validParams = __spreadProps(__spreadValues({}, params), { since: (_a = params.since) == null ? void 0 : _a.toISOString().slice(0, 19).replace("T", " "), m: params.m && ModesEnum[params.m], a: Number(params.a), mods: getModsEnum(diffIncreaseMods) }); const beatmaps = yield this.request("get_beatmaps", validParams); return beatmaps.map((beatmap) => { return map(__spreadProps(__spreadValues({}, beatmap), { approved: StatusEnum[Number(beatmap.approved)], genre: GenresEnum[Number(beatmap.genre_id)], language: LanguagesEnum[Number(beatmap.language_id)], mode: ModesEnum[Number(beatmap.mode)], storyboard: beatmap.storyboard === "1", video: beatmap.video === "1", download_available: beatmap.download_unavailable === "0", audio_available: beatmap.audio_unavailable === "0", tags: beatmap.tags.split(" ") })); });