summary refs log tree commit diff
path: root/src/api
diff options
context:
space:
mode:
authorMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2023-01-05 17:12:21 +1100
committerMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2023-01-05 17:16:55 +1100
commita3f2f997a3ddfafdff19bd8911f7b610326c02d8 (patch)
tree7086d2a6971ed06c6d09cda6c277fb544c3c608d /src/api
parentchannel flags whoops (diff)
downloadserver-a3f2f997a3ddfafdff19bd8911f7b610326c02d8.tar.xz
Prettier
Diffstat (limited to 'src/api')
-rw-r--r--src/api/Server.ts17
-rw-r--r--src/api/middlewares/TestClient.ts120
-rw-r--r--src/api/routes/applications/#id/bot/index.ts65
-rw-r--r--src/api/routes/applications/#id/index.ts70
-rw-r--r--src/api/routes/applications/#id/skus.ts2
-rw-r--r--src/api/routes/applications/index.ts46
-rw-r--r--src/api/routes/auth/generate-registration-tokens.ts45
-rw-r--r--src/api/routes/auth/register.ts44
-rw-r--r--src/api/routes/channels/#channel_id/messages/#message_id/index.ts2
-rw-r--r--src/api/routes/channels/#channel_id/messages/index.ts53
-rw-r--r--src/api/routes/channels/#channel_id/permissions.ts2
-rw-r--r--src/api/routes/channels/#channel_id/webhooks.ts14
-rw-r--r--src/api/routes/download/index.ts15
-rw-r--r--src/api/routes/guild-recommendations.ts2
-rw-r--r--src/api/routes/guilds/#guild_id/index.ts16
-rw-r--r--src/api/routes/guilds/#guild_id/members/#member_id/index.ts86
-rw-r--r--src/api/routes/guilds/#guild_id/messages/search.ts14
-rw-r--r--src/api/routes/guilds/#guild_id/profile/index.ts64
-rw-r--r--src/api/routes/guilds/#guild_id/roles/#role_id/index.ts8
-rw-r--r--src/api/routes/guilds/#guild_id/roles/index.ts10
-rw-r--r--src/api/routes/oauth2/authorize.ts142
-rw-r--r--src/api/routes/policies/stats.ts11
-rw-r--r--src/api/routes/stop.ts16
-rw-r--r--src/api/routes/store/published-listings/skus/#sku_id/subscription-plans.ts136
-rw-r--r--src/api/routes/updates.ts15
-rw-r--r--src/api/routes/users/#id/profile.ts163
-rw-r--r--src/api/routes/users/@me/guilds/#guild_id/settings.ts3
-rw-r--r--src/api/routes/users/@me/index.ts2
-rw-r--r--src/api/routes/users/@me/settings.ts2
-rw-r--r--src/api/util/handlers/Message.ts21
-rw-r--r--src/api/util/index.ts2
-rw-r--r--src/api/util/utility/EmbedHandlers.ts164
32 files changed, 847 insertions, 525 deletions
diff --git a/src/api/Server.ts b/src/api/Server.ts
index b8d1f9f6..fff94936 100644
--- a/src/api/Server.ts
+++ b/src/api/Server.ts
@@ -14,7 +14,7 @@ import { initInstance } from "./util/handlers/Instance";
 import { registerRoutes } from "@fosscord/util";
 import { red } from "picocolors";
 
-export interface FosscordServerOptions extends ServerOptions { }
+export interface FosscordServerOptions extends ServerOptions {}
 
 declare global {
 	namespace Express {
@@ -76,15 +76,12 @@ export class FosscordServer extends Server {
 		// 404 is not an error in express, so this should not be an error middleware
 		// this is a fine place to put the 404 handler because its after we register the routes
 		// and since its not an error middleware, our error handler below still works.
-		api.use(
-			"*",
-			(req: Request, res: Response, next: NextFunction) => {
-				res.status(404).json({
-					message: "404 endpoint not found",
-					code: 0,
-				});
-			},
-		);
+		api.use("*", (req: Request, res: Response, next: NextFunction) => {
+			res.status(404).json({
+				message: "404 endpoint not found",
+				code: 0,
+			});
+		});
 
 		this.app = app;
 
diff --git a/src/api/middlewares/TestClient.ts b/src/api/middlewares/TestClient.ts
index e55a7c7b..35cf1798 100644
--- a/src/api/middlewares/TestClient.ts
+++ b/src/api/middlewares/TestClient.ts
@@ -22,18 +22,30 @@ export default function TestClient(app: Application) {
 
 	const agent = new ProxyAgent();
 
-	let html = fs.readFileSync(path.join(ASSET_FOLDER_PATH, "client_test", "index.html"), { encoding: "utf-8" });
-
-	html = applyEnv(html);	// update window.GLOBAL_ENV according to config
-
-	html = applyPlugins(html);	// inject our plugins
-	app.use("/assets/plugins", express.static(path.join(ASSET_FOLDER_PATH, "plugins")));
-	app.use("/assets/inline-plugins", express.static(path.join(ASSET_FOLDER_PATH, "inline-plugins")));
+	let html = fs.readFileSync(
+		path.join(ASSET_FOLDER_PATH, "client_test", "index.html"),
+		{ encoding: "utf-8" },
+	);
+
+	html = applyEnv(html); // update window.GLOBAL_ENV according to config
+
+	html = applyPlugins(html); // inject our plugins
+	app.use(
+		"/assets/plugins",
+		express.static(path.join(ASSET_FOLDER_PATH, "plugins")),
+	);
+	app.use(
+		"/assets/inline-plugins",
+		express.static(path.join(ASSET_FOLDER_PATH, "inline-plugins")),
+	);
 
 	// Asset memory cache
-	const assetCache = new Map<string, { response: FetchResponse; buffer: Buffer; }>();
+	const assetCache = new Map<
+		string,
+		{ response: FetchResponse; buffer: Buffer }
+	>();
 
-	// Fetches uncached ( on disk ) assets from discord.com and stores them in memory cache. 
+	// Fetches uncached ( on disk ) assets from discord.com and stores them in memory cache.
 	app.get("/assets/:file", async (req, res) => {
 		delete req.headers.host;
 
@@ -43,13 +55,15 @@ export default function TestClient(app: Application) {
 		let buffer: Buffer;
 		const cache = assetCache.get(req.params.file);
 		if (!cache) {
-			response = await fetch(`https://discord.com/assets/${req.params.file}`, {
-				agent,
-				headers: { ...req.headers as { [key: string]: string; } },
-			});
+			response = await fetch(
+				`https://discord.com/assets/${req.params.file}`,
+				{
+					agent,
+					headers: { ...(req.headers as { [key: string]: string }) },
+				},
+			);
 			buffer = await response.buffer();
-		}
-		else {
+		} else {
 			response = cache.response;
 			buffer = cache.buffer;
 		}
@@ -62,8 +76,8 @@ export default function TestClient(app: Application) {
 			"transfer-encoding",
 			"expect-ct",
 			"access-control-allow-origin",
-			"content-encoding"
-		].forEach(headerName => {
+			"content-encoding",
+		].forEach((headerName) => {
 			response.headers.delete(headerName);
 		});
 		response.headers.forEach((value, name) => res.set(name, value));
@@ -72,8 +86,13 @@ export default function TestClient(app: Application) {
 
 		// TODO: I don't like this. Figure out a way to get client cacher to download *all* assets.
 		if (response.status == 200) {
-			console.warn(`[TestClient] Cache miss for file ${req.params.file}! Use 'npm run generate:client' to cache and patch.`);
-			await fs.promises.appendFile(path.join(ASSET_FOLDER_PATH, "cacheMisses"), req.params.file + "\n");
+			console.warn(
+				`[TestClient] Cache miss for file ${req.params.file}! Use 'npm run generate:client' to cache and patch.`,
+			);
+			await fs.promises.appendFile(
+				path.join(ASSET_FOLDER_PATH, "cacheMisses"),
+				req.params.file + "\n",
+			);
 		}
 
 		return res.send(buffer);
@@ -81,17 +100,23 @@ export default function TestClient(app: Application) {
 
 	// Instead of our generated html, send developers.html for developers endpoint
 	app.get("/developers*", (req, res) => {
-		res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);	// 24 hours
+		res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24); // 24 hours
 		res.set("content-type", "text/html");
-		res.send(fs.readFileSync(path.join(ASSET_FOLDER_PATH, "client_test", "developers.html"), { encoding: "utf-8" }));
+		res.send(
+			fs.readFileSync(
+				path.join(ASSET_FOLDER_PATH, "client_test", "developers.html"),
+				{ encoding: "utf-8" },
+			),
+		);
 	});
 
 	// Send our generated index.html for all routes.
 	app.get("*", (req, res) => {
-		res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);	// 24 hours
+		res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24); // 24 hours
 		res.set("content-type", "text/html");
 
-		if (req.url.startsWith("/api") || req.url.startsWith("/__development")) return;
+		if (req.url.startsWith("/api") || req.url.startsWith("/__development"))
+			return;
 
 		return res.send(html);
 	});
@@ -101,16 +126,26 @@ export default function TestClient(app: Application) {
 const applyEnv = (html: string): string => {
 	const config = Config.get();
 
-	const cdn = (config.cdn.endpointClient || config.cdn.endpointPublic || process.env.CDN || "")
-		.replace(/(https?)?(:\/\/?)/g, "");
+	const cdn = (
+		config.cdn.endpointClient ||
+		config.cdn.endpointPublic ||
+		process.env.CDN ||
+		""
+	).replace(/(https?)?(:\/\/?)/g, "");
 
-	const gateway = (config.gateway.endpointClient || config.gateway.endpointPublic || process.env.GATEWAY || "");
+	const gateway =
+		config.gateway.endpointClient ||
+		config.gateway.endpointPublic ||
+		process.env.GATEWAY ||
+		"";
 
-	if (cdn)
-		html = html.replace(/CDN_HOST: .+/, `CDN_HOST: \`${cdn}\`,`);
+	if (cdn) html = html.replace(/CDN_HOST: .+/, `CDN_HOST: \`${cdn}\`,`);
 
 	if (gateway)
-		html = html.replace(/GATEWAY_ENDPOINT: .+/, `GATEWAY_ENDPOINT: \`${gateway}\`,`);
+		html = html.replace(
+			/GATEWAY_ENDPOINT: .+/,
+			`GATEWAY_ENDPOINT: \`${gateway}\`,`,
+		);
 
 	return html;
 };
@@ -118,28 +153,37 @@ const applyEnv = (html: string): string => {
 // Injects inline, preload, and standard plugins into index.html.
 const applyPlugins = (html: string): string => {
 	// Inline plugins. Injected as <script src="/assets/inline-plugins/name.js"> into head.
-	const inlineFiles = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "inline-plugins"));
+	const inlineFiles = fs.readdirSync(
+		path.join(ASSET_FOLDER_PATH, "inline-plugins"),
+	);
 	const inline = inlineFiles
-		.filter(x => x.endsWith(".js"))
-		.map(x => `<script src="/assets/inline-plugins/${x}"></script>`)
+		.filter((x) => x.endsWith(".js"))
+		.map((x) => `<script src="/assets/inline-plugins/${x}"></script>`)
 		.join("\n");
 	html = html.replace("<!-- inline plugin marker -->", inline);
 
 	// Preload plugins. Text content of each plugin is injected into head.
-	const preloadFiles = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "preload-plugins"));
+	const preloadFiles = fs.readdirSync(
+		path.join(ASSET_FOLDER_PATH, "preload-plugins"),
+	);
 	const preload = preloadFiles
-		.filter(x => x.endsWith(".js"))
-		.map(x => `<script>${fs.readFileSync(path.join(ASSET_FOLDER_PATH, "preload-plugins", x))}</script>`)
+		.filter((x) => x.endsWith(".js"))
+		.map(
+			(x) =>
+				`<script>${fs.readFileSync(
+					path.join(ASSET_FOLDER_PATH, "preload-plugins", x),
+				)}</script>`,
+		)
 		.join("\n");
 	html = html.replace("<!-- preload plugin marker -->", preload);
 
 	// Normal plugins. Injected as <script src="/assets/plugins/name.js"> into body.
 	const pluginFiles = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "plugins"));
 	const plugins = pluginFiles
-		.filter(x => x.endsWith(".js"))
-		.map(x => `<script src="/assets/plugins/${x}"></script>`)
+		.filter((x) => x.endsWith(".js"))
+		.map((x) => `<script src="/assets/plugins/${x}"></script>`)
 		.join("\n");
 	html = html.replace("<!-- plugin marker -->", plugins);
 
 	return html;
-};
\ No newline at end of file
+};
diff --git a/src/api/routes/applications/#id/bot/index.ts b/src/api/routes/applications/#id/bot/index.ts
index c21e19ca..c4cfccd8 100644
--- a/src/api/routes/applications/#id/bot/index.ts
+++ b/src/api/routes/applications/#id/bot/index.ts
@@ -1,13 +1,23 @@
 import { Request, Response, Router } from "express";
 import { route } from "@fosscord/api";
-import { Application, generateToken, User, BotModifySchema, handleFile, DiscordApiErrors } from "@fosscord/util";
+import {
+	Application,
+	generateToken,
+	User,
+	BotModifySchema,
+	handleFile,
+	DiscordApiErrors,
+} from "@fosscord/util";
 import { HTTPError } from "lambert-server";
 import { verifyToken } from "node-2fa";
 
 const router: Router = Router();
 
 router.post("/", route({}), async (req: Request, res: Response) => {
-	const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["owner"] });
+	const app = await Application.findOneOrFail({
+		where: { id: req.params.id },
+		relations: ["owner"],
+	});
 
 	if (app.owner.id != req.user_id)
 		throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
@@ -31,7 +41,7 @@ router.post("/", route({}), async (req: Request, res: Response) => {
 	await app.save();
 
 	res.send({
-		token: await generateToken(user.id)
+		token: await generateToken(user.id),
 	}).status(204);
 });
 
@@ -42,7 +52,10 @@ router.post("/reset", route({}), async (req: Request, res: Response) => {
 	if (owner.id != req.user_id)
 		throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
 
-	if (owner.totp_secret && (!req.body.code || verifyToken(owner.totp_secret, req.body.code)))
+	if (
+		owner.totp_secret &&
+		(!req.body.code || verifyToken(owner.totp_secret, req.body.code))
+	)
 		throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
 
 	bot.data = { hash: undefined, valid_tokens_since: new Date() };
@@ -54,30 +67,36 @@ router.post("/reset", route({}), async (req: Request, res: Response) => {
 	res.json({ token }).status(200);
 });
 
-router.patch("/", route({ body: "BotModifySchema" }), async (req: Request, res: Response) => {
-	const body = req.body as BotModifySchema;
-	if (!body.avatar?.trim()) delete body.avatar;
+router.patch(
+	"/",
+	route({ body: "BotModifySchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as BotModifySchema;
+		if (!body.avatar?.trim()) delete body.avatar;
 
-	const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["bot", "owner"] });
+		const app = await Application.findOneOrFail({
+			where: { id: req.params.id },
+			relations: ["bot", "owner"],
+		});
 
-	if (!app.bot)
-		throw DiscordApiErrors.BOT_ONLY_ENDPOINT;
+		if (!app.bot) throw DiscordApiErrors.BOT_ONLY_ENDPOINT;
 
-	if (app.owner.id != req.user_id)
-		throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
+		if (app.owner.id != req.user_id)
+			throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
 
-	if (body.avatar)
-		body.avatar = await handleFile(
-			`/avatars/${app.id}`,
-			body.avatar as string,
-		);
+		if (body.avatar)
+			body.avatar = await handleFile(
+				`/avatars/${app.id}`,
+				body.avatar as string,
+			);
 
-	app.bot.assign(body);
+		app.bot.assign(body);
 
-	app.bot.save();
+		app.bot.save();
 
-	await app.save();
-	res.json(app).status(200);
-});
+		await app.save();
+		res.json(app).status(200);
+	},
+);
 
-export default router;
\ No newline at end of file
+export default router;
diff --git a/src/api/routes/applications/#id/index.ts b/src/api/routes/applications/#id/index.ts
index 79df256a..11cd5a56 100644
--- a/src/api/routes/applications/#id/index.ts
+++ b/src/api/routes/applications/#id/index.ts
@@ -1,57 +1,81 @@
 import { Request, Response, Router } from "express";
 import { route } from "@fosscord/api";
-import { Application, OrmUtils, DiscordApiErrors, ApplicationModifySchema, User } from "@fosscord/util";
+import {
+	Application,
+	OrmUtils,
+	DiscordApiErrors,
+	ApplicationModifySchema,
+	User,
+} from "@fosscord/util";
 import { verifyToken } from "node-2fa";
 import { HTTPError } from "lambert-server";
 
 const router: Router = Router();
 
 router.get("/", route({}), async (req: Request, res: Response) => {
-	const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["owner", "bot"] });
+	const app = await Application.findOneOrFail({
+		where: { id: req.params.id },
+		relations: ["owner", "bot"],
+	});
 	if (app.owner.id != req.user_id)
 		throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
 
 	return res.json(app);
 });
 
