Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions DiscordBot/commands/tournaments/matchup/stopAutoLobby.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Axios from "axios";
import { ChatInputCommandInteraction, Message, SlashCommandBuilder } from "discord.js";
import { config } from "node-config-ts";
import { Command } from "../..";
Expand All @@ -7,6 +6,8 @@ import { Matchup } from "../../../../Models/tournaments/matchup";
import { extractParameter } from "../../../functions/parameterFunctions";
import respond from "../../../functions/respond";
import { securityChecks } from "../../../functions/tournamentFunctions/securityChecks";
import { post } from "../../../../Server/utils/fetch";
import { basicAuth } from "../../../../Server/utils/auth";

async function run (m: Message | ChatInputCommandInteraction) {
if (m instanceof ChatInputCommandInteraction)
Expand All @@ -32,15 +33,16 @@ async function run (m: Message | ChatInputCommandInteraction) {
}

const baseUrl = matchup.baseURL;

const { data } = await Axios.post(`${baseUrl}/api/bancho/stopAutoLobby`, {
const data = await post(`${baseUrl}/api/bancho/stopAutoLobby`, {
matchupID: ID,
}, {
auth: config.interOpAuth,
headers: {
Authentication: basicAuth(config.interOpAuth),
},
});

if (!data.success) {
await respond(m, data.error);
await respond(m, typeof data.error === "string" ? data.error : data.error.message);
return;
}

Expand Down
12 changes: 6 additions & 6 deletions DiscordBot/functions/beatmapParse.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@

import axios from "axios";
import { Entry, Parse } from "unzipper";
import { ChatInputCommandInteraction, ForumChannel, Message } from "discord.js";
import { CustomBeatmap } from "../../Models/tournaments/mappools/customBeatmap";
Expand All @@ -24,15 +22,17 @@ export async function beatmapParse (m: Message | ChatInputCommandInteraction, di
let beatmapAttributes: ParserBeatmapAttributes | undefined = undefined;
let beatmapStrains: ParserStrains | undefined = undefined;
let background: string | undefined = undefined;
let axiosData: any = null;
let fetchData: any = null;
try {
const { data } = await axios.get(link, { responseType: "stream" });
axiosData = data;
const res = await fetch(link);
if (!res.ok)
throw new Error(`Status code: ${res.status}`);
fetchData = res.body!;
} catch (e) {
await respond(m, "Can't download the map. Make sure the link is valid");
return;
}
const zip = axiosData.pipe(Parse({ forceStream: true }));
const zip = fetchData.pipe(Parse({ forceStream: true }));
let foundBeatmap = false;
for await (const _entry of zip) {
const entry = _entry as Entry;
Expand Down
13 changes: 13 additions & 0 deletions Interfaces/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export class HTTPError extends Error {
public statusCode: number;
public message: string;
public cause?: unknown;

constructor (statusCode: number, message: string, options?: ErrorOptions) {
super(message);

this.statusCode = statusCode;
this.message = message;
this.cause = options?.cause;
}
}
4 changes: 2 additions & 2 deletions Models/user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

import { Entity, Column, BaseEntity, PrimaryGeneratedColumn, CreateDateColumn, OneToMany, JoinTable, Brackets, Index, ManyToMany } from "typeorm";
import Axios from "axios";
import { bwsFilter, osuV2Me, osuV2User, osuV2UserBadge, osuV2UserStatistics } from "../Interfaces/osuAPIV2";
import { DemeritReport } from "./demerits";
import { MCAEligibility } from "./MCA_AYIM/mcaEligibility";
Expand Down Expand Up @@ -38,6 +37,7 @@ import { MatchupMessage } from "./tournaments/matchupMessage";
import { UserStatistics } from "./userStatistics";
import { ModeDivisionType, modeIDToMode } from "../Interfaces/modes";
import { MappoolReplay } from "./tournaments/mappools/mappoolReplay";
import { HTTPError } from "../Interfaces/error";

export class OAuth {

Expand Down Expand Up @@ -442,7 +442,7 @@ export class User extends BaseEntity {
return osuV2Client.getMe(accessToken, modeIDToMode()[modeID]);
} catch (e) {
// Invalid access token or it's not found
if (Axios.isAxiosError(e) && e.code === "401" || e instanceof Error)
if (e instanceof HTTPError && e.statusCode === 401 || e instanceof Error)
return osuV2Client.getUser(this.osu.userID, modeIDToMode()[modeID]);
throw e;
}
Expand Down
15 changes: 9 additions & 6 deletions Server/api/routes/github.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CorsaceRouter } from "../../corsaceRouter";
import { createHmac, timingSafeEqual } from "crypto";
import { config } from "node-config-ts";
import axios from "axios";
import { post } from "../../utils/fetch";

// List of allowed events as per https://discord.com/developers/docs/resources/webhook#execute-githubcompatible-webhook
const ALLOWED_EVENTS = ["commit_comment", "create", "delete", "fork", "issue_comment", "issues", "member", "public", "pull_request", "pull_request_review", "pull_request_review_comment", "push", "release", "watch", "check_run", "check_suite", "discussion", "discussion_comment"];
Expand Down Expand Up @@ -46,19 +46,22 @@ githubRouter.$post<any>("/", async (ctx) => {
}

try {
const res = await axios.post(`${config.github.webhookUrl}/github`, ctx.request.body, {
const res = await post<any>(`${config.github.webhookUrl}/github`, ctx.request.body, {
headers: {
"content-type": "application/json",
"Content-Type": "application/json",
"User-Agent": ctx.get("User-Agent"),
"X-GitHub-Delivery": ctx.get("X-GitHub-Delivery"),
"X-GitHub-Event": ctx.get("X-GitHub-Event"),
"X-GitHub-Hook-ID": ctx.get("X-GitHub-Hook-ID"),
"X-GitHub-Hook-Installation-Target-ID": ctx.get("X-GitHub-Hook-Installation-Target-ID"),
"X-GitHub-Hook-Installation-Target-Type": ctx.get("X-GitHub-Hook-Installation-Target-Type"),
},
});
ctx.status = res.status;
ctx.body = res.data;
}, true);
if (!res.success)
throw typeof res.error === "string" ? new Error(res.error) : res.error;

ctx.status = 200;
ctx.body = res;
} catch (e) {
ctx.status = 500;
ctx.body = {
Expand Down
91 changes: 61 additions & 30 deletions Server/api/routes/guestRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,47 @@ import { User } from "../../../Models/user";
import { isLoggedIn } from "../../../Server/middleware";
import { isEligibleFor, currentMCA } from "../../../Server/middleware/mca-ayim";
import { config } from "node-config-ts";
import axios from "axios";
import { GuestRequest } from "../../../Models/MCA_AYIM/guestRequest";
import { ModeDivision } from "../../../Models/MCA_AYIM/modeDivision";
import { RequestStatus } from "../../../Interfaces/guestRequests";
import { MCAAuthenticatedState } from "koa";
import { ModeDivisionType } from "../../../Interfaces/modes";
import { get } from "../../utils/fetch";

interface BodyData {
mode: string;
url: string;
}

async function validateBody (user: User, year: number, data: BodyData, currentRequestId?: number): Promise<{ error: string } | { beatmap: Beatmap; mode: ModeDivision; }> {
async function validateBody (
user: User,
year: number,
data: BodyData,
currentRequestId?: number
): Promise<{ error: string } | { beatmap: Beatmap; mode: ModeDivision }> {
// Validate mode
if (!(data.mode in ModeDivisionType)) {
return {
return {
error: "Invalid mode, please use standard, taiko, fruits or mania",
};
}
const modeId = ModeDivisionType[data.mode as keyof typeof ModeDivisionType];
const mode = await ModeDivision.findOneOrFail({ where: { ID: modeId }});

if (isEligibleFor(user, mode.ID, year)) {
return {
error: `User is already eligible for ${mode.name} (${year})`,
};
}

// Check if there's already a guest difficulty request sent
if (user.guestRequests.some(r => r.mca.year === year && r.mode.ID === mode.ID && (!currentRequestId || r.ID !== currentRequestId))) {
if (
user.guestRequests.some(
(r) =>
r.mca.year === year &&
r.mode.ID === mode.ID &&
(!currentRequestId || r.ID !== currentRequestId)
)
) {
return {
error: "A guest request for this year + mode already exists!",
};
Expand All @@ -42,14 +53,16 @@ async function validateBody (user: User, year: number, data: BodyData, currentRe
// Check URL
const linkRegex = /(osu|old)\.ppy\.sh\/(b|beatmaps|beatmapsets)\/(\d+)(#(osu|taiko|fruits|mania)\/(\d+))?/i;
if (!linkRegex.test(data.url)) {
return { error: "Invalid URL!"};
return { error: "Invalid URL!" };
}
const res = linkRegex.exec(data.url);
let beatmapID = "0";

if (res) {
if (res[2] === "beatmapsets" && !res[6]) {
return { error: "/beatmapsets/ URL does not have a specific difficulty linked!"};
return {
error: "/beatmapsets/ URL does not have a specific difficulty linked!",
};
} else if (res[2] === "beatmapsets") {
beatmapID = res[6];
} else {
Expand All @@ -58,35 +71,51 @@ async function validateBody (user: User, year: number, data: BodyData, currentRe
}

// Get beatmap information
const { data: beatmaps } = await axios.get<any[]>(`${config.osu.proxyBaseUrl ?? "https://osu.ppy.sh"}/api/get_beatmaps?k=${config.osu.v1.apiKey}&b=${beatmapID}`);
if (beatmaps.length !== 1) {
return { error: "Error in obtaining beatmap info!"};
let beatmap: any;
try {
const response = await get<any[]>(`${config.osu.proxyBaseUrl ?? "https://osu.ppy.sh"}/api/get_beatmaps?k=${config.osu.v1.apiKey}&b=${beatmapID}`);
if (!response.success)
return { error: typeof response.error === "string" ? response.error : response.error.message };

const beatmaps = response;
if (beatmaps.length !== 1)
return { error: "Error in obtaining beatmap info!" };

beatmap = beatmaps[0];
} catch (error) {
return { error: "Error in obtaining beatmap info!" };
}
const beatmap = beatmaps[0];

// Check the year
if (new Date(beatmap.approved_date).getUTCFullYear() !== year) {
return { error: "This map was not ranked in " + year + "!"};
return { error: "This map was not ranked in " + year + "!" };
}

// Find beatmap in DB
const dbMap = await Beatmap.findOne({where: { ID: beatmap.beatmap_id }});
if (!dbMap) {
return { error: "Map is not in our database! If this map was ranked this year, please let VINXIS know." };
}
const dbMap = await Beatmap.findOne({ where: { ID: beatmap.beatmap_id }});
if (!dbMap)
return {
error:
"Map is not in our database! If this map was ranked this year, please let VINXIS know.",
};

// Check mode consistency
if (dbMap.mode.ID !== mode.ID && mode.name !== "storyboard") {
return { error: "Map is not the correct mode! This beatmap's mode is " + dbMap.mode.name + " while the mode you are applying to is for " + mode.name};
}
if (dbMap.mode.ID !== mode.ID && mode.name !== "storyboard")
return {
error:
"Map is not the correct mode! This beatmap's mode is " +
dbMap.mode.name +
" while the mode you are applying to is for " +
mode.name,
};

return {
return {
beatmap: dbMap,
mode,
};
}

const guestRequestRouter = new CorsaceRouter<MCAAuthenticatedState>();
const guestRequestRouter = new CorsaceRouter<MCAAuthenticatedState>();

guestRequestRouter.$use(isLoggedIn);
guestRequestRouter.$use(currentMCA);
Expand All @@ -97,13 +126,13 @@ guestRequestRouter.$post<{ request: GuestRequest }>("/create", async (ctx) => {
const res = await validateBody(user, mca.year, ctx.request.body);

if ("error" in res) {
return ctx.body = {
return (ctx.body = {
success: false,
error: res.error,
};
});
}

// Create guest requesst
// Create guest request
const request = new GuestRequest();
request.user = user;
request.mca = mca;
Expand All @@ -122,22 +151,24 @@ guestRequestRouter.$post<{ request: GuestRequest }>("/:id/update", async (ctx) =
const mca = ctx.state.mca;
const id: number = parseInt(ctx.params.id);
const user = ctx.state.user;
const request = user.guestRequests.find(r => r.ID === id && r.mca.year === mca.year);
const request = user.guestRequests.find(
(r) => r.ID === id && r.mca.year === mca.year
);

if (!request || request.status === RequestStatus.Accepted) {
return ctx.body = {
return (ctx.body = {
success: false,
error: "Not valid request",
};
});
}

const res = await validateBody(user, mca.year, ctx.request.body, id);

if ("error" in res) {
return ctx.body = {
return (ctx.body = {
success: false,
error: res.error,
};
});
}

request.mode = res.mode;
Expand Down
4 changes: 2 additions & 2 deletions Server/api/routes/mappers.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { CorsaceRouter } from "../../corsaceRouter";
import axios from "axios";
import { User } from "../../../Models/user";
import { MapperQuery } from "../../../Interfaces/queries";
import { parseQueryParam } from "../../../Server/utils/query";
import { osuV2Client } from "../../osu";
import { HTTPError } from "../../../Interfaces/error";

const mappersRouter = new CorsaceRouter();

Expand Down Expand Up @@ -43,7 +43,7 @@ mappersRouter.$get<{ users: User[] }>("/search", async (ctx) => {
const data = await osuV2Client.getUserFriends(accessToken);
query.friends = data.map(friend => friend.id);
} catch (e) {
if (axios.isAxiosError(e) && (e.response?.status === 401 || e.response?.status === 403))
if (e instanceof HTTPError && (e.statusCode === 401 || e.statusCode === 403))
return ctx.body = {
success: false,
error: "Please re-login via osu! again in order to use the friends filter! If you logged in again via osu! and it still isn't working, contact VINXIS!",
Expand Down
Loading