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
JavaScript
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(" ")
}));
});