-router.patch("/", route({ body: "ApplicationModifySchema" }), async (req: Request, res: Response) => {
-	const body = req.body as ApplicationModifySchema;
+router.patch(
+	"/",
+	route({ body: "ApplicationModifySchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as ApplicationModifySchema;
 
-	const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["owner", "bot"] });
+		const app = await Application.findOneOrFail({
+			where: { id: req.params.id },
+			relations: ["owner", "bot"],
+		});
 
-	if (app.owner.id != req.user_id)
-		throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
+		if (app.owner.id != req.user_id)
+			throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
 
-	if (app.owner.totp_secret && (!req.body.code || verifyToken(app.owner.totp_secret, req.body.code)))
-		throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
+		if (
+			app.owner.totp_secret &&
+			(!req.body.code ||
+				verifyToken(app.owner.totp_secret, req.body.code))
+		)
+			throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
 
-	if (app.bot) {
-		app.bot.assign({ bio: body.description });
-		await app.bot.save();
-	}
+		if (app.bot) {
+			app.bot.assign({ bio: body.description });
+			await app.bot.save();
+		}
 
-	app.assign(body);
+		app.assign(body);
 
-	await app.save();
+		await app.save();
 
-	return res.json(app);
-});
+		return res.json(app);
+	},
+);
 
 router.post("/delete", route({}), async (req: Request, res: Response) => {
-	const app = await Application.findOneOrFail({ where: { id: req.params.id }, relations: ["bot", "owner"] });
+	const app = await Application.findOneOrFail({
+		where: { id: req.params.id },
+		relations: ["bot", "owner"],
+	});
 	if (app.owner.id != req.user_id)
 		throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
 
-	if (app.owner.totp_secret && (!req.body.code || verifyToken(app.owner.totp_secret, req.body.code)))
+	if (
+		app.owner.totp_secret &&
+		(!req.body.code || verifyToken(app.owner.totp_secret, req.body.code))
+	)
 		throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
 
-	if (app.bot)
-		await User.delete({ id: app.bot.id });
+	if (app.bot) await User.delete({ id: app.bot.id });
 
 	await Application.delete({ id: app.id });
 
 	res.send().status(200);
 });
 
-
-export default router;
\ No newline at end of file
+export default router;
diff --git a/src/api/routes/applications/#id/skus.ts b/src/api/routes/applications/#id/skus.ts
index 5b667f36..2383e6f7 100644
--- a/src/api/routes/applications/#id/skus.ts
+++ b/src/api/routes/applications/#id/skus.ts
@@ -8,4 +8,4 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	res.json([]).status(200);
 });
 
-export default router;
\ No newline at end of file
+export default router;
diff --git a/src/api/routes/applications/index.ts b/src/api/routes/applications/index.ts
index 94cfa5c5..a6b35bfa 100644
--- a/src/api/routes/applications/index.ts
+++ b/src/api/routes/applications/index.ts
@@ -1,30 +1,42 @@
 import { Request, Response, Router } from "express";
 import { route } from "@fosscord/api";
-import { Application, ApplicationCreateSchema, trimSpecial, User } from "@fosscord/util";
+import {
+	Application,
+	ApplicationCreateSchema,
+	trimSpecial,
+	User,
+} from "@fosscord/util";
 
 const router: Router = Router();
 
 router.get("/", route({}), async (req: Request, res: Response) => {
-	let results = await Application.find({ where: { owner: { id: req.user_id } }, relations: ["owner", "bot"] });
+	let results = await Application.find({
+		where: { owner: { id: req.user_id } },
+		relations: ["owner", "bot"],
+	});
 	res.json(results).status(200);
 });
 
-router.post("/", route({ body: "ApplicationCreateSchema" }), async (req: Request, res: Response) => {
-	const body = req.body as ApplicationCreateSchema;
-	const user = await User.findOneOrFail({ where: { id: req.user_id } });
+router.post(
+	"/",
+	route({ body: "ApplicationCreateSchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as ApplicationCreateSchema;
+		const user = await User.findOneOrFail({ where: { id: req.user_id } });
 
-	const app = Application.create({
-		name: trimSpecial(body.name),
-		description: "",
-		bot_public: true,
-		owner: user,
-		verify_key: "IMPLEMENTME",
-		flags: 0,
-	});
+		const app = Application.create({
+			name: trimSpecial(body.name),
+			description: "",
+			bot_public: true,
+			owner: user,
+			verify_key: "IMPLEMENTME",
+			flags: 0,
+		});
 
-	await app.save();
+		await app.save();
 
-	res.json(app);
-});
+		res.json(app);
+	},
+);
 
-export default router;
\ No newline at end of file
+export default router;
diff --git a/src/api/routes/auth/generate-registration-tokens.ts b/src/api/routes/auth/generate-registration-tokens.ts
index e328fe5e..0d4cf067 100644
--- a/src/api/routes/auth/generate-registration-tokens.ts
+++ b/src/api/routes/auth/generate-registration-tokens.ts
@@ -5,24 +5,37 @@ import { Request, Response, Router } from "express";
 const router: Router = Router();
 export default router;
 
-router.get("/", route({ right: "OPERATOR" }), async (req: Request, res: Response) => {
-	const count = req.query.count ? parseInt(req.query.count as string) : 1;
-	const length = req.query.length ? parseInt(req.query.length as string) : 255;
+router.get(
+	"/",
+	route({ right: "OPERATOR" }),
+	async (req: Request, res: Response) => {
+		const count = req.query.count ? parseInt(req.query.count as string) : 1;
+		const length = req.query.length
+			? parseInt(req.query.length as string)
+			: 255;
 
-	let tokens: ValidRegistrationToken[] = [];
+		let tokens: ValidRegistrationToken[] = [];
 
-	for (let i = 0; i < count; i++) {
-		const token = ValidRegistrationToken.create({
-			token: random(length),
-			expires_at: Date.now() + Config.get().security.defaultRegistrationTokenExpiration
-		});
-		tokens.push(token);
-	}
+		for (let i = 0; i < count; i++) {
+			const token = ValidRegistrationToken.create({
+				token: random(length),
+				expires_at:
+					Date.now() +
+					Config.get().security.defaultRegistrationTokenExpiration,
+			});
+			tokens.push(token);
+		}
 
-	// Why are these options used, exactly?
-	await ValidRegistrationToken.save(tokens, { chunk: 1000, reload: false, transaction: false });
+		// Why are these options used, exactly?
+		await ValidRegistrationToken.save(tokens, {
+			chunk: 1000,
+			reload: false,
+			transaction: false,
+		});
 
-	if (req.query.plain) return res.send(tokens.map(x => x.token).join("\n"));
+		if (req.query.plain)
+			return res.send(tokens.map((x) => x.token).join("\n"));
 
-	return res.json({ tokens: tokens.map(x => x.token) });
-}); 
\ No newline at end of file
+		return res.json({ tokens: tokens.map((x) => x.token) });
+	},
+);
diff --git a/src/api/routes/auth/register.ts b/src/api/routes/auth/register.ts
index c8c515e7..3d968114 100644
--- a/src/api/routes/auth/register.ts
+++ b/src/api/routes/auth/register.ts
@@ -33,16 +33,22 @@ router.post(
 		// Reg tokens
 		// They're a one time use token that bypasses registration limits ( rates, disabled reg, etc )
 		let regTokenUsed = false;
-		if (req.get("Referrer") && req.get("Referrer")?.includes("token=")) {	// eg theyre on https://staging.fosscord.com/register?token=whatever
+		if (req.get("Referrer") && req.get("Referrer")?.includes("token=")) {
+			// eg theyre on https://staging.fosscord.com/register?token=whatever
 			const token = req.get("Referrer")!.split("token=")[1].split("&")[0];
 			if (token) {
-				const regToken = await ValidRegistrationToken.findOne({ where: { token, expires_at: MoreThan(new Date()), } });
+				const regToken = await ValidRegistrationToken.findOne({
+					where: { token, expires_at: MoreThan(new Date()) },
+				});
 				await ValidRegistrationToken.delete({ token });
 				regTokenUsed = true;
-				console.log(`[REGISTER] Registration token ${token} used for registration!`);
-			}
-			else {
-				console.log(`[REGISTER] Invalid registration token ${token} used for registration by ${ip}!`);
+				console.log(
+					`[REGISTER] Registration token ${token} used for registration!`,
+				);
+			} else {
+				console.log(
+					`[REGISTER] Invalid registration token ${token} used for registration by ${ip}!`,
+				);
 			}
 		}
 
@@ -78,7 +84,11 @@ router.post(
 			});
 		}
 
-		if (!regTokenUsed && register.requireCaptcha && security.captcha.enabled) {
+		if (
+			!regTokenUsed &&
+			register.requireCaptcha &&
+			security.captcha.enabled
+		) {
 			const { sitekey, service } = security.captcha;
 			if (!body.captcha_key) {
 				return res?.status(400).json({
@@ -220,14 +230,26 @@ router.post(
 		if (
 			!regTokenUsed &&
 			limits.absoluteRate.register.enabled &&
-			(await User.count({ where: { created_at: MoreThan(new Date(Date.now() - limits.absoluteRate.register.window)) } }))
-			>= limits.absoluteRate.register.limit
+			(await User.count({
+				where: {
+					created_at: MoreThan(
+						new Date(
+							Date.now() - limits.absoluteRate.register.window,
+						),
+					),
+				},
+			})) >= limits.absoluteRate.register.limit
 		) {
 			console.log(
-				`Global register ratelimit exceeded for ${getIpAdress(req)}, ${req.body.username}, ${req.body.invite || "No invite given"}`
+				`Global register ratelimit exceeded for ${getIpAdress(req)}, ${
+					req.body.username
+				}, ${req.body.invite || "No invite given"}`,
 			);
 			throw FieldErrors({
-				email: { code: "TOO_MANY_REGISTRATIONS", message: req.t("auth:register.TOO_MANY_REGISTRATIONS") }
+				email: {
+					code: "TOO_MANY_REGISTRATIONS",
+					message: req.t("auth:register.TOO_MANY_REGISTRATIONS"),
+				},
 			});
 		}
 
diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/index.ts b/src/api/routes/channels/#channel_id/messages/#message_id/index.ts
index cd9da184..d57d9a1b 100644
--- a/src/api/routes/channels/#channel_id/messages/#message_id/index.ts
+++ b/src/api/routes/channels/#channel_id/messages/#message_id/index.ts
@@ -179,7 +179,7 @@ router.put(
 			channel.save(),
 		]);
 
-		postHandleMessage(message).catch((e) => { }); // no await as it shouldnt block the message send function and silently catch error
+		postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error
 
 		return res.json(message);
 	},
diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts
index 523b0cf8..2968437d 100644
--- a/src/api/routes/channels/#channel_id/messages/index.ts
+++ b/src/api/routes/channels/#channel_id/messages/index.ts
@@ -21,7 +21,12 @@ import {
 	Rights,
 } from "@fosscord/util";
 import { HTTPError } from "lambert-server";
-import { handleMessage, postHandleMessage, route, getIpAdress } from "@fosscord/api";
+import {
+	handleMessage,
+	postHandleMessage,
+	route,
+	getIpAdress,
+} from "@fosscord/api";
 import multer from "multer";
 import { yellow } from "picocolors";
 import { FindManyOptions, LessThan, MoreThan } from "typeorm";
@@ -80,7 +85,7 @@ router.get("/", async (req: Request, res: Response) => {
 	permissions.hasThrow("VIEW_CHANNEL");
 	if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
 
-	var query: FindManyOptions<Message> & { where: { id?: any; }; } = {
+	var query: FindManyOptions<Message> & { where: { id?: any } } = {
 		order: { timestamp: "DESC" },
 		take: limit,
 		where: { channel_id },
@@ -138,8 +143,9 @@ router.get("/", async (req: Request, res: Response) => {
 				const uri = y.proxy_url.startsWith("http")
 					? y.proxy_url
 					: `https://example.org${y.proxy_url}`;
-				y.proxy_url = `${endpoint == null ? "" : endpoint}${new URL(uri).pathname
-					}`;
+				y.proxy_url = `${endpoint == null ? "" : endpoint}${
+					new URL(uri).pathname
+				}`;
 			});
 
 			/**
@@ -211,8 +217,8 @@ router.post(
 				where: {
 					nonce: body.nonce,
 					channel_id: channel.id,
-					author_id: req.user_id
-				}
+					author_id: req.user_id,
+				},
 			});
 			if (existing) {
 				return res.json(existing);
@@ -225,13 +231,21 @@ router.post(
 				const count = await Message.count({
 					where: {
 						channel_id,
-						timestamp: MoreThan(new Date(Date.now() - limits.absoluteRate.sendMessage.window))
-					}
+						timestamp: MoreThan(
+							new Date(
+								Date.now() -
+									limits.absoluteRate.sendMessage.window,
+							),
+						),
+					},
 				});
 
 				if (count >= limits.absoluteRate.sendMessage.limit)
 					throw FieldErrors({
-						channel_id: { code: "TOO_MANY_MESSAGES", message: req.t("common:toomany.MESSAGE") }
+						channel_id: {
+							code: "TOO_MANY_MESSAGES",
+							message: req.t("common:toomany.MESSAGE"),
+						},
 					});
 			}
 		}
@@ -247,7 +261,7 @@ router.post(
 					Attachment.create({ ...file, proxy_url: file.url }),
 				);
 			} catch (error) {
-				return res.status(400).json({ message: error!.toString() })
+				return res.status(400).json({ message: error!.toString() });
 			}
 		}
 
@@ -296,19 +310,18 @@ router.post(
 			if (!message.member) {
 				message.member = await Member.findOneOrFail({
 					where: { id: req.user_id, guild_id: message.guild_id },
-					relations: ["roles"]
+					relations: ["roles"],
 				});
 			}
 
 			//@ts-ignore
-			message.member.roles =
-				message.member.roles.
-					filter(x => x.id != x.guild_id)
-					.map(x => x.id);
+			message.member.roles = message.member.roles
+				.filter((x) => x.id != x.guild_id)
+				.map((x) => x.id);
 		}
 
 		let read_state = await ReadState.findOne({
-			where: { user_id: req.user_id, channel_id }
+			where: { user_id: req.user_id, channel_id },
 		});
 		if (!read_state)
 			read_state = ReadState.create({ user_id: req.user_id, channel_id });
@@ -324,14 +337,14 @@ router.post(
 			} as MessageCreateEvent),
 			message.guild_id
 				? Member.update(
-					{ id: req.user_id, guild_id: message.guild_id },
-					{ last_message_id: message.id },
-				)
+						{ id: req.user_id, guild_id: message.guild_id },
+						{ last_message_id: message.id },
+				  )
 				: null,
 			channel.save(),
 		]);
 
-		postHandleMessage(message).catch((e) => { }); // no await as it shouldnt block the message send function and silently catch error
+		postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error
 
 		return res.json(message);
 	},
diff --git a/src/api/routes/channels/#channel_id/permissions.ts b/src/api/routes/channels/#channel_id/permissions.ts
index 0a816223..b08cd0c8 100644
--- a/src/api/routes/channels/#channel_id/permissions.ts
+++ b/src/api/routes/channels/#channel_id/permissions.ts
@@ -5,7 +5,7 @@ import {
 	emitEvent,
 	Member,
 	Role,
-	ChannelPermissionOverwriteSchema
+	ChannelPermissionOverwriteSchema,
 } from "@fosscord/util";
 import { Router, Response, Request } from "express";
 import { HTTPError } from "lambert-server";
diff --git a/src/api/routes/channels/#channel_id/webhooks.ts b/src/api/routes/channels/#channel_id/webhooks.ts
index 13f421f1..f303ef80 100644
--- a/src/api/routes/channels/#channel_id/webhooks.ts
+++ b/src/api/routes/channels/#channel_id/webhooks.ts
@@ -1,6 +1,15 @@
 import { Router, Response, Request } from "express";
 import { route } from "@fosscord/api";
-import { Channel, Config, handleFile, trimSpecial, User, Webhook, WebhookCreateSchema, WebhookType } from "@fosscord/util";
+import {
+	Channel,
+	Config,
+	handleFile,
+	trimSpecial,
+	User,
+	Webhook,
+	WebhookCreateSchema,
+	WebhookType,
+} from "@fosscord/util";
 import { HTTPError } from "lambert-server";
 import { isTextChannel } from "./messages/index";
 import { DiscordApiErrors } from "@fosscord/util";
@@ -38,8 +47,7 @@ router.post(
 		if (name === "clyde") throw new HTTPError("Invalid name", 400);
 		if (name === "Fosscord Ghost") throw new HTTPError("Invalid name", 400);
 
-		if (avatar)
-			avatar = await handleFile(`/avatars/${channel_id}`, avatar);
+		if (avatar) avatar = await handleFile(`/avatars/${channel_id}`, avatar);
 
 		const hook = Webhook.create({
 			type: WebhookType.Incoming,
diff --git a/src/api/routes/download/index.ts b/src/api/routes/download/index.ts
index 371c0fd7..1c135f25 100644
--- a/src/api/routes/download/index.ts
+++ b/src/api/routes/download/index.ts
@@ -12,19 +12,20 @@ const router = Router();
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const { platform } = req.query;
 
-	if (!platform) throw FieldErrors({
-		platform: {
-			code: "BASE_TYPE_REQUIRED",
-			message: req.t("common:field.BASE_TYPE_REQUIRED"),
-		}
-	});
+	if (!platform)
+		throw FieldErrors({
+			platform: {
+				code: "BASE_TYPE_REQUIRED",
+				message: req.t("common:field.BASE_TYPE_REQUIRED"),
+			},
+		});
 
 	const release = await Release.findOneOrFail({
 		where: {
 			enabled: true,
 			platform: platform as string,
 		},
-		order: { pub_date: "DESC" }
+		order: { pub_date: "DESC" },
 	});
 
 	res.redirect(release.url);
diff --git a/src/api/routes/guild-recommendations.ts b/src/api/routes/guild-recommendations.ts
index 302bbb9c..8bf1e508 100644
--- a/src/api/routes/guild-recommendations.ts
+++ b/src/api/routes/guild-recommendations.ts
@@ -9,7 +9,7 @@ const router = Router();
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const { limit, personalization_disabled } = req.query;
 	var showAllGuilds = Config.get().guild.discovery.showAllGuilds;
-	
+
 	const genLoadId = (size: Number) =>
 		[...Array(size)]
 			.map(() => Math.floor(Math.random() * 16).toString(16))
diff --git a/src/api/routes/guilds/#guild_id/index.ts b/src/api/routes/guilds/#guild_id/index.ts
index 1e976293..79c20678 100644
--- a/src/api/routes/guilds/#guild_id/index.ts
+++ b/src/api/routes/guilds/#guild_id/index.ts
@@ -69,15 +69,21 @@ router.patch(
 				body.splash,
 			);
 
-		if (body.discovery_splash && body.discovery_splash !== guild.discovery_splash)
+		if (
+			body.discovery_splash &&
+			body.discovery_splash !== guild.discovery_splash
+		)
 			body.discovery_splash = await handleFile(
 				`/discovery-splashes/${guild_id}`,
 				body.discovery_splash,
 			);
 
 		if (body.features) {
-			const diff = guild.features.filter(x => !body.features?.includes(x))
-				.concat(body.features.filter(x => !guild.features.includes(x)));
+			const diff = guild.features
+				.filter((x) => !body.features?.includes(x))
+				.concat(
+					body.features.filter((x) => !guild.features.includes(x)),
+				);
 
 			// TODO move these
 			const MUTABLE_FEATURES = [
@@ -89,7 +95,9 @@ router.patch(
 			for (var feature of diff) {
 				if (MUTABLE_FEATURES.includes(feature)) continue;
 
-				throw FosscordApiErrors.FEATURE_IS_IMMUTABLE.withParams(feature);
+				throw FosscordApiErrors.FEATURE_IS_IMMUTABLE.withParams(
+					feature,
+				);
 			}
 
 			// for some reason, they don't update in the assign.
diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts
index f1d343d9..0fcdd57c 100644
--- a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts
+++ b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts
@@ -27,42 +27,56 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	return res.json(member);
 });
 
-router.patch("/", route({ body: "MemberChangeSchema" }), async (req: Request, res: Response) => {
-	let { guild_id, member_id } = req.params;
-	if (member_id === "@me") member_id = req.user_id;
-	const body = req.body as MemberChangeSchema;
-
-	let member = await Member.findOneOrFail({ where: { id: member_id, guild_id }, relations: ["roles", "user"] });
-	const permission = await getPermission(req.user_id, guild_id);
-	const everyone = await Role.findOneOrFail({ where: { guild_id: guild_id, name: "@everyone", position: 0 } });
-
-	if (body.avatar) body.avatar = await handleFile(`/guilds/${guild_id}/users/${member_id}/avatars`, body.avatar as string);
-
-	member.assign(body);
-
-	if ('roles' in body) {
-		permission.hasThrow("MANAGE_ROLES");
-
-		body.roles = body.roles || [];
-		body.roles.filter(x => !!x);
-
-		if (body.roles.indexOf(everyone.id) === -1) body.roles.push(everyone.id);
-		member.roles = body.roles.map((x) => Role.create({ id: x })); // foreign key constraint will fail if role doesn't exist
-	}
-
-	await member.save();
-
-	member.roles = member.roles.filter((x) => x.id !== everyone.id);
-
-	// do not use promise.all as we have to first write to db before emitting the event to catch errors
-	await emitEvent({
-		event: "GUILD_MEMBER_UPDATE",
-		guild_id,
-		data: { ...member, roles: member.roles.map((x) => x.id) }
-	} as GuildMemberUpdateEvent);
-
-	res.json(member);
-});
+router.patch(
+	"/",
+	route({ body: "MemberChangeSchema" }),
+	async (req: Request, res: Response) => {
+		let { guild_id, member_id } = req.params;
+		if (member_id === "@me") member_id = req.user_id;
+		const body = req.body as MemberChangeSchema;
+
+		let member = await Member.findOneOrFail({
+			where: { id: member_id, guild_id },
+			relations: ["roles", "user"],
+		});
+		const permission = await getPermission(req.user_id, guild_id);
+		const everyone = await Role.findOneOrFail({
+			where: { guild_id: guild_id, name: "@everyone", position: 0 },
+		});
+
+		if (body.avatar)
+			body.avatar = await handleFile(
+				`/guilds/${guild_id}/users/${member_id}/avatars`,
+				body.avatar as string,
+			);
+
+		member.assign(body);
+
+		if ("roles" in body) {
+			permission.hasThrow("MANAGE_ROLES");
+
+			body.roles = body.roles || [];
+			body.roles.filter((x) => !!x);
+
+			if (body.roles.indexOf(everyone.id) === -1)
+				body.roles.push(everyone.id);
+			member.roles = body.roles.map((x) => Role.create({ id: x })); // foreign key constraint will fail if role doesn't exist
+		}
+
+		await member.save();
+
+		member.roles = member.roles.filter((x) => x.id !== everyone.id);
+
+		// do not use promise.all as we have to first write to db before emitting the event to catch errors
+		await emitEvent({
+			event: "GUILD_MEMBER_UPDATE",
+			guild_id,
+			data: { ...member, roles: member.roles.map((x) => x.id) },
+		} as GuildMemberUpdateEvent);
+
+		res.json(member);
+	},
+);
 
 router.put("/", route({}), async (req: Request, res: Response) => {
 	// TODO: Lurker mode
diff --git a/src/api/routes/guilds/#guild_id/messages/search.ts b/src/api/routes/guilds/#guild_id/messages/search.ts
index ccee59f7..88488871 100644
--- a/src/api/routes/guilds/#guild_id/messages/search.ts
+++ b/src/api/routes/guilds/#guild_id/messages/search.ts
@@ -72,12 +72,20 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	if (channel_id) query.where!.channel = { id: channel_id };
 	else {
 		// get all channel IDs that this user can access
-		const channels = await Channel.find({ where: { guild_id: req.params.guild_id }, select: ["id"] });
+		const channels = await Channel.find({
+			where: { guild_id: req.params.guild_id },
+			select: ["id"],
+		});
 		const ids = [];
 
 		for (var channel of channels) {
-			const perm = await getPermission(req.user_id, req.params.guild_id, channel.id);
-			if (!perm.has("VIEW_CHANNEL") || !perm.has("READ_MESSAGE_HISTORY")) continue;
+			const perm = await getPermission(
+				req.user_id,
+				req.params.guild_id,
+				channel.id,
+			);
+			if (!perm.has("VIEW_CHANNEL") || !perm.has("READ_MESSAGE_HISTORY"))
+				continue;
 			ids.push(channel.id);
 		}
 
diff --git a/src/api/routes/guilds/#guild_id/profile/index.ts b/src/api/routes/guilds/#guild_id/profile/index.ts
index ddc30943..20a7fa95 100644
--- a/src/api/routes/guilds/#guild_id/profile/index.ts
+++ b/src/api/routes/guilds/#guild_id/profile/index.ts
@@ -1,30 +1,48 @@
 import { route } from "@fosscord/api";
-import { emitEvent, GuildMemberUpdateEvent, handleFile, Member, MemberChangeProfileSchema, OrmUtils } from "@fosscord/util";
+import {
+	emitEvent,
+	GuildMemberUpdateEvent,
+	handleFile,
+	Member,
+	MemberChangeProfileSchema,
+	OrmUtils,
+} from "@fosscord/util";
 import { Request, Response, Router } from "express";
 
 const router = Router();
 
-router.patch("/:member_id", route({ body: "MemberChangeProfileSchema" }), async (req: Request, res: Response) => {
-	let { guild_id, member_id } = req.params;
-	if (member_id === "@me") member_id = req.user_id;
-	const body = req.body as MemberChangeProfileSchema;
-
-	let member = await Member.findOneOrFail({ where: { id: req.user_id, guild_id }, relations: ["roles", "user"] });
-
-	if (body.banner) body.banner = await handleFile(`/guilds/${guild_id}/users/${req.user_id}/avatars`, body.banner as string);
-
-	member = await OrmUtils.mergeDeep(member, body);
-
-	await member.save();
-
-	// do not use promise.all as we have to first write to db before emitting the event to catch errors
-	await emitEvent({
-		event: "GUILD_MEMBER_UPDATE",
-		guild_id,
-		data: { ...member, roles: member.roles.map((x) => x.id) }
-	} as GuildMemberUpdateEvent);
-
-	res.json(member);
-});
+router.patch(
+	"/:member_id",
+	route({ body: "MemberChangeProfileSchema" }),
+	async (req: Request, res: Response) => {
+		let { guild_id, member_id } = req.params;
+		if (member_id === "@me") member_id = req.user_id;
+		const body = req.body as MemberChangeProfileSchema;
+
+		let member = await Member.findOneOrFail({
+			where: { id: req.user_id, guild_id },
+			relations: ["roles", "user"],
+		});
+
+		if (body.banner)
+			body.banner = await handleFile(
+				`/guilds/${guild_id}/users/${req.user_id}/avatars`,
+				body.banner as string,
+			);
+
+		member = await OrmUtils.mergeDeep(member, body);
+
+		await member.save();
+
+		// do not use promise.all as we have to first write to db before emitting the event to catch errors
+		await emitEvent({
+			event: "GUILD_MEMBER_UPDATE",
+			guild_id,
+			data: { ...member, roles: member.roles.map((x) => x.id) },
+		} as GuildMemberUpdateEvent);
+
+		res.json(member);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts b/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts
index cd5959ff..84648703 100644
--- a/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts
+++ b/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts
@@ -63,12 +63,14 @@ router.patch(
 			);
 		else body.icon = undefined;
 
-		const role = await Role.findOneOrFail({ where: { id: role_id, guild: { id: guild_id } } });
+		const role = await Role.findOneOrFail({
+			where: { id: role_id, guild: { id: guild_id } },
+		});
 		role.assign({
 			...body,
 			permissions: String(
-				req.permission!.bitfield & BigInt(body.permissions || "0")
-			)
+				req.permission!.bitfield & BigInt(body.permissions || "0"),
+			),
 		});
 
 		await Promise.all([
diff --git a/src/api/routes/guilds/#guild_id/roles/index.ts b/src/api/routes/guilds/#guild_id/roles/index.ts
index 534a5967..4cd47cf3 100644
--- a/src/api/routes/guilds/#guild_id/roles/index.ts
+++ b/src/api/routes/guilds/#guild_id/roles/index.ts
@@ -61,9 +61,13 @@ router.post(
 		await Promise.all([
 			role.save(),
 			// Move all existing roles up one position, to accommodate the new role
-			Role.createQueryBuilder('roles')
-				.where({ guild: { id: guild_id }, name: Not("@everyone"), id: Not(role.id) })
-				.update({ position: () => 'position + 1' })
+			Role.createQueryBuilder("roles")
+				.where({
+					guild: { id: guild_id },
+					name: Not("@everyone"),
+					id: Not(role.id),
+				})
+				.update({ position: () => "position + 1" })
 				.execute(),
 			emitEvent({
 				event: "GUILD_ROLE_CREATE",
diff --git a/src/api/routes/oauth2/authorize.ts b/src/api/routes/oauth2/authorize.ts
index e4c2e986..6374972e 100644
--- a/src/api/routes/oauth2/authorize.ts
+++ b/src/api/routes/oauth2/authorize.ts
@@ -1,17 +1,24 @@
 import { Router, Request, Response } from "express";
 import { route } from "@fosscord/api";
-import { ApiError, Application, ApplicationAuthorizeSchema, getPermission, DiscordApiErrors, Member, Permissions, User, getRights, Rights, MemberPrivateProjection } from "@fosscord/util";
+import {
+	ApiError,
+	Application,
+	ApplicationAuthorizeSchema,
+	getPermission,
+	DiscordApiErrors,
+	Member,
+	Permissions,
+	User,
+	getRights,
+	Rights,
+	MemberPrivateProjection,
+} from "@fosscord/util";
 const router = Router();
 
 // TODO: scopes, other oauth types
 
 router.get("/", route({}), async (req: Request, res: Response) => {
-	const {
-		client_id,
-		scope,
-		response_type,
-		redirect_url,
-	} = req.query;
+	const { client_id, scope, response_type, redirect_url } = req.query;
 
 	const app = await Application.findOne({
 		where: {
@@ -33,7 +40,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 			id: req.user_id,
 			bot: false,
 		},
-		select: ["id", "username", "avatar", "discriminator", "public_flags"]
+		select: ["id", "username", "avatar", "discriminator", "public_flags"],
 	});
 
 	const guilds = await Member.find({
@@ -44,21 +51,23 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 		},
 		relations: ["guild", "roles"],
 		//@ts-ignore
-		select: ["guild.id", "guild.name", "guild.icon", "guild.mfa_level", "guild.owner_id", "roles.id"]
+		// prettier-ignore
+		select: ["guild.id", "guild.name", "guild.icon", "guild.mfa_level", "guild.owner_id", "roles.id"],
 	});
 
-	const guildsWithPermissions = guilds.map(x => {
-		const perms = x.guild.owner_id === user.id
-			? new Permissions(Permissions.FLAGS.ADMINISTRATOR)
-			: Permissions.finalPermission({
-				user: {
-					id: user.id,
-					roles: x.roles?.map(x => x.id) || [],
-				},
-				guild: {
-					roles: x?.roles || [],
-				}
-			});
+	const guildsWithPermissions = guilds.map((x) => {
+		const perms =
+			x.guild.owner_id === user.id
+				? new Permissions(Permissions.FLAGS.ADMINISTRATOR)
+				: Permissions.finalPermission({
+						user: {
+							id: user.id,
+							roles: x.roles?.map((x) => x.id) || [],
+						},
+						guild: {
+							roles: x?.roles || [],
+						},
+				  });
 
 		return {
 			id: x.guild.id,
@@ -75,7 +84,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 			id: user.id,
 			username: user.username,
 			avatar: user.avatar,
-			avatar_decoration: null,	// TODO
+			avatar_decoration: null, // TODO
 			discriminator: user.discriminator,
 			public_flags: user.public_flags,
 		},
@@ -87,7 +96,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 			summary: app.summary,
 			type: app.type,
 			hook: app.hook,
-			guild_id: null,	// TODO support guilds
+			guild_id: null, // TODO support guilds
 			bot_public: app.bot_public,
 			bot_require_code_grant: app.bot_require_code_grant,
 			verify_key: app.verify_key,
@@ -97,50 +106,63 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 			id: bot.id,
 			username: bot.username,
 			avatar: bot.avatar,
-			avatar_decoration: null,	// TODO
+			avatar_decoration: null, // TODO
 			discriminator: bot.discriminator,
 			public_flags: bot.public_flags,
 			bot: true,
-			approximated_guild_count: 0,	// TODO
+			approximated_guild_count: 0, // TODO
 		},
 		authorized: false,
 	});
 });
 
-router.post("/", route({ body: "ApplicationAuthorizeSchema" }), async (req: Request, res: Response) => {
-	const body = req.body as ApplicationAuthorizeSchema;
-	const {
-		client_id,
-		scope,
-		response_type,
-		redirect_url
-	} = req.query;
-
-	// TODO: captcha verification
-	// TODO: MFA verification
-
-	const perms = await getPermission(req.user_id, body.guild_id, undefined, { member_relations: ["user"] });
-	// getPermission cache won't exist if we're owner
-	if (Object.keys(perms.cache || {}).length > 0 && perms.cache.member!.user.bot) throw DiscordApiErrors.UNAUTHORIZED;
-	perms.hasThrow("MANAGE_GUILD");
-
-	const app = await Application.findOne({
-		where: {
-			id: client_id as string,
-		},
-		relations: ["bot"],
-	});
-
-	// TODO: use DiscordApiErrors
-	// findOneOrFail throws code 404
-	if (!app) throw new ApiError("Unknown Application", 10002, 404);
-	if (!app.bot) throw new ApiError("OAuth2 application does not have a bot", 50010, 400);
-
-	await Member.addToGuild(app.id, body.guild_id);
-
-	return res.json({
-		location: "/oauth2/authorized",	// redirect URL
-	});
-});
+router.post(
+	"/",
+	route({ body: "ApplicationAuthorizeSchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as ApplicationAuthorizeSchema;
+		const { client_id, scope, response_type, redirect_url } = req.query;
+
+		// TODO: captcha verification
+		// TODO: MFA verification
+
+		const perms = await getPermission(
+			req.user_id,
+			body.guild_id,
+			undefined,
+			{ member_relations: ["user"] },
+		);
+		// getPermission cache won't exist if we're owner
+		if (
+			Object.keys(perms.cache || {}).length > 0 &&
+			perms.cache.member!.user.bot
+		)
+			throw DiscordApiErrors.UNAUTHORIZED;
+		perms.hasThrow("MANAGE_GUILD");
+
+		const app = await Application.findOne({
+			where: {
+				id: client_id as string,
+			},
+			relations: ["bot"],
+		});
+
+		// TODO: use DiscordApiErrors
+		// findOneOrFail throws code 404
+		if (!app) throw new ApiError("Unknown Application", 10002, 404);
+		if (!app.bot)
+			throw new ApiError(
+				"OAuth2 application does not have a bot",
+				50010,
+				400,
+			);
+
+		await Member.addToGuild(app.id, body.guild_id);
+
+		return res.json({
+			location: "/oauth2/authorized", // redirect URL
+		});
+	},
+);
 
 export default router;
diff --git a/src/api/routes/policies/stats.ts b/src/api/routes/policies/stats.ts
index 5ef4c3c6..dc4652fc 100644
--- a/src/api/routes/policies/stats.ts
+++ b/src/api/routes/policies/stats.ts
@@ -1,5 +1,12 @@
 import { route } from "@fosscord/api";
-import { Config, getRights, Guild, Member, Message, User } from "@fosscord/util";
+import {
+	Config,
+	getRights,
+	Guild,
+	Member,
+	Message,
+	User,
+} from "@fosscord/util";
 import { Request, Response, Router } from "express";
 const router = Router();
 
@@ -15,7 +22,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 			guild: await Guild.count(),
 			message: await Message.count(),
 			members: await Member.count(),
-		}
+		},
 	});
 });
 
diff --git a/src/api/routes/stop.ts b/src/api/routes/stop.ts
index 1b4e1da9..3f49b360 100644
--- a/src/api/routes/stop.ts
+++ b/src/api/routes/stop.ts
@@ -3,10 +3,14 @@ import { route } from "@fosscord/api";
 
 const router: Router = Router();
 
-router.post("/", route({ right: "OPERATOR" }), async (req: Request, res: Response) => {
-	console.log(`/stop was called by ${req.user_id} at ${new Date()}`);
-	res.sendStatus(200);
-	process.kill(process.pid, "SIGTERM");
-});
+router.post(
+	"/",
+	route({ right: "OPERATOR" }),
+	async (req: Request, res: Response) => {
+		console.log(`/stop was called by ${req.user_id} at ${new Date()}`);
+		res.sendStatus(200);
+		process.kill(process.pid, "SIGTERM");
+	},
+);
 
-export default router;
\ No newline at end of file
+export default router;
diff --git a/src/api/routes/store/published-listings/skus/#sku_id/subscription-plans.ts b/src/api/routes/store/published-listings/skus/#sku_id/subscription-plans.ts
index 7c544921..6b49e959 100644
--- a/src/api/routes/store/published-listings/skus/#sku_id/subscription-plans.ts
+++ b/src/api/routes/store/published-listings/skus/#sku_id/subscription-plans.ts
@@ -16,7 +16,7 @@ const skus = new Map([
 				sku_id: "521842865731534868",
 				currency: "eur",
 				price: 0,
-				price_tier: null
+				price_tier: null,
 			},
 			{
 				id: "511651860671627264",
@@ -27,9 +27,9 @@ const skus = new Map([
 				sku_id: "521842865731534868",
 				currency: "eur",
 				price: 0,
-				price_tier: null
-			}
-		]
+				price_tier: null,
+			},
+		],
 	],
 	[
 		"521846918637420545",
@@ -43,7 +43,7 @@ const skus = new Map([
 				sku_id: "521846918637420545",
 				currency: "eur",
 				price: 0,
-				price_tier: null
+				price_tier: null,
 			},
 			{
 				id: "511651876987469824",
@@ -54,7 +54,7 @@ const skus = new Map([
 				sku_id: "521846918637420545",
 				currency: "eur",
 				price: 0,
-				price_tier: null
+				price_tier: null,
 			},
 			{
 				id: "978380684370378761",
@@ -65,9 +65,9 @@ const skus = new Map([
 				sku_id: "521846918637420545",
 				currency: "eur",
 				price: 0,
-				price_tier: null
-			}
-		]
+				price_tier: null,
+			},
+		],
 	],
 	[
 		"521847234246082599",
@@ -81,7 +81,7 @@ const skus = new Map([
 				sku_id: "521847234246082599",
 				currency: "eur",
 				price: 0,
-				price_tier: null
+				price_tier: null,
 			},
 			{
 				id: "511651880837840896",
@@ -92,7 +92,7 @@ const skus = new Map([
 				sku_id: "521847234246082599",
 				currency: "eur",
 				price: 0,
-				price_tier: null
+				price_tier: null,
 			},
 			{
 				id: "511651885459963904",
@@ -103,9 +103,9 @@ const skus = new Map([
 				sku_id: "521847234246082599",
 				currency: "eur",
 				price: 0,
-				price_tier: null
-			}
-		]
+				price_tier: null,
+			},
+		],
 	],
 	[
 		"590663762298667008",
@@ -120,7 +120,7 @@ const skus = new Map([
 				discount_price: 0,
 				currency: "eur",
 				price: 0,
-				price_tier: null
+				price_tier: null,
 			},
 			{
 				id: "590665538238152709",
@@ -132,9 +132,9 @@ const skus = new Map([
 				discount_price: 0,
 				currency: "eur",
 				price: 0,
-				price_tier: null
-			}
-		]
+				price_tier: null,
+			},
+		],
 	],
 	[
 		"978380684370378762",
@@ -158,33 +158,33 @@ const skus = new Map([
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
-								]
+										exponent: 2,
+									},
+								],
 							},
 							payment_source_prices: {
 								"775487223059316758": [
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
+										exponent: 2,
+									},
 								],
 								"736345864146255982": [
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
+										exponent: 2,
+									},
 								],
 								"683074999590060249": [
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
-								]
-							}
+										exponent: 2,
+									},
+								],
+							},
 						},
 						"3": {
 							country_prices: {
@@ -193,33 +193,33 @@ const skus = new Map([
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
-								]
+										exponent: 2,
+									},
+								],
 							},
 							payment_source_prices: {
 								"775487223059316758": [
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
+										exponent: 2,
+									},
 								],
 								"736345864146255982": [
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
+										exponent: 2,
+									},
 								],
 								"683074999590060249": [
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
-								]
-							}
+										exponent: 2,
+									},
+								],
+							},
 						},
 						"4": {
 							country_prices: {
@@ -228,33 +228,33 @@ const skus = new Map([
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
-								]
+										exponent: 2,
+									},
+								],
 							},
 							payment_source_prices: {
 								"775487223059316758": [
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
+										exponent: 2,
+									},
 								],
 								"736345864146255982": [
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
+										exponent: 2,
+									},
 								],
 								"683074999590060249": [
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
-								]
-							}
+										exponent: 2,
+									},
+								],
+							},
 						},
 						"1": {
 							country_prices: {
@@ -263,39 +263,39 @@ const skus = new Map([
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
-								]
+										exponent: 2,
+									},
+								],
 							},
 							payment_source_prices: {
 								"775487223059316758": [
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
+										exponent: 2,
+									},
 								],
 								"736345864146255982": [
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
+										exponent: 2,
+									},
 								],
 								"683074999590060249": [
 									{
 										currency: "usd",
 										amount: 0,
-										exponent: 2
-									}
-								]
-							}
-						}
-					}
-				}
-			]
-		]
-	]
+										exponent: 2,
+									},
+								],
+							},
+						},
+					},
+				},
+			],
+		],
+	],
 ]);
 
 router.get("/", route({}), async (req: Request, res: Response) => {
diff --git a/src/api/routes/updates.ts b/src/api/routes/updates.ts
index 275c458b..7e9128f4 100644
--- a/src/api/routes/updates.ts
+++ b/src/api/routes/updates.ts
@@ -8,19 +8,20 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	const { client } = Config.get();
 	const platform = req.query.platform;
 
-	if (!platform) throw FieldErrors({
-		platform: {
-			code: "BASE_TYPE_REQUIRED",
-			message: req.t("common:field.BASE_TYPE_REQUIRED"),
-		}
-	});
+	if (!platform)
+		throw FieldErrors({
+			platform: {
+				code: "BASE_TYPE_REQUIRED",
+				message: req.t("common:field.BASE_TYPE_REQUIRED"),
+			},
+		});
 
 	const release = await Release.findOneOrFail({
 		where: {
 			enabled: true,
 			platform: platform as string,
 		},
-		order: { pub_date: "DESC" }
+		order: { pub_date: "DESC" },
 	});
 
 	res.json({
diff --git a/src/api/routes/users/#id/profile.ts b/src/api/routes/users/#id/profile.ts
index ac844427..5c649056 100644
--- a/src/api/routes/users/#id/profile.ts
+++ b/src/api/routes/users/#id/profile.ts
@@ -89,79 +89,94 @@ router.get(
 			bot: user.bot,
 		};
 
-	const userProfile = {
-		bio: req.user_bot ? null : user.bio,
-		accent_color: user.accent_color,
-		banner: user.banner,
-		pronouns: user.pronouns,
-		theme_colors: user.theme_colors,
-	};
-
-	const guildMemberDto = guild_member
-		? {
-				avatar: guild_member.avatar,
-				banner: guild_member.banner,
-				bio: req.user_bot ? null : guild_member.bio,
-				communication_disabled_until: guild_member.communication_disabled_until,
-				deaf: guild_member.deaf,
-				flags: user.flags,
-				is_pending: guild_member.pending,
-				pending: guild_member.pending, // why is this here twice, discord?
-				joined_at: guild_member.joined_at,
-				mute: guild_member.mute,
-				nick: guild_member.nick,
-				premium_since: guild_member.premium_since,
-				roles: guild_member.roles.map((x) => x.id).filter((id) => id != guild_id),
-				user: userDto
-		  }
-		: undefined;
-
-	const guildMemberProfile = {
-		accent_color: null,
-		banner: guild_member?.banner || null,
-		bio: guild_member?.bio || "",
-		guild_id
-	};
-	res.json({
-		connected_accounts: user.connected_accounts,
-		premium_guild_since: premium_guild_since, // TODO
-		premium_since: user.premium_since, // TODO
-		mutual_guilds: mutual_guilds, // TODO {id: "", nick: null} when ?with_mutual_guilds=true
-		user: userDto,
-		premium_type: user.premium_type,
-		profile_themes_experiment_bucket: 4,	// TODO: This doesn't make it available, for some reason?
-		user_profile: userProfile,
-		guild_member: guild_id && guildMemberDto,
-		guild_member_profile: guild_id && guildMemberProfile
-	});
-});
-
-router.patch("/", route({ body: "UserProfileModifySchema" }), async (req: Request, res: Response) => {
-	const body = req.body as UserProfileModifySchema;
-
-	if (body.banner) body.banner = await handleFile(`/banners/${req.user_id}`, body.banner as string);
-	let user = await User.findOneOrFail({ where: { id: req.user_id }, select: [...PrivateUserProjection, "data"] });
-
-	user.assign(body);
-	await user.save();
-
-	// @ts-ignore
-	delete user.data;
-
-	// TODO: send update member list event in gateway
-	await emitEvent({
-		event: "USER_UPDATE",
-		user_id: req.user_id,
-		data: user
-	} as UserUpdateEvent);
-
-	res.json({
-		accent_color: user.accent_color,
-		bio: user.bio,
-		banner: user.banner,
-		theme_colors: user.theme_colors,
-		pronouns: user.pronouns,
-	});
-});
+		const userProfile = {
+			bio: req.user_bot ? null : user.bio,
+			accent_color: user.accent_color,
+			banner: user.banner,
+			pronouns: user.pronouns,
+			theme_colors: user.theme_colors,
+		};
+
+		const guildMemberDto = guild_member
+			? {
+					avatar: guild_member.avatar,
+					banner: guild_member.banner,
+					bio: req.user_bot ? null : guild_member.bio,
+					communication_disabled_until:
+						guild_member.communication_disabled_until,
+					deaf: guild_member.deaf,
+					flags: user.flags,
+					is_pending: guild_member.pending,
+					pending: guild_member.pending, // why is this here twice, discord?
+					joined_at: guild_member.joined_at,
+					mute: guild_member.mute,
+					nick: guild_member.nick,
+					premium_since: guild_member.premium_since,
+					roles: guild_member.roles
+						.map((x) => x.id)
+						.filter((id) => id != guild_id),
+					user: userDto,
+			  }
+			: undefined;
+
+		const guildMemberProfile = {
+			accent_color: null,
+			banner: guild_member?.banner || null,
+			bio: guild_member?.bio || "",
+			guild_id,
+		};
+		res.json({
+			connected_accounts: user.connected_accounts,
+			premium_guild_since: premium_guild_since, // TODO
+			premium_since: user.premium_since, // TODO
+			mutual_guilds: mutual_guilds, // TODO {id: "", nick: null} when ?with_mutual_guilds=true
+			user: userDto,
+			premium_type: user.premium_type,
+			profile_themes_experiment_bucket: 4, // TODO: This doesn't make it available, for some reason?
+			user_profile: userProfile,
+			guild_member: guild_id && guildMemberDto,
+			guild_member_profile: guild_id && guildMemberProfile,
+		});
+	},
+);
+
+router.patch(
+	"/",
+	route({ body: "UserProfileModifySchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as UserProfileModifySchema;
+
+		if (body.banner)
+			body.banner = await handleFile(
+				`/banners/${req.user_id}`,
+				body.banner as string,
+			);
+		let user = await User.findOneOrFail({
+			where: { id: req.user_id },
+			select: [...PrivateUserProjection, "data"],
+		});
+
+		user.assign(body);
+		await user.save();
+
+		// @ts-ignore
+		delete user.data;
+
+		// TODO: send update member list event in gateway
+		await emitEvent({
+			event: "USER_UPDATE",
+			user_id: req.user_id,
+			data: user,
+		} as UserUpdateEvent);
+
+		res.json({
+			accent_color: user.accent_color,
+			bio: user.bio,
+			banner: user.banner,
+			theme_colors: user.theme_colors,
+			pronouns: user.pronouns,
+		});
+	},
+);
 
 export default router;
diff --git a/src/api/routes/users/@me/guilds/#guild_id/settings.ts b/src/api/routes/users/@me/guilds/#guild_id/settings.ts
index 4538785c..436261d4 100644
--- a/src/api/routes/users/@me/guilds/#guild_id/settings.ts
+++ b/src/api/routes/users/@me/guilds/#guild_id/settings.ts
@@ -32,8 +32,7 @@ router.patch(
 
 		const user = await Member.findOneOrFail({
 			where: { id: req.user_id, guild_id: req.params.guild_id },
-			select: ["settings"]
-
+			select: ["settings"],
 		});
 		OrmUtils.mergeDeep(user.settings || {}, body);
 		Member.update({ id: req.user_id, guild_id: req.params.guild_id }, user);
diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts
index 3ac48f27..37356d9d 100644
--- a/src/api/routes/users/@me/index.ts
+++ b/src/api/routes/users/@me/index.ts
@@ -98,7 +98,7 @@ router.patch(
 			}
 			user.data.hash = await bcrypt.hash(body.new_password, 12);
 			user.data.valid_tokens_since = new Date();
-			newToken = await generateToken(user.id) as string;
+			newToken = (await generateToken(user.id)) as string;
 		}
 
 		if (body.username) {
diff --git a/src/api/routes/users/@me/settings.ts b/src/api/routes/users/@me/settings.ts
index 0fd8220a..cce366ac 100644
--- a/src/api/routes/users/@me/settings.ts
+++ b/src/api/routes/users/@me/settings.ts
@@ -21,7 +21,7 @@ router.patch(
 
 		const user = await User.findOneOrFail({
 			where: { id: req.user_id, bot: false },
-			relations: ["settings"]
+			relations: ["settings"],
 		});
 
 		user.settings.assign(body);
diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts
index 37269185..93dc3bf4 100644
--- a/src/api/util/handlers/Message.ts
+++ b/src/api/util/handlers/Message.ts
@@ -53,7 +53,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
 		channel_id: opts.channel_id,
 		attachments: opts.attachments || [],
 		embeds: opts.embeds || [],
-		reactions: /*opts.reactions ||*/[],
+		reactions: /*opts.reactions ||*/ [],
 		type: opts.type ?? 0,
 	});
 
@@ -180,7 +180,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
 
 // TODO: cache link result in db
 export async function postHandleMessage(message: Message) {
-	const content = message.content?.replace(/ *\`[^)]*\` */g, "");	// remove markdown
+	const content = message.content?.replace(/ *\`[^)]*\` */g, ""); // remove markdown
 	var links = content?.match(LINK_REGEX);
 	if (!links) return;
 
@@ -201,8 +201,12 @@ export async function postHandleMessage(message: Message) {
 		}
 
 		// bit gross, but whatever!
-		const endpointPublic = Config.get().cdn.endpointPublic || "http://127.0.0.1";	// lol
-		const handler = url.hostname == new URL(endpointPublic).hostname ? EmbedHandlers["self"] : EmbedHandlers[url.hostname] || EmbedHandlers["default"];
+		const endpointPublic =
+			Config.get().cdn.endpointPublic || "http://127.0.0.1"; // lol
+		const handler =
+			url.hostname == new URL(endpointPublic).hostname
+				? EmbedHandlers["self"]
+				: EmbedHandlers[url.hostname] || EmbedHandlers["default"];
 
 		try {
 			let res = await handler(url);
@@ -218,11 +222,10 @@ export async function postHandleMessage(message: Message) {
 				cachePromises.push(cache.save());
 				data.embeds.push(embed);
 			}
-		}
-		catch (e) {
-			Sentry.captureException(e, scope => {
+		} catch (e) {
+			Sentry.captureException(e, (scope) => {
 				scope.clear();
-				scope.setContext("request", { url })
+				scope.setContext("request", { url });
 				return scope;
 			});
 			continue;
@@ -257,7 +260,7 @@ export async function sendMessage(opts: MessageOptions) {
 		} as MessageCreateEvent),
 	]);
 
-	postHandleMessage(message).catch((e) => { }); // no await as it should catch error non-blockingly
+	postHandleMessage(message).catch((e) => {}); // no await as it should catch error non-blockingly
 
 	return message;
 }
diff --git a/src/api/util/index.ts b/src/api/util/index.ts
index ffad0607..5921f011 100644
--- a/src/api/util/index.ts
+++ b/src/api/util/index.ts
@@ -7,4 +7,4 @@ export * from "./handlers/route";
 export * from "./utility/String";
 export * from "./handlers/Voice";
 export * from "./utility/captcha";
-export * from "./utility/EmbedHandlers";
\ No newline at end of file
+export * from "./utility/EmbedHandlers";
diff --git a/src/api/util/utility/EmbedHandlers.ts b/src/api/util/utility/EmbedHandlers.ts
index dca264d0..2549937e 100644
--- a/src/api/util/utility/EmbedHandlers.ts
+++ b/src/api/util/utility/EmbedHandlers.ts
@@ -16,8 +16,13 @@ export const DEFAULT_FETCH_OPTIONS: any = {
 	method: "GET",
 };
 
-export const getProxyUrl = (url: URL, width: number, height: number): string => {
-	const { resizeWidthMax, resizeHeightMax, imagorServerUrl } = Config.get().cdn;
+export const getProxyUrl = (
+	url: URL,
+	width: number,
+	height: number,
+): string => {
+	const { resizeWidthMax, resizeHeightMax, imagorServerUrl } =
+		Config.get().cdn;
 	const secret = Config.get().security.requestSignature;
 	width = Math.min(width || 500, resizeWidthMax || width);
 	height = Math.min(height || 500, resizeHeightMax || width);
@@ -26,16 +31,20 @@ export const getProxyUrl = (url: URL, width: number, height: number): string =>
 	if (imagorServerUrl) {
 		let path = `${width}x${height}/${url.host}${url.pathname}`;
 
-		const hash = crypto.createHmac('sha1', secret)
+		const hash = crypto
+			.createHmac("sha1", secret)
 			.update(path)
-			.digest('base64')
-			.replace(/\+/g, '-').replace(/\//g, '_');
+			.digest("base64")
+			.replace(/\+/g, "-")
+			.replace(/\//g, "_");
 
 		return `${imagorServerUrl}/${hash}/${path}`;
 	}
 
 	// TODO: Imagor documentation
-	console.log("Imagor has not been set up correctly. docs.fosscord.com/set/up/a/page/about/this");
+	console.log(
+		"Imagor has not been set up correctly. docs.fosscord.com/set/up/a/page/about/this",
+	);
 	return "";
 };
 
@@ -69,8 +78,7 @@ const doFetch = async (url: URL) => {
 			...DEFAULT_FETCH_OPTIONS,
 			size: Config.get().limits.message.maxEmbedDownloadSize,
 		});
-	}
-	catch (e) {
+	} catch (e) {
 		return null;
 	}
 };
@@ -88,12 +96,10 @@ const genericImageHandler = async (url: URL): Promise<Embed | null> => {
 		width = result.width;
 		height = result.height;
 		image = url.href;
-	}
-	else if (type.headers.get("content-type")?.indexOf("video") !== -1) {
+	} else if (type.headers.get("content-type")?.indexOf("video") !== -1) {
 		// TODO
 		return null;
-	}
-	else {
+	} else {
 		// have to download the page, unfortunately
 		const response = await doFetch(url);
 		if (!response) return null;
@@ -113,13 +119,15 @@ const genericImageHandler = async (url: URL): Promise<Embed | null> => {
 			height: height,
 			url: url.href,
 			proxy_url: getProxyUrl(new URL(image), width, height),
-		}
+		},
 	};
 };
 
-export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed[] | null>; } = {
+export const EmbedHandlers: {
+	[key: string]: (url: URL) => Promise<Embed | Embed[] | null>;
+} = {
 	// the url does not have a special handler
-	"default": async (url: URL) => {
+	default: async (url: URL) => {
 		const type = await fetch(url, {
 			...DEFAULT_FETCH_OPTIONS,
 			method: "HEAD",
@@ -154,7 +162,13 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed
 				width: metas.width,
 				height: metas.height,
 				url: metas.image,
-				proxy_url: metas.image ? getProxyUrl(new URL(metas.image), metas.width!, metas.height!) : undefined,
+				proxy_url: metas.image
+					? getProxyUrl(
+							new URL(metas.image),
+							metas.width!,
+							metas.height!,
+					  )
+					: undefined,
 			},
 			description: metas.description,
 		};
@@ -169,26 +183,28 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed
 	// TODO: facebook
 	// have to use their APIs or something because they don't send the metas in initial html
 
-	"twitter.com": (url: URL) => { return EmbedHandlers["www.twitter.com"](url); },
+	"twitter.com": (url: URL) => {
+		return EmbedHandlers["www.twitter.com"](url);
+	},
 	"www.twitter.com": async (url: URL) => {
 		const token = Config.get().external.twitter;
 		if (!token) return null;
 
-		if (!url.href.includes("/status/")) return null;	// TODO;
-		const id = url.pathname.split("/")[3];	// super bad lol
+		if (!url.href.includes("/status/")) return null; // TODO;
+		const id = url.pathname.split("/")[3]; // super bad lol
 		if (!parseInt(id)) return null;
-		const endpointUrl = `https://api.twitter.com/2/tweets/${id}` +
+		const endpointUrl =
+			`https://api.twitter.com/2/tweets/${id}` +
 			`?expansions=author_id,attachments.media_keys` +
-			 `&media.fields=url,width,height` +
-			 `&tweet.fields=created_at,public_metrics` +
-			 `&user.fields=profile_image_url`;
-
+			`&media.fields=url,width,height` +
+			`&tweet.fields=created_at,public_metrics` +
+			`&user.fields=profile_image_url`;
 
 		const response = await fetch(endpointUrl, {
 			...DEFAULT_FETCH_OPTIONS,
 			headers: {
 				authorization: `Bearer ${token}`,
-			}
+			},
 		});
 		const json = await response.json();
 		if (json.errors) return null;
@@ -196,7 +212,9 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed
 		const text = json.data.text;
 		const created_at = new Date(json.data.created_at);
 		const metrics = json.data.public_metrics;
-		let media = json.includes.media?.filter((x: any) => x.type == "photo") as any[];	// TODO: video
+		let media = json.includes.media?.filter(
+			(x: any) => x.type == "photo",
+		) as any[]; // TODO: video
 
 		const embed: Embed = {
 			type: EmbedType.rich,
@@ -205,19 +223,38 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed
 			author: {
 				url: `https://twitter.com/${author.username}`,
 				name: `${author.name} (@${author.username})`,
-				proxy_icon_url: getProxyUrl(new URL(author.profile_image_url), 400, 400),
+				proxy_icon_url: getProxyUrl(
+					new URL(author.profile_image_url),
+					400,
+					400,
+				),
 				icon_url: author.profile_image_url,
 			},
 			timestamp: created_at,
 			fields: [
-				{ inline: true, name: "Likes", value: metrics.like_count.toString() },
-				{ inline: true, name: "Retweet", value: metrics.retweet_count.toString() },
+				{
+					inline: true,
+					name: "Likes",
+					value: metrics.like_count.toString(),
+				},
+				{
+					inline: true,
+					name: "Retweet",
+					value: metrics.retweet_count.toString(),
+				},
 			],
 			color: 1942002,
 			footer: {
 				text: "Twitter",
-				proxy_icon_url: getProxyUrl(new URL("https://abs.twimg.com/icons/apple-touch-icon-192x192.png"), 192, 192),
-				icon_url: "https://abs.twimg.com/icons/apple-touch-icon-192x192.png"
+				proxy_icon_url: getProxyUrl(
+					new URL(
+						"https://abs.twimg.com/icons/apple-touch-icon-192x192.png",
+					),
+					192,
+					192,
+				),
+				icon_url:
+					"https://abs.twimg.com/icons/apple-touch-icon-192x192.png",
 			},
 			// Discord doesn't send this?
 			// provider: {
@@ -231,7 +268,11 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed
 				width: media[0].width,
 				height: media[0].height,
 				url: media[0].url,
-				proxy_url: getProxyUrl(new URL(media[0].url), media[0].width, media[0].height)
+				proxy_url: getProxyUrl(
+					new URL(media[0].url),
+					media[0].width,
+					media[0].height,
+				),
 			};
 			media.shift();
 		}
@@ -265,17 +306,21 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed
 			thumbnail: {
 				width: 640,
 				height: 640,
-				proxy_url: metas.image ? getProxyUrl(new URL(metas.image!), 640, 640) : undefined,
+				proxy_url: metas.image
+					? getProxyUrl(new URL(metas.image!), 640, 640)
+					: undefined,
 				url: metas.image,
 			},
 			provider: {
 				url: "https://spotify.com",
 				name: "Spotify",
-			}
+			},
 		};
 	},
 
-	"pixiv.net": (url: URL) => { return EmbedHandlers["www.pixiv.net"](url); },
+	"pixiv.net": (url: URL) => {
+		return EmbedHandlers["www.pixiv.net"](url);
+	},
 	"www.pixiv.net": async (url: URL) => {
 		const response = await doFetch(url);
 		if (!response) return null;
@@ -291,12 +336,18 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed
 				width: metas.width,
 				height: metas.height,
 				url: url.href,
-				proxy_url: metas.image ? getProxyUrl(new URL(metas.image!), metas.width!, metas.height!) : undefined,
+				proxy_url: metas.image
+					? getProxyUrl(
+							new URL(metas.image!),
+							metas.width!,
+							metas.height!,
+					  )
+					: undefined,
 			},
 			provider: {
 				url: "https://pixiv.net",
-				name: "Pixiv"
-			}
+				name: "Pixiv",
+			},
 		};
 	},
 
@@ -310,35 +361,42 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed
 			type: EmbedType.rich,
 			title: metas.title,
 			description: metas.description,
-			image: {	// TODO: meant to be thumbnail.
+			image: {
+				// TODO: meant to be thumbnail.
 				// isn't this standard across all of steam?
 				width: 460,
 				height: 215,
 				url: metas.image,
-				proxy_url: metas.image ? getProxyUrl(new URL(metas.image!), 460, 215) : undefined,
+				proxy_url: metas.image
+					? getProxyUrl(new URL(metas.image!), 460, 215)
+					: undefined,
 			},
 			provider: {
 				url: "https://store.steampowered.com",
-				name: "Steam"
+				name: "Steam",
 			},
 			// TODO: fields for release date
 			// TODO: Video
 		};
 	},
 
-	"reddit.com": (url: URL) => { return EmbedHandlers["www.reddit.com"](url); },
+	"reddit.com": (url: URL) => {
+		return EmbedHandlers["www.reddit.com"](url);
+	},
 	"www.reddit.com": async (url: URL) => {
 		const res = await EmbedHandlers["default"](url);
 		return {
 			...res,
 			color: 16777215,
 			provider: {
-				name: "reddit"
-			}
+				name: "reddit",
+			},
 		};
 	},
 
-	"youtube.com": (url: URL) => { return EmbedHandlers["www.youtube.com"](url); },
+	"youtube.com": (url: URL) => {
+		return EmbedHandlers["www.youtube.com"](url);
+	},
 	"www.youtube.com": async (url: URL): Promise<Embed | null> => {
 		const response = await doFetch(url);
 		if (!response) return null;
@@ -358,7 +416,13 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed
 				width: metas.width,
 				height: metas.height,
 				url: metas.image,
-				proxy_url: metas.image ? getProxyUrl(new URL(metas.image!), metas.width!, metas.height!) : undefined,
+				proxy_url: metas.image
+					? getProxyUrl(
+							new URL(metas.image!),
+							metas.width!,
+							metas.height!,
+					  )
+					: undefined,
 			},
 			provider: {
 				url: "https://www.youtube.com",
@@ -369,12 +433,12 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed
 			author: {
 				name: metas.author,
 				// TODO: author channel url
-			}
+			},
 		};
 	},
 
 	// the url is an image from this instance
-	"self": async (url: URL): Promise<Embed | null> => {
+	self: async (url: URL): Promise<Embed | null> => {
 		const result = await probe(url.href);
 
 		return {
@@ -385,7 +449,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed
 				height: result.height,
 				url: url.href,
 				proxy_url: url.href,
-			}
+			},
 		};
 	},
-};;
\ No newline at end of file
+};