summary refs log tree commit diff
path: root/src/api
diff options
context:
space:
mode:
authorMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2022-09-26 22:29:30 +1000
committerMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2022-09-26 22:41:21 +1000
commitdbaf39237ae3a41b6b1ac6a6cd3486129599b815 (patch)
tree350cca4889d0804445eddbd8467d274b27307776 /src/api
parentRemove the cdn storage location log (diff)
downloadserver-dbaf39237ae3a41b6b1ac6a6cd3486129599b815.tar.xz
Prettier
Diffstat (limited to 'src/api')
-rw-r--r--src/api/Server.ts52
-rw-r--r--src/api/index.ts2
-rw-r--r--src/api/middlewares/Authentication.ts18
-rw-r--r--src/api/middlewares/BodyParser.ts3
-rw-r--r--src/api/middlewares/CORS.ts12
-rw-r--r--src/api/middlewares/ErrorHandler.ts25
-rw-r--r--src/api/middlewares/RateLimit.ts70
-rw-r--r--src/api/middlewares/TestClient.ts103
-rw-r--r--src/api/middlewares/Translation.ts14
-rw-r--r--src/api/routes/-/monitorz.ts24
-rw-r--r--src/api/routes/auth/location-metadata.ts14
-rw-r--r--src/api/routes/auth/login.ts175
-rw-r--r--src/api/routes/auth/logout.ts5
-rw-r--r--src/api/routes/auth/mfa/totp.ts73
-rw-r--r--src/api/routes/auth/register.ts303
-rw-r--r--src/api/routes/auth/verify/view-backup-codes-challenge.ts34
-rw-r--r--src/api/routes/channels/#channel_id/index.ts117
-rw-r--r--src/api/routes/channels/#channel_id/invites.ts65
-rw-r--r--src/api/routes/channels/#channel_id/messages/#message_id/ack.ts62
-rw-r--r--src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts52
-rw-r--r--src/api/routes/channels/#channel_id/messages/#message_id/index.ts164
-rw-r--r--src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts327
-rw-r--r--src/api/routes/channels/#channel_id/messages/bulk-delete.ts85
-rw-r--r--src/api/routes/channels/#channel_id/messages/index.ts138
-rw-r--r--src/api/routes/channels/#channel_id/permissions.ts79
-rw-r--r--src/api/routes/channels/#channel_id/pins.ts163
-rw-r--r--src/api/routes/channels/#channel_id/purge.ts105
-rw-r--r--src/api/routes/channels/#channel_id/recipients.ts42
-rw-r--r--src/api/routes/channels/#channel_id/typing.ts54
-rw-r--r--src/api/routes/channels/#channel_id/webhooks.ts41
-rw-r--r--src/api/routes/discoverable-guilds.ts26
-rw-r--r--src/api/routes/discovery.ts4
-rw-r--r--src/api/routes/downloads.ts7
-rw-r--r--src/api/routes/experiments.ts2
-rw-r--r--src/api/routes/gateway/bot.ts10
-rw-r--r--src/api/routes/gateway/index.ts10
-rw-r--r--src/api/routes/gifs/search.ts19
-rw-r--r--src/api/routes/gifs/trending-gifs.ts19
-rw-r--r--src/api/routes/gifs/trending.ts46
-rw-r--r--src/api/routes/guild-recommendations.ts15
-rw-r--r--src/api/routes/guilds/#guild_id/audit-logs.ts2
-rw-r--r--src/api/routes/guilds/#guild_id/bans.ts304
-rw-r--r--src/api/routes/guilds/#guild_id/channels.ts101
-rw-r--r--src/api/routes/guilds/#guild_id/delete.ts26
-rw-r--r--src/api/routes/guilds/#guild_id/discovery-requirements.ts46
-rw-r--r--src/api/routes/guilds/#guild_id/emojis.ts157
-rw-r--r--src/api/routes/guilds/#guild_id/index.ts106
-rw-r--r--src/api/routes/guilds/#guild_id/invites.ts17
-rw-r--r--src/api/routes/guilds/#guild_id/member-verification.ts4
-rw-r--r--src/api/routes/guilds/#guild_id/members/#member_id/index.ts110
-rw-r--r--src/api/routes/guilds/#guild_id/members/#member_id/nick.ts28
-rw-r--r--src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts28
-rw-r--r--src/api/routes/guilds/#guild_id/members/index.ts5
-rw-r--r--src/api/routes/guilds/#guild_id/messages/search.ts100
-rw-r--r--src/api/routes/guilds/#guild_id/prune.ts75
-rw-r--r--src/api/routes/guilds/#guild_id/regions.ts7
-rw-r--r--src/api/routes/guilds/#guild_id/roles/#role_id/index.ts107
-rw-r--r--src/api/routes/guilds/#guild_id/roles/index.ts139
-rw-r--r--src/api/routes/guilds/#guild_id/stickers.ts56
-rw-r--r--src/api/routes/guilds/#guild_id/templates.ts132
-rw-r--r--src/api/routes/guilds/#guild_id/vanity-url.ts120
-rw-r--r--src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts93
-rw-r--r--src/api/routes/guilds/#guild_id/welcome-screen.ts40
-rw-r--r--src/api/routes/guilds/#guild_id/widget.json.ts29
-rw-r--r--src/api/routes/guilds/#guild_id/widget.png.ts92
-rw-r--r--src/api/routes/guilds/#guild_id/widget.ts33
-rw-r--r--src/api/routes/guilds/index.ts61
-rw-r--r--src/api/routes/guilds/templates/index.ts155
-rw-r--r--src/api/routes/invites/index.ts64
-rw-r--r--src/api/routes/partners/#guild_id/requirements.ts47
-rw-r--r--src/api/routes/policies/instance/domains.ts19
-rw-r--r--src/api/routes/policies/instance/index.ts3
-rw-r--r--src/api/routes/policies/instance/limits.ts2
-rw-r--r--src/api/routes/scheduled-maintenances/upcoming_json.ts16
-rw-r--r--src/api/routes/stop.ts16
-rw-r--r--src/api/routes/store/published-listings/applications.ts20
-rw-r--r--src/api/routes/store/published-listings/applications/#id/subscription-plans.ts4
-rw-r--r--src/api/routes/store/published-listings/skus.ts20
-rw-r--r--src/api/routes/store/published-listings/skus/#sku_id/subscription-plans.ts40
-rw-r--r--src/api/routes/updates.ts6
-rw-r--r--src/api/routes/users/#id/profile.ts150
-rw-r--r--src/api/routes/users/#id/relationships.ts63
-rw-r--r--src/api/routes/users/@me/channels.ts33
-rw-r--r--src/api/routes/users/@me/delete.ts10
-rw-r--r--src/api/routes/users/@me/disable.ts10
-rw-r--r--src/api/routes/users/@me/email-settings.ts4
-rw-r--r--src/api/routes/users/@me/guilds.ts39
-rw-r--r--src/api/routes/users/@me/guilds/#guild_id/settings.ts42
-rw-r--r--src/api/routes/users/@me/index.ts187
-rw-r--r--src/api/routes/users/@me/mfa/codes-verification.ts72
-rw-r--r--src/api/routes/users/@me/mfa/codes.ts83
-rw-r--r--src/api/routes/users/@me/mfa/totp/disable.ts81
-rw-r--r--src/api/routes/users/@me/mfa/totp/enable.ts90
-rw-r--r--src/api/routes/users/@me/notes.ts34
-rw-r--r--src/api/routes/users/@me/relationships.ts175
-rw-r--r--src/api/routes/users/@me/settings.ts26
-rw-r--r--src/api/start.ts2
-rw-r--r--src/api/util/handlers/Message.ts186
-rw-r--r--src/api/util/handlers/Voice.ts11
-rw-r--r--src/api/util/handlers/route.ts35
-rw-r--r--src/api/util/index.ts2
-rw-r--r--src/api/util/utility/Base64.ts3
-rw-r--r--src/api/util/utility/RandomInviteID.ts13
-rw-r--r--src/api/util/utility/String.ts14
-rw-r--r--src/api/util/utility/captcha.ts19
-rw-r--r--src/api/util/utility/ipAddress.ts44
-rw-r--r--src/api/util/utility/passwordStrength.ts20
107 files changed, 4108 insertions, 2349 deletions
diff --git a/src/api/Server.ts b/src/api/Server.ts
index 4cf0917d..bd9bc4b9 100644
--- a/src/api/Server.ts
+++ b/src/api/Server.ts
@@ -12,7 +12,7 @@ import { initTranslation } from "./middlewares/Translation";
 import morgan from "morgan";
 import { initInstance } from "./util/handlers/Instance";
 import { registerRoutes } from "@fosscord/util";
-import { red } from "picocolors"
+import { red } from "picocolors";
 
 export interface FosscordServerOptions extends ServerOptions {}
 
@@ -44,13 +44,18 @@ export class FosscordServer extends Server {
 			this.app.use(
 				morgan("combined", {
 					skip: (req, res) => {
-						var skip = !(process.env["LOG_REQUESTS"]?.includes(res.statusCode.toString()) ?? false);
-						if (process.env["LOG_REQUESTS"]?.charAt(0) == "-") skip = !skip;
+						var skip = !(
+							process.env["LOG_REQUESTS"]?.includes(
+								res.statusCode.toString(),
+							) ?? false
+						);
+						if (process.env["LOG_REQUESTS"]?.charAt(0) == "-")
+							skip = !skip;
 						return skip;
-					}
-				})
+					},
+				}),
 			);
-		};
+		}
 
 		this.app.use(CORS);
 		this.app.use(BodyParser({ inflate: true, limit: "10mb" }));
@@ -63,16 +68,22 @@ export class FosscordServer extends Server {
 		await initRateLimits(api);
 		await initTranslation(api);
 
-		this.routes = await registerRoutes(this, path.join(__dirname, "routes", "/"));
+		this.routes = await registerRoutes(
+			this,
+			path.join(__dirname, "routes", "/"),
+		);
 
-		api.use("*", (error: any, req: Request, res: Response, next: NextFunction) => {
-			if (error) return next(error);
-			res.status(404).json({
-				message: "404 endpoint not found",
-				code: 0
-			});
-			next();
-		});
+		api.use(
+			"*",
+			(error: any, req: Request, res: Response, next: NextFunction) => {
+				if (error) return next(error);
+				res.status(404).json({
+					message: "404 endpoint not found",
+					code: 0,
+				});
+				next();
+			},
+		);
 
 		this.app = app;
 
@@ -87,8 +98,13 @@ export class FosscordServer extends Server {
 		this.app.use(ErrorHandler);
 		TestClient(this.app);
 
-		if (logRequests) console.log(red(`Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!`));
-		
+		if (logRequests)
+			console.log(
+				red(
+					`Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!`,
+				),
+			);
+
 		return super.start();
 	}
-};
\ No newline at end of file
+}
diff --git a/src/api/index.ts b/src/api/index.ts
index 09663452..adc7649c 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -1,3 +1,3 @@
 export * from "./Server";
 export * from "./middlewares/";
-export * from "./util/";
\ No newline at end of file
+export * from "./util/";
diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts
index 1df7911b..50048b12 100644
--- a/src/api/middlewares/Authentication.ts
+++ b/src/api/middlewares/Authentication.ts
@@ -10,7 +10,7 @@ export const NO_AUTHORIZATION_ROUTES = [
 	"/auth/mfa/totp",
 	// Routes with a seperate auth system
 	"/webhooks/",
-	// Public information endpoints 
+	// Public information endpoints
 	"/ping",
 	"/gateway",
 	"/experiments",
@@ -26,7 +26,7 @@ export const NO_AUTHORIZATION_ROUTES = [
 	// Public policy pages
 	"/policies/instance",
 	// Asset delivery
-	/\/guilds\/\d+\/widget\.(json|png)/
+	/\/guilds\/\d+\/widget\.(json|png)/,
 ];
 
 export const API_PREFIX = /^\/api(\/v\d+)?/;
@@ -43,7 +43,11 @@ declare global {
 	}
 }
 
-export async function Authentication(req: Request, res: Response, next: NextFunction) {
+export async function Authentication(
+	req: Request,
+	res: Response,
+	next: NextFunction,
+) {
 	if (req.method === "OPTIONS") return res.sendStatus(204);
 	const url = req.url.replace(API_PREFIX, "");
 	if (url.startsWith("/invites") && req.method === "GET") return next();
@@ -54,12 +58,16 @@ export async function Authentication(req: Request, res: Response, next: NextFunc
 		})
 	)
 		return next();
-	if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401));
+	if (!req.headers.authorization)
+		return next(new HTTPError("Missing Authorization Header", 401));
 
 	try {
 		const { jwtSecret } = Config.get().security;
 
-		const { decoded, user }: any = await checkToken(req.headers.authorization, jwtSecret);
+		const { decoded, user }: any = await checkToken(
+			req.headers.authorization,
+			jwtSecret,
+		);
 
 		req.token = decoded;
 		req.user_id = decoded.id;
diff --git a/src/api/middlewares/BodyParser.ts b/src/api/middlewares/BodyParser.ts
index 4cb376bc..7741f1fd 100644
--- a/src/api/middlewares/BodyParser.ts
+++ b/src/api/middlewares/BodyParser.ts
@@ -6,7 +6,8 @@ export function BodyParser(opts?: OptionsJson) {
 	const jsonParser = bodyParser.json(opts);
 
 	return (req: Request, res: Response, next: NextFunction) => {
-		if (!req.headers["content-type"]) req.headers["content-type"] = "application/json";
+		if (!req.headers["content-type"])
+			req.headers["content-type"] = "application/json";
 
 		jsonParser(req, res, (err) => {
 			if (err) {
diff --git a/src/api/middlewares/CORS.ts b/src/api/middlewares/CORS.ts
index 20260cf9..2dce51c6 100644
--- a/src/api/middlewares/CORS.ts
+++ b/src/api/middlewares/CORS.ts
@@ -7,10 +7,16 @@ export function CORS(req: Request, res: Response, next: NextFunction) {
 	// TODO: use better CSP
 	res.set(
 		"Content-security-policy",
-		"default-src *  data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';"
+		"default-src *  data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';",
+	);
+	res.set(
+		"Access-Control-Allow-Headers",
+		req.header("Access-Control-Request-Headers") || "*",
+	);
+	res.set(
+		"Access-Control-Allow-Methods",
+		req.header("Access-Control-Request-Methods") || "*",
 	);
-	res.set("Access-Control-Allow-Headers", req.header("Access-Control-Request-Headers") || "*");
-	res.set("Access-Control-Allow-Methods", req.header("Access-Control-Request-Methods") || "*");
 
 	next();
 }
diff --git a/src/api/middlewares/ErrorHandler.ts b/src/api/middlewares/ErrorHandler.ts
index 2012b91c..bf3b011a 100644
--- a/src/api/middlewares/ErrorHandler.ts
+++ b/src/api/middlewares/ErrorHandler.ts
@@ -3,7 +3,12 @@ import { HTTPError } from "lambert-server";
 import { ApiError, FieldError } from "@fosscord/util";
 const EntityNotFoundErrorRegex = /"(\w+)"/;
 
-export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) {
+export function ErrorHandler(
+	error: Error,
+	req: Request,
+	res: Response,
+	next: NextFunction,
+) {
 	if (!error) return next();
 
 	try {
@@ -12,20 +17,28 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne
 		let message = error?.toString();
 		let errors = undefined;
 
-		if (error instanceof HTTPError && error.code) code = httpcode = error.code;
+		if (error instanceof HTTPError && error.code)
+			code = httpcode = error.code;
 		else if (error instanceof ApiError) {
 			code = error.code;
 			message = error.message;
 			httpcode = error.httpStatus;
 		} else if (error.name === "EntityNotFoundError") {
-			message = `${error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item"} could not be found`;
+			message = `${
+				error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item"
+			} could not be found`;
 			code = httpcode = 404;
 		} else if (error instanceof FieldError) {
 			code = Number(error.code);
 			message = error.message;
 			errors = error.errors;
 		} else {
-			console.error(`[Error] ${code} ${req.url}\n`, errors || error, "\nbody:", req.body);
+			console.error(
+				`[Error] ${code} ${req.url}\n`,
+				errors || error,
+				"\nbody:",
+				req.body,
+			);
 
 			if (req.server?.options?.production) {
 				// don't expose internal errors to the user, instead human errors should be thrown as HTTPError
@@ -39,6 +52,8 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne
 		res.status(httpcode).json({ code: code, message, errors });
 	} catch (error) {
 		console.error(`[Internal Server Error] 500`, error);
-		return res.status(500).json({ code: 500, message: "Internal Server Error" });
+		return res
+			.status(500)
+			.json({ code: 500, message: "Internal Server Error" });
 	}
 }
diff --git a/src/api/middlewares/RateLimit.ts b/src/api/middlewares/RateLimit.ts
index 57645c0b..b3976a16 100644
--- a/src/api/middlewares/RateLimit.ts
+++ b/src/api/middlewares/RateLimit.ts
@@ -40,21 +40,32 @@ export default function rateLimit(opts: {
 	success?: boolean;
 	onlyIp?: boolean;
 }): any {
-	return async (req: Request, res: Response, next: NextFunction): Promise<any> => {
+	return async (
+		req: Request,
+		res: Response,
+		next: NextFunction,
+	): Promise<any> => {
 		// exempt user? if so, immediately short circuit
 		if (req.user_id) {
 			const rights = await getRights(req.user_id);
 			if (rights.has("BYPASS_RATE_LIMITS")) return next();
 		}
 
-		const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, "");
+		const bucket_id =
+			opts.bucket ||
+			req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, "");
 		let executor_id = getIpAdress(req);
 		if (!opts.onlyIp && req.user_id) executor_id = req.user_id;
 
 		let max_hits = opts.count;
 		if (opts.bot && req.user_bot) max_hits = opts.bot;
-		if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method)) max_hits = opts.GET;
-		else if (opts.MODIFY && ["POST", "DELETE", "PATCH", "PUT"].includes(req.method)) max_hits = opts.MODIFY;
+		if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method))
+			max_hits = opts.GET;
+		else if (
+			opts.MODIFY &&
+			["POST", "DELETE", "PATCH", "PUT"].includes(req.method)
+		)
+			max_hits = opts.MODIFY;
 
 		let offender = Cache.get(executor_id + bucket_id);
 
@@ -75,11 +86,15 @@ export default function rateLimit(opts: {
 				const global = bucket_id === "global";
 				// each block violation pushes the expiry one full window further
 				reset += opts.window * 1000;
-				offender.expires_at = new Date(offender.expires_at.getTime() + opts.window * 1000);
+				offender.expires_at = new Date(
+					offender.expires_at.getTime() + opts.window * 1000,
+				);
 				resetAfterMs = reset - Date.now();
 				resetAfterSec = Math.ceil(resetAfterMs / 1000);
 
-				console.log(`blocked bucket: ${bucket_id} ${executor_id}`, { resetAfterMs });
+				console.log(`blocked bucket: ${bucket_id} ${executor_id}`, {
+					resetAfterMs,
+				});
 				return (
 					res
 						.status(429)
@@ -91,20 +106,33 @@ export default function rateLimit(opts: {
 						.set("Retry-After", `${Math.ceil(resetAfterSec)}`)
 						.set("X-RateLimit-Bucket", `${bucket_id}`)
 						// TODO: error rate limit message translation
-						.send({ message: "You are being rate limited.", retry_after: resetAfterSec, global })
+						.send({
+							message: "You are being rate limited.",
+							retry_after: resetAfterSec,
+							global,
+						})
 				);
 			}
 		}
 
 		next();
-		const hitRouteOpts = { bucket_id, executor_id, max_hits, window: opts.window };
+		const hitRouteOpts = {
+			bucket_id,
+			executor_id,
+			max_hits,
+			window: opts.window,
+		};
 
 		if (opts.error || opts.success) {
 			res.once("finish", () => {
 				// check if error and increment error rate limit
 				if (res.statusCode >= 400 && opts.error) {
 					return hitRoute(hitRouteOpts);
-				} else if (res.statusCode >= 200 && res.statusCode < 300 && opts.success) {
+				} else if (
+					res.statusCode >= 200 &&
+					res.statusCode < 300 &&
+					opts.success
+				) {
 					return hitRoute(hitRouteOpts);
 				}
 			});
@@ -141,8 +169,8 @@ export async function initRateLimits(app: Router) {
 		rateLimit({
 			bucket: "global",
 			onlyIp: true,
-			...ip
-		})
+			...ip,
+		}),
 	);
 	app.use(rateLimit({ bucket: "global", ...global }));
 	app.use(
@@ -150,17 +178,25 @@ export async function initRateLimits(app: Router) {
 			bucket: "error",
 			error: true,
 			onlyIp: true,
-			...error
-		})
+			...error,
+		}),
 	);
 	app.use("/guilds/:id", rateLimit(routes.guild));
 	app.use("/webhooks/:id", rateLimit(routes.webhook));
 	app.use("/channels/:id", rateLimit(routes.channel));
 	app.use("/auth/login", rateLimit(routes.auth.login));
-	app.use("/auth/register", rateLimit({ onlyIp: true, success: true, ...routes.auth.register }));
+	app.use(
+		"/auth/register",
+		rateLimit({ onlyIp: true, success: true, ...routes.auth.register }),
+	);
 }
 
-async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number; }) {
+async function hitRoute(opts: {
+	executor_id: string;
+	bucket_id: string;
+	max_hits: number;
+	window: number;
+}) {
 	const id = opts.executor_id + opts.bucket_id;
 	let limit = Cache.get(id);
 	if (!limit) {
@@ -169,7 +205,7 @@ async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits
 			executor_id: opts.executor_id,
 			expires_at: new Date(Date.now() + opts.window * 1000),
 			hits: 0,
-			blocked: false
+			blocked: false,
 		};
 		Cache.set(id, limit);
 	}
@@ -205,4 +241,4 @@ async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits
 	}
 	await ratelimit.save();
 	*/
-}
\ No newline at end of file
+}
diff --git a/src/api/middlewares/TestClient.ts b/src/api/middlewares/TestClient.ts
index e68abf98..62128b36 100644
--- a/src/api/middlewares/TestClient.ts
+++ b/src/api/middlewares/TestClient.ts
@@ -2,7 +2,7 @@ import express, { Request, Response, Application } from "express";
 import fs from "fs";
 import path from "path";
 import fetch, { Response as FetchResponse } from "node-fetch";
-import ProxyAgent from 'proxy-agent';
+import ProxyAgent from "proxy-agent";
 import { Config } from "@fosscord/util";
 
 const ASSET_FOLDER_PATH = path.join(__dirname, "..", "..", "..", "assets");
@@ -11,40 +11,67 @@ let hasWarnedAboutCache = false;
 
 export default function TestClient(app: Application) {
 	const agent = new ProxyAgent();
-	const assetCache = new Map<string, { response: FetchResponse; buffer: Buffer; }>();
-	const indexHTML = fs.readFileSync(path.join(ASSET_FOLDER_PATH, "client_test", "index.html"), { encoding: "utf8" });
+	const assetCache = new Map<
+		string,
+		{ response: FetchResponse; buffer: Buffer }
+	>();
+	const indexHTML = fs.readFileSync(
+		path.join(ASSET_FOLDER_PATH, "client_test", "index.html"),
+		{ encoding: "utf8" },
+	);
 
 	var html = indexHTML;
-	const CDN_ENDPOINT = (Config.get().cdn.endpointClient || Config.get()?.cdn.endpointPublic || process.env.CDN || "").replace(
-		/(https?)?(:\/\/?)/g,
+	const CDN_ENDPOINT = (
+		Config.get().cdn.endpointClient ||
+		Config.get()?.cdn.endpointPublic ||
+		process.env.CDN ||
 		""
-	);
-	const GATEWAY_ENDPOINT = Config.get().gateway.endpointClient || Config.get()?.gateway.endpointPublic || process.env.GATEWAY || "";
+	).replace(/(https?)?(:\/\/?)/g, "");
+	const GATEWAY_ENDPOINT =
+		Config.get().gateway.endpointClient ||
+		Config.get()?.gateway.endpointPublic ||
+		process.env.GATEWAY ||
+		"";
 
 	if (CDN_ENDPOINT) {
 		html = html.replace(/CDN_HOST: .+/, `CDN_HOST: \`${CDN_ENDPOINT}\`,`);
 	}
 	if (GATEWAY_ENDPOINT) {
-		html = html.replace(/GATEWAY_ENDPOINT: .+/, `GATEWAY_ENDPOINT: \`${GATEWAY_ENDPOINT}\`,`);
+		html = html.replace(
+			/GATEWAY_ENDPOINT: .+/,
+			`GATEWAY_ENDPOINT: \`${GATEWAY_ENDPOINT}\`,`,
+		);
 	}
 	// inline plugins
 	var files = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "preload-plugins"));
 	var plugins = "";
-	files.forEach(x => { if (x.endsWith(".js")) plugins += `<script>${fs.readFileSync(path.join(ASSET_FOLDER_PATH, "preload-plugins", x))}</script>\n`; });
+	files.forEach((x) => {
+		if (x.endsWith(".js"))
+			plugins += `<script>${fs.readFileSync(
+				path.join(ASSET_FOLDER_PATH, "preload-plugins", x),
+			)}</script>\n`;
+	});
 	html = html.replaceAll("<!-- preload plugin marker -->", plugins);
 
 	// plugins
 	files = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "plugins"));
 	plugins = "";
-	files.forEach(x => { if (x.endsWith(".js")) plugins += `<script src='/assets/plugins/${x}'></script>\n`; });
+	files.forEach((x) => {
+		if (x.endsWith(".js"))
+			plugins += `<script src='/assets/plugins/${x}'></script>\n`;
+	});
 	html = html.replaceAll("<!-- plugin marker -->", plugins);
 	//preload plugins
 	files = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "preload-plugins"));
 	plugins = "";
-	files.forEach(x => { if (x.endsWith(".js")) plugins += `<script>${fs.readFileSync(path.join(ASSET_FOLDER_PATH, "preload-plugins", x))}</script>\n`; });
+	files.forEach((x) => {
+		if (x.endsWith(".js"))
+			plugins += `<script>${fs.readFileSync(
+				path.join(ASSET_FOLDER_PATH, "preload-plugins", x),
+			)}</script>\n`;
+	});
 	html = html.replaceAll("<!-- preload plugin marker -->", plugins);
 
-
 	app.use("/assets", express.static(path.join(ASSET_FOLDER_PATH, "public")));
 	app.use("/assets", express.static(path.join(ASSET_FOLDER_PATH, "cache")));
 
@@ -52,7 +79,9 @@ export default function TestClient(app: Application) {
 		if (!hasWarnedAboutCache) {
 			hasWarnedAboutCache = true;
 			if (req.params.file.includes(".js"))
-				console.warn(`[TestClient] Cache miss for file ${req.params.file}! Use 'npm run generate:client' to cache and patch.`);
+				console.warn(
+					`[TestClient] Cache miss for file ${req.params.file}! Use 'npm run generate:client' to cache and patch.`,
+				);
 		}
 
 		delete req.headers.host;
@@ -60,13 +89,16 @@ export default function TestClient(app: Application) {
 		var buffer: Buffer;
 		const cache = assetCache.get(req.params.file);
 		if (!cache) {
-			response = await fetch(`https://discord.com/assets/${req.params.file}`, {
-				agent,
-				// @ts-ignore
-				headers: {
-					...req.headers
-				}
-			});
+			response = await fetch(
+				`https://discord.com/assets/${req.params.file}`,
+				{
+					agent,
+					// @ts-ignore
+					headers: {
+						...req.headers,
+					},
+				},
+			);
 			buffer = await response.buffer();
 		} else {
 			response = cache.response;
@@ -83,7 +115,7 @@ export default function TestClient(app: Application) {
 					"transfer-encoding",
 					"expect-ct",
 					"access-control-allow-origin",
-					"content-encoding"
+					"content-encoding",
 				].includes(name.toLowerCase())
 			) {
 				return;
@@ -99,20 +131,35 @@ export default function TestClient(app: Application) {
 		res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
 		res.set("content-type", "text/html");
 
-		if (!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance.");
-
-		res.send(fs.readFileSync(path.join(ASSET_FOLDER_PATH, "client_test", "developers.html"), { encoding: "utf8" }));
+		if (!useTestClient)
+			return res.send(
+				"Test client is disabled on this instance. Use a stand-alone client to connect this instance.",
+			);
+
+		res.send(
+			fs.readFileSync(
+				path.join(ASSET_FOLDER_PATH, "client_test", "developers.html"),
+				{ encoding: "utf8" },
+			),
+		);
 	});
 	app.get("*", (req: Request, res: Response) => {
 		const { useTestClient } = Config.get().client;
 		res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
 		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;
 
-		if (!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance.");
-		if (req.url.startsWith("/invite")) return res.send(html.replace("9b2b7f0632acd0c5e781", "9f24f709a3de09b67c49"));
+		if (!useTestClient)
+			return res.send(
+				"Test client is disabled on this instance. Use a stand-alone client to connect this instance.",
+			);
+		if (req.url.startsWith("/invite"))
+			return res.send(
+				html.replace("9b2b7f0632acd0c5e781", "9f24f709a3de09b67c49"),
+			);
 
 		res.send(html);
 	});
-}
\ No newline at end of file
+}
diff --git a/src/api/middlewares/Translation.ts b/src/api/middlewares/Translation.ts
index c0b7a4b8..05038040 100644
--- a/src/api/middlewares/Translation.ts
+++ b/src/api/middlewares/Translation.ts
@@ -9,8 +9,12 @@ const ASSET_FOLDER_PATH = path.join(__dirname, "..", "..", "..", "assets");
 
 export async function initTranslation(router: Router) {
 	const languages = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "locales"));
-	const namespaces = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "locales", "en"));
-	const ns = namespaces.filter((x) => x.endsWith(".json")).map((x) => x.slice(0, x.length - 5));
+	const namespaces = fs.readdirSync(
+		path.join(ASSET_FOLDER_PATH, "locales", "en"),
+	);
+	const ns = namespaces
+		.filter((x) => x.endsWith(".json"))
+		.map((x) => x.slice(0, x.length - 5));
 
 	await i18next
 		.use(i18nextBackend)
@@ -21,9 +25,11 @@ export async function initTranslation(router: Router) {
 			fallbackLng: "en",
 			ns,
 			backend: {
-				loadPath:  path.join(ASSET_FOLDER_PATH, "locales") + "/{{lng}}/{{ns}}.json",
+				loadPath:
+					path.join(ASSET_FOLDER_PATH, "locales") +
+					"/{{lng}}/{{ns}}.json",
 			},
-			load: "all"
+			load: "all",
 		});
 
 	router.use(i18nextMiddleware.handle(i18next, {}));
diff --git a/src/api/routes/-/monitorz.ts b/src/api/routes/-/monitorz.ts
index f85cd099..630a832b 100644
--- a/src/api/routes/-/monitorz.ts
+++ b/src/api/routes/-/monitorz.ts
@@ -5,14 +5,18 @@ import os from "os";
 
 const router = Router();
 
-router.get("/", route({ right: "OPERATOR" }), async (req: Request, res: Response) => {
-	return res.json({
-		load: os.loadavg(),
-		procUptime: process.uptime(),
-		sysUptime: os.uptime(),
-		memPercent: 100 - ((os.freemem() / os.totalmem()) * 100),
-		sessions: await Session.count(),
-	})
-})
+router.get(
+	"/",
+	route({ right: "OPERATOR" }),
+	async (req: Request, res: Response) => {
+		return res.json({
+			load: os.loadavg(),
+			procUptime: process.uptime(),
+			sysUptime: os.uptime(),
+			memPercent: 100 - (os.freemem() / os.totalmem()) * 100,
+			sessions: await Session.count(),
+		});
+	},
+);
 
-export default router;
\ No newline at end of file
+export default router;
diff --git a/src/api/routes/auth/location-metadata.ts b/src/api/routes/auth/location-metadata.ts
index f4c2bd16..0ae946ed 100644
--- a/src/api/routes/auth/location-metadata.ts
+++ b/src/api/routes/auth/location-metadata.ts
@@ -3,11 +3,15 @@ import { route } from "@fosscord/api";
 import { getIpAdress, IPAnalysis } from "@fosscord/api";
 const router = Router();
 
-router.get("/",route({}), async (req: Request, res: Response) => {
-    //TODO
-    //Note: It's most likely related to legal. At the moment Discord hasn't finished this too
-    const country_code = (await IPAnalysis(getIpAdress(req))).country_code;
-	res.json({ consent_required: false, country_code: country_code, promotional_email_opt_in: { required: true, pre_checked: false}});
+router.get("/", route({}), async (req: Request, res: Response) => {
+	//TODO
+	//Note: It's most likely related to legal. At the moment Discord hasn't finished this too
+	const country_code = (await IPAnalysis(getIpAdress(req))).country_code;
+	res.json({
+		consent_required: false,
+		country_code: country_code,
+		promotional_email_opt_in: { required: true, pre_checked: false },
+	});
 });
 
 export default router;
diff --git a/src/api/routes/auth/login.ts b/src/api/routes/auth/login.ts
index 9bed5aab..9ea2606c 100644
--- a/src/api/routes/auth/login.ts
+++ b/src/api/routes/auth/login.ts
@@ -1,84 +1,127 @@
 import { Request, Response, Router } from "express";
 import { route, getIpAdress, verifyCaptcha } from "@fosscord/api";
 import bcrypt from "bcrypt";
-import { Config, User, generateToken, adjustEmail, FieldErrors, LoginSchema } from "@fosscord/util";
+import {
+	Config,
+	User,
+	generateToken,
+	adjustEmail,
+	FieldErrors,
+	LoginSchema,
+} from "@fosscord/util";
 import crypto from "crypto";
 
 const router: Router = Router();
 export default router;
 
-router.post("/", route({ body: "LoginSchema" }), async (req: Request, res: Response) => {
-	const { login, password, captcha_key, undelete } = req.body as LoginSchema;
-	const email = adjustEmail(login);
-	console.log("login", email);
+router.post(
+	"/",
+	route({ body: "LoginSchema" }),
+	async (req: Request, res: Response) => {
+		const { login, password, captcha_key, undelete } =
+			req.body as LoginSchema;
+		const email = adjustEmail(login);
+		console.log("login", email);
+
+		const config = Config.get();
+
+		if (config.login.requireCaptcha && config.security.captcha.enabled) {
+			const { sitekey, service } = config.security.captcha;
+			if (!captcha_key) {
+				return res.status(400).json({
+					captcha_key: ["captcha-required"],
+					captcha_sitekey: sitekey,
+					captcha_service: service,
+				});
+			}
+
+			const ip = getIpAdress(req);
+			const verify = await verifyCaptcha(captcha_key, ip);
+			if (!verify.success) {
+				return res.status(400).json({
+					captcha_key: verify["error-codes"],
+					captcha_sitekey: sitekey,
+					captcha_service: service,
+				});
+			}
+		}
 
-	const config = Config.get();
+		const user = await User.findOneOrFail({
+			where: [{ phone: login }, { email: login }],
+			select: [
+				"data",
+				"id",
+				"disabled",
+				"deleted",
+				"settings",
+				"totp_secret",
+				"mfa_enabled",
+			],
+		}).catch((e) => {
+			throw FieldErrors({
+				login: {
+					message: req.t("auth:login.INVALID_LOGIN"),
+					code: "INVALID_LOGIN",
+				},
+			});
+		});
+
+		if (undelete) {
+			// undelete refers to un'disable' here
+			if (user.disabled)
+				await User.update({ id: user.id }, { disabled: false });
+			if (user.deleted)
+				await User.update({ id: user.id }, { deleted: false });
+		} else {
+			if (user.deleted)
+				return res.status(400).json({
+					message: "This account is scheduled for deletion.",
+					code: 20011,
+				});
+			if (user.disabled)
+				return res.status(400).json({
+					message: req.t("auth:login.ACCOUNT_DISABLED"),
+					code: 20013,
+				});
+		}
 
-	if (config.login.requireCaptcha && config.security.captcha.enabled) {
-		const { sitekey, service } = config.security.captcha;
-		if (!captcha_key) {
-			return res.status(400).json({
-				captcha_key: ["captcha-required"],
-				captcha_sitekey: sitekey,
-				captcha_service: service
+		// the salt is saved in the password refer to bcrypt docs
+		const same_password = await bcrypt.compare(
+			password,
+			user.data.hash || "",
+		);
+		if (!same_password) {
+			throw FieldErrors({
+				password: {
+					message: req.t("auth:login.INVALID_PASSWORD"),
+					code: "INVALID_PASSWORD",
+				},
 			});
 		}
 
-		const ip = getIpAdress(req);
-		const verify = await verifyCaptcha(captcha_key, ip);
-		if (!verify.success) {
-			return res.status(400).json({
-				captcha_key: verify["error-codes"],
-				captcha_sitekey: sitekey,
-				captcha_service: service
+		if (user.mfa_enabled) {
+			// TODO: This is not a discord.com ticket. I'm not sure what it is but I'm lazy
+			const ticket = crypto.randomBytes(40).toString("hex");
+
+			await User.update({ id: user.id }, { totp_last_ticket: ticket });
+
+			return res.json({
+				ticket: ticket,
+				mfa: true,
+				sms: false, // TODO
+				token: null,
 			});
 		}
-	}
-
-	const user = await User.findOneOrFail({
-		where: [{ phone: login }, { email: login }],
-		select: ["data", "id", "disabled", "deleted", "settings", "totp_secret", "mfa_enabled"]
-	}).catch((e) => {
-		throw FieldErrors({ login: { message: req.t("auth:login.INVALID_LOGIN"), code: "INVALID_LOGIN" } });
-	});
-
-	if (undelete) {
-		// undelete refers to un'disable' here
-		if (user.disabled) await User.update({ id: user.id }, { disabled: false });
-		if (user.deleted) await User.update({ id: user.id }, { deleted: false });
-	} else {
-		if (user.deleted) return res.status(400).json({ message: "This account is scheduled for deletion.", code: 20011 });
-		if (user.disabled) return res.status(400).json({ message: req.t("auth:login.ACCOUNT_DISABLED"), code: 20013 });
-	}
-
-	// the salt is saved in the password refer to bcrypt docs
-	const same_password = await bcrypt.compare(password, user.data.hash || "");
-	if (!same_password) {
-		throw FieldErrors({ password: { message: req.t("auth:login.INVALID_PASSWORD"), code: "INVALID_PASSWORD" } });
-	}
-
-	if (user.mfa_enabled) {
-		// TODO: This is not a discord.com ticket. I'm not sure what it is but I'm lazy
-		const ticket = crypto.randomBytes(40).toString("hex");
-
-		await User.update({ id: user.id }, { totp_last_ticket: ticket });
-
-		return res.json({
-			ticket: ticket,
-			mfa: true,
-			sms: false,	// TODO
-			token: null,
-		})
-	}
-
-	const token = await generateToken(user.id);
-
-	// Notice this will have a different token structure, than discord
-	// Discord header is just the user id as string, which is not possible with npm-jsonwebtoken package
-	// https://user-images.githubusercontent.com/6506416/81051916-dd8c9900-8ec2-11ea-8794-daf12d6f31f0.png
-
-	res.json({ token, settings: user.settings });
-});
+
+		const token = await generateToken(user.id);
+
+		// Notice this will have a different token structure, than discord
+		// Discord header is just the user id as string, which is not possible with npm-jsonwebtoken package
+		// https://user-images.githubusercontent.com/6506416/81051916-dd8c9900-8ec2-11ea-8794-daf12d6f31f0.png
+
+		res.json({ token, settings: user.settings });
+	},
+);
 
 /**
  * POST /auth/login
diff --git a/src/api/routes/auth/logout.ts b/src/api/routes/auth/logout.ts
index e806fed9..e1bdbea3 100644
--- a/src/api/routes/auth/logout.ts
+++ b/src/api/routes/auth/logout.ts
@@ -10,7 +10,8 @@ router.post("/", route({}), async (req: Request, res: Response) => {
 	} else {
 		delete req.body.provider;
 		delete req.body.voip_provider;
-		if (Object.keys(req.body).length != 0) console.log(`[LOGOUT]: Extra fields sent in logout!`, req.body);
+		if (Object.keys(req.body).length != 0)
+			console.log(`[LOGOUT]: Extra fields sent in logout!`, req.body);
 	}
 	res.status(204).send();
-});
\ No newline at end of file
+});
diff --git a/src/api/routes/auth/mfa/totp.ts b/src/api/routes/auth/mfa/totp.ts
index 96a48b66..83cf7648 100644
--- a/src/api/routes/auth/mfa/totp.ts
+++ b/src/api/routes/auth/mfa/totp.ts
@@ -5,45 +5,48 @@ import { verifyToken } from "node-2fa";
 import { HTTPError } from "lambert-server";
 const router = Router();
 
-router.post("/", route({ body: "TotpSchema" }), async (req: Request, res: Response) => {
-	const { code, ticket, gift_code_sku_id, login_source } = req.body as TotpSchema;
+router.post(
+	"/",
+	route({ body: "TotpSchema" }),
+	async (req: Request, res: Response) => {
+		const { code, ticket, gift_code_sku_id, login_source } =
+			req.body as TotpSchema;
 
-	const user = await User.findOneOrFail({
-		where: {
-			totp_last_ticket: ticket,
-		},
-		select: [
-			"id",
-			"totp_secret",
-			"settings",
-		],
-	});
+		const user = await User.findOneOrFail({
+			where: {
+				totp_last_ticket: ticket,
+			},
+			select: ["id", "totp_secret", "settings"],
+		});
 
-	const backup = await BackupCode.findOne({
-		where: {
-			code: code,
-			expired: false,
-			consumed: false,
-			user: { id: user.id }
-		}
-	});
+		const backup = await BackupCode.findOne({
+			where: {
+				code: code,
+				expired: false,
+				consumed: false,
+				user: { id: user.id },
+			},
+		});
 
-	if (!backup) {
-		const ret = verifyToken(user.totp_secret!, code);
-		if (!ret || ret.delta != 0)
-			throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
-	}
-	else {
-		backup.consumed = true;
-		await backup.save();
-	}
+		if (!backup) {
+			const ret = verifyToken(user.totp_secret!, code);
+			if (!ret || ret.delta != 0)
+				throw new HTTPError(
+					req.t("auth:login.INVALID_TOTP_CODE"),
+					60008,
+				);
+		} else {
+			backup.consumed = true;
+			await backup.save();
+		}
 
-	await User.update({ id: user.id }, { totp_last_ticket: "" });
+		await User.update({ id: user.id }, { totp_last_ticket: "" });
 
-	return res.json({
-		token: await generateToken(user.id),
-		user_settings: user.settings,
-	});
-});
+		return res.json({
+			token: await generateToken(user.id),
+			user_settings: user.settings,
+		});
+	},
+);
 
 export default router;
diff --git a/src/api/routes/auth/register.ts b/src/api/routes/auth/register.ts
index 84f8f838..3479c4a0 100644
--- a/src/api/routes/auth/register.ts
+++ b/src/api/routes/auth/register.ts
@@ -1,156 +1,215 @@
 import { Request, Response, Router } from "express";
-import { Config, generateToken, Invite, FieldErrors, User, adjustEmail, RegisterSchema } from "@fosscord/util";
-import { route, getIpAdress, IPAnalysis, isProxy, verifyCaptcha } from "@fosscord/api";
+import {
+	Config,
+	generateToken,
+	Invite,
+	FieldErrors,
+	User,
+	adjustEmail,
+	RegisterSchema,
+} from "@fosscord/util";
+import {
+	route,
+	getIpAdress,
+	IPAnalysis,
+	isProxy,
+	verifyCaptcha,
+} from "@fosscord/api";
 import bcrypt from "bcrypt";
 import { HTTPError } from "lambert-server";
 
 const router: Router = Router();
 
-router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Response) => {
-	const body = req.body as RegisterSchema;
-	const { register, security } = Config.get();
-	const ip = getIpAdress(req);
-
-	// email will be slightly modified version of the user supplied email -> e.g. protection against GMail Trick
-	let email = adjustEmail(body.email);
-
-	// check if registration is allowed
-	if (!register.allowNewRegistration) {
-		throw FieldErrors({
-			email: { code: "REGISTRATION_DISABLED", message: req.t("auth:register.REGISTRATION_DISABLED") }
-		});
-	}
-
-	// check if the user agreed to the Terms of Service
-	if (!body.consent) {
-		throw FieldErrors({
-			consent: { code: "CONSENT_REQUIRED", message: req.t("auth:register.CONSENT_REQUIRED") }
-		});
-	}
-
-	if (register.disabled) {
-		throw FieldErrors({
-			email: {
-				code: "DISABLED",
-				message: "registration is disabled on this instance"
-			}
-		});
-	}
-
-	if (register.requireCaptcha && security.captcha.enabled) {
-		const { sitekey, service } = security.captcha;
-		if (!body.captcha_key) {
-			return res?.status(400).json({
-				captcha_key: ["captcha-required"],
-				captcha_sitekey: sitekey,
-				captcha_service: service
+router.post(
+	"/",
+	route({ body: "RegisterSchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as RegisterSchema;
+		const { register, security } = Config.get();
+		const ip = getIpAdress(req);
+
+		// email will be slightly modified version of the user supplied email -> e.g. protection against GMail Trick
+		let email = adjustEmail(body.email);
+
+		// check if registration is allowed
+		if (!register.allowNewRegistration) {
+			throw FieldErrors({
+				email: {
+					code: "REGISTRATION_DISABLED",
+					message: req.t("auth:register.REGISTRATION_DISABLED"),
+				},
 			});
 		}
 
-		const verify = await verifyCaptcha(body.captcha_key, ip);
-		if (!verify.success) {
-			return res.status(400).json({
-				captcha_key: verify["error-codes"],
-				captcha_sitekey: sitekey,
-				captcha_service: service
+		// check if the user agreed to the Terms of Service
+		if (!body.consent) {
+			throw FieldErrors({
+				consent: {
+					code: "CONSENT_REQUIRED",
+					message: req.t("auth:register.CONSENT_REQUIRED"),
+				},
 			});
 		}
-	}
-
-	if (!register.allowMultipleAccounts) {
-		// TODO: check if fingerprint was eligible generated
-		const exists = await User.findOne({ where: { fingerprints: body.fingerprint }, select: ["id"] });
 
-		if (exists) {
+		if (register.disabled) {
 			throw FieldErrors({
 				email: {
-					code: "EMAIL_ALREADY_REGISTERED",
-					message: req.t("auth:register.EMAIL_ALREADY_REGISTERED")
-				}
+					code: "DISABLED",
+					message: "registration is disabled on this instance",
+				},
 			});
 		}
-	}
 
-	if (register.blockProxies) {
-		if (isProxy(await IPAnalysis(ip))) {
-			console.log(`proxy ${ip} blocked from registration`);
-			throw new HTTPError("Your IP is blocked from registration");
+		if (register.requireCaptcha && security.captcha.enabled) {
+			const { sitekey, service } = security.captcha;
+			if (!body.captcha_key) {
+				return res?.status(400).json({
+					captcha_key: ["captcha-required"],
+					captcha_sitekey: sitekey,
+					captcha_service: service,
+				});
+			}
+
+			const verify = await verifyCaptcha(body.captcha_key, ip);
+			if (!verify.success) {
+				return res.status(400).json({
+					captcha_key: verify["error-codes"],
+					captcha_sitekey: sitekey,
+					captcha_service: service,
+				});
+			}
 		}
-	}
 
-	// TODO: gift_code_sku_id?
-	// TODO: check password strength
+		if (!register.allowMultipleAccounts) {
+			// TODO: check if fingerprint was eligible generated
+			const exists = await User.findOne({
+				where: { fingerprints: body.fingerprint },
+				select: ["id"],
+			});
+
+			if (exists) {
+				throw FieldErrors({
+					email: {
+						code: "EMAIL_ALREADY_REGISTERED",
+						message: req.t(
+							"auth:register.EMAIL_ALREADY_REGISTERED",
+						),
+					},
+				});
+			}
+		}
 
-	if (email) {
-		// replace all dots and chars after +, if its a gmail.com email
-		if (!email) {
-			throw FieldErrors({ email: { code: "INVALID_EMAIL", message: req?.t("auth:register.INVALID_EMAIL") } });
+		if (register.blockProxies) {
+			if (isProxy(await IPAnalysis(ip))) {
+				console.log(`proxy ${ip} blocked from registration`);
+				throw new HTTPError("Your IP is blocked from registration");
+			}
 		}
 
-		// check if there is already an account with this email
-		const exists = await User.findOne({ where: { email: email } });
+		// TODO: gift_code_sku_id?
+		// TODO: check password strength
+
+		if (email) {
+			// replace all dots and chars after +, if its a gmail.com email
+			if (!email) {
+				throw FieldErrors({
+					email: {
+						code: "INVALID_EMAIL",
+						message: req?.t("auth:register.INVALID_EMAIL"),
+					},
+				});
+			}
 
-		if (exists) {
+			// check if there is already an account with this email
+			const exists = await User.findOne({ where: { email: email } });
+
+			if (exists) {
+				throw FieldErrors({
+					email: {
+						code: "EMAIL_ALREADY_REGISTERED",
+						message: req.t(
+							"auth:register.EMAIL_ALREADY_REGISTERED",
+						),
+					},
+				});
+			}
+		} else if (register.email.required) {
 			throw FieldErrors({
 				email: {
-					code: "EMAIL_ALREADY_REGISTERED",
-					message: req.t("auth:register.EMAIL_ALREADY_REGISTERED")
-				}
+					code: "BASE_TYPE_REQUIRED",
+					message: req.t("common:field.BASE_TYPE_REQUIRED"),
+				},
 			});
 		}
-	} else if (register.email.required) {
-		throw FieldErrors({
-			email: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") }
-		});
-	}
-
-	if (register.dateOfBirth.required && !body.date_of_birth) {
-		throw FieldErrors({
-			date_of_birth: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") }
-		});
-	} else if (register.dateOfBirth.required && register.dateOfBirth.minimum) {
-		const minimum = new Date();
-		minimum.setFullYear(minimum.getFullYear() - register.dateOfBirth.minimum);
-		body.date_of_birth = new Date(body.date_of_birth as Date);
-
-		// higher is younger
-		if (body.date_of_birth > minimum) {
+
+		if (register.dateOfBirth.required && !body.date_of_birth) {
 			throw FieldErrors({
 				date_of_birth: {
-					code: "DATE_OF_BIRTH_UNDERAGE",
-					message: req.t("auth:register.DATE_OF_BIRTH_UNDERAGE", { years: register.dateOfBirth.minimum })
-				}
+					code: "BASE_TYPE_REQUIRED",
+					message: req.t("common:field.BASE_TYPE_REQUIRED"),
+				},
+			});
+		} else if (
+			register.dateOfBirth.required &&
+			register.dateOfBirth.minimum
+		) {
+			const minimum = new Date();
+			minimum.setFullYear(
+				minimum.getFullYear() - register.dateOfBirth.minimum,
+			);
+			body.date_of_birth = new Date(body.date_of_birth as Date);
+
+			// higher is younger
+			if (body.date_of_birth > minimum) {
+				throw FieldErrors({
+					date_of_birth: {
+						code: "DATE_OF_BIRTH_UNDERAGE",
+						message: req.t("auth:register.DATE_OF_BIRTH_UNDERAGE", {
+							years: register.dateOfBirth.minimum,
+						}),
+					},
+				});
+			}
+		}
+
+		if (body.password) {
+			// the salt is saved in the password refer to bcrypt docs
+			body.password = await bcrypt.hash(body.password, 12);
+		} else if (register.password.required) {
+			throw FieldErrors({
+				password: {
+					code: "BASE_TYPE_REQUIRED",
+					message: req.t("common:field.BASE_TYPE_REQUIRED"),
+				},
+			});
+		}
+
+		if (
+			!body.invite &&
+			(register.requireInvite ||
+				(register.guestsRequireInvite && !register.email))
+		) {
+			// require invite to register -> e.g. for organizations to send invites to their employees
+			throw FieldErrors({
+				email: {
+					code: "INVITE_ONLY",
+					message: req.t("auth:register.INVITE_ONLY"),
+				},
 			});
 		}
-	}
-
-	if (body.password) {
-		// the salt is saved in the password refer to bcrypt docs
-		body.password = await bcrypt.hash(body.password, 12);
-	} else if (register.password.required) {
-		throw FieldErrors({
-			password: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") }
-		});
-	}
-
-	if (!body.invite && (register.requireInvite || (register.guestsRequireInvite && !register.email))) {
-		// require invite to register -> e.g. for organizations to send invites to their employees
-		throw FieldErrors({
-			email: { code: "INVITE_ONLY", message: req.t("auth:register.INVITE_ONLY") }
-		});
-	}
-
-	const user = await User.register({ ...body, req });
-
-	if (body.invite) {
-		// await to fail if the invite doesn't exist (necessary for requireInvite to work properly) (username only signups are possible)
-		await Invite.joinGuild(user.id, body.invite);
-	}
-
-	console.log("register", body.email, body.username, ip);
-
-	return res.json({ token: await generateToken(user.id) });
-});
+
+		const user = await User.register({ ...body, req });
+
+		if (body.invite) {
+			// await to fail if the invite doesn't exist (necessary for requireInvite to work properly) (username only signups are possible)
+			await Invite.joinGuild(user.id, body.invite);
+		}
+
+		console.log("register", body.email, body.username, ip);
+
+		return res.json({ token: await generateToken(user.id) });
+	},
+);
 
 export default router;
 
diff --git a/src/api/routes/auth/verify/view-backup-codes-challenge.ts b/src/api/routes/auth/verify/view-backup-codes-challenge.ts
index 24de8ec5..65f0a57c 100644
--- a/src/api/routes/auth/verify/view-backup-codes-challenge.ts
+++ b/src/api/routes/auth/verify/view-backup-codes-challenge.ts
@@ -4,19 +4,31 @@ import { FieldErrors, User, BackupCodesChallengeSchema } from "@fosscord/util";
 import bcrypt from "bcrypt";
 const router = Router();
 
-router.post("/", route({ body: "BackupCodesChallengeSchema" }), async (req: Request, res: Response) => {
-	const { password } = req.body as BackupCodesChallengeSchema;
+router.post(
+	"/",
+	route({ body: "BackupCodesChallengeSchema" }),
+	async (req: Request, res: Response) => {
+		const { password } = req.body as BackupCodesChallengeSchema;
 
-	const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["data"] });
+		const user = await User.findOneOrFail({
+			where: { id: req.user_id },
+			select: ["data"],
+		});
 
-	if (!await bcrypt.compare(password, user.data.hash || "")) {
-		throw FieldErrors({ password: { message: req.t("auth:login.INVALID_PASSWORD"), code: "INVALID_PASSWORD" } });
-	}
+		if (!(await bcrypt.compare(password, user.data.hash || ""))) {
+			throw FieldErrors({
+				password: {
+					message: req.t("auth:login.INVALID_PASSWORD"),
+					code: "INVALID_PASSWORD",
+				},
+			});
+		}
 
-	return res.json({
-		nonce: "NoncePlaceholder",
-		regenerate_nonce: "RegenNoncePlaceholder",
-	});
-});
+		return res.json({
+			nonce: "NoncePlaceholder",
+			regenerate_nonce: "RegenNoncePlaceholder",
+		});
+	},
+);
 
 export default router;
diff --git a/src/api/routes/channels/#channel_id/index.ts b/src/api/routes/channels/#channel_id/index.ts
index 8dbefe1b..a164fff6 100644
--- a/src/api/routes/channels/#channel_id/index.ts
+++ b/src/api/routes/channels/#channel_id/index.ts
@@ -6,7 +6,7 @@ import {
 	emitEvent,
 	Recipient,
 	handleFile,
-	ChannelModifySchema
+	ChannelModifySchema,
 } from "@fosscord/util";
 import { Request, Response, Router } from "express";
 import { route } from "@fosscord/api";
@@ -15,56 +15,89 @@ const router: Router = Router();
 // TODO: delete channel
 // TODO: Get channel
 
-router.get("/", route({ permission: "VIEW_CHANNEL" }), async (req: Request, res: Response) => {
-	const { channel_id } = req.params;
+router.get(
+	"/",
+	route({ permission: "VIEW_CHANNEL" }),
+	async (req: Request, res: Response) => {
+		const { channel_id } = req.params;
 
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
 
-	return res.send(channel);
-});
+		return res.send(channel);
+	},
+);
 
-router.delete("/", route({ permission: "MANAGE_CHANNELS" }), async (req: Request, res: Response) => {
-	const { channel_id } = req.params;
+router.delete(
+	"/",
+	route({ permission: "MANAGE_CHANNELS" }),
+	async (req: Request, res: Response) => {
+		const { channel_id } = req.params;
 
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients"] });
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+			relations: ["recipients"],
+		});
 
-	if (channel.type === ChannelType.DM) {
-		const recipient = await Recipient.findOneOrFail({ where: { channel_id: channel_id, user_id: req.user_id } });
-		recipient.closed = true;
-		await Promise.all([
-			recipient.save(),
-			emitEvent({ event: "CHANNEL_DELETE", data: channel, user_id: req.user_id } as ChannelDeleteEvent)
-		]);
-	} else if (channel.type === ChannelType.GROUP_DM) {
-		await Channel.removeRecipientFromChannel(channel, req.user_id);
-	} else {
-		await Promise.all([
-			Channel.delete({ id: channel_id }),
-			emitEvent({ event: "CHANNEL_DELETE", data: channel, channel_id } as ChannelDeleteEvent)
-		]);
-	}
+		if (channel.type === ChannelType.DM) {
+			const recipient = await Recipient.findOneOrFail({
+				where: { channel_id: channel_id, user_id: req.user_id },
+			});
+			recipient.closed = true;
+			await Promise.all([
+				recipient.save(),
+				emitEvent({
+					event: "CHANNEL_DELETE",
+					data: channel,
+					user_id: req.user_id,
+				} as ChannelDeleteEvent),
+			]);
+		} else if (channel.type === ChannelType.GROUP_DM) {
+			await Channel.removeRecipientFromChannel(channel, req.user_id);
+		} else {
+			await Promise.all([
+				Channel.delete({ id: channel_id }),
+				emitEvent({
+					event: "CHANNEL_DELETE",
+					data: channel,
+					channel_id,
+				} as ChannelDeleteEvent),
+			]);
+		}
 
-	res.send(channel);
-});
+		res.send(channel);
+	},
+);
 
-router.patch("/", route({ body: "ChannelModifySchema", permission: "MANAGE_CHANNELS" }), async (req: Request, res: Response) => {
-	var payload = req.body as ChannelModifySchema;
-	const { channel_id } = req.params;
-	if (payload.icon) payload.icon = await handleFile(`/channel-icons/${channel_id}`, payload.icon);
+router.patch(
+	"/",
+	route({ body: "ChannelModifySchema", permission: "MANAGE_CHANNELS" }),
+	async (req: Request, res: Response) => {
+		var payload = req.body as ChannelModifySchema;
+		const { channel_id } = req.params;
+		if (payload.icon)
+			payload.icon = await handleFile(
+				`/channel-icons/${channel_id}`,
+				payload.icon,
+			);
 
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
-	channel.assign(payload);
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
+		channel.assign(payload);
 
-	await Promise.all([
-		channel.save(),
-		emitEvent({
-			event: "CHANNEL_UPDATE",
-			data: channel,
-			channel_id
-		} as ChannelUpdateEvent)
-	]);
+		await Promise.all([
+			channel.save(),
+			emitEvent({
+				event: "CHANNEL_UPDATE",
+				data: channel,
+				channel_id,
+			} as ChannelUpdateEvent),
+		]);
 
-	res.send(channel);
-});
+		res.send(channel);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/channels/#channel_id/invites.ts b/src/api/routes/channels/#channel_id/invites.ts
index 246a2c69..afaabf47 100644
--- a/src/api/routes/channels/#channel_id/invites.ts
+++ b/src/api/routes/channels/#channel_id/invites.ts
@@ -2,16 +2,33 @@ import { Router, Request, Response } from "express";
 import { HTTPError } from "lambert-server";
 import { route } from "@fosscord/api";
 import { random } from "@fosscord/api";
-import { Channel, Invite, InviteCreateEvent, emitEvent, User, Guild, PublicInviteRelation } from "@fosscord/util";
+import {
+	Channel,
+	Invite,
+	InviteCreateEvent,
+	emitEvent,
+	User,
+	Guild,
+	PublicInviteRelation,
+} from "@fosscord/util";
 import { isTextChannel } from "./messages";
 
 const router: Router = Router();
 
-router.post("/", route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT_INVITE", right: "CREATE_INVITES" }),
+router.post(
+	"/",
+	route({
+		body: "InviteCreateSchema",
+		permission: "CREATE_INSTANT_INVITE",
+		right: "CREATE_INVITES",
+	}),
 	async (req: Request, res: Response) => {
 		const { user_id } = req;
 		const { channel_id } = req.params;
-		const channel = await Channel.findOneOrFail({ where: { id: channel_id }, select: ["id", "name", "type", "guild_id"] });
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+			select: ["id", "name", "type", "guild_id"],
+		});
 		isTextChannel(channel.type);
 
 		if (!channel.guild_id) {
@@ -31,30 +48,44 @@ router.post("/", route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT
 			created_at: new Date(),
 			guild_id,
 			channel_id: channel_id,
-			inviter_id: user_id
+			inviter_id: user_id,
 		}).save();
 		const data = invite.toJSON();
 		data.inviter = await User.getPublicUser(req.user_id);
 		data.guild = await Guild.findOne({ where: { id: guild_id } });
 		data.channel = channel;
 
-		await emitEvent({ event: "INVITE_CREATE", data, guild_id } as InviteCreateEvent);
+		await emitEvent({
+			event: "INVITE_CREATE",
+			data,
+			guild_id,
+		} as InviteCreateEvent);
 		res.status(201).send(data);
-	});
+	},
+);
 
-router.get("/", route({ permission: "MANAGE_CHANNELS" }), async (req: Request, res: Response) => {
-	const { user_id } = req;
-	const { channel_id } = req.params;
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
+router.get(
+	"/",
+	route({ permission: "MANAGE_CHANNELS" }),
+	async (req: Request, res: Response) => {
+		const { user_id } = req;
+		const { channel_id } = req.params;
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
 
-	if (!channel.guild_id) {
-		throw new HTTPError("This channel doesn't exist", 404);
-	}
-	const { guild_id } = channel;
+		if (!channel.guild_id) {
+			throw new HTTPError("This channel doesn't exist", 404);
+		}
+		const { guild_id } = channel;
 
-	const invites = await Invite.find({ where: { guild_id }, relations: PublicInviteRelation });
+		const invites = await Invite.find({
+			where: { guild_id },
+			relations: PublicInviteRelation,
+		});
 
-	res.status(200).send(invites);
-});
+		res.status(200).send(invites);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/ack.ts b/src/api/routes/channels/#channel_id/messages/#message_id/ack.ts
index bedd453c..1a30143f 100644
--- a/src/api/routes/channels/#channel_id/messages/#message_id/ack.ts
+++ b/src/api/routes/channels/#channel_id/messages/#message_id/ack.ts
@@ -1,4 +1,9 @@
-import { emitEvent, getPermission, MessageAckEvent, ReadState } from "@fosscord/util";
+import {
+	emitEvent,
+	getPermission,
+	MessageAckEvent,
+	ReadState,
+} from "@fosscord/util";
 import { Request, Response, Router } from "express";
 import { route } from "@fosscord/api";
 
@@ -8,29 +13,40 @@ const router = Router();
 // TODO: send read state event to all channel members
 // TODO: advance-only notification cursor
 
-router.post("/", route({ body: "MessageAcknowledgeSchema" }), async (req: Request, res: Response) => {
-	const { channel_id, message_id } = req.params;
+router.post(
+	"/",
+	route({ body: "MessageAcknowledgeSchema" }),
+	async (req: Request, res: Response) => {
+		const { channel_id, message_id } = req.params;
 
-	const permission = await getPermission(req.user_id, undefined, channel_id);
-	permission.hasThrow("VIEW_CHANNEL");
-
-	let read_state = await ReadState.findOne({ where: { user_id: req.user_id, channel_id } });
-	if (!read_state) read_state = ReadState.create({ user_id: req.user_id, channel_id });
-	read_state.last_message_id = message_id;
-
-	await read_state.save();
-
-	await emitEvent({
-		event: "MESSAGE_ACK",
-		user_id: req.user_id,
-		data: {
+		const permission = await getPermission(
+			req.user_id,
+			undefined,
 			channel_id,
-			message_id,
-			version: 3763
-		}
-	} as MessageAckEvent);
-
-	res.json({ token: null });
-});
+		);
+		permission.hasThrow("VIEW_CHANNEL");
+
+		let read_state = await ReadState.findOne({
+			where: { user_id: req.user_id, channel_id },
+		});
+		if (!read_state)
+			read_state = ReadState.create({ user_id: req.user_id, channel_id });
+		read_state.last_message_id = message_id;
+
+		await read_state.save();
+
+		await emitEvent({
+			event: "MESSAGE_ACK",
+			user_id: req.user_id,
+			data: {
+				channel_id,
+				message_id,
+				version: 3763,
+			},
+		} as MessageAckEvent);
+
+		res.json({ token: null });
+	},
+);
 
 export default router;
diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts b/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts
index b2cb6763..d8b55ccd 100644
--- a/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts
+++ b/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts
@@ -3,26 +3,36 @@ import { route } from "@fosscord/api";
 
 const router = Router();
 
-router.post("/", route({ permission: "MANAGE_MESSAGES" }), (req: Request, res: Response) => {
-	// TODO:
-	res.json({
-		id: "",
-		type: 0,
-		content: "",
-		channel_id: "",
-		author: { id: "", username: "", avatar: "", discriminator: "", public_flags: 64 },
-		attachments: [],
-		embeds: [],
-		mentions: [],
-		mention_roles: [],
-		pinned: false,
-		mention_everyone: false,
-		tts: false,
-		timestamp: "",
-		edited_timestamp: null,
-		flags: 1,
-		components: []
-	}).status(200);
-});
+router.post(
+	"/",
+	route({ permission: "MANAGE_MESSAGES" }),
+	(req: Request, res: Response) => {
+		// TODO:
+		res.json({
+			id: "",
+			type: 0,
+			content: "",
+			channel_id: "",
+			author: {
+				id: "",
+				username: "",
+				avatar: "",
+				discriminator: "",
+				public_flags: 64,
+			},
+			attachments: [],
+			embeds: [],
+			mentions: [],
+			mention_roles: [],
+			pinned: false,
+			mention_everyone: false,
+			tts: false,
+			timestamp: "",
+			edited_timestamp: null,
+			flags: 1,
+			components: [],
+		}).status(200);
+	},
+);
 
 export default router;
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 46b0d6bd..3abfebe8 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
@@ -26,55 +26,69 @@ const messageUpload = multer({
 	limits: {
 		fileSize: 1024 * 1024 * 100,
 		fields: 10,
-		files: 1
+		files: 1,
 	},
-	storage: multer.memoryStorage()
+	storage: multer.memoryStorage(),
 }); // max upload 50 mb
 
-router.patch("/", route({ body: "MessageCreateSchema", permission: "SEND_MESSAGES", right: "SEND_MESSAGES" }), async (req: Request, res: Response) => {
-	const { message_id, channel_id } = req.params;
-	var body = req.body as MessageCreateSchema;
+router.patch(
+	"/",
+	route({
+		body: "MessageCreateSchema",
+		permission: "SEND_MESSAGES",
+		right: "SEND_MESSAGES",
+	}),
+	async (req: Request, res: Response) => {
+		const { message_id, channel_id } = req.params;
+		var body = req.body as MessageCreateSchema;
 
-	const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] });
+		const message = await Message.findOneOrFail({
+			where: { id: message_id, channel_id },
+			relations: ["attachments"],
+		});
 
-	const permissions = await getPermission(req.user_id, undefined, channel_id);
+		const permissions = await getPermission(
+			req.user_id,
+			undefined,
+			channel_id,
+		);
 
-	const rights = await getRights(req.user_id);
+		const rights = await getRights(req.user_id);
 
-	if ((req.user_id !== message.author_id)) {
-		if (!rights.has("MANAGE_MESSAGES")) {
-			permissions.hasThrow("MANAGE_MESSAGES");
-			body = { flags: body.flags };
-			// guild admins can only suppress embeds of other messages, no such restriction imposed to instance-wide admins
-		}
-	} else rights.hasThrow("SELF_EDIT_MESSAGES");
-
-	const new_message = await handleMessage({
-		...message,
-		// TODO: should message_reference be overridable?
-		// @ts-ignore
-		message_reference: message.message_reference,
-		...body,
-		author_id: message.author_id,
-		channel_id,
-		id: message_id,
-		edited_timestamp: new Date()
-	});
-
-	await Promise.all([
-		new_message!.save(),
-		await emitEvent({
-			event: "MESSAGE_UPDATE",
+		if (req.user_id !== message.author_id) {
+			if (!rights.has("MANAGE_MESSAGES")) {
+				permissions.hasThrow("MANAGE_MESSAGES");
+				body = { flags: body.flags };
+				// guild admins can only suppress embeds of other messages, no such restriction imposed to instance-wide admins
+			}
+		} else rights.hasThrow("SELF_EDIT_MESSAGES");
+
+		const new_message = await handleMessage({
+			...message,
+			// TODO: should message_reference be overridable?
+			// @ts-ignore
+			message_reference: message.message_reference,
+			...body,
+			author_id: message.author_id,
 			channel_id,
-			data: { ...new_message, nonce: undefined }
-		} as MessageUpdateEvent)
-	]);
+			id: message_id,
+			edited_timestamp: new Date(),
+		});
 
-	postHandleMessage(message);
+		await Promise.all([
+			new_message!.save(),
+			await emitEvent({
+				event: "MESSAGE_UPDATE",
+				channel_id,
+				data: { ...new_message, nonce: undefined },
+			} as MessageUpdateEvent),
+		]);
 
-	return res.json(message);
-});
+		postHandleMessage(message);
 
+		return res.json(message);
+	},
+);
 
 // Backfill message with specific timestamp
 router.put(
@@ -87,7 +101,11 @@ router.put(
 
 		next();
 	},
-	route({ body: "MessageCreateSchema", permission: "SEND_MESSAGES", right: "SEND_BACKDATED_EVENTS" }),
+	route({
+		body: "MessageCreateSchema",
+		permission: "SEND_MESSAGES",
+		right: "SEND_BACKDATED_EVENTS",
+	}),
 	async (req: Request, res: Response) => {
 		const { channel_id, message_id } = req.params;
 		var body = req.body as MessageCreateSchema;
@@ -107,20 +125,30 @@ router.put(
 			throw FosscordApiErrors.CANNOT_BACKFILL_TO_THE_FUTURE;
 		}
 
-		const exists = await Message.findOne({ where: { id: message_id, channel_id: channel_id } });
+		const exists = await Message.findOne({
+			where: { id: message_id, channel_id: channel_id },
+		});
 		if (exists) {
 			throw FosscordApiErrors.CANNOT_REPLACE_BY_BACKFILL;
 		}
 
 		if (req.file) {
 			try {
-				const file = await uploadFile(`/attachments/${req.params.channel_id}`, req.file);
-				attachments.push(Attachment.create({ ...file, proxy_url: file.url }));
+				const file = await uploadFile(
+					`/attachments/${req.params.channel_id}`,
+					req.file,
+				);
+				attachments.push(
+					Attachment.create({ ...file, proxy_url: file.url }),
+				);
 			} catch (error) {
 				return res.status(400).json(error);
 			}
 		}
-		const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients", "recipients.user"] });
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+			relations: ["recipients", "recipients.user"],
+		});
 
 		const embeds = body.embeds || [];
 		if (body.embed) embeds.push(body.embed);
@@ -142,27 +170,43 @@ router.put(
 
 		await Promise.all([
 			message.save(),
-			emitEvent({ event: "MESSAGE_CREATE", channel_id: channel_id, data: message } as MessageCreateEvent),
-			channel.save()
+			emitEvent({
+				event: "MESSAGE_CREATE",
+				channel_id: channel_id,
+				data: message,
+			} as MessageCreateEvent),
+			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);
-	}
+	},
 );
 
-router.get("/", route({ permission: "VIEW_CHANNEL" }), async (req: Request, res: Response) => {
-	const { message_id, channel_id } = req.params;
+router.get(
+	"/",
+	route({ permission: "VIEW_CHANNEL" }),
+	async (req: Request, res: Response) => {
+		const { message_id, channel_id } = req.params;
 
-	const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] });
+		const message = await Message.findOneOrFail({
+			where: { id: message_id, channel_id },
+			relations: ["attachments"],
+		});
 
-	const permissions = await getPermission(req.user_id, undefined, channel_id);
+		const permissions = await getPermission(
+			req.user_id,
+			undefined,
+			channel_id,
+		);
 
-	if (message.author_id !== req.user_id) permissions.hasThrow("READ_MESSAGE_HISTORY");
+		if (message.author_id !== req.user_id)
+			permissions.hasThrow("READ_MESSAGE_HISTORY");
 
-	return res.json(message);
-});
+		return res.json(message);
+	},
+);
 
 router.delete("/", route({}), async (req: Request, res: Response) => {
 	const { message_id, channel_id } = req.params;
@@ -172,9 +216,13 @@ router.delete("/", route({}), async (req: Request, res: Response) => {
 
 	const rights = await getRights(req.user_id);
 
-	if ((message.author_id !== req.user_id)) {
+	if (message.author_id !== req.user_id) {
 		if (!rights.has("MANAGE_MESSAGES")) {
-			const permission = await getPermission(req.user_id, channel.guild_id, channel_id);
+			const permission = await getPermission(
+				req.user_id,
+				channel.guild_id,
+				channel_id,
+			);
 			permission.hasThrow("MANAGE_MESSAGES");
 		}
 	} else rights.hasThrow("SELF_DELETE_MESSAGES");
@@ -187,8 +235,8 @@ router.delete("/", route({}), async (req: Request, res: Response) => {
 		data: {
 			id: message_id,
 			channel_id,
-			guild_id: channel.guild_id
-		}
+			guild_id: channel.guild_id,
+		},
 	} as MessageDeleteEvent);
 
 	res.sendStatus(204);
diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts
index c3cca05d..9f774682 100644
--- a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts
+++ b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts
@@ -11,7 +11,7 @@ import {
 	MessageReactionRemoveEvent,
 	PartialEmoji,
 	PublicUserProjection,
-	User
+	User,
 } from "@fosscord/util";
 import { route } from "@fosscord/api";
 import { Router, Response, Request } from "express";
@@ -27,159 +27,224 @@ function getEmoji(emoji: string): PartialEmoji {
 	if (parts)
 		return {
 			name: parts[0],
-			id: parts[1]
+			id: parts[1],
 		};
 
 	return {
 		id: undefined,
-		name: emoji
+		name: emoji,
 	};
 }
 
-router.delete("/", route({ permission: "MANAGE_MESSAGES" }), async (req: Request, res: Response) => {
-	const { message_id, channel_id } = req.params;
+router.delete(
+	"/",
+	route({ permission: "MANAGE_MESSAGES" }),
+	async (req: Request, res: Response) => {
+		const { message_id, channel_id } = req.params;
 
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
 
-	await Message.update({ id: message_id, channel_id }, { reactions: [] });
+		await Message.update({ id: message_id, channel_id }, { reactions: [] });
 
-	await emitEvent({
-		event: "MESSAGE_REACTION_REMOVE_ALL",
-		channel_id,
-		data: {
+		await emitEvent({
+			event: "MESSAGE_REACTION_REMOVE_ALL",
 			channel_id,
-			message_id,
-			guild_id: channel.guild_id
+			data: {
+				channel_id,
+				message_id,
+				guild_id: channel.guild_id,
+			},
+		} as MessageReactionRemoveAllEvent);
+
+		res.sendStatus(204);
+	},
+);
+
+router.delete(
+	"/:emoji",
+	route({ permission: "MANAGE_MESSAGES" }),
+	async (req: Request, res: Response) => {
+		const { message_id, channel_id } = req.params;
+		const emoji = getEmoji(req.params.emoji);
+
+		const message = await Message.findOneOrFail({
+			where: { id: message_id, channel_id },
+		});
+
+		const already_added = message.reactions.find(
+			(x) =>
+				(x.emoji.id === emoji.id && emoji.id) ||
+				x.emoji.name === emoji.name,
+		);
+		if (!already_added) throw new HTTPError("Reaction not found", 404);
+		message.reactions.remove(already_added);
+
+		await Promise.all([
+			message.save(),
+			emitEvent({
+				event: "MESSAGE_REACTION_REMOVE_EMOJI",
+				channel_id,
+				data: {
+					channel_id,
+					message_id,
+					guild_id: message.guild_id,
+					emoji,
+				},
+			} as MessageReactionRemoveEmojiEvent),
+		]);
+
+		res.sendStatus(204);
+	},
+);
+
+router.get(
+	"/:emoji",
+	route({ permission: "VIEW_CHANNEL" }),
+	async (req: Request, res: Response) => {
+		const { message_id, channel_id } = req.params;
+		const emoji = getEmoji(req.params.emoji);
+
+		const message = await Message.findOneOrFail({
+			where: { id: message_id, channel_id },
+		});
+		const reaction = message.reactions.find(
+			(x) =>
+				(x.emoji.id === emoji.id && emoji.id) ||
+				x.emoji.name === emoji.name,
+		);
+		if (!reaction) throw new HTTPError("Reaction not found", 404);
+
+		const users = await User.find({
+			where: {
+				id: In(reaction.user_ids),
+			},
+			select: PublicUserProjection,
+		});
+
+		res.json(users);
+	},
+);
+
+router.put(
+	"/:emoji/:user_id",
+	route({ permission: "READ_MESSAGE_HISTORY", right: "SELF_ADD_REACTIONS" }),
+	async (req: Request, res: Response) => {
+		const { message_id, channel_id, user_id } = req.params;
+		if (user_id !== "@me") throw new HTTPError("Invalid user");
+		const emoji = getEmoji(req.params.emoji);
+
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
+		const message = await Message.findOneOrFail({
+			where: { id: message_id, channel_id },
+		});
+		const already_added = message.reactions.find(
+			(x) =>
+				(x.emoji.id === emoji.id && emoji.id) ||
+				x.emoji.name === emoji.name,
+		);
+
+		if (!already_added) req.permission!.hasThrow("ADD_REACTIONS");
+
+		if (emoji.id) {
+			const external_emoji = await Emoji.findOneOrFail({
+				where: { id: emoji.id },
+			});
+			if (!already_added) req.permission!.hasThrow("USE_EXTERNAL_EMOJIS");
+			emoji.animated = external_emoji.animated;
+			emoji.name = external_emoji.name;
 		}
-	} as MessageReactionRemoveAllEvent);
-
-	res.sendStatus(204);
-});
 
-router.delete("/:emoji", route({ permission: "MANAGE_MESSAGES" }), async (req: Request, res: Response) => {
-	const { message_id, channel_id } = req.params;
-	const emoji = getEmoji(req.params.emoji);
-
-	const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } });
-
-	const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
-	if (!already_added) throw new HTTPError("Reaction not found", 404);
-	message.reactions.remove(already_added);
-
-	await Promise.all([
-		message.save(),
-		emitEvent({
-			event: "MESSAGE_REACTION_REMOVE_EMOJI",
+		if (already_added) {
+			if (already_added.user_ids.includes(req.user_id))
+				return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error
+			already_added.count++;
+		} else
+			message.reactions.push({
+				count: 1,
+				emoji,
+				user_ids: [req.user_id],
+			});
+
+		await message.save();
+
+		const member =
+			channel.guild_id &&
+			(await Member.findOneOrFail({ where: { id: req.user_id } }));
+
+		await emitEvent({
+			event: "MESSAGE_REACTION_ADD",
 			channel_id,
 			data: {
+				user_id: req.user_id,
 				channel_id,
 				message_id,
-				guild_id: message.guild_id,
-				emoji
-			}
-		} as MessageReactionRemoveEmojiEvent)
-	]);
-
-	res.sendStatus(204);
-});
-
-router.get("/:emoji", route({ permission: "VIEW_CHANNEL" }), async (req: Request, res: Response) => {
-	const { message_id, channel_id } = req.params;
-	const emoji = getEmoji(req.params.emoji);
-
-	const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } });
-	const reaction = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
-	if (!reaction) throw new HTTPError("Reaction not found", 404);
-
-	const users = await User.find({
-		where: {
-			id: In(reaction.user_ids)
-		},
-		select: PublicUserProjection
-	});
-
-	res.json(users);
-});
-
-router.put("/:emoji/:user_id", route({ permission: "READ_MESSAGE_HISTORY", right: "SELF_ADD_REACTIONS" }), async (req: Request, res: Response) => {
-	const { message_id, channel_id, user_id } = req.params;
-	if (user_id !== "@me") throw new HTTPError("Invalid user");
-	const emoji = getEmoji(req.params.emoji);
-
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
-	const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } });
-	const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
-
-	if (!already_added) req.permission!.hasThrow("ADD_REACTIONS");
-
-	if (emoji.id) {
-		const external_emoji = await Emoji.findOneOrFail({ where: { id: emoji.id } });
-		if (!already_added) req.permission!.hasThrow("USE_EXTERNAL_EMOJIS");
-		emoji.animated = external_emoji.animated;
-		emoji.name = external_emoji.name;
-	}
-
-	if (already_added) {
-		if (already_added.user_ids.includes(req.user_id)) return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error
-		already_added.count++;
-	} else message.reactions.push({ count: 1, emoji, user_ids: [req.user_id] });
-
-	await message.save();
-
-	const member = channel.guild_id && (await Member.findOneOrFail({ where: { id: req.user_id } }));
-
-	await emitEvent({
-		event: "MESSAGE_REACTION_ADD",
-		channel_id,
-		data: {
-			user_id: req.user_id,
-			channel_id,
-			message_id,
-			guild_id: channel.guild_id,
-			emoji,
-			member
+				guild_id: channel.guild_id,
+				emoji,
+				member,
+			},
+		} as MessageReactionAddEvent);
+
+		res.sendStatus(204);
+	},
+);
+
+router.delete(
+	"/:emoji/:user_id",
+	route({}),
+	async (req: Request, res: Response) => {
+		var { message_id, channel_id, user_id } = req.params;
+
+		const emoji = getEmoji(req.params.emoji);
+
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
+		const message = await Message.findOneOrFail({
+			where: { id: message_id, channel_id },
+		});
+
+		if (user_id === "@me") user_id = req.user_id;
+		else {
+			const permissions = await getPermission(
+				req.user_id,
+				undefined,
+				channel_id,
+			);
+			permissions.hasThrow("MANAGE_MESSAGES");
 		}
-	} as MessageReactionAddEvent);
 
-	res.sendStatus(204);
-});
+		const already_added = message.reactions.find(
+			(x) =>
+				(x.emoji.id === emoji.id && emoji.id) ||
+				x.emoji.name === emoji.name,
+		);
+		if (!already_added || !already_added.user_ids.includes(user_id))
+			throw new HTTPError("Reaction not found", 404);
 
-router.delete("/:emoji/:user_id", route({}), async (req: Request, res: Response) => {
-	var { message_id, channel_id, user_id } = req.params;
+		already_added.count--;
 
-	const emoji = getEmoji(req.params.emoji);
+		if (already_added.count <= 0) message.reactions.remove(already_added);
 
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
-	const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } });
+		await message.save();
 
-	if (user_id === "@me") user_id = req.user_id;
-	else {
-		const permissions = await getPermission(req.user_id, undefined, channel_id);
-		permissions.hasThrow("MANAGE_MESSAGES");
-	}
-
-	const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
-	if (!already_added || !already_added.user_ids.includes(user_id)) throw new HTTPError("Reaction not found", 404);
-
-	already_added.count--;
-
-	if (already_added.count <= 0) message.reactions.remove(already_added);
-
-	await message.save();
-
-	await emitEvent({
-		event: "MESSAGE_REACTION_REMOVE",
-		channel_id,
-		data: {
-			user_id: req.user_id,
+		await emitEvent({
+			event: "MESSAGE_REACTION_REMOVE",
 			channel_id,
-			message_id,
-			guild_id: channel.guild_id,
-			emoji
-		}
-	} as MessageReactionRemoveEvent);
-
-	res.sendStatus(204);
-});
+			data: {
+				user_id: req.user_id,
+				channel_id,
+				message_id,
+				guild_id: channel.guild_id,
+				emoji,
+			},
+		} as MessageReactionRemoveEvent);
+
+		res.sendStatus(204);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/channels/#channel_id/messages/bulk-delete.ts b/src/api/routes/channels/#channel_id/messages/bulk-delete.ts
index 6493c16a..553ab17e 100644
--- a/src/api/routes/channels/#channel_id/messages/bulk-delete.ts
+++ b/src/api/routes/channels/#channel_id/messages/bulk-delete.ts
@@ -1,5 +1,13 @@
 import { Router, Response, Request } from "express";
-import { Channel, Config, emitEvent, getPermission, getRights, MessageDeleteBulkEvent, Message } from "@fosscord/util";
+import {
+	Channel,
+	Config,
+	emitEvent,
+	getPermission,
+	getRights,
+	MessageDeleteBulkEvent,
+	Message,
+} from "@fosscord/util";
 import { HTTPError } from "lambert-server";
 import { route } from "@fosscord/api";
 
@@ -10,33 +18,48 @@ export default router;
 // should users be able to bulk delete messages or only bots? ANSWER: all users
 // should this request fail, if you provide messages older than 14 days/invalid ids? ANSWER: NO
 // https://discord.com/developers/docs/resources/channel#bulk-delete-messages
-router.post("/", route({ body: "BulkDeleteSchema" }), async (req: Request, res: Response) => {
-	const { channel_id } = req.params;
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
-	if (!channel.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400);
-
-	const rights = await getRights(req.user_id);
-	rights.hasThrow("SELF_DELETE_MESSAGES");
-	
-	let superuser = rights.has("MANAGE_MESSAGES");
-	const permission = await getPermission(req.user_id, channel?.guild_id, channel_id);
-		
-	const { maxBulkDelete } = Config.get().limits.message;
-
-	const { messages } = req.body as { messages: string[] };
-	if (messages.length === 0) throw new HTTPError("You must specify messages to bulk delete");
-	if (!superuser) {
-		permission.hasThrow("MANAGE_MESSAGES");
-		if (messages.length > maxBulkDelete) throw new HTTPError(`You cannot delete more than ${maxBulkDelete} messages`);
-	}
-
-	await Message.delete(messages);
-
-	await emitEvent({
-		event: "MESSAGE_DELETE_BULK",
-		channel_id,
-		data: { ids: messages, channel_id, guild_id: channel.guild_id }
-	} as MessageDeleteBulkEvent);
-
-	res.sendStatus(204);
-});
+router.post(
+	"/",
+	route({ body: "BulkDeleteSchema" }),
+	async (req: Request, res: Response) => {
+		const { channel_id } = req.params;
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
+		if (!channel.guild_id)
+			throw new HTTPError("Can't bulk delete dm channel messages", 400);
+
+		const rights = await getRights(req.user_id);
+		rights.hasThrow("SELF_DELETE_MESSAGES");
+
+		let superuser = rights.has("MANAGE_MESSAGES");
+		const permission = await getPermission(
+			req.user_id,
+			channel?.guild_id,
+			channel_id,
+		);
+
+		const { maxBulkDelete } = Config.get().limits.message;
+
+		const { messages } = req.body as { messages: string[] };
+		if (messages.length === 0)
+			throw new HTTPError("You must specify messages to bulk delete");
+		if (!superuser) {
+			permission.hasThrow("MANAGE_MESSAGES");
+			if (messages.length > maxBulkDelete)
+				throw new HTTPError(
+					`You cannot delete more than ${maxBulkDelete} messages`,
+				);
+		}
+
+		await Message.delete(messages);
+
+		await emitEvent({
+			event: "MESSAGE_DELETE_BULK",
+			channel_id,
+			data: { ids: messages, channel_id, guild_id: channel.guild_id },
+		} as MessageDeleteBulkEvent);
+
+		res.sendStatus(204);
+	},
+);
diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts
index bee93e80..631074c6 100644
--- a/src/api/routes/channels/#channel_id/messages/index.ts
+++ b/src/api/routes/channels/#channel_id/messages/index.ts
@@ -61,36 +61,50 @@ router.get("/", async (req: Request, res: Response) => {
 	const before = req.query.before ? `${req.query.before}` : undefined;
 	const after = req.query.after ? `${req.query.after}` : undefined;
 	const limit = Number(req.query.limit) || 50;
-	if (limit < 1 || limit > 100) throw new HTTPError("limit must be between 1 and 100", 422);
+	if (limit < 1 || limit > 100)
+		throw new HTTPError("limit must be between 1 and 100", 422);
 
 	var halfLimit = Math.floor(limit / 2);
 
-	const permissions = await getPermission(req.user_id, channel.guild_id, channel_id);
+	const permissions = await getPermission(
+		req.user_id,
+		channel.guild_id,
+		channel_id,
+	);
 	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 },
-		relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"]
+		relations: [
+			"author",
+			"webhook",
+			"application",
+			"mentions",
+			"mention_roles",
+			"mention_channels",
+			"sticker_items",
+			"attachments",
+		],
 	};
 
 	if (after) {
-		if (BigInt(after) > BigInt(Snowflake.generate())) return res.status(422);
+		if (BigInt(after) > BigInt(Snowflake.generate()))
+			return res.status(422);
 		query.where.id = MoreThan(after);
-	}
-	else if (before) {
-		if (BigInt(before) < BigInt(req.params.channel_id)) return res.status(422);
+	} else if (before) {
+		if (BigInt(before) < BigInt(req.params.channel_id))
+			return res.status(422);
 		query.where.id = LessThan(before);
-	}
-	else if (around) {
+	} else if (around) {
 		query.where.id = [
 			MoreThan((BigInt(around) - BigInt(halfLimit)).toString()),
-			LessThan((BigInt(around) + BigInt(halfLimit)).toString())
+			LessThan((BigInt(around) + BigInt(halfLimit)).toString()),
 		];
 
-		return res.json([]);	// TODO: fix around
+		return res.json([]); // TODO: fix around
 	}
 
 	const messages = await Message.find(query);
@@ -105,11 +119,22 @@ router.get("/", async (req: Request, res: Response) => {
 				delete x.user_ids;
 			});
 			// @ts-ignore
-			if (!x.author) x.author = { id: "4", discriminator: "0000", username: "Fosscord Ghost", public_flags: "0", avatar: null };
+			if (!x.author)
+				x.author = {
+					id: "4",
+					discriminator: "0000",
+					username: "Fosscord Ghost",
+					public_flags: "0",
+					avatar: null,
+				};
 			x.attachments?.forEach((y: any) => {
 				// dynamically set attachment proxy_url in case the endpoint changed
-				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}`;
+				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
+				}`;
 			});
 
 			/**
@@ -123,7 +148,7 @@ router.get("/", async (req: Request, res: Response) => {
 			// }
 
 			return x;
-		})
+		}),
 	);
 });
 
@@ -134,7 +159,7 @@ const messageUpload = multer({
 		fields: 10,
 		// files: 1
 	},
-	storage: multer.memoryStorage()
+	storage: multer.memoryStorage(),
 }); // max upload 50 mb
 /**
  TODO: dynamically change limit of MessageCreateSchema with config
@@ -155,24 +180,38 @@ router.post(
 
 		next();
 	},
-	route({ body: "MessageCreateSchema", permission: "SEND_MESSAGES", right: "SEND_MESSAGES" }),
+	route({
+		body: "MessageCreateSchema",
+		permission: "SEND_MESSAGES",
+		right: "SEND_MESSAGES",
+	}),
 	async (req: Request, res: Response) => {
 		const { channel_id } = req.params;
 		var body = req.body as MessageCreateSchema;
 		const attachments: Attachment[] = [];
 
-		const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients", "recipients.user"] });
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+			relations: ["recipients", "recipients.user"],
+		});
 		if (!channel.isWritable()) {
-			throw new HTTPError(`Cannot send messages to channel of type ${channel.type}`, 400);
+			throw new HTTPError(
+				`Cannot send messages to channel of type ${channel.type}`,
+				400,
+			);
 		}
 
-		const files = req.files as Express.Multer.File[] ?? [];
+		const files = (req.files as Express.Multer.File[]) ?? [];
 		for (var currFile of files) {
 			try {
-				const file = await uploadFile(`/attachments/${channel.id}`, currFile);
-				attachments.push(Attachment.create({ ...file, proxy_url: file.url }));
-			}
-			catch (error) {
+				const file = await uploadFile(
+					`/attachments/${channel.id}`,
+					currFile,
+				);
+				attachments.push(
+					Attachment.create({ ...file, proxy_url: file.url }),
+				);
+			} catch (error) {
 				return res.status(400).json(error);
 			}
 		}
@@ -188,7 +227,7 @@ router.post(
 			channel_id,
 			attachments,
 			edited_timestamp: undefined,
-			timestamp: new Date()
+			timestamp: new Date(),
 		});
 
 		channel.last_message_id = message.id;
@@ -205,32 +244,47 @@ router.post(
 							recipient.save(),
 							emitEvent({
 								event: "CHANNEL_CREATE",
-								data: channel_dto.excludedRecipients([recipient.user_id]),
-								user_id: recipient.user_id
-							})
+								data: channel_dto.excludedRecipients([
+									recipient.user_id,
+								]),
+								user_id: recipient.user_id,
+							}),
 						]);
 					}
-				})
+				}),
 			);
 		}
 
-		const member = await Member.findOneOrFail({ where: { id: req.user_id }, relations: ["roles"] });
-		member.roles = member.roles.filter((role: Role) => {
-			return role.id !== role.guild_id;
-		}).map((role: Role) => {
-			return role.id;
-		}) as any;
+		const member = await Member.findOneOrFail({
+			where: { id: req.user_id },
+			relations: ["roles"],
+		});
+		member.roles = member.roles
+			.filter((role: Role) => {
+				return role.id !== role.guild_id;
+			})
+			.map((role: Role) => {
+				return role.id;
+			}) as any;
 
 		await Promise.all([
 			message.save(),
-			emitEvent({ event: "MESSAGE_CREATE", channel_id: channel_id, data: message } as MessageCreateEvent),
-			message.guild_id ? Member.update({ id: req.user_id, guild_id: message.guild_id }, { last_message_id: message.id }) : null,
-			channel.save()
+			emitEvent({
+				event: "MESSAGE_CREATE",
+				channel_id: channel_id,
+				data: message,
+			} as MessageCreateEvent),
+			message.guild_id
+				? Member.update(
+						{ 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 e74a0255..89be843f 100644
--- a/src/api/routes/channels/#channel_id/permissions.ts
+++ b/src/api/routes/channels/#channel_id/permissions.ts
@@ -6,7 +6,7 @@ import {
 	emitEvent,
 	getPermission,
 	Member,
-	Role
+	Role,
 } from "@fosscord/util";
 import { Router, Response, Request } from "express";
 import { HTTPError } from "lambert-server";
@@ -16,69 +16,90 @@ const router: Router = Router();
 
 // TODO: Only permissions your bot has in the guild or channel can be allowed/denied (unless your bot has a MANAGE_ROLES overwrite in the channel)
 
-export interface ChannelPermissionOverwriteSchema extends ChannelPermissionOverwrite { }
+export interface ChannelPermissionOverwriteSchema
+	extends ChannelPermissionOverwrite {}
 
 router.put(
 	"/:overwrite_id",
-	route({ body: "ChannelPermissionOverwriteSchema", permission: "MANAGE_ROLES" }),
+	route({
+		body: "ChannelPermissionOverwriteSchema",
+		permission: "MANAGE_ROLES",
+	}),
 	async (req: Request, res: Response) => {
 		const { channel_id, overwrite_id } = req.params;
 		const body = req.body as ChannelPermissionOverwriteSchema;
 
-		var channel = await Channel.findOneOrFail({ where: { id: channel_id } });
+		var channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
 		if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
 
 		if (body.type === 0) {
-			if (!(await Role.count({ where: { id: overwrite_id } }))) throw new HTTPError("role not found", 404);
+			if (!(await Role.count({ where: { id: overwrite_id } })))
+				throw new HTTPError("role not found", 404);
 		} else if (body.type === 1) {
-			if (!(await Member.count({ where: { id: overwrite_id } }))) throw new HTTPError("user not found", 404);
+			if (!(await Member.count({ where: { id: overwrite_id } })))
+				throw new HTTPError("user not found", 404);
 		} else throw new HTTPError("type not supported", 501);
 
-		// @ts-ignore
-		var overwrite: ChannelPermissionOverwrite = channel.permission_overwrites.find((x) => x.id === overwrite_id);
+		//@ts-ignore
+		var overwrite: ChannelPermissionOverwrite =
+			channel.permission_overwrites?.find((x) => x.id === overwrite_id);
 		if (!overwrite) {
 			// @ts-ignore
 			overwrite = {
 				id: overwrite_id,
-				type: body.type
+				type: body.type,
 			};
 			channel.permission_overwrites!.push(overwrite);
 		}
-		overwrite.allow = String(req.permission!.bitfield & (BigInt(body.allow) || BigInt("0")));
-		overwrite.deny = String(req.permission!.bitfield & (BigInt(body.deny) || BigInt("0")));
+		overwrite.allow = String(
+			req.permission!.bitfield & (BigInt(body.allow) || BigInt("0")),
+		);
+		overwrite.deny = String(
+			req.permission!.bitfield & (BigInt(body.deny) || BigInt("0")),
+		);
 
 		await Promise.all([
 			channel.save(),
 			emitEvent({
 				event: "CHANNEL_UPDATE",
 				channel_id,
-				data: channel
-			} as ChannelUpdateEvent)
+				data: channel,
+			} as ChannelUpdateEvent),
 		]);
 
 		return res.sendStatus(204);
-	}
+	},
 );
 
 // TODO: check permission hierarchy
-router.delete("/:overwrite_id", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
-	const { channel_id, overwrite_id } = req.params;
+router.delete(
+	"/:overwrite_id",
+	route({ permission: "MANAGE_ROLES" }),
+	async (req: Request, res: Response) => {
+		const { channel_id, overwrite_id } = req.params;
 
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
-	if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
+		if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
 
-	channel.permission_overwrites = channel.permission_overwrites!.filter((x) => x.id === overwrite_id);
+		channel.permission_overwrites = channel.permission_overwrites!.filter(
+			(x) => x.id === overwrite_id,
+		);
 
-	await Promise.all([
-		channel.save(),
-		emitEvent({
-			event: "CHANNEL_UPDATE",
-			channel_id,
-			data: channel
-		} as ChannelUpdateEvent)
-	]);
+		await Promise.all([
+			channel.save(),
+			emitEvent({
+				event: "CHANNEL_UPDATE",
+				channel_id,
+				data: channel,
+			} as ChannelUpdateEvent),
+		]);
 
-	return res.sendStatus(204);
-});
+		return res.sendStatus(204);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/channels/#channel_id/pins.ts b/src/api/routes/channels/#channel_id/pins.ts
index 30507c71..d3f6960a 100644
--- a/src/api/routes/channels/#channel_id/pins.ts
+++ b/src/api/routes/channels/#channel_id/pins.ts
@@ -6,7 +6,7 @@ import {
 	getPermission,
 	Message,
 	MessageUpdateEvent,
-	DiscordApiErrors
+	DiscordApiErrors,
 } from "@fosscord/util";
 import { Router, Request, Response } from "express";
 import { HTTPError } from "lambert-server";
@@ -14,77 +14,100 @@ import { route } from "@fosscord/api";
 
 const router: Router = Router();
 
-router.put("/:message_id", route({ permission: "VIEW_CHANNEL" }), async (req: Request, res: Response) => {
-	const { channel_id, message_id } = req.params;
-
-	const message = await Message.findOneOrFail({ where: { id: message_id } });
-
-	// * in dm channels anyone can pin messages -> only check for guilds
-	if (message.guild_id) req.permission!.hasThrow("MANAGE_MESSAGES");
-
-	const pinned_count = await Message.count({ where: { channel: { id: channel_id }, pinned: true } });
-	const { maxPins } = Config.get().limits.channel;
-	if (pinned_count >= maxPins) throw DiscordApiErrors.MAXIMUM_PINS.withParams(maxPins);
-
-	await Promise.all([
-		Message.update({ id: message_id }, { pinned: true }),
-		emitEvent({
-			event: "MESSAGE_UPDATE",
-			channel_id,
-			data: message
-		} as MessageUpdateEvent),
-		emitEvent({
-			event: "CHANNEL_PINS_UPDATE",
-			channel_id,
-			data: {
+router.put(
+	"/:message_id",
+	route({ permission: "VIEW_CHANNEL" }),
+	async (req: Request, res: Response) => {
+		const { channel_id, message_id } = req.params;
+
+		const message = await Message.findOneOrFail({
+			where: { id: message_id },
+		});
+
+		// * in dm channels anyone can pin messages -> only check for guilds
+		if (message.guild_id) req.permission!.hasThrow("MANAGE_MESSAGES");
+
+		const pinned_count = await Message.count({
+			where: { channel: { id: channel_id }, pinned: true },
+		});
+		const { maxPins } = Config.get().limits.channel;
+		if (pinned_count >= maxPins)
+			throw DiscordApiErrors.MAXIMUM_PINS.withParams(maxPins);
+
+		await Promise.all([
+			Message.update({ id: message_id }, { pinned: true }),
+			emitEvent({
+				event: "MESSAGE_UPDATE",
 				channel_id,
-				guild_id: message.guild_id,
-				last_pin_timestamp: undefined
-			}
-		} as ChannelPinsUpdateEvent)
-	]);
-
-	res.sendStatus(204);
-});
-
-router.delete("/:message_id", route({ permission: "VIEW_CHANNEL" }), async (req: Request, res: Response) => {
-	const { channel_id, message_id } = req.params;
-
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
-	if (channel.guild_id) req.permission!.hasThrow("MANAGE_MESSAGES");
-
-	const message = await Message.findOneOrFail({ where: { id: message_id } });
-	message.pinned = false;
-
-	await Promise.all([
-		message.save(),
-
-		emitEvent({
-			event: "MESSAGE_UPDATE",
-			channel_id,
-			data: message
-		} as MessageUpdateEvent),
-
-		emitEvent({
-			event: "CHANNEL_PINS_UPDATE",
-			channel_id,
-			data: {
+				data: message,
+			} as MessageUpdateEvent),
+			emitEvent({
+				event: "CHANNEL_PINS_UPDATE",
 				channel_id,
-				guild_id: channel.guild_id,
-				last_pin_timestamp: undefined
-			}
-		} as ChannelPinsUpdateEvent)
-	]);
-
-	res.sendStatus(204);
-});
-
-router.get("/", route({ permission: ["READ_MESSAGE_HISTORY"] }), async (req: Request, res: Response) => {
-	const { channel_id } = req.params;
-
-	let pins = await Message.find({ where: { channel_id: channel_id, pinned: true } });
+				data: {
+					channel_id,
+					guild_id: message.guild_id,
+					last_pin_timestamp: undefined,
+				},
+			} as ChannelPinsUpdateEvent),
+		]);
+
+		res.sendStatus(204);
+	},
+);
+
+router.delete(
+	"/:message_id",
+	route({ permission: "VIEW_CHANNEL" }),
+	async (req: Request, res: Response) => {
+		const { channel_id, message_id } = req.params;
+
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
+		if (channel.guild_id) req.permission!.hasThrow("MANAGE_MESSAGES");
+
+		const message = await Message.findOneOrFail({
+			where: { id: message_id },
+		});
+		message.pinned = false;
+
+		await Promise.all([
+			message.save(),
+
+			emitEvent({
+				event: "MESSAGE_UPDATE",
+				channel_id,
+				data: message,
+			} as MessageUpdateEvent),
 
-	res.send(pins);
-});
+			emitEvent({
+				event: "CHANNEL_PINS_UPDATE",
+				channel_id,
+				data: {
+					channel_id,
+					guild_id: channel.guild_id,
+					last_pin_timestamp: undefined,
+				},
+			} as ChannelPinsUpdateEvent),
+		]);
+
+		res.sendStatus(204);
+	},
+);
+
+router.get(
+	"/",
+	route({ permission: ["READ_MESSAGE_HISTORY"] }),
+	async (req: Request, res: Response) => {
+		const { channel_id } = req.params;
+
+		let pins = await Message.find({
+			where: { channel_id: channel_id, pinned: true },
+		});
+
+		res.send(pins);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/channels/#channel_id/purge.ts b/src/api/routes/channels/#channel_id/purge.ts
index 9fe6b658..a9f88662 100644
--- a/src/api/routes/channels/#channel_id/purge.ts
+++ b/src/api/routes/channels/#channel_id/purge.ts
@@ -21,52 +21,79 @@ export default router;
 /**
 TODO: apply the delete bit by bit to prevent client and database stress
 **/
-router.post("/", route({ /*body: "PurgeSchema",*/ }), async (req: Request, res: Response) => {
-	const { channel_id } = req.params;
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
+router.post(
+	"/",
+	route({
+		/*body: "PurgeSchema",*/
+	}),
+	async (req: Request, res: Response) => {
+		const { channel_id } = req.params;
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
 
-	if (!channel.guild_id) throw new HTTPError("Can't purge dm channels", 400);
-	isTextChannel(channel.type);
+		if (!channel.guild_id)
+			throw new HTTPError("Can't purge dm channels", 400);
+		isTextChannel(channel.type);
 
-	const rights = await getRights(req.user_id);
-	if (!rights.has("MANAGE_MESSAGES")) {
-		const permissions = await getPermission(req.user_id, channel.guild_id, channel_id);
-		permissions.hasThrow("MANAGE_MESSAGES");
-		permissions.hasThrow("MANAGE_CHANNELS");
-	}
+		const rights = await getRights(req.user_id);
+		if (!rights.has("MANAGE_MESSAGES")) {
+			const permissions = await getPermission(
+				req.user_id,
+				channel.guild_id,
+				channel_id,
+			);
+			permissions.hasThrow("MANAGE_MESSAGES");
+			permissions.hasThrow("MANAGE_CHANNELS");
+		}
 
-	const { before, after } = req.body as PurgeSchema;
+		const { before, after } = req.body as PurgeSchema;
 
-	// TODO: send the deletion event bite-by-bite to prevent client stress
-
-	var query: FindManyOptions<Message> & { where: { id?: any; }; } = {
-		order: { id: "ASC" },
-		// take: limit,
-		where: {
-			channel_id,
-			id: Between(after, before), // the right way around
-			author_id: rights.has("SELF_DELETE_MESSAGES") ? undefined : Not(req.user_id)
-			// if you lack the right of self-deletion, you can't delete your own messages, even in purges
-		},
-		relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"]
-	};
+		// TODO: send the deletion event bite-by-bite to prevent client stress
 
+		var query: FindManyOptions<Message> & { where: { id?: any } } = {
+			order: { id: "ASC" },
+			// take: limit,
+			where: {
+				channel_id,
+				id: Between(after, before), // the right way around
+				author_id: rights.has("SELF_DELETE_MESSAGES")
+					? undefined
+					: Not(req.user_id),
+				// if you lack the right of self-deletion, you can't delete your own messages, even in purges
+			},
+			relations: [
+				"author",
+				"webhook",
+				"application",
+				"mentions",
+				"mention_roles",
+				"mention_channels",
+				"sticker_items",
+				"attachments",
+			],
+		};
 
-	const messages = await Message.find(query);
-	const endpoint = Config.get().cdn.endpointPublic;
+		const messages = await Message.find(query);
+		const endpoint = Config.get().cdn.endpointPublic;
 
-	if (messages.length == 0) {
-		res.sendStatus(304);
-		return;
-	}
+		if (messages.length == 0) {
+			res.sendStatus(304);
+			return;
+		}
 
-	await Message.delete(messages.map((x) => x.id));
+		await Message.delete(messages.map((x) => x.id));
 
-	await emitEvent({
-		event: "MESSAGE_DELETE_BULK",
-		channel_id,
-		data: { ids: messages.map(x => x.id), channel_id, guild_id: channel.guild_id }
-	} as MessageDeleteBulkEvent);
+		await emitEvent({
+			event: "MESSAGE_DELETE_BULK",
+			channel_id,
+			data: {
+				ids: messages.map((x) => x.id),
+				channel_id,
+				guild_id: channel.guild_id,
+			},
+		} as MessageDeleteBulkEvent);
 
-	res.sendStatus(204);
-});
+		res.sendStatus(204);
+	},
+);
diff --git a/src/api/routes/channels/#channel_id/recipients.ts b/src/api/routes/channels/#channel_id/recipients.ts
index 25854415..cc7e5756 100644
--- a/src/api/routes/channels/#channel_id/recipients.ts
+++ b/src/api/routes/channels/#channel_id/recipients.ts
@@ -8,7 +8,7 @@ import {
 	emitEvent,
 	PublicUserProjection,
 	Recipient,
-	User
+	User,
 } from "@fosscord/util";
 import { route } from "@fosscord/api";
 
@@ -16,34 +16,48 @@ const router: Router = Router();
 
 router.put("/:user_id", route({}), async (req: Request, res: Response) => {
 	const { channel_id, user_id } = req.params;
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients"] });
+	const channel = await Channel.findOneOrFail({
+		where: { id: channel_id },
+		relations: ["recipients"],
+	});
 
 	if (channel.type !== ChannelType.GROUP_DM) {
-		const recipients = [...channel.recipients!.map((r) => r.user_id), user_id].unique();
+		const recipients = [
+			...channel.recipients!.map((r) => r.user_id),
+			user_id,
+		].unique();
 
-		const new_channel = await Channel.createDMChannel(recipients, req.user_id);
+		const new_channel = await Channel.createDMChannel(
+			recipients,
+			req.user_id,
+		);
 		return res.status(201).json(new_channel);
 	} else {
 		if (channel.recipients!.map((r) => r.user_id).includes(user_id)) {
 			throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error?
 		}
 
-		channel.recipients!.push(Recipient.create({ channel_id: channel_id, user_id: user_id }));
+		channel.recipients!.push(
+			Recipient.create({ channel_id: channel_id, user_id: user_id }),
+		);
 		await channel.save();
 
 		await emitEvent({
 			event: "CHANNEL_CREATE",
 			data: await DmChannelDTO.from(channel, [user_id]),
-			user_id: user_id
+			user_id: user_id,
 		});
 
 		await emitEvent({
 			event: "CHANNEL_RECIPIENT_ADD",
 			data: {
 				channel_id: channel_id,
-				user: await User.findOneOrFail({ where: { id: user_id }, select: PublicUserProjection })
+				user: await User.findOneOrFail({
+					where: { id: user_id },
+					select: PublicUserProjection,
+				}),
 			},
-			channel_id: channel_id
+			channel_id: channel_id,
 		} as ChannelRecipientAddEvent);
 		return res.sendStatus(204);
 	}
@@ -51,8 +65,16 @@ router.put("/:user_id", route({}), async (req: Request, res: Response) => {
 
 router.delete("/:user_id", route({}), async (req: Request, res: Response) => {
 	const { channel_id, user_id } = req.params;
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients"] });
-	if (!(channel.type === ChannelType.GROUP_DM && (channel.owner_id === req.user_id || user_id === req.user_id)))
+	const channel = await Channel.findOneOrFail({
+		where: { id: channel_id },
+		relations: ["recipients"],
+	});
+	if (
+		!(
+			channel.type === ChannelType.GROUP_DM &&
+			(channel.owner_id === req.user_id || user_id === req.user_id)
+		)
+	)
 		throw DiscordApiErrors.MISSING_PERMISSIONS;
 
 	if (!channel.recipients!.map((r) => r.user_id).includes(user_id)) {
diff --git a/src/api/routes/channels/#channel_id/typing.ts b/src/api/routes/channels/#channel_id/typing.ts
index 99460f6e..03f76205 100644
--- a/src/api/routes/channels/#channel_id/typing.ts
+++ b/src/api/routes/channels/#channel_id/typing.ts
@@ -4,26 +4,42 @@ import { Router, Request, Response } from "express";
 
 const router: Router = Router();
 
-router.post("/", route({ permission: "SEND_MESSAGES" }), async (req: Request, res: Response) => {
-	const { channel_id } = req.params;
-	const user_id = req.user_id;
-	const timestamp = Date.now();
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
-	const member = await Member.findOne({ where: { id: user_id, guild_id: channel.guild_id }, relations: ["roles", "user"] });
+router.post(
+	"/",
+	route({ permission: "SEND_MESSAGES" }),
+	async (req: Request, res: Response) => {
+		const { channel_id } = req.params;
+		const user_id = req.user_id;
+		const timestamp = Date.now();
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
+		const member = await Member.findOne({
+			where: { id: user_id, guild_id: channel.guild_id },
+			relations: ["roles", "user"],
+		});
 
-	await emitEvent({
-		event: "TYPING_START",
-		channel_id: channel_id,
-		data: {
-			...(member ? { member: { ...member, roles: member?.roles?.map((x) => x.id) } } : null),
-			channel_id,
-			timestamp,
-			user_id,
-			guild_id: channel.guild_id
-		}
-	} as TypingStartEvent);
+		await emitEvent({
+			event: "TYPING_START",
+			channel_id: channel_id,
+			data: {
+				...(member
+					? {
+							member: {
+								...member,
+								roles: member?.roles?.map((x) => x.id),
+							},
+					  }
+					: null),
+				channel_id,
+				timestamp,
+				user_id,
+				guild_id: channel.guild_id,
+			},
+		} as TypingStartEvent);
 
-	res.sendStatus(204);
-});
+		res.sendStatus(204);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/channels/#channel_id/webhooks.ts b/src/api/routes/channels/#channel_id/webhooks.ts
index 99c104ca..da8fe73c 100644
--- a/src/api/routes/channels/#channel_id/webhooks.ts
+++ b/src/api/routes/channels/#channel_id/webhooks.ts
@@ -13,22 +13,29 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 });
 
 // TODO: use Image Data Type for avatar instead of String
-router.post("/", route({ body: "WebhookCreateSchema", permission: "MANAGE_WEBHOOKS" }), async (req: Request, res: Response) => {
-	const channel_id = req.params.channel_id;
-	const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
-
-	isTextChannel(channel.type);
-	if (!channel.guild_id) throw new HTTPError("Not a guild channel", 400);
-
-	const webhook_count = await Webhook.count({ where: { channel_id } });
-	const { maxWebhooks } = Config.get().limits.channel;
-	if (webhook_count > maxWebhooks) throw DiscordApiErrors.MAXIMUM_WEBHOOKS.withParams(maxWebhooks);
-
-	var { avatar, name } = req.body as { name: string; avatar?: string };
-	name = trimSpecial(name);
-	if (name === "clyde") throw new HTTPError("Invalid name", 400);
-
-	// TODO: save webhook in database and send response
-});
+router.post(
+	"/",
+	route({ body: "WebhookCreateSchema", permission: "MANAGE_WEBHOOKS" }),
+	async (req: Request, res: Response) => {
+		const channel_id = req.params.channel_id;
+		const channel = await Channel.findOneOrFail({
+			where: { id: channel_id },
+		});
+
+		isTextChannel(channel.type);
+		if (!channel.guild_id) throw new HTTPError("Not a guild channel", 400);
+
+		const webhook_count = await Webhook.count({ where: { channel_id } });
+		const { maxWebhooks } = Config.get().limits.channel;
+		if (webhook_count > maxWebhooks)
+			throw DiscordApiErrors.MAXIMUM_WEBHOOKS.withParams(maxWebhooks);
+
+		var { avatar, name } = req.body as { name: string; avatar?: string };
+		name = trimSpecial(name);
+		if (name === "clyde") throw new HTTPError("Invalid name", 400);
+
+		// TODO: save webhook in database and send response
+	},
+);
 
 export default router;
diff --git a/src/api/routes/discoverable-guilds.ts b/src/api/routes/discoverable-guilds.ts
index 383e2b24..0e7cfbab 100644
--- a/src/api/routes/discoverable-guilds.ts
+++ b/src/api/routes/discoverable-guilds.ts
@@ -17,19 +17,33 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	if (categories == undefined) {
 		guilds = showAllGuilds
 			? await Guild.find({ take: Math.abs(Number(limit || configLimit)) })
-			: await Guild.find({ where: { features: Like(`%DISCOVERABLE%`) }, take: Math.abs(Number(limit || configLimit)) });
+			: await Guild.find({
+					where: { features: Like(`%DISCOVERABLE%`) },
+					take: Math.abs(Number(limit || configLimit)),
+			  });
 	} else {
 		guilds = showAllGuilds
-			? await Guild.find({ where: { primary_category_id: categories.toString() }, take: Math.abs(Number(limit || configLimit)) })
+			? await Guild.find({
+					where: { primary_category_id: categories.toString() },
+					take: Math.abs(Number(limit || configLimit)),
+			  })
 			: await Guild.find({
-				where: { primary_category_id: categories.toString(), features: Like("%DISCOVERABLE%") },
-				take: Math.abs(Number(limit || configLimit))
-			});
+					where: {
+						primary_category_id: categories.toString(),
+						features: Like("%DISCOVERABLE%"),
+					},
+					take: Math.abs(Number(limit || configLimit)),
+			  });
 	}
 
 	const total = guilds ? guilds.length : undefined;
 
-	res.send({ total: total, guilds: guilds, offset: Number(offset || Config.get().guild.discovery.offset), limit: Number(limit || configLimit) });
+	res.send({
+		total: total,
+		guilds: guilds,
+		offset: Number(offset || Config.get().guild.discovery.offset),
+		limit: Number(limit || configLimit),
+	});
 });
 
 export default router;
diff --git a/src/api/routes/discovery.ts b/src/api/routes/discovery.ts
index 6ab2cc13..90450035 100644
--- a/src/api/routes/discovery.ts
+++ b/src/api/routes/discovery.ts
@@ -10,7 +10,9 @@ router.get("/categories", route({}), async (req: Request, res: Response) => {
 
 	const { locale, primary_only } = req.query;
 
-	const out = primary_only ? await Categories.find() : await Categories.find({ where: { is_primary: true } });
+	const out = primary_only
+		? await Categories.find()
+		: await Categories.find({ where: { is_primary: true } });
 
 	res.send(out);
 });
diff --git a/src/api/routes/downloads.ts b/src/api/routes/downloads.ts
index df3df911..bc0750f7 100644
--- a/src/api/routes/downloads.ts
+++ b/src/api/routes/downloads.ts
@@ -10,9 +10,12 @@ router.get("/:branch", route({}), async (req: Request, res: Response) => {
 	const { platform } = req.query;
 	//TODO
 
-	if (!platform || !["linux", "osx", "win"].includes(platform.toString())) return res.status(404);
+	if (!platform || !["linux", "osx", "win"].includes(platform.toString()))
+		return res.status(404);
 
-	const release = await Release.findOneOrFail({ where: { name: client.releases.upstreamVersion } });
+	const release = await Release.findOneOrFail({
+		where: { name: client.releases.upstreamVersion },
+	});
 
 	res.redirect(release[`win_url`]);
 });
diff --git a/src/api/routes/experiments.ts b/src/api/routes/experiments.ts
index 7be86fb8..b2b7d724 100644
--- a/src/api/routes/experiments.ts
+++ b/src/api/routes/experiments.ts
@@ -5,7 +5,7 @@ const router = Router();
 
 router.get("/", route({}), (req: Request, res: Response) => {
 	// TODO:
-	res.send({ fingerprint: "", assignments: [], guild_experiments:[] });
+	res.send({ fingerprint: "", assignments: [], guild_experiments: [] });
 });
 
 export default router;
diff --git a/src/api/routes/gateway/bot.ts b/src/api/routes/gateway/bot.ts
index f1dbb9df..2e26d019 100644
--- a/src/api/routes/gateway/bot.ts
+++ b/src/api/routes/gateway/bot.ts
@@ -18,9 +18,9 @@ export interface GatewayBotResponse {
 const options: RouteOptions = {
 	test: {
 		response: {
-			body: "GatewayBotResponse"
-		}
-	}
+			body: "GatewayBotResponse",
+		},
+	},
 };
 
 router.get("/", route(options), (req: Request, res: Response) => {
@@ -32,8 +32,8 @@ router.get("/", route(options), (req: Request, res: Response) => {
 			total: 1000,
 			remaining: 999,
 			reset_after: 14400000,
-			max_concurrency: 1
-		}
+			max_concurrency: 1,
+		},
 	});
 });
 
diff --git a/src/api/routes/gateway/index.ts b/src/api/routes/gateway/index.ts
index 9bad7478..a6ed9dc4 100644
--- a/src/api/routes/gateway/index.ts
+++ b/src/api/routes/gateway/index.ts
@@ -11,14 +11,16 @@ export interface GatewayResponse {
 const options: RouteOptions = {
 	test: {
 		response: {
-			body: "GatewayResponse"
-		}
-	}
+			body: "GatewayResponse",
+		},
+	},
 };
 
 router.get("/", route(options), (req: Request, res: Response) => {
 	const { endpointPublic } = Config.get().gateway;
-	res.json({ url: endpointPublic || process.env.GATEWAY || "ws://localhost:3002" });
+	res.json({
+		url: endpointPublic || process.env.GATEWAY || "ws://localhost:3002",
+	});
 });
 
 export default router;
diff --git a/src/api/routes/gifs/search.ts b/src/api/routes/gifs/search.ts
index c7468641..54352215 100644
--- a/src/api/routes/gifs/search.ts
+++ b/src/api/routes/gifs/search.ts
@@ -1,6 +1,6 @@
 import { Router, Response, Request } from "express";
 import fetch from "node-fetch";
-import ProxyAgent from 'proxy-agent';
+import ProxyAgent from "proxy-agent";
 import { route } from "@fosscord/api";
 import { getGifApiKey, parseGifResult } from "./trending";
 
@@ -11,16 +11,19 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	const { q, media_format, locale } = req.query;
 
 	const apiKey = getGifApiKey();
-	
+
 	const agent = new ProxyAgent();
 
-	const response = await fetch(`https://g.tenor.com/v1/search?q=${q}&media_format=${media_format}&locale=${locale}&key=${apiKey}`, {
-		agent,
-		method: "get",
-		headers: { "Content-Type": "application/json" }
-	});
+	const response = await fetch(
+		`https://g.tenor.com/v1/search?q=${q}&media_format=${media_format}&locale=${locale}&key=${apiKey}`,
+		{
+			agent,
+			method: "get",
+			headers: { "Content-Type": "application/json" },
+		},
+	);
 
-	const { results } = await response.json() as any;	// TODO: types
+	const { results } = (await response.json()) as any; // TODO: types
 
 	res.json(results.map(parseGifResult)).status(200);
 });
diff --git a/src/api/routes/gifs/trending-gifs.ts b/src/api/routes/gifs/trending-gifs.ts
index 52a8969d..e4b28e24 100644
--- a/src/api/routes/gifs/trending-gifs.ts
+++ b/src/api/routes/gifs/trending-gifs.ts
@@ -1,6 +1,6 @@
 import { Router, Response, Request } from "express";
 import fetch from "node-fetch";
-import ProxyAgent from 'proxy-agent';
+import ProxyAgent from "proxy-agent";
 import { route } from "@fosscord/api";
 import { getGifApiKey, parseGifResult } from "./trending";
 
@@ -11,16 +11,19 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	const { media_format, locale } = req.query;
 
 	const apiKey = getGifApiKey();
-	
+
 	const agent = new ProxyAgent();
 
-	const response = await fetch(`https://g.tenor.com/v1/trending?media_format=${media_format}&locale=${locale}&key=${apiKey}`, {
-		agent,
-		method: "get",
-		headers: { "Content-Type": "application/json" }
-	});
+	const response = await fetch(
+		`https://g.tenor.com/v1/trending?media_format=${media_format}&locale=${locale}&key=${apiKey}`,
+		{
+			agent,
+			method: "get",
+			headers: { "Content-Type": "application/json" },
+		},
+	);
 
-	const { results } = await response.json() as any; // TODO: types
+	const { results } = (await response.json()) as any; // TODO: types
 
 	res.json(results.map(parseGifResult)).status(200);
 });
diff --git a/src/api/routes/gifs/trending.ts b/src/api/routes/gifs/trending.ts
index aa976c3f..58044ea5 100644
--- a/src/api/routes/gifs/trending.ts
+++ b/src/api/routes/gifs/trending.ts
@@ -1,6 +1,6 @@
 import { Router, Response, Request } from "express";
 import fetch from "node-fetch";
-import ProxyAgent from 'proxy-agent';
+import ProxyAgent from "proxy-agent";
 import { route } from "@fosscord/api";
 import { Config } from "@fosscord/util";
 import { HTTPError } from "lambert-server";
@@ -16,14 +16,15 @@ export function parseGifResult(result: any) {
 		gif_src: result.media[0].gif.url,
 		width: result.media[0].mp4.dims[0],
 		height: result.media[0].mp4.dims[1],
-		preview: result.media[0].mp4.preview
+		preview: result.media[0].mp4.preview,
 	};
 }
 
 export function getGifApiKey() {
 	const { enabled, provider, apiKey } = Config.get().gif;
 	if (!enabled) throw new HTTPError(`Gifs are disabled`);
-	if (provider !== "tenor" || !apiKey) throw new HTTPError(`${provider} gif provider not supported`);
+	if (provider !== "tenor" || !apiKey)
+		throw new HTTPError(`${provider} gif provider not supported`);
 
 	return apiKey;
 }
@@ -34,28 +35,37 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	const { media_format, locale } = req.query;
 
 	const apiKey = getGifApiKey();
-	
+
 	const agent = new ProxyAgent();
 
 	const [responseSource, trendGifSource] = await Promise.all([
-		fetch(`https://g.tenor.com/v1/categories?locale=${locale}&key=${apiKey}`, {
-			agent,
-			method: "get",
-			headers: { "Content-Type": "application/json" }
-		}),
-		fetch(`https://g.tenor.com/v1/trending?locale=${locale}&key=${apiKey}`, {
-			agent,
-			method: "get",
-			headers: { "Content-Type": "application/json" }
-		})
+		fetch(
+			`https://g.tenor.com/v1/categories?locale=${locale}&key=${apiKey}`,
+			{
+				agent,
+				method: "get",
+				headers: { "Content-Type": "application/json" },
+			},
+		),
+		fetch(
+			`https://g.tenor.com/v1/trending?locale=${locale}&key=${apiKey}`,
+			{
+				agent,
+				method: "get",
+				headers: { "Content-Type": "application/json" },
+			},
+		),
 	]);
 
-	const { tags } = await responseSource.json() as any; // TODO: types
-	const { results } = await trendGifSource.json() as any; //TODO: types;
+	const { tags } = (await responseSource.json()) as any; // TODO: types
+	const { results } = (await trendGifSource.json()) as any; //TODO: types;
 
 	res.json({
-		categories: tags.map((x: any) => ({ name: x.searchterm, src: x.image })),
-		gifs: [parseGifResult(results[0])]
+		categories: tags.map((x: any) => ({
+			name: x.searchterm,
+			src: x.image,
+		})),
+		gifs: [parseGifResult(results[0])],
 	}).status(200);
 });
 
diff --git a/src/api/routes/guild-recommendations.ts b/src/api/routes/guild-recommendations.ts
index b851d710..bda37973 100644
--- a/src/api/routes/guild-recommendations.ts
+++ b/src/api/routes/guild-recommendations.ts
@@ -13,12 +13,21 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	// TODO: implement this with default typeorm query
 	// const guilds = await Guild.find({ where: { features: "DISCOVERABLE" } }); //, take: Math.abs(Number(limit)) });
 
-	const genLoadId = (size: Number) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
+	const genLoadId = (size: Number) =>
+		[...Array(size)]
+			.map(() => Math.floor(Math.random() * 16).toString(16))
+			.join("");
 
 	const guilds = showAllGuilds
 		? await Guild.find({ take: Math.abs(Number(limit || 24)) })
-		: await Guild.find({ where: { features: Like("%DISCOVERABLE%") }, take: Math.abs(Number(limit || 24)) });
-	res.send({ recommended_guilds: guilds, load_id: `server_recs/${genLoadId(32)}` }).status(200);
+		: await Guild.find({
+				where: { features: Like("%DISCOVERABLE%") },
+				take: Math.abs(Number(limit || 24)),
+		  });
+	res.send({
+		recommended_guilds: guilds,
+		load_id: `server_recs/${genLoadId(32)}`,
+	}).status(200);
 });
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/audit-logs.ts b/src/api/routes/guilds/#guild_id/audit-logs.ts
index b54835fc..76a11f6b 100644
--- a/src/api/routes/guilds/#guild_id/audit-logs.ts
+++ b/src/api/routes/guilds/#guild_id/audit-logs.ts
@@ -11,7 +11,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 		webhooks: [],
 		guild_scheduled_events: [],
 		threads: [],
-		application_commands: []
+		application_commands: [],
 	});
 });
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/bans.ts b/src/api/routes/guilds/#guild_id/bans.ts
index ed00f9c0..930985d7 100644
--- a/src/api/routes/guilds/#guild_id/bans.ts
+++ b/src/api/routes/guilds/#guild_id/bans.ts
@@ -1,5 +1,15 @@
 import { Request, Response, Router } from "express";
-import { DiscordApiErrors, emitEvent, GuildBanAddEvent, GuildBanRemoveEvent, Ban, User, Member, BanRegistrySchema, BanModeratorSchema } from "@fosscord/util";
+import {
+	DiscordApiErrors,
+	emitEvent,
+	GuildBanAddEvent,
+	GuildBanRemoveEvent,
+	Ban,
+	User,
+	Member,
+	BanRegistrySchema,
+	BanModeratorSchema,
+} from "@fosscord/util";
 import { HTTPError } from "lambert-server";
 import { getIpAdress, route } from "@fosscord/api";
 
@@ -7,150 +17,184 @@ const router: Router = Router();
 
 /* TODO: Deleting the secrets is just a temporary go-around. Views should be implemented for both safety and better handling. */
 
-router.get("/", route({ permission: "BAN_MEMBERS" }), async (req: Request, res: Response) => {
-	const { guild_id } = req.params;
+router.get(
+	"/",
+	route({ permission: "BAN_MEMBERS" }),
+	async (req: Request, res: Response) => {
+		const { guild_id } = req.params;
 
-	let bans = await Ban.find({ where: { guild_id: guild_id } });
-	let promisesToAwait: object[] = [];
-	const bansObj: object[] = [];
+		let bans = await Ban.find({ where: { guild_id: guild_id } });
+		let promisesToAwait: object[] = [];
+		const bansObj: object[] = [];
 
-	bans.filter((ban) => ban.user_id !== ban.executor_id); // pretend self-bans don't exist to prevent victim chasing
+		bans.filter((ban) => ban.user_id !== ban.executor_id); // pretend self-bans don't exist to prevent victim chasing
 
-	bans.forEach((ban) => {
-		promisesToAwait.push(User.getPublicUser(ban.user_id));
-	});
-
-	const bannedUsers: object[] = await Promise.all(promisesToAwait);
-
-	bans.forEach((ban, index) => {
-		const user = bannedUsers[index] as User;
-		bansObj.push({
-			reason: ban.reason,
-			user: {
-				username: user.username,
-				discriminator: user.discriminator,
-				id: user.id,
-				avatar: user.avatar,
-				public_flags: user.public_flags
-			}
+		bans.forEach((ban) => {
+			promisesToAwait.push(User.getPublicUser(ban.user_id));
 		});
-	});
-
-	return res.json(bansObj);
-});
-
-router.get("/:user", route({ permission: "BAN_MEMBERS" }), async (req: Request, res: Response) => {
-	const { guild_id } = req.params;
-	const user_id = req.params.ban;
-
-	let ban = await Ban.findOneOrFail({ where: { guild_id: guild_id, user_id: user_id } }) as BanRegistrySchema;
-
-	if (ban.user_id === ban.executor_id) throw DiscordApiErrors.UNKNOWN_BAN;
-	// pretend self-bans don't exist to prevent victim chasing
-
-	/* Filter secret from registry. */
-
-	ban = ban as BanModeratorSchema;
-
-	delete ban.ip;
-
-	return res.json(ban);
-});
-
-router.put("/:user_id", route({ body: "BanCreateSchema", permission: "BAN_MEMBERS" }), async (req: Request, res: Response) => {
-	const { guild_id } = req.params;
-	const banned_user_id = req.params.user_id;
 
-	if ((req.user_id === banned_user_id) && (banned_user_id === req.permission!.cache.guild?.owner_id))
-		throw new HTTPError("You are the guild owner, hence can't ban yourself", 403);
-
-	if (req.permission!.cache.guild?.owner_id === banned_user_id) throw new HTTPError("You can't ban the owner", 400);
-
-	const banned_user = await User.getPublicUser(banned_user_id);
+		const bannedUsers: object[] = await Promise.all(promisesToAwait);
+
+		bans.forEach((ban, index) => {
+			const user = bannedUsers[index] as User;
+			bansObj.push({
+				reason: ban.reason,
+				user: {
+					username: user.username,
+					discriminator: user.discriminator,
+					id: user.id,
+					avatar: user.avatar,
+					public_flags: user.public_flags,
+				},
+			});
+		});
 
-	const ban = Ban.create({
-		user_id: banned_user_id,
-		guild_id: guild_id,
-		ip: getIpAdress(req),
-		executor_id: req.user_id,
-		reason: req.body.reason // || otherwise empty
-	});
+		return res.json(bansObj);
+	},
+);
+
+router.get(
+	"/:user",
+	route({ permission: "BAN_MEMBERS" }),
+	async (req: Request, res: Response) => {
+		const { guild_id } = req.params;
+		const user_id = req.params.ban;
+
+		let ban = (await Ban.findOneOrFail({
+			where: { guild_id: guild_id, user_id: user_id },
+		})) as BanRegistrySchema;
+
+		if (ban.user_id === ban.executor_id) throw DiscordApiErrors.UNKNOWN_BAN;
+		// pretend self-bans don't exist to prevent victim chasing
+
+		/* Filter secret from registry. */
+
+		ban = ban as BanModeratorSchema;
+
+		delete ban.ip;
+
+		return res.json(ban);
+	},
+);
+
+router.put(
+	"/:user_id",
+	route({ body: "BanCreateSchema", permission: "BAN_MEMBERS" }),
+	async (req: Request, res: Response) => {
+		const { guild_id } = req.params;
+		const banned_user_id = req.params.user_id;
+
+		if (
+			req.user_id === banned_user_id &&
+			banned_user_id === req.permission!.cache.guild?.owner_id
+		)
+			throw new HTTPError(
+				"You are the guild owner, hence can't ban yourself",
+				403,
+			);
+
+		if (req.permission!.cache.guild?.owner_id === banned_user_id)
+			throw new HTTPError("You can't ban the owner", 400);
+
+		const banned_user = await User.getPublicUser(banned_user_id);
+
+		const ban = Ban.create({
+			user_id: banned_user_id,
+			guild_id: guild_id,
+			ip: getIpAdress(req),
+			executor_id: req.user_id,
+			reason: req.body.reason, // || otherwise empty
+		});
 
-	await Promise.all([
-		Member.removeFromGuild(banned_user_id, guild_id),
-		ban.save(),
-		emitEvent({
-			event: "GUILD_BAN_ADD",
-			data: {
-				guild_id: guild_id,
-				user: banned_user
-			},
-			guild_id: guild_id
-		} as GuildBanAddEvent)
-	]);
-
-	return res.json(ban);
-});
-
-router.put("/@me", route({ body: "BanCreateSchema" }), async (req: Request, res: Response) => {
-	const { guild_id } = req.params;
-
-	const banned_user = await User.getPublicUser(req.params.user_id);
-
-	if (req.permission!.cache.guild?.owner_id === req.params.user_id)
-		throw new HTTPError("You are the guild owner, hence can't ban yourself", 403);
-
-	const ban = Ban.create({
-		user_id: req.params.user_id,
-		guild_id: guild_id,
-		ip: getIpAdress(req),
-		executor_id: req.params.user_id,
-		reason: req.body.reason // || otherwise empty
-	});
-
-	await Promise.all([
-		Member.removeFromGuild(req.user_id, guild_id),
-		ban.save(),
-		emitEvent({
-			event: "GUILD_BAN_ADD",
-			data: {
+		await Promise.all([
+			Member.removeFromGuild(banned_user_id, guild_id),
+			ban.save(),
+			emitEvent({
+				event: "GUILD_BAN_ADD",
+				data: {
+					guild_id: guild_id,
+					user: banned_user,
+				},
 				guild_id: guild_id,
-				user: banned_user
-			},
-			guild_id: guild_id
-		} as GuildBanAddEvent)
-	]);
+			} as GuildBanAddEvent),
+		]);
+
+		return res.json(ban);
+	},
+);
+
+router.put(
+	"/@me",
+	route({ body: "BanCreateSchema" }),
+	async (req: Request, res: Response) => {
+		const { guild_id } = req.params;
+
+		const banned_user = await User.getPublicUser(req.params.user_id);
+
+		if (req.permission!.cache.guild?.owner_id === req.params.user_id)
+			throw new HTTPError(
+				"You are the guild owner, hence can't ban yourself",
+				403,
+			);
+
+		const ban = Ban.create({
+			user_id: req.params.user_id,
+			guild_id: guild_id,
+			ip: getIpAdress(req),
+			executor_id: req.params.user_id,
+			reason: req.body.reason, // || otherwise empty
+		});
 
-	return res.json(ban);
-});
+		await Promise.all([
+			Member.removeFromGuild(req.user_id, guild_id),
+			ban.save(),
+			emitEvent({
+				event: "GUILD_BAN_ADD",
+				data: {
+					guild_id: guild_id,
+					user: banned_user,
+				},
+				guild_id: guild_id,
+			} as GuildBanAddEvent),
+		]);
 
-router.delete("/:user_id", route({ permission: "BAN_MEMBERS" }), async (req: Request, res: Response) => {
-	const { guild_id, user_id } = req.params;
+		return res.json(ban);
+	},
+);
 
-	let ban = await Ban.findOneOrFail({ where: { guild_id: guild_id, user_id: user_id } });
+router.delete(
+	"/:user_id",
+	route({ permission: "BAN_MEMBERS" }),
+	async (req: Request, res: Response) => {
+		const { guild_id, user_id } = req.params;
 
-	if (ban.user_id === ban.executor_id) throw DiscordApiErrors.UNKNOWN_BAN;
-	// make self-bans irreversible and hide them from view to avoid victim chasing
+		let ban = await Ban.findOneOrFail({
+			where: { guild_id: guild_id, user_id: user_id },
+		});
 
-	const banned_user = await User.getPublicUser(user_id);
+		if (ban.user_id === ban.executor_id) throw DiscordApiErrors.UNKNOWN_BAN;
+		// make self-bans irreversible and hide them from view to avoid victim chasing
 
-	await Promise.all([
-		Ban.delete({
-			user_id: user_id,
-			guild_id
-		}),
+		const banned_user = await User.getPublicUser(user_id);
 
-		emitEvent({
-			event: "GUILD_BAN_REMOVE",
-			data: {
+		await Promise.all([
+			Ban.delete({
+				user_id: user_id,
 				guild_id,
-				user: banned_user
-			},
-			guild_id
-		} as GuildBanRemoveEvent)
-	]);
-
-	return res.status(204).send();
-});
+			}),
+
+			emitEvent({
+				event: "GUILD_BAN_REMOVE",
+				data: {
+					guild_id,
+					user: banned_user,
+				},
+				guild_id,
+			} as GuildBanRemoveEvent),
+		]);
+
+		return res.status(204).send();
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts
index 7a5b50d1..af17465d 100644
--- a/src/api/routes/guilds/#guild_id/channels.ts
+++ b/src/api/routes/guilds/#guild_id/channels.ts
@@ -1,5 +1,10 @@
 import { Router, Response, Request } from "express";
-import { Channel, ChannelUpdateEvent, emitEvent, ChannelModifySchema } from "@fosscord/util";
+import {
+	Channel,
+	ChannelUpdateEvent,
+	emitEvent,
+	ChannelModifySchema,
+} from "@fosscord/util";
 import { HTTPError } from "lambert-server";
 import { route } from "@fosscord/api";
 const router = Router();
@@ -11,49 +16,77 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	res.json(channels);
 });
 
-router.post("/", route({ body: "ChannelModifySchema", permission: "MANAGE_CHANNELS" }), async (req: Request, res: Response) => {
-	// creates a new guild channel https://discord.com/developers/docs/resources/guild#create-guild-channel
-	const { guild_id } = req.params;
-	const body = req.body as ChannelModifySchema;
+router.post(
+	"/",
+	route({ body: "ChannelModifySchema", permission: "MANAGE_CHANNELS" }),
+	async (req: Request, res: Response) => {
+		// creates a new guild channel https://discord.com/developers/docs/resources/guild#create-guild-channel
+		const { guild_id } = req.params;
+		const body = req.body as ChannelModifySchema;
 
-	const channel = await Channel.createChannel({ ...body, guild_id }, req.user_id);
+		const channel = await Channel.createChannel(
+			{ ...body, guild_id },
+			req.user_id,
+		);
 
-	res.status(201).json(channel);
-});
+		res.status(201).json(channel);
+	},
+);
 
-export type ChannelReorderSchema = { id: string; position?: number; lock_permissions?: boolean; parent_id?: string; }[];
+export type ChannelReorderSchema = {
+	id: string;
+	position?: number;
+	lock_permissions?: boolean;
+	parent_id?: string;
+}[];
 
-router.patch("/", route({ body: "ChannelReorderSchema", permission: "MANAGE_CHANNELS" }), async (req: Request, res: Response) => {
-	// changes guild channel position
-	const { guild_id } = req.params;
-	const body = req.body as ChannelReorderSchema;
+router.patch(
+	"/",
+	route({ body: "ChannelReorderSchema", permission: "MANAGE_CHANNELS" }),
+	async (req: Request, res: Response) => {
+		// changes guild channel position
+		const { guild_id } = req.params;
+		const body = req.body as ChannelReorderSchema;
 
-	await Promise.all([
-		body.map(async (x) => {
-			if (x.position == null && !x.parent_id) throw new HTTPError(`You need to at least specify position or parent_id`, 400);
+		await Promise.all([
+			body.map(async (x) => {
+				if (x.position == null && !x.parent_id)
+					throw new HTTPError(
+						`You need to at least specify position or parent_id`,
+						400,
+					);
 
-			const opts: any = {};
-			if (x.position != null) opts.position = x.position;
+				const opts: any = {};
+				if (x.position != null) opts.position = x.position;
 
-			if (x.parent_id) {
-				opts.parent_id = x.parent_id;
-				const parent_channel = await Channel.findOneOrFail({
-					where: { id: x.parent_id, guild_id },
-					select: ["permission_overwrites"]
-				});
-				if (x.lock_permissions) {
-					opts.permission_overwrites = parent_channel.permission_overwrites;
+				if (x.parent_id) {
+					opts.parent_id = x.parent_id;
+					const parent_channel = await Channel.findOneOrFail({
+						where: { id: x.parent_id, guild_id },
+						select: ["permission_overwrites"],
+					});
+					if (x.lock_permissions) {
+						opts.permission_overwrites =
+							parent_channel.permission_overwrites;
+					}
 				}
-			}
 
-			await Channel.update({ guild_id, id: x.id }, opts);
-			const channel = await Channel.findOneOrFail({ where: { guild_id, id: x.id } });
+				await Channel.update({ guild_id, id: x.id }, opts);
+				const channel = await Channel.findOneOrFail({
+					where: { guild_id, id: x.id },
+				});
 
-			await emitEvent({ event: "CHANNEL_UPDATE", data: channel, channel_id: x.id, guild_id } as ChannelUpdateEvent);
-		})
-	]);
+				await emitEvent({
+					event: "CHANNEL_UPDATE",
+					data: channel,
+					channel_id: x.id,
+					guild_id,
+				} as ChannelUpdateEvent);
+			}),
+		]);
 
-	res.sendStatus(204);
-});
+		res.sendStatus(204);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/delete.ts b/src/api/routes/guilds/#guild_id/delete.ts
index bd158c56..b951e4f4 100644
--- a/src/api/routes/guilds/#guild_id/delete.ts
+++ b/src/api/routes/guilds/#guild_id/delete.ts
@@ -1,4 +1,14 @@
-import { Channel, emitEvent, GuildDeleteEvent, Guild, Member, Message, Role, Invite, Emoji } from "@fosscord/util";
+import {
+	Channel,
+	emitEvent,
+	GuildDeleteEvent,
+	Guild,
+	Member,
+	Message,
+	Role,
+	Invite,
+	Emoji,
+} from "@fosscord/util";
 import { Router, Request, Response } from "express";
 import { HTTPError } from "lambert-server";
 import { route } from "@fosscord/api";
@@ -10,18 +20,22 @@ const router = Router();
 router.post("/", route({}), async (req: Request, res: Response) => {
 	var { guild_id } = req.params;
 
-	const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: ["owner_id"] });
-	if (guild.owner_id !== req.user_id) throw new HTTPError("You are not the owner of this guild", 401);
+	const guild = await Guild.findOneOrFail({
+		where: { id: guild_id },
+		select: ["owner_id"],
+	});
+	if (guild.owner_id !== req.user_id)
+		throw new HTTPError("You are not the owner of this guild", 401);
 
 	await Promise.all([
 		Guild.delete({ id: guild_id }), // this will also delete all guild related data
 		emitEvent({
 			event: "GUILD_DELETE",
 			data: {
-				id: guild_id
+				id: guild_id,
 			},
-			guild_id: guild_id
-		} as GuildDeleteEvent)
+			guild_id: guild_id,
+		} as GuildDeleteEvent),
 	]);
 
 	return res.sendStatus(204);
diff --git a/src/api/routes/guilds/#guild_id/discovery-requirements.ts b/src/api/routes/guilds/#guild_id/discovery-requirements.ts
index ad20633f..7e63c06b 100644
--- a/src/api/routes/guilds/#guild_id/discovery-requirements.ts
+++ b/src/api/routes/guilds/#guild_id/discovery-requirements.ts
@@ -6,33 +6,33 @@ import { route } from "@fosscord/api";
 const router = Router();
 
 router.get("/", route({}), async (req: Request, res: Response) => {
-	const { guild_id } = req.params;	
-    // TODO:
-    // Load from database
-    // Admin control, but for now it allows anyone to be discoverable
+	const { guild_id } = req.params;
+	// TODO:
+	// Load from database
+	// Admin control, but for now it allows anyone to be discoverable
 
 	res.send({
 		guild_id: guild_id,
 		safe_environment: true,
-        healthy: true,
-        health_score_pending: false,
-        size: true,
-        nsfw_properties: {},
-        protected: true,
-        sufficient: true,
-        sufficient_without_grace_period: true,
-        valid_rules_channel: true,
-        retention_healthy: true,
-        engagement_healthy: true,
-        age: true,
-        minimum_age: 0,
-        health_score: {
-            avg_nonnew_participators: 0,
-            avg_nonnew_communicators: 0,
-            num_intentful_joiners: 0,
-            perc_ret_w1_intentful: 0
-        },
-        minimum_size: 0
+		healthy: true,
+		health_score_pending: false,
+		size: true,
+		nsfw_properties: {},
+		protected: true,
+		sufficient: true,
+		sufficient_without_grace_period: true,
+		valid_rules_channel: true,
+		retention_healthy: true,
+		engagement_healthy: true,
+		age: true,
+		minimum_age: 0,
+		health_score: {
+			avg_nonnew_participators: 0,
+			avg_nonnew_communicators: 0,
+			num_intentful_joiners: 0,
+			perc_ret_w1_intentful: 0,
+		},
+		minimum_size: 0,
 	});
 });
 
diff --git a/src/api/routes/guilds/#guild_id/emojis.ts b/src/api/routes/guilds/#guild_id/emojis.ts
index cf9d742a..6e8570eb 100644
--- a/src/api/routes/guilds/#guild_id/emojis.ts
+++ b/src/api/routes/guilds/#guild_id/emojis.ts
@@ -1,5 +1,17 @@
 import { Router, Request, Response } from "express";
-import { Config, DiscordApiErrors, emitEvent, Emoji, GuildEmojisUpdateEvent, handleFile, Member, Snowflake, User, EmojiCreateSchema, EmojiModifySchema } from "@fosscord/util";
+import {
+	Config,
+	DiscordApiErrors,
+	emitEvent,
+	Emoji,
+	GuildEmojisUpdateEvent,
+	handleFile,
+	Member,
+	Snowflake,
+	User,
+	EmojiCreateSchema,
+	EmojiModifySchema,
+} from "@fosscord/util";
 import { route } from "@fosscord/api";
 
 const router = Router();
@@ -9,7 +21,10 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 
 	await Member.IsInGuildOrFail(req.user_id, guild_id);
 
-	const emojis = await Emoji.find({ where: { guild_id: guild_id }, relations: ["user"] });
+	const emojis = await Emoji.find({
+		where: { guild_id: guild_id },
+		relations: ["user"],
+	});
 
 	return res.json(emojis);
 });
@@ -19,89 +34,115 @@ router.get("/:emoji_id", route({}), async (req: Request, res: Response) => {
 
 	await Member.IsInGuildOrFail(req.user_id, guild_id);
 
-	const emoji = await Emoji.findOneOrFail({ where: { guild_id: guild_id, id: emoji_id }, relations: ["user"] });
+	const emoji = await Emoji.findOneOrFail({
+		where: { guild_id: guild_id, id: emoji_id },
+		relations: ["user"],
+	});
 
 	return res.json(emoji);
 });
 
-router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => {
-	const { guild_id } = req.params;
-	const body = req.body as EmojiCreateSchema;
-
-	const id = Snowflake.generate();
-	const emoji_count = await Emoji.count({ where: { guild_id: guild_id } });
-	const { maxEmojis } = Config.get().limits.guild;
-
-	if (emoji_count >= maxEmojis) throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(maxEmojis);
-	if (body.require_colons == null) body.require_colons = true;
-
-	const user = await User.findOneOrFail({ where: { id: req.user_id } });
-	body.image = (await handleFile(`/emojis/${id}`, body.image)) as string;
-
-	const emoji = await Emoji.create({
-		id: id,
-		guild_id: guild_id,
-		...body,
-		require_colons: body.require_colons ?? undefined,	// schema allows nulls, db does not
-		user: user,
-		managed: false,
-		animated: false, // TODO: Add support animated emojis
-		available: true,
-		roles: []
-	}).save();
-
-	await emitEvent({
-		event: "GUILD_EMOJIS_UPDATE",
-		guild_id: guild_id,
-		data: {
+router.post(
+	"/",
+	route({
+		body: "EmojiCreateSchema",
+		permission: "MANAGE_EMOJIS_AND_STICKERS",
+	}),
+	async (req: Request, res: Response) => {
+		const { guild_id } = req.params;
+		const body = req.body as EmojiCreateSchema;
+
+		const id = Snowflake.generate();
+		const emoji_count = await Emoji.count({
+			where: { guild_id: guild_id },
+		});
+		const { maxEmojis } = Config.get().limits.guild;
+
+		if (emoji_count >= maxEmojis)
+			throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(
+				maxEmojis,
+			);
+		if (body.require_colons == null) body.require_colons = true;
+
+		const user = await User.findOneOrFail({ where: { id: req.user_id } });
+		body.image = (await handleFile(`/emojis/${id}`, body.image)) as string;
+
+		const emoji = await Emoji.create({
+			id: id,
 			guild_id: guild_id,
-			emojis: await Emoji.find({ where: { guild_id: guild_id } })
-		}
-	} as GuildEmojisUpdateEvent);
+			...body,
+			require_colons: body.require_colons ?? undefined, // schema allows nulls, db does not
+			user: user,
+			managed: false,
+			animated: false, // TODO: Add support animated emojis
+			available: true,
+			roles: [],
+		}).save();
 
-	return res.status(201).json(emoji);
-});
+		await emitEvent({
+			event: "GUILD_EMOJIS_UPDATE",
+			guild_id: guild_id,
+			data: {
+				guild_id: guild_id,
+				emojis: await Emoji.find({ where: { guild_id: guild_id } }),
+			},
+		} as GuildEmojisUpdateEvent);
+
+		return res.status(201).json(emoji);
+	},
+);
 
 router.patch(
 	"/:emoji_id",
-	route({ body: "EmojiModifySchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }),
+	route({
+		body: "EmojiModifySchema",
+		permission: "MANAGE_EMOJIS_AND_STICKERS",
+	}),
 	async (req: Request, res: Response) => {
 		const { emoji_id, guild_id } = req.params;
 		const body = req.body as EmojiModifySchema;
 
-		const emoji = await Emoji.create({ ...body, id: emoji_id, guild_id: guild_id }).save();
+		const emoji = await Emoji.create({
+			...body,
+			id: emoji_id,
+			guild_id: guild_id,
+		}).save();
 
 		await emitEvent({
 			event: "GUILD_EMOJIS_UPDATE",
 			guild_id: guild_id,
 			data: {
 				guild_id: guild_id,
-				emojis: await Emoji.find({ where: { guild_id: guild_id } })
-			}
+				emojis: await Emoji.find({ where: { guild_id: guild_id } }),
+			},
 		} as GuildEmojisUpdateEvent);
 
 		return res.json(emoji);
-	}
+	},
 );
 
-router.delete("/:emoji_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => {
-	const { emoji_id, guild_id } = req.params;
+router.delete(
+	"/:emoji_id",
+	route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }),
+	async (req: Request, res: Response) => {
+		const { emoji_id, guild_id } = req.params;
 
-	await Emoji.delete({
-		id: emoji_id,
-		guild_id: guild_id
-	});
+		await Emoji.delete({
+			id: emoji_id,
+			guild_id: guild_id,
+		});
 
-	await emitEvent({
-		event: "GUILD_EMOJIS_UPDATE",
-		guild_id: guild_id,
-		data: {
+		await emitEvent({
+			event: "GUILD_EMOJIS_UPDATE",
 			guild_id: guild_id,
-			emojis: await Emoji.find({ where: { guild_id: guild_id } })
-		}
-	} as GuildEmojisUpdateEvent);
+			data: {
+				guild_id: guild_id,
+				emojis: await Emoji.find({ where: { guild_id: guild_id } }),
+			},
+		} as GuildEmojisUpdateEvent);
 
-	res.sendStatus(204);
-});
+		res.sendStatus(204);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/index.ts b/src/api/routes/guilds/#guild_id/index.ts
index afeb0938..715a3835 100644
--- a/src/api/routes/guilds/#guild_id/index.ts
+++ b/src/api/routes/guilds/#guild_id/index.ts
@@ -1,5 +1,15 @@
 import { Request, Response, Router } from "express";
-import { DiscordApiErrors, emitEvent, getPermission, getRights, Guild, GuildUpdateEvent, handleFile, Member, GuildCreateSchema } from "@fosscord/util";
+import {
+	DiscordApiErrors,
+	emitEvent,
+	getPermission,
+	getRights,
+	Guild,
+	GuildUpdateEvent,
+	handleFile,
+	Member,
+	GuildCreateSchema,
+} from "@fosscord/util";
 import { HTTPError } from "lambert-server";
 import { route } from "@fosscord/api";
 
@@ -26,9 +36,13 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 
 	const [guild, member] = await Promise.all([
 		Guild.findOneOrFail({ where: { id: guild_id } }),
-		Member.findOne({ where: { guild_id: guild_id, id: req.user_id } })
+		Member.findOne({ where: { guild_id: guild_id, id: req.user_id } }),
 	]);
-	if (!member) throw new HTTPError("You are not a member of the guild you are trying to access", 401);
+	if (!member)
+		throw new HTTPError(
+			"You are not a member of the guild you are trying to access",
+			401,
+		);
 
 	// @ts-ignore
 	guild.joined_at = member?.joined_at;
@@ -36,39 +50,57 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	return res.send(guild);
 });
 
-router.patch("/", route({ body: "GuildUpdateSchema" }), async (req: Request, res: Response) => {
-	const body = req.body as GuildUpdateSchema;
-	const { guild_id } = req.params;
-
-
-	const rights = await getRights(req.user_id);
-	const permission = await getPermission(req.user_id, guild_id);
-
-	if (!rights.has("MANAGE_GUILDS") || !permission.has("MANAGE_GUILD"))
-		throw DiscordApiErrors.MISSING_PERMISSIONS.withParams("MANAGE_GUILD");
-
-	// TODO: guild update check image
-
-	if (body.icon) body.icon = await handleFile(`/icons/${guild_id}`, body.icon);
-	if (body.banner) body.banner = await handleFile(`/banners/${guild_id}`, body.banner);
-	if (body.splash) body.splash = await handleFile(`/splashes/${guild_id}`, body.splash);
-
-	var guild = await Guild.findOneOrFail({
-		where: { id: guild_id },
-		relations: ["emojis", "roles", "stickers"]
-	});
-	// TODO: check if body ids are valid
-	guild.assign(body);
-
-	const data = guild.toJSON();
-	// TODO: guild hashes
-	// TODO: fix vanity_url_code, template_id
-	delete data.vanity_url_code;
-	delete data.template_id;
-
-	await Promise.all([guild.save(), emitEvent({ event: "GUILD_UPDATE", data, guild_id } as GuildUpdateEvent)]);
-
-	return res.json(data);
-});
+router.patch(
+	"/",
+	route({ body: "GuildUpdateSchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as GuildUpdateSchema;
+		const { guild_id } = req.params;
+
+		const rights = await getRights(req.user_id);
+		const permission = await getPermission(req.user_id, guild_id);
+
+		if (!rights.has("MANAGE_GUILDS") || !permission.has("MANAGE_GUILD"))
+			throw DiscordApiErrors.MISSING_PERMISSIONS.withParams(
+				"MANAGE_GUILD",
+			);
+
+		// TODO: guild update check image
+
+		if (body.icon)
+			body.icon = await handleFile(`/icons/${guild_id}`, body.icon);
+		if (body.banner)
+			body.banner = await handleFile(`/banners/${guild_id}`, body.banner);
+		if (body.splash)
+			body.splash = await handleFile(
+				`/splashes/${guild_id}`,
+				body.splash,
+			);
+
+		var guild = await Guild.findOneOrFail({
+			where: { id: guild_id },
+			relations: ["emojis", "roles", "stickers"],
+		});
+		// TODO: check if body ids are valid
+		guild.assign(body);
+
+		const data = guild.toJSON();
+		// TODO: guild hashes
+		// TODO: fix vanity_url_code, template_id
+		delete data.vanity_url_code;
+		delete data.template_id;
+
+		await Promise.all([
+			guild.save(),
+			emitEvent({
+				event: "GUILD_UPDATE",
+				data,
+				guild_id,
+			} as GuildUpdateEvent),
+		]);
+
+		return res.json(data);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/invites.ts b/src/api/routes/guilds/#guild_id/invites.ts
index b7534e31..4d033e9c 100644
--- a/src/api/routes/guilds/#guild_id/invites.ts
+++ b/src/api/routes/guilds/#guild_id/invites.ts
@@ -4,12 +4,19 @@ import { Request, Response, Router } from "express";
 
 const router = Router();
 
-router.get("/", route({ permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
-	const { guild_id } = req.params;
+router.get(
+	"/",
+	route({ permission: "MANAGE_GUILD" }),
+	async (req: Request, res: Response) => {
+		const { guild_id } = req.params;
 
-	const invites = await Invite.find({ where: { guild_id }, relations: PublicInviteRelation });
+		const invites = await Invite.find({
+			where: { guild_id },
+			relations: PublicInviteRelation,
+		});
 
-	return res.json(invites);
-});
+		return res.json(invites);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/member-verification.ts b/src/api/routes/guilds/#guild_id/member-verification.ts
index 265a1b35..c2f946b2 100644
--- a/src/api/routes/guilds/#guild_id/member-verification.ts
+++ b/src/api/routes/guilds/#guild_id/member-verification.ts
@@ -2,12 +2,12 @@ import { Router, Request, Response } from "express";
 import { route } from "@fosscord/api";
 const router = Router();
 
-router.get("/",route({}), async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
 	// TODO: member verification
 
 	res.status(404).json({
 		message: "Unknown Guild Member Verification Form",
-		code: 10068
+		code: 10068,
 	});
 });
 
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 407619d3..2d867920 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
@@ -1,5 +1,16 @@
 import { Request, Response, Router } from "express";
-import { Member, getPermission, getRights, Role, GuildMemberUpdateEvent, emitEvent, Sticker, Emoji, Guild, MemberChangeSchema } from "@fosscord/util";
+import {
+	Member,
+	getPermission,
+	getRights,
+	Role,
+	GuildMemberUpdateEvent,
+	emitEvent,
+	Sticker,
+	Emoji,
+	Guild,
+	MemberChangeSchema,
+} from "@fosscord/util";
 import { route } from "@fosscord/api";
 
 const router = Router();
@@ -8,48 +19,63 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	const { guild_id, member_id } = req.params;
 	await Member.IsInGuildOrFail(req.user_id, guild_id);
 
-	const member = await Member.findOneOrFail({ where: { id: member_id, guild_id } });
+	const member = await Member.findOneOrFail({
+		where: { id: member_id, guild_id },
+	});
 
 	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;
-
-	const 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.roles) {
-		permission.hasThrow("MANAGE_ROLES");
-
-		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
-	}
-
-	if ('nick' in body) {
-		permission.hasThrow(req.user_id == member.user.id ? "CHANGE_NICKNAME" : "MANAGE_NICKNAMES");
-		member.nick = body.nick?.trim() || undefined;
-	}
-
-	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;
+
+		const 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.roles) {
+			permission.hasThrow("MANAGE_ROLES");
+
+			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
+		}
+
+		if ("nick" in body) {
+			permission.hasThrow(
+				req.user_id == member.user.id
+					? "CHANGE_NICKNAME"
+					: "MANAGE_NICKNAMES",
+			);
+			member.nick = body.nick?.trim() || undefined;
+		}
+
+		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
 
 	const rights = await getRights(req.user_id);
@@ -59,23 +85,23 @@ router.put("/", route({}), async (req: Request, res: Response) => {
 		member_id = req.user_id;
 		rights.hasThrow("JOIN_GUILDS");
 	} else {
-		// TODO: join others by controller	
+		// TODO: join others by controller
 	}
 
 	var guild = await Guild.findOneOrFail({
-		where: { id: guild_id }
+		where: { id: guild_id },
 	});
 
 	var emoji = await Emoji.find({
-		where: { guild_id: guild_id }
+		where: { guild_id: guild_id },
 	});
 
 	var roles = await Role.find({
-		where: { guild_id: guild_id }
+		where: { guild_id: guild_id },
 	});
 
 	var stickers = await Sticker.find({
-		where: { guild_id: guild_id }
+		where: { guild_id: guild_id },
 	});
 
 	await Member.addToGuild(member_id, guild_id);
diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts b/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts
index edd47605..20443821 100644
--- a/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts
+++ b/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts
@@ -4,19 +4,23 @@ import { Request, Response, Router } from "express";
 
 const router = Router();
 
-router.patch("/", route({ body: "MemberNickChangeSchema" }), async (req: Request, res: Response) => {
-	var { guild_id, member_id } = req.params;
-	var permissionString: PermissionResolvable = "MANAGE_NICKNAMES";
-	if (member_id === "@me") {
-		member_id = req.user_id;
-		permissionString = "CHANGE_NICKNAME";
-	}
+router.patch(
+	"/",
+	route({ body: "MemberNickChangeSchema" }),
+	async (req: Request, res: Response) => {
+		var { guild_id, member_id } = req.params;
+		var permissionString: PermissionResolvable = "MANAGE_NICKNAMES";
+		if (member_id === "@me") {
+			member_id = req.user_id;
+			permissionString = "CHANGE_NICKNAME";
+		}
 
-	const perms = await getPermission(req.user_id, guild_id);
-	perms.hasThrow(permissionString);
+		const perms = await getPermission(req.user_id, guild_id);
+		perms.hasThrow(permissionString);
 
-	await Member.changeNickname(member_id, guild_id, req.body.nick);
-	res.status(200).send();
-});
+		await Member.changeNickname(member_id, guild_id, req.body.nick);
+		res.status(200).send();
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts b/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts
index 8f5ca7ba..c0383912 100644
--- a/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts
+++ b/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts
@@ -4,18 +4,26 @@ import { Request, Response, Router } from "express";
 
 const router = Router();
 
-router.delete("/", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
-	const { guild_id, role_id, member_id } = req.params;
+router.delete(
+	"/",
+	route({ permission: "MANAGE_ROLES" }),
+	async (req: Request, res: Response) => {
+		const { guild_id, role_id, member_id } = req.params;
 
-	await Member.removeRole(member_id, guild_id, role_id);
-	res.sendStatus(204);
-});
+		await Member.removeRole(member_id, guild_id, role_id);
+		res.sendStatus(204);
+	},
+);
 
-router.put("/", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
-	const { guild_id, role_id, member_id } = req.params;
+router.put(
+	"/",
+	route({ permission: "MANAGE_ROLES" }),
+	async (req: Request, res: Response) => {
+		const { guild_id, role_id, member_id } = req.params;
 
-	await Member.addRole(member_id, guild_id, role_id);
-	res.sendStatus(204);
-});
+		await Member.addRole(member_id, guild_id, role_id);
+		res.sendStatus(204);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/members/index.ts b/src/api/routes/guilds/#guild_id/members/index.ts
index b730a4e7..b516b9e9 100644
--- a/src/api/routes/guilds/#guild_id/members/index.ts
+++ b/src/api/routes/guilds/#guild_id/members/index.ts
@@ -12,7 +12,8 @@ const router = Router();
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const { guild_id } = req.params;
 	const limit = Number(req.query.limit) || 1;
-	if (limit > 1000 || limit < 1) throw new HTTPError("Limit must be between 1 and 1000");
+	if (limit > 1000 || limit < 1)
+		throw new HTTPError("Limit must be between 1 and 1000");
 	const after = `${req.query.after}`;
 	const query = after ? { id: MoreThan(after) } : {};
 
@@ -22,7 +23,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 		where: { guild_id, ...query },
 		select: PublicMemberProjection,
 		take: limit,
-		order: { id: "ASC" }
+		order: { id: "ASC" },
 	});
 
 	return res.json(members);
diff --git a/src/api/routes/guilds/#guild_id/messages/search.ts b/src/api/routes/guilds/#guild_id/messages/search.ts
index a7516ebd..f2d8087e 100644
--- a/src/api/routes/guilds/#guild_id/messages/search.ts
+++ b/src/api/routes/guilds/#guild_id/messages/search.ts
@@ -10,36 +10,62 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	const {
 		channel_id,
 		content,
-		include_nsfw,		// TODO
+		include_nsfw, // TODO
 		offset,
 		sort_order,
-		sort_by,			// TODO: Handle 'relevance'
+		sort_by, // TODO: Handle 'relevance'
 		limit,
 		author_id,
 	} = req.query;
 
 	const parsedLimit = Number(limit) || 50;
-	if (parsedLimit < 1 || parsedLimit > 100) throw new HTTPError("limit must be between 1 and 100", 422);
+	if (parsedLimit < 1 || parsedLimit > 100)
+		throw new HTTPError("limit must be between 1 and 100", 422);
 
 	if (sort_order) {
-		if (typeof sort_order != "string"
-			|| ["desc", "asc"].indexOf(sort_order) == -1)
-			throw FieldErrors({ sort_order: { message: "Value must be one of ('desc', 'asc').", code: "BASE_TYPE_CHOICES" } }); // todo this is wrong
+		if (
+			typeof sort_order != "string" ||
+			["desc", "asc"].indexOf(sort_order) == -1
+		)
+			throw FieldErrors({
+				sort_order: {
+					message: "Value must be one of ('desc', 'asc').",
+					code: "BASE_TYPE_CHOICES",
+				},
+			}); // todo this is wrong
 	}
 
-	const permissions = await getPermission(req.user_id, req.params.guild_id, channel_id as string);
+	const permissions = await getPermission(
+		req.user_id,
+		req.params.guild_id,
+		channel_id as string,
+	);
 	permissions.hasThrow("VIEW_CHANNEL");
-	if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json({ messages: [], total_results: 0 });
+	if (!permissions.has("READ_MESSAGE_HISTORY"))
+		return res.json({ messages: [], total_results: 0 });
 
 	var query: FindManyOptions<Message> = {
-		order: { timestamp: sort_order ? sort_order.toUpperCase() as "ASC" | "DESC" : "DESC" },
+		order: {
+			timestamp: sort_order
+				? (sort_order.toUpperCase() as "ASC" | "DESC")
+				: "DESC",
+		},
 		take: parsedLimit || 0,
 		where: {
 			guild: {
 				id: req.params.guild_id,
 			},
 		},
-		relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"],
+		relations: [
+			"author",
+			"webhook",
+			"application",
+			"mentions",
+			"mention_roles",
+			"mention_channels",
+			"sticker_items",
+			"attachments",
+		],
 		skip: offset ? Number(offset) : 0,
 	};
 	//@ts-ignore
@@ -51,32 +77,34 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 
 	const messages: Message[] = await Message.find(query);
 
-	const messagesDto = messages.map(x => [{
-		id: x.id,
-		type: x.type,
-		content: x.content,
-		channel_id: x.channel_id,
-		author: {
-			id: x.author?.id,
-			username: x.author?.username,
-			avatar: x.author?.avatar,
-			avatar_decoration: null,
-			discriminator: x.author?.discriminator,
-			public_flags: x.author?.public_flags,
+	const messagesDto = messages.map((x) => [
+		{
+			id: x.id,
+			type: x.type,
+			content: x.content,
+			channel_id: x.channel_id,
+			author: {
+				id: x.author?.id,
+				username: x.author?.username,
+				avatar: x.author?.avatar,
+				avatar_decoration: null,
+				discriminator: x.author?.discriminator,
+				public_flags: x.author?.public_flags,
+			},
+			attachments: x.attachments,
+			embeds: x.embeds,
+			mentions: x.mentions,
+			mention_roles: x.mention_roles,
+			pinned: x.pinned,
+			mention_everyone: x.mention_everyone,
+			tts: x.tts,
+			timestamp: x.timestamp,
+			edited_timestamp: x.edited_timestamp,
+			flags: x.flags,
+			components: x.components,
+			hit: true,
 		},
-		attachments: x.attachments,
-		embeds: x.embeds,
-		mentions: x.mentions,
-		mention_roles: x.mention_roles,
-		pinned: x.pinned,
-		mention_everyone: x.mention_everyone,
-		tts: x.tts,
-		timestamp: x.timestamp,
-		edited_timestamp: x.edited_timestamp,
-		flags: x.flags,
-		components: x.components,
-		hit: true,
-	}]);
+	]);
 
 	return res.json({
 		messages: messagesDto,
@@ -84,4 +112,4 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	});
 });
 
-export default router;
\ No newline at end of file
+export default router;
diff --git a/src/api/routes/guilds/#guild_id/prune.ts b/src/api/routes/guilds/#guild_id/prune.ts
index 2e674349..d11244b1 100644
--- a/src/api/routes/guilds/#guild_id/prune.ts
+++ b/src/api/routes/guilds/#guild_id/prune.ts
@@ -5,7 +5,12 @@ import { route } from "@fosscord/api";
 const router = Router();
 
 //Returns all inactive members, respecting role hierarchy
-export const inactiveMembers = async (guild_id: string, user_id: string, days: number, roles: string[] = []) => {
+export const inactiveMembers = async (
+	guild_id: string,
+	user_id: string,
+	days: number,
+	roles: string[] = [],
+) => {
 	var date = new Date();
 	date.setDate(date.getDate() - days);
 	//Snowflake should have `generateFromTime` method? Or similar?
@@ -19,21 +24,27 @@ export const inactiveMembers = async (guild_id: string, user_id: string, days: n
 		where: [
 			{
 				guild_id,
-				last_message_id: LessThan(minId.toString())
+				last_message_id: LessThan(minId.toString()),
 			},
 			{
-				last_message_id: IsNull()
-			}
+				last_message_id: IsNull(),
+			},
 		],
-		relations: ["roles"]
+		relations: ["roles"],
 	});
 	console.log(members);
 	if (!members.length) return [];
 
 	//I'm sure I can do this in the above db query ( and it would probably be better to do so ), but oh well.
-	if (roles.length && members.length) members = members.filter((user) => user.roles?.some((role) => roles.includes(role.id)));
-
-	const me = await Member.findOneOrFail({ where: { id: user_id, guild_id }, relations: ["roles"] });
+	if (roles.length && members.length)
+		members = members.filter((user) =>
+			user.roles?.some((role) => roles.includes(role.id)),
+		);
+
+	const me = await Member.findOneOrFail({
+		where: { id: user_id, guild_id },
+		relations: ["roles"],
+	});
 	const myHighestRole = Math.max(...(me.roles?.map((x) => x.position) || []));
 
 	const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
@@ -44,8 +55,8 @@ export const inactiveMembers = async (guild_id: string, user_id: string, days: n
 			member.roles?.some(
 				(role) =>
 					role.position < myHighestRole || //roles higher than me can't be kicked
-					me.id === guild.owner_id //owner can kick anyone
-			)
+					me.id === guild.owner_id, //owner can kick anyone
+			),
 	);
 
 	return members;
@@ -57,23 +68,39 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	var roles = req.query.include_roles;
 	if (typeof roles === "string") roles = [roles]; //express will return array otherwise
 
-	const members = await inactiveMembers(req.params.guild_id, req.user_id, days, roles as string[]);
+	const members = await inactiveMembers(
+		req.params.guild_id,
+		req.user_id,
+		days,
+		roles as string[],
+	);
 
 	res.send({ pruned: members.length });
 });
 
-router.post("/", route({ permission: "KICK_MEMBERS", right: "KICK_BAN_MEMBERS" }), async (req: Request, res: Response) => {
-	const days = parseInt(req.body.days);
-
-	var roles = req.query.include_roles;
-	if (typeof roles === "string") roles = [roles];
-
-	const { guild_id } = req.params;
-	const members = await inactiveMembers(guild_id, req.user_id, days, roles as string[]);
-
-	await Promise.all(members.map((x) => Member.removeFromGuild(x.id, guild_id)));
-
-	res.send({ purged: members.length });
-});
+router.post(
+	"/",
+	route({ permission: "KICK_MEMBERS", right: "KICK_BAN_MEMBERS" }),
+	async (req: Request, res: Response) => {
+		const days = parseInt(req.body.days);
+
+		var roles = req.query.include_roles;
+		if (typeof roles === "string") roles = [roles];
+
+		const { guild_id } = req.params;
+		const members = await inactiveMembers(
+			guild_id,
+			req.user_id,
+			days,
+			roles as string[],
+		);
+
+		await Promise.all(
+			members.map((x) => Member.removeFromGuild(x.id, guild_id)),
+		);
+
+		res.send({ purged: members.length });
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/regions.ts b/src/api/routes/guilds/#guild_id/regions.ts
index 308d5ee5..0b275ea4 100644
--- a/src/api/routes/guilds/#guild_id/regions.ts
+++ b/src/api/routes/guilds/#guild_id/regions.ts
@@ -9,7 +9,12 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	const { guild_id } = req.params;
 	const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
 	//TODO we should use an enum for guild's features and not hardcoded strings
-	return res.json(await getVoiceRegions(getIpAdress(req), guild.features.includes("VIP_REGIONS")));
+	return res.json(
+		await getVoiceRegions(
+			getIpAdress(req),
+			guild.features.includes("VIP_REGIONS"),
+		),
+	);
 });
 
 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 87cf5261..e274e3d0 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
@@ -1,5 +1,13 @@
 import { Router, Request, Response } from "express";
-import { Role, Member, GuildRoleUpdateEvent, GuildRoleDeleteEvent, emitEvent, handleFile, RoleModifySchema } from "@fosscord/util";
+import {
+	Role,
+	Member,
+	GuildRoleUpdateEvent,
+	GuildRoleDeleteEvent,
+	emitEvent,
+	handleFile,
+	RoleModifySchema,
+} from "@fosscord/util";
 import { route } from "@fosscord/api";
 import { HTTPError } from "lambert-server";
 
@@ -12,57 +20,72 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	return res.json(role);
 });
 
-router.delete("/", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
-	const { guild_id, role_id } = req.params;
-	if (role_id === guild_id) throw new HTTPError("You can't delete the @everyone role");
+router.delete(
+	"/",
+	route({ permission: "MANAGE_ROLES" }),
+	async (req: Request, res: Response) => {
+		const { guild_id, role_id } = req.params;
+		if (role_id === guild_id)
+			throw new HTTPError("You can't delete the @everyone role");
 
-	await Promise.all([
-		Role.delete({
-			id: role_id,
-			guild_id: guild_id
-		}),
-		emitEvent({
-			event: "GUILD_ROLE_DELETE",
-			guild_id,
-			data: {
+		await Promise.all([
+			Role.delete({
+				id: role_id,
+				guild_id: guild_id,
+			}),
+			emitEvent({
+				event: "GUILD_ROLE_DELETE",
 				guild_id,
-				role_id
-			}
-		} as GuildRoleDeleteEvent)
-	]);
+				data: {
+					guild_id,
+					role_id,
+				},
+			} as GuildRoleDeleteEvent),
+		]);
 
-	res.sendStatus(204);
-});
+		res.sendStatus(204);
+	},
+);
 
 // TODO: check role hierarchy
 
-router.patch("/", route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
-	const { role_id, guild_id } = req.params;
-	const body = req.body as RoleModifySchema;
+router.patch(
+	"/",
+	route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" }),
+	async (req: Request, res: Response) => {
+		const { role_id, guild_id } = req.params;
+		const body = req.body as RoleModifySchema;
 
-	if (body.icon && body.icon.length) body.icon = await handleFile(`/role-icons/${role_id}`, body.icon as string);
-	else body.icon = undefined;
+		if (body.icon && body.icon.length)
+			body.icon = await handleFile(
+				`/role-icons/${role_id}`,
+				body.icon as string,
+			);
+		else body.icon = undefined;
 
-	const role = Role.create({
-		...body,
-		id: role_id,
-		guild_id,
-		permissions: String(req.permission!.bitfield & BigInt(body.permissions || "0"))
-	});
-
-	await Promise.all([
-		role.save(),
-		emitEvent({
-			event: "GUILD_ROLE_UPDATE",
+		const role = Role.create({
+			...body,
+			id: role_id,
 			guild_id,
-			data: {
+			permissions: String(
+				req.permission!.bitfield & BigInt(body.permissions || "0"),
+			),
+		});
+
+		await Promise.all([
+			role.save(),
+			emitEvent({
+				event: "GUILD_ROLE_UPDATE",
 				guild_id,
-				role
-			}
-		} as GuildRoleUpdateEvent)
-	]);
+				data: {
+					guild_id,
+					role,
+				},
+			} as GuildRoleUpdateEvent),
+		]);
 
-	res.json(role);
-});
+		res.json(role);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/roles/index.ts b/src/api/routes/guilds/#guild_id/roles/index.ts
index c5a86400..e3c7373e 100644
--- a/src/api/routes/guilds/#guild_id/roles/index.ts
+++ b/src/api/routes/guilds/#guild_id/roles/index.ts
@@ -29,70 +29,87 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	return res.json(roles);
 });
 
-router.post("/", route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
-	const guild_id = req.params.guild_id;
-	const body = req.body as RoleModifySchema;
-
-	const role_count = await Role.count({ where: { guild_id } });
-	const { maxRoles } = Config.get().limits.guild;
-
-	if (role_count > maxRoles) throw DiscordApiErrors.MAXIMUM_ROLES.withParams(maxRoles);
-
-	const role = Role.create({
-		// values before ...body are default and can be overriden
-		position: 0,
-		hoist: false,
-		color: 0,
-		mentionable: false,
-		...body,
-		guild_id: guild_id,
-		managed: false,
-		permissions: String(req.permission!.bitfield & BigInt(body.permissions || "0")),
-		tags: undefined,
-		icon: undefined,
-		unicode_emoji: undefined
-	});
-
-	await Promise.all([
-		role.save(),
-		emitEvent({
-			event: "GUILD_ROLE_CREATE",
-			guild_id,
-			data: {
-				guild_id,
-				role: role
-			}
-		} as GuildRoleCreateEvent)
-	]);
-
-	res.json(role);
-});
-
-router.patch("/", route({ body: "RolePositionUpdateSchema" }), async (req: Request, res: Response) => {
-	const { guild_id } = req.params;
-	const body = req.body as RolePositionUpdateSchema;
-
-	const perms = await getPermission(req.user_id, guild_id);
-	perms.hasThrow("MANAGE_ROLES");
-
-	await Promise.all(body.map(async (x) => Role.update({ guild_id, id: x.id }, { position: x.position })));
-
-	const roles = await Role.find({ where: body.map((x) => ({ id: x.id, guild_id })) });
-
-	await Promise.all(
-		roles.map((x) =>
+router.post(
+	"/",
+	route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" }),
+	async (req: Request, res: Response) => {
+		const guild_id = req.params.guild_id;
+		const body = req.body as RoleModifySchema;
+
+		const role_count = await Role.count({ where: { guild_id } });
+		const { maxRoles } = Config.get().limits.guild;
+
+		if (role_count > maxRoles)
+			throw DiscordApiErrors.MAXIMUM_ROLES.withParams(maxRoles);
+
+		const role = Role.create({
+			// values before ...body are default and can be overriden
+			position: 0,
+			hoist: false,
+			color: 0,
+			mentionable: false,
+			...body,
+			guild_id: guild_id,
+			managed: false,
+			permissions: String(
+				req.permission!.bitfield & BigInt(body.permissions || "0"),
+			),
+			tags: undefined,
+			icon: undefined,
+			unicode_emoji: undefined,
+		});
+
+		await Promise.all([
+			role.save(),
 			emitEvent({
-				event: "GUILD_ROLE_UPDATE",
+				event: "GUILD_ROLE_CREATE",
 				guild_id,
 				data: {
 					guild_id,
-					role: x
-				}
-			} as GuildRoleUpdateEvent)
-		)
-	);
-
-	res.json(roles);
-});
+					role: role,
+				},
+			} as GuildRoleCreateEvent),
+		]);
+
+		res.json(role);
+	},
+);
+
+router.patch(
+	"/",
+	route({ body: "RolePositionUpdateSchema" }),
+	async (req: Request, res: Response) => {
+		const { guild_id } = req.params;
+		const body = req.body as RolePositionUpdateSchema;
+
+		const perms = await getPermission(req.user_id, guild_id);
+		perms.hasThrow("MANAGE_ROLES");
+
+		await Promise.all(
+			body.map(async (x) =>
+				Role.update({ guild_id, id: x.id }, { position: x.position }),
+			),
+		);
+
+		const roles = await Role.find({
+			where: body.map((x) => ({ id: x.id, guild_id })),
+		});
+
+		await Promise.all(
+			roles.map((x) =>
+				emitEvent({
+					event: "GUILD_ROLE_UPDATE",
+					guild_id,
+					data: {
+						guild_id,
+						role: x,
+					},
+				} as GuildRoleUpdateEvent),
+			),
+		);
+
+		res.json(roles);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/stickers.ts b/src/api/routes/guilds/#guild_id/stickers.ts
index fc0f49ab..3b1f5f8e 100644
--- a/src/api/routes/guilds/#guild_id/stickers.ts
+++ b/src/api/routes/guilds/#guild_id/stickers.ts
@@ -26,15 +26,18 @@ const bodyParser = multer({
 	limits: {
 		fileSize: 1024 * 1024 * 100,
 		fields: 10,
-		files: 1
+		files: 1,
 	},
-	storage: multer.memoryStorage()
+	storage: multer.memoryStorage(),
 }).single("file");
 
 router.post(
 	"/",
 	bodyParser,
-	route({ permission: "MANAGE_EMOJIS_AND_STICKERS", body: "ModifyGuildStickerSchema" }),
+	route({
+		permission: "MANAGE_EMOJIS_AND_STICKERS",
+		body: "ModifyGuildStickerSchema",
+	}),
 	async (req: Request, res: Response) => {
 		if (!req.file) throw new HTTPError("missing file");
 
@@ -49,15 +52,15 @@ router.post(
 				id,
 				type: StickerType.GUILD,
 				format_type: getStickerFormat(req.file.mimetype),
-				available: true
+				available: true,
 			}).save(),
-			uploadFile(`/stickers/${id}`, req.file)
+			uploadFile(`/stickers/${id}`, req.file),
 		]);
 
 		await sendStickerUpdateEvent(guild_id);
 
 		res.json(sticker);
-	}
+	},
 );
 
 export function getStickerFormat(mime_type: string) {
@@ -71,7 +74,9 @@ export function getStickerFormat(mime_type: string) {
 		case "image/gif":
 			return StickerFormatType.GIF;
 		default:
-			throw new HTTPError("invalid sticker format: must be png, apng or lottie");
+			throw new HTTPError(
+				"invalid sticker format: must be png, apng or lottie",
+			);
 	}
 }
 
@@ -79,21 +84,30 @@ router.get("/:sticker_id", route({}), async (req: Request, res: Response) => {
 	const { guild_id, sticker_id } = req.params;
 	await Member.IsInGuildOrFail(req.user_id, guild_id);
 
-	res.json(await Sticker.findOneOrFail({ where: { guild_id, id: sticker_id } }));
+	res.json(
+		await Sticker.findOneOrFail({ where: { guild_id, id: sticker_id } }),
+	);
 });
 
 router.patch(
 	"/:sticker_id",
-	route({ body: "ModifyGuildStickerSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }),
+	route({
+		body: "ModifyGuildStickerSchema",
+		permission: "MANAGE_EMOJIS_AND_STICKERS",
+	}),
 	async (req: Request, res: Response) => {
 		const { guild_id, sticker_id } = req.params;
 		const body = req.body as ModifyGuildStickerSchema;
 
-		const sticker = await Sticker.create({ ...body, guild_id, id: sticker_id }).save();
+		const sticker = await Sticker.create({
+			...body,
+			guild_id,
+			id: sticker_id,
+		}).save();
 		await sendStickerUpdateEvent(guild_id);
 
 		return res.json(sticker);
-	}
+	},
 );
 
 async function sendStickerUpdateEvent(guild_id: string) {
@@ -102,18 +116,22 @@ async function sendStickerUpdateEvent(guild_id: string) {
 		guild_id: guild_id,
 		data: {
 			guild_id: guild_id,
-			stickers: await Sticker.find({ where: { guild_id: guild_id } })
-		}
+			stickers: await Sticker.find({ where: { guild_id: guild_id } }),
+		},
 	} as GuildStickersUpdateEvent);
 }
 
-router.delete("/:sticker_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => {
-	const { guild_id, sticker_id } = req.params;
+router.delete(
+	"/:sticker_id",
+	route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }),
+	async (req: Request, res: Response) => {
+		const { guild_id, sticker_id } = req.params;
 
-	await Sticker.delete({ guild_id, id: sticker_id });
-	await sendStickerUpdateEvent(guild_id);
+		await Sticker.delete({ guild_id, id: sticker_id });
+		await sendStickerUpdateEvent(guild_id);
 
-	return res.sendStatus(204);
-});
+		return res.sendStatus(204);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/templates.ts b/src/api/routes/guilds/#guild_id/templates.ts
index 628321f5..3b5eddaa 100644
--- a/src/api/routes/guilds/#guild_id/templates.ts
+++ b/src/api/routes/guilds/#guild_id/templates.ts
@@ -20,63 +20,97 @@ const TemplateGuildProjection: (keyof Guild)[] = [
 	"afk_channel_id",
 	"system_channel_id",
 	"system_channel_flags",
-	"icon"
+	"icon",
 ];
 
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const { guild_id } = req.params;
 
-	var templates = await Template.find({ where: { source_guild_id: guild_id } });
-
-	return res.json(templates);
-});
-
-router.post("/", route({ body: "TemplateCreateSchema", permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
-	const { guild_id } = req.params;
-	const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: TemplateGuildProjection });
-	const exists = await Template.findOneOrFail({ where: { id: guild_id } }).catch((e) => { });
-	if (exists) throw new HTTPError("Template already exists", 400);
-
-	const template = await Template.create({
-		...req.body,
-		code: generateCode(),
-		creator_id: req.user_id,
-		created_at: new Date(),
-		updated_at: new Date(),
-		source_guild_id: guild_id,
-		serialized_source_guild: guild
-	}).save();
-
-	res.json(template);
-});
-
-router.delete("/:code", route({ permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
-	const { code, guild_id } = req.params;
-
-	const template = await Template.delete({
-		code,
-		source_guild_id: guild_id
+	var templates = await Template.find({
+		where: { source_guild_id: guild_id },
 	});
 
-	res.json(template);
-});
-
-router.put("/:code", route({ permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
-	const { code, guild_id } = req.params;
-	const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: TemplateGuildProjection });
-
-	const template = await Template.create({ code, serialized_source_guild: guild }).save();
-
-	res.json(template);
+	return res.json(templates);
 });
 
-router.patch("/:code", route({ body: "TemplateModifySchema", permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
-	const { code, guild_id } = req.params;
-	const { name, description } = req.body;
-
-	const template = await Template.create({ code, name: name, description: description, source_guild_id: guild_id }).save();
-
-	res.json(template);
-});
+router.post(
+	"/",
+	route({ body: "TemplateCreateSchema", permission: "MANAGE_GUILD" }),
+	async (req: Request, res: Response) => {
+		const { guild_id } = req.params;
+		const guild = await Guild.findOneOrFail({
+			where: { id: guild_id },
+			select: TemplateGuildProjection,
+		});
+		const exists = await Template.findOneOrFail({
+			where: { id: guild_id },
+		}).catch((e) => {});
+		if (exists) throw new HTTPError("Template already exists", 400);
+
+		const template = await Template.create({
+			...req.body,
+			code: generateCode(),
+			creator_id: req.user_id,
+			created_at: new Date(),
+			updated_at: new Date(),
+			source_guild_id: guild_id,
+			serialized_source_guild: guild,
+		}).save();
+
+		res.json(template);
+	},
+);
+
+router.delete(
+	"/:code",
+	route({ permission: "MANAGE_GUILD" }),
+	async (req: Request, res: Response) => {
+		const { code, guild_id } = req.params;
+
+		const template = await Template.delete({
+			code,
+			source_guild_id: guild_id,
+		});
+
+		res.json(template);
+	},
+);
+
+router.put(
+	"/:code",
+	route({ permission: "MANAGE_GUILD" }),
+	async (req: Request, res: Response) => {
+		const { code, guild_id } = req.params;
+		const guild = await Guild.findOneOrFail({
+			where: { id: guild_id },
+			select: TemplateGuildProjection,
+		});
+
+		const template = await Template.create({
+			code,
+			serialized_source_guild: guild,
+		}).save();
+
+		res.json(template);
+	},
+);
+
+router.patch(
+	"/:code",
+	route({ body: "TemplateModifySchema", permission: "MANAGE_GUILD" }),
+	async (req: Request, res: Response) => {
+		const { code, guild_id } = req.params;
+		const { name, description } = req.body;
+
+		const template = await Template.create({
+			code,
+			name: name,
+			description: description,
+			source_guild_id: guild_id,
+		}).save();
+
+		res.json(template);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/vanity-url.ts b/src/api/routes/guilds/#guild_id/vanity-url.ts
index d1fe4726..9a96b066 100644
--- a/src/api/routes/guilds/#guild_id/vanity-url.ts
+++ b/src/api/routes/guilds/#guild_id/vanity-url.ts
@@ -1,4 +1,10 @@
-import { Channel, ChannelType, Guild, Invite, VanityUrlSchema } from "@fosscord/util";
+import {
+	Channel,
+	ChannelType,
+	Guild,
+	Invite,
+	VanityUrlSchema,
+} from "@fosscord/util";
 import { Router, Request, Response } from "express";
 import { route } from "@fosscord/api";
 import { HTTPError } from "lambert-server";
@@ -7,52 +13,70 @@ const router = Router();
 
 const InviteRegex = /\W/g;
 
-router.get("/", route({ permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
-	const { guild_id } = req.params;
-	const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
-
-	if (!guild.features.includes("ALIASABLE_NAMES")) {
-		const invite = await Invite.findOne({ where: { guild_id: guild_id, vanity_url: true } });
-		if (!invite) return res.json({ code: null });
-
-		return res.json({ code: invite.code, uses: invite.uses });
-	} else {
-		const invite = await Invite.find({ where: { guild_id: guild_id, vanity_url: true } });
-		if (!invite || invite.length == 0) return res.json({ code: null });
-
-		return res.json(invite.map((x) => ({ code: x.code, uses: x.uses })));
-	}
-});
-
-router.patch("/", route({ body: "VanityUrlSchema", permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
-	const { guild_id } = req.params;
-	const body = req.body as VanityUrlSchema;
-	const code = body.code?.replace(InviteRegex, "");
-
-	const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
-	if (!guild.features.includes("VANITY_URL")) throw new HTTPError("Your guild doesn't support vanity urls");
-
-	if (!code || code.length === 0) throw new HTTPError("Code cannot be null or empty");
-
-	const invite = await Invite.findOne({ where: { code } });
-	if (invite) throw new HTTPError("Invite already exists");
-
-	const { id } = await Channel.findOneOrFail({ where: { guild_id, type: ChannelType.GUILD_TEXT } });
-
-	await Invite.create({
-		vanity_url: true,
-		code: code,
-		temporary: false,
-		uses: 0,
-		max_uses: 0,
-		max_age: 0,
-		created_at: new Date(),
-		expires_at: new Date(),
-		guild_id: guild_id,
-		channel_id: id
-	}).save();
-
-	return res.json({ code: code });
-});
+router.get(
+	"/",
+	route({ permission: "MANAGE_GUILD" }),
+	async (req: Request, res: Response) => {
+		const { guild_id } = req.params;
+		const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
+
+		if (!guild.features.includes("ALIASABLE_NAMES")) {
+			const invite = await Invite.findOne({
+				where: { guild_id: guild_id, vanity_url: true },
+			});
+			if (!invite) return res.json({ code: null });
+
+			return res.json({ code: invite.code, uses: invite.uses });
+		} else {
+			const invite = await Invite.find({
+				where: { guild_id: guild_id, vanity_url: true },
+			});
+			if (!invite || invite.length == 0) return res.json({ code: null });
+
+			return res.json(
+				invite.map((x) => ({ code: x.code, uses: x.uses })),
+			);
+		}
+	},
+);
+
+router.patch(
+	"/",
+	route({ body: "VanityUrlSchema", permission: "MANAGE_GUILD" }),
+	async (req: Request, res: Response) => {
+		const { guild_id } = req.params;
+		const body = req.body as VanityUrlSchema;
+		const code = body.code?.replace(InviteRegex, "");
+
+		const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
+		if (!guild.features.includes("VANITY_URL"))
+			throw new HTTPError("Your guild doesn't support vanity urls");
+
+		if (!code || code.length === 0)
+			throw new HTTPError("Code cannot be null or empty");
+
+		const invite = await Invite.findOne({ where: { code } });
+		if (invite) throw new HTTPError("Invite already exists");
+
+		const { id } = await Channel.findOneOrFail({
+			where: { guild_id, type: ChannelType.GUILD_TEXT },
+		});
+
+		await Invite.create({
+			vanity_url: true,
+			code: code,
+			temporary: false,
+			uses: 0,
+			max_uses: 0,
+			max_age: 0,
+			created_at: new Date(),
+			expires_at: new Date(),
+			guild_id: guild_id,
+			channel_id: id,
+		}).save();
+
+		return res.json({ code: code });
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts b/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts
index 006e997f..af03a07e 100644
--- a/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts
+++ b/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts
@@ -1,52 +1,71 @@
-import { Channel, ChannelType, DiscordApiErrors, emitEvent, getPermission, VoiceState, VoiceStateUpdateEvent, VoiceStateUpdateSchema } from "@fosscord/util";
+import {
+	Channel,
+	ChannelType,
+	DiscordApiErrors,
+	emitEvent,
+	getPermission,
+	VoiceState,
+	VoiceStateUpdateEvent,
+	VoiceStateUpdateSchema,
+} from "@fosscord/util";
 import { route } from "@fosscord/api";
 import { Request, Response, Router } from "express";
 
 const router = Router();
 //TODO need more testing when community guild and voice stage channel are working
 
-router.patch("/", route({ body: "VoiceStateUpdateSchema" }), async (req: Request, res: Response) => {
-	const body = req.body as VoiceStateUpdateSchema;
-	var { guild_id, user_id } = req.params;
-	if (user_id === "@me") user_id = req.user_id;
+router.patch(
+	"/",
+	route({ body: "VoiceStateUpdateSchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as VoiceStateUpdateSchema;
+		var { guild_id, user_id } = req.params;
+		if (user_id === "@me") user_id = req.user_id;
 
-	const perms = await getPermission(req.user_id, guild_id, body.channel_id);
+		const perms = await getPermission(
+			req.user_id,
+			guild_id,
+			body.channel_id,
+		);
 
-	/*
+		/*
 	From https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state
 	You must have the MUTE_MEMBERS permission to unsuppress others. You can always suppress yourself.
 	You must have the REQUEST_TO_SPEAK permission to request to speak. You can always clear your own request to speak.
 	 */
-	if (body.suppress && user_id !== req.user_id) {
-		perms.hasThrow("MUTE_MEMBERS");
-	}
-	if (!body.suppress) body.request_to_speak_timestamp = new Date();
-	if (body.request_to_speak_timestamp) perms.hasThrow("REQUEST_TO_SPEAK");
-
-	const voice_state = await VoiceState.findOne({
-		where: {
-			guild_id,
-			channel_id: body.channel_id,
-			user_id
+		if (body.suppress && user_id !== req.user_id) {
+			perms.hasThrow("MUTE_MEMBERS");
+		}
+		if (!body.suppress) body.request_to_speak_timestamp = new Date();
+		if (body.request_to_speak_timestamp) perms.hasThrow("REQUEST_TO_SPEAK");
+
+		const voice_state = await VoiceState.findOne({
+			where: {
+				guild_id,
+				channel_id: body.channel_id,
+				user_id,
+			},
+		});
+		if (!voice_state) throw DiscordApiErrors.UNKNOWN_VOICE_STATE;
+
+		voice_state.assign(body);
+		const channel = await Channel.findOneOrFail({
+			where: { guild_id, id: body.channel_id },
+		});
+		if (channel.type !== ChannelType.GUILD_STAGE_VOICE) {
+			throw DiscordApiErrors.CANNOT_EXECUTE_ON_THIS_CHANNEL_TYPE;
 		}
-	});
-	if (!voice_state) throw DiscordApiErrors.UNKNOWN_VOICE_STATE;
-
-	voice_state.assign(body);
-	const channel = await Channel.findOneOrFail({ where: { guild_id, id: body.channel_id } });
-	if (channel.type !== ChannelType.GUILD_STAGE_VOICE) {
-		throw DiscordApiErrors.CANNOT_EXECUTE_ON_THIS_CHANNEL_TYPE;
-	}
-
-	await Promise.all([
-		voice_state.save(),
-		emitEvent({
-			event: "VOICE_STATE_UPDATE",
-			data: voice_state,
-			guild_id
-		} as VoiceStateUpdateEvent)
-	]);
-	return res.sendStatus(204);
-});
+
+		await Promise.all([
+			voice_state.save(),
+			emitEvent({
+				event: "VOICE_STATE_UPDATE",
+				data: voice_state,
+				guild_id,
+			} as VoiceStateUpdateEvent),
+		]);
+		return res.sendStatus(204);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/welcome-screen.ts b/src/api/routes/guilds/#guild_id/welcome-screen.ts
index 57da062d..80ab138b 100644
--- a/src/api/routes/guilds/#guild_id/welcome-screen.ts
+++ b/src/api/routes/guilds/#guild_id/welcome-screen.ts
@@ -14,20 +14,30 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	res.json(guild.welcome_screen);
 });
 
-router.patch("/", route({ body: "GuildUpdateWelcomeScreenSchema", permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
-	const guild_id = req.params.guild_id;
-	const body = req.body as GuildUpdateWelcomeScreenSchema;
-
-	const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
-
-	if (!guild.welcome_screen.enabled) throw new HTTPError("Welcome screen disabled", 400);
-	if (body.welcome_channels) guild.welcome_screen.welcome_channels = body.welcome_channels; // TODO: check if they exist and are valid
-	if (body.description) guild.welcome_screen.description = body.description;
-	if (body.enabled != null) guild.welcome_screen.enabled = body.enabled;
-
-	await guild.save();
-
-	res.sendStatus(204);
-});
+router.patch(
+	"/",
+	route({
+		body: "GuildUpdateWelcomeScreenSchema",
+		permission: "MANAGE_GUILD",
+	}),
+	async (req: Request, res: Response) => {
+		const guild_id = req.params.guild_id;
+		const body = req.body as GuildUpdateWelcomeScreenSchema;
+
+		const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
+
+		if (!guild.welcome_screen.enabled)
+			throw new HTTPError("Welcome screen disabled", 400);
+		if (body.welcome_channels)
+			guild.welcome_screen.welcome_channels = body.welcome_channels; // TODO: check if they exist and are valid
+		if (body.description)
+			guild.welcome_screen.description = body.description;
+		if (body.enabled != null) guild.welcome_screen.enabled = body.enabled;
+
+		await guild.save();
+
+		res.sendStatus(204);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/#guild_id/widget.json.ts b/src/api/routes/guilds/#guild_id/widget.json.ts
index be5bf23f..2c3124a2 100644
--- a/src/api/routes/guilds/#guild_id/widget.json.ts
+++ b/src/api/routes/guilds/#guild_id/widget.json.ts
@@ -1,5 +1,12 @@
 import { Request, Response, Router } from "express";
-import { Config, Permissions, Guild, Invite, Channel, Member } from "@fosscord/util";
+import {
+	Config,
+	Permissions,
+	Guild,
+	Invite,
+	Channel,
+	Member,
+} from "@fosscord/util";
 import { HTTPError } from "lambert-server";
 import { random, route } from "@fosscord/api";
 
@@ -21,7 +28,9 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	if (!guild.widget_enabled) throw new HTTPError("Widget Disabled", 404);
 
 	// Fetch existing widget invite for widget channel
-	var invite = await Invite.findOne({ where: { channel_id: guild.widget_channel_id } });
+	var invite = await Invite.findOne({
+		where: { channel_id: guild.widget_channel_id },
+	});
 
 	if (guild.widget_channel_id && !invite) {
 		// Create invite for channel if none exists
@@ -45,16 +54,24 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	// Fetch voice channels, and the @everyone permissions object
 	const channels = [] as any[];
 
-	(await Channel.find({ where: { guild_id: guild_id, type: 2 }, order: { position: "ASC" } })).filter((doc) => {
+	(
+		await Channel.find({
+			where: { guild_id: guild_id, type: 2 },
+			order: { position: "ASC" },
+		})
+	).filter((doc) => {
 		// Only return channels where @everyone has the CONNECT permission
 		if (
 			doc.permission_overwrites === undefined ||
-			Permissions.channelPermission(doc.permission_overwrites, Permissions.FLAGS.CONNECT) === Permissions.FLAGS.CONNECT
+			Permissions.channelPermission(
+				doc.permission_overwrites,
+				Permissions.FLAGS.CONNECT,
+			) === Permissions.FLAGS.CONNECT
 		) {
 			channels.push({
 				id: doc.id,
 				name: doc.name,
-				position: doc.position
+				position: doc.position,
 			});
 		}
 	});
@@ -70,7 +87,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 		instant_invite: invite?.code,
 		channels: channels,
 		members: members,
-		presence_count: guild.presence_count
+		presence_count: guild.presence_count,
 	};
 
 	res.set("Cache-Control", "public, max-age=300");
diff --git a/src/api/routes/guilds/#guild_id/widget.png.ts b/src/api/routes/guilds/#guild_id/widget.png.ts
index c17d511e..eaec8f07 100644
--- a/src/api/routes/guilds/#guild_id/widget.png.ts
+++ b/src/api/routes/guilds/#guild_id/widget.png.ts
@@ -24,8 +24,13 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 
 	// Fetch parameter
 	const style = req.query.style?.toString() || "shield";
-	if (!["shield", "banner1", "banner2", "banner3", "banner4"].includes(style)) {
-		throw new HTTPError("Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').", 400);
+	if (
+		!["shield", "banner1", "banner2", "banner3", "banner4"].includes(style)
+	) {
+		throw new HTTPError(
+			"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
+			400,
+		);
 	}
 
 	// Setup canvas
@@ -34,7 +39,17 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	const sizeOf = require("image-size");
 
 	// TODO: Widget style templates need Fosscord branding
-	const source = path.join(__dirname, "..", "..", "..", "..", "..", "assets", "widget", `${style}.png`);
+	const source = path.join(
+		__dirname,
+		"..",
+		"..",
+		"..",
+		"..",
+		"..",
+		"assets",
+		"widget",
+		`${style}.png`,
+	);
 	if (!fs.existsSync(source)) {
 		throw new HTTPError("Widget template does not exist.", 400);
 	}
@@ -50,30 +65,68 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	switch (style) {
 		case "shield":
 			ctx.textAlign = "center";
-			await drawText(ctx, 73, 13, "#FFFFFF", "thin 10px Verdana", presence);
+			await drawText(
+				ctx,
+				73,
+				13,
+				"#FFFFFF",
+				"thin 10px Verdana",
+				presence,
+			);
 			break;
 		case "banner1":
 			if (icon) await drawIcon(ctx, 20, 27, 50, icon);
 			await drawText(ctx, 83, 51, "#FFFFFF", "12px Verdana", name, 22);
-			await drawText(ctx, 83, 66, "#C9D2F0FF", "thin 11px Verdana", presence);
+			await drawText(
+				ctx,
+				83,
+				66,
+				"#C9D2F0FF",
+				"thin 11px Verdana",
+				presence,
+			);
 			break;
 		case "banner2":
 			if (icon) await drawIcon(ctx, 13, 19, 36, icon);
 			await drawText(ctx, 62, 34, "#FFFFFF", "12px Verdana", name, 15);
-			await drawText(ctx, 62, 49, "#C9D2F0FF", "thin 11px Verdana", presence);
+			await drawText(
+				ctx,
+				62,
+				49,
+				"#C9D2F0FF",
+				"thin 11px Verdana",
+				presence,
+			);
 			break;
 		case "banner3":
 			if (icon) await drawIcon(ctx, 20, 20, 50, icon);
 			await drawText(ctx, 83, 44, "#FFFFFF", "12px Verdana", name, 27);
-			await drawText(ctx, 83, 58, "#C9D2F0FF", "thin 11px Verdana", presence);
+			await drawText(
+				ctx,
+				83,
+				58,
+				"#C9D2F0FF",
+				"thin 11px Verdana",
+				presence,
+			);
 			break;
 		case "banner4":
 			if (icon) await drawIcon(ctx, 21, 136, 50, icon);
 			await drawText(ctx, 84, 156, "#FFFFFF", "13px Verdana", name, 27);
-			await drawText(ctx, 84, 171, "#C9D2F0FF", "thin 12px Verdana", presence);
+			await drawText(
+				ctx,
+				84,
+				171,
+				"#C9D2F0FF",
+				"thin 12px Verdana",
+				presence,
+			);
 			break;
 		default:
-			throw new HTTPError("Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').", 400);
+			throw new HTTPError(
+				"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
+				400,
+			);
 	}
 
 	// Return final image
@@ -83,7 +136,13 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	return res.send(buffer);
 });
 
-async function drawIcon(canvas: any, x: number, y: number, scale: number, icon: string) {
+async function drawIcon(
+	canvas: any,
+	x: number,
+	y: number,
+	scale: number,
+	icon: string,
+) {
 	// @ts-ignore
 	const img = new require("canvas").Image();
 	img.src = icon;
@@ -101,10 +160,19 @@ async function drawIcon(canvas: any, x: number, y: number, scale: number, icon:
 	canvas.restore();
 }
 
-async function drawText(canvas: any, x: number, y: number, color: string, font: string, text: string, maxcharacters?: number) {
+async function drawText(
+	canvas: any,
+	x: number,
+	y: number,
+	color: string,
+	font: string,
+	text: string,
+	maxcharacters?: number,
+) {
 	canvas.fillStyle = color;
 	canvas.font = font;
-	if (text.length > (maxcharacters || 0) && maxcharacters) text = text.slice(0, maxcharacters) + "...";
+	if (text.length > (maxcharacters || 0) && maxcharacters)
+		text = text.slice(0, maxcharacters) + "...";
 	canvas.fillText(text, x, y);
 }
 
diff --git a/src/api/routes/guilds/#guild_id/widget.ts b/src/api/routes/guilds/#guild_id/widget.ts
index dbb4cc0c..108339e1 100644
--- a/src/api/routes/guilds/#guild_id/widget.ts
+++ b/src/api/routes/guilds/#guild_id/widget.ts
@@ -10,18 +10,31 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 
 	const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
 
-	return res.json({ enabled: guild.widget_enabled || false, channel_id: guild.widget_channel_id || null });
+	return res.json({
+		enabled: guild.widget_enabled || false,
+		channel_id: guild.widget_channel_id || null,
+	});
 });
 
 // https://discord.com/developers/docs/resources/guild#modify-guild-widget
-router.patch("/", route({ body: "WidgetModifySchema", permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
-	const body = req.body as WidgetModifySchema;
-	const { guild_id } = req.params;
-
-	await Guild.update({ id: guild_id }, { widget_enabled: body.enabled, widget_channel_id: body.channel_id });
-	// Widget invite for the widget_channel_id gets created as part of the /guilds/{guild.id}/widget.json request
-
-	return res.json(body);
-});
+router.patch(
+	"/",
+	route({ body: "WidgetModifySchema", permission: "MANAGE_GUILD" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as WidgetModifySchema;
+		const { guild_id } = req.params;
+
+		await Guild.update(
+			{ id: guild_id },
+			{
+				widget_enabled: body.enabled,
+				widget_channel_id: body.channel_id,
+			},
+		);
+		// Widget invite for the widget_channel_id gets created as part of the /guilds/{guild.id}/widget.json request
+
+		return res.json(body);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/index.ts b/src/api/routes/guilds/index.ts
index 0807cb96..69575aea 100644
--- a/src/api/routes/guilds/index.ts
+++ b/src/api/routes/guilds/index.ts
@@ -1,32 +1,47 @@
 import { Router, Request, Response } from "express";
-import { Role, Guild, Config, getRights, Member, DiscordApiErrors, GuildCreateSchema } from "@fosscord/util";
+import {
+	Role,
+	Guild,
+	Config,
+	getRights,
+	Member,
+	DiscordApiErrors,
+	GuildCreateSchema,
+} from "@fosscord/util";
 import { route } from "@fosscord/api";
 
 const router: Router = Router();
 
 //TODO: create default channel
 
-router.post("/", route({ body: "GuildCreateSchema", right: "CREATE_GUILDS" }), async (req: Request, res: Response) => {
-	const body = req.body as GuildCreateSchema;
-
-	const { maxGuilds } = Config.get().limits.user;
-	const guild_count = await Member.count({ where: { id: req.user_id } });
-	const rights = await getRights(req.user_id);
-	if ((guild_count >= maxGuilds) && !rights.has("MANAGE_GUILDS")) {
-		throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds);
-	}
-
-	const guild = await Guild.createGuild({ ...body, owner_id: req.user_id });
-
-	const { autoJoin } = Config.get().guild;
-	if (autoJoin.enabled && !autoJoin.guilds?.length) {
-		// @ts-ignore
-		await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } });
-	}
-
-	await Member.addToGuild(req.user_id, guild.id);
-
-	res.status(201).json({ id: guild.id });
-});
+router.post(
+	"/",
+	route({ body: "GuildCreateSchema", right: "CREATE_GUILDS" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as GuildCreateSchema;
+
+		const { maxGuilds } = Config.get().limits.user;
+		const guild_count = await Member.count({ where: { id: req.user_id } });
+		const rights = await getRights(req.user_id);
+		if (guild_count >= maxGuilds && !rights.has("MANAGE_GUILDS")) {
+			throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds);
+		}
+
+		const guild = await Guild.createGuild({
+			...body,
+			owner_id: req.user_id,
+		});
+
+		const { autoJoin } = Config.get().guild;
+		if (autoJoin.enabled && !autoJoin.guilds?.length) {
+			// @ts-ignore
+			await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } });
+		}
+
+		await Member.addToGuild(req.user_id, guild.id);
+
+		res.status(201).json({ id: guild.id });
+	},
+);
 
 export default router;
diff --git a/src/api/routes/guilds/templates/index.ts b/src/api/routes/guilds/templates/index.ts
index 4e7abcc5..240bf074 100644
--- a/src/api/routes/guilds/templates/index.ts
+++ b/src/api/routes/guilds/templates/index.ts
@@ -1,29 +1,58 @@
 import { Request, Response, Router } from "express";
-import { Template, Guild, Role, Snowflake, Config, Member, GuildTemplateCreateSchema } from "@fosscord/util";
+import {
+	Template,
+	Guild,
+	Role,
+	Snowflake,
+	Config,
+	Member,
+	GuildTemplateCreateSchema,
+} from "@fosscord/util";
 import { route } from "@fosscord/api";
 import { DiscordApiErrors } from "@fosscord/util";
 import fetch from "node-fetch";
 const router: Router = Router();
 
 router.get("/:code", route({}), async (req: Request, res: Response) => {
-	const { allowDiscordTemplates, allowRaws, enabled } = Config.get().templates;
-	if (!enabled) res.json({ code: 403, message: "Template creation & usage is disabled on this instance." }).sendStatus(403);
+	const { allowDiscordTemplates, allowRaws, enabled } =
+		Config.get().templates;
+	if (!enabled)
+		res.json({
+			code: 403,
+			message: "Template creation & usage is disabled on this instance.",
+		}).sendStatus(403);
 
 	const { code } = req.params;
 
 	if (code.startsWith("discord:")) {
-		if (!allowDiscordTemplates) return res.json({ code: 403, message: "Discord templates cannot be used on this instance." }).sendStatus(403);
+		if (!allowDiscordTemplates)
+			return res
+				.json({
+					code: 403,
+					message:
+						"Discord templates cannot be used on this instance.",
+				})
+				.sendStatus(403);
 		const discordTemplateID = code.split("discord:", 2)[1];
 
-		const discordTemplateData = await fetch(`https://discord.com/api/v9/guilds/templates/${discordTemplateID}`, {
-			method: "get",
-			headers: { "Content-Type": "application/json" }
-		});
+		const discordTemplateData = await fetch(
+			`https://discord.com/api/v9/guilds/templates/${discordTemplateID}`,
+			{
+				method: "get",
+				headers: { "Content-Type": "application/json" },
+			},
+		);
 		return res.json(await discordTemplateData.json());
 	}
 
 	if (code.startsWith("external:")) {
-		if (!allowRaws) return res.json({ code: 403, message: "Importing raws is disabled on this instance." }).sendStatus(403);
+		if (!allowRaws)
+			return res
+				.json({
+					code: 403,
+					message: "Importing raws is disabled on this instance.",
+				})
+				.sendStatus(403);
 
 		return res.json(code.split("external:", 2)[1]);
 	}
@@ -32,48 +61,72 @@ router.get("/:code", route({}), async (req: Request, res: Response) => {
 	res.json(template);
 });
 
-router.post("/:code", route({ body: "GuildTemplateCreateSchema" }), async (req: Request, res: Response) => {
-	const { enabled, allowTemplateCreation, allowDiscordTemplates, allowRaws } = Config.get().templates;
-	if (!enabled) return res.json({ code: 403, message: "Template creation & usage is disabled on this instance." }).sendStatus(403);
-	if (!allowTemplateCreation) return res.json({ code: 403, message: "Template creation is disabled on this instance." }).sendStatus(403);
-
-	const { code } = req.params;
-	const body = req.body as GuildTemplateCreateSchema;
-
-	const { maxGuilds } = Config.get().limits.user;
-
-	const guild_count = await Member.count({ where: { id: req.user_id } });
-	if (guild_count >= maxGuilds) {
-		throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds);
-	}
-
-	const template = await Template.findOneOrFail({ where: { code: code } });
+router.post(
+	"/:code",
+	route({ body: "GuildTemplateCreateSchema" }),
+	async (req: Request, res: Response) => {
+		const {
+			enabled,
+			allowTemplateCreation,
+			allowDiscordTemplates,
+			allowRaws,
+		} = Config.get().templates;
+		if (!enabled)
+			return res
+				.json({
+					code: 403,
+					message:
+						"Template creation & usage is disabled on this instance.",
+				})
+				.sendStatus(403);
+		if (!allowTemplateCreation)
+			return res
+				.json({
+					code: 403,
+					message: "Template creation is disabled on this instance.",
+				})
+				.sendStatus(403);
+
+		const { code } = req.params;
+		const body = req.body as GuildTemplateCreateSchema;
+
+		const { maxGuilds } = Config.get().limits.user;
+
+		const guild_count = await Member.count({ where: { id: req.user_id } });
+		if (guild_count >= maxGuilds) {
+			throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds);
+		}
+
+		const template = await Template.findOneOrFail({
+			where: { code: code },
+		});
 
-	const guild_id = Snowflake.generate();
-
-	const [guild, role] = await Promise.all([
-		Guild.create({
-			...body,
-			...template.serialized_source_guild,
-			id: guild_id,
-			owner_id: req.user_id
-		}).save(),
-		Role.create({
-			id: guild_id,
-			guild_id: guild_id,
-			color: 0,
-			hoist: false,
-			managed: true,
-			mentionable: true,
-			name: "@everyone",
-			permissions: BigInt("2251804225").toString(), // TODO: where did this come from?
-			position: 0,
-		}).save()
-	]);
-
-	await Member.addToGuild(req.user_id, guild_id);
-
-	res.status(201).json({ id: guild.id });
-});
+		const guild_id = Snowflake.generate();
+
+		const [guild, role] = await Promise.all([
+			Guild.create({
+				...body,
+				...template.serialized_source_guild,
+				id: guild_id,
+				owner_id: req.user_id,
+			}).save(),
+			Role.create({
+				id: guild_id,
+				guild_id: guild_id,
+				color: 0,
+				hoist: false,
+				managed: true,
+				mentionable: true,
+				name: "@everyone",
+				permissions: BigInt("2251804225").toString(), // TODO: where did this come from?
+				position: 0,
+			}).save(),
+		]);
+
+		await Member.addToGuild(req.user_id, guild_id);
+
+		res.status(201).json({ id: guild.id });
+	},
+);
 
 export default router;
diff --git a/src/api/routes/invites/index.ts b/src/api/routes/invites/index.ts
index c268085f..ce0ba982 100644
--- a/src/api/routes/invites/index.ts
+++ b/src/api/routes/invites/index.ts
@@ -1,5 +1,13 @@
 import { Router, Request, Response } from "express";
-import { emitEvent, getPermission, Guild, Invite, InviteDeleteEvent, User, PublicInviteRelation } from "@fosscord/util";
+import {
+	emitEvent,
+	getPermission,
+	Guild,
+	Invite,
+	InviteDeleteEvent,
+	User,
+	PublicInviteRelation,
+} from "@fosscord/util";
 import { route } from "@fosscord/api";
 import { HTTPError } from "lambert-server";
 
@@ -8,24 +16,45 @@ const router: Router = Router();
 router.get("/:code", route({}), async (req: Request, res: Response) => {
 	const { code } = req.params;
 
-	const invite = await Invite.findOneOrFail({ where: { code }, relations: PublicInviteRelation });
+	const invite = await Invite.findOneOrFail({
+		where: { code },
+		relations: PublicInviteRelation,
+	});
 
 	res.status(200).send(invite);
 });
 
-router.post("/:code", route({ right: "USE_MASS_INVITES" }), async (req: Request, res: Response) => {
-	const { code } = req.params;
-	const { guild_id } = await Invite.findOneOrFail({ where: { code: code } });
-	const { features } = await Guild.findOneOrFail({ where: { id: guild_id } });
-	const { public_flags } = await User.findOneOrFail({ where: { id: req.user_id } });
+router.post(
+	"/:code",
+	route({ right: "USE_MASS_INVITES" }),
+	async (req: Request, res: Response) => {
+		const { code } = req.params;
+		const { guild_id } = await Invite.findOneOrFail({
+			where: { code: code },
+		});
+		const { features } = await Guild.findOneOrFail({
+			where: { id: guild_id },
+		});
+		const { public_flags } = await User.findOneOrFail({
+			where: { id: req.user_id },
+		});
 
-	if (features.includes("INTERNAL_EMPLOYEE_ONLY") && (public_flags & 1) !== 1) throw new HTTPError("Only intended for the staff of this server.", 401);
-	if (features.includes("INVITES_CLOSED")) throw new HTTPError("Sorry, this guild has joins closed.", 403);
+		if (
+			features.includes("INTERNAL_EMPLOYEE_ONLY") &&
+			(public_flags & 1) !== 1
+		)
+			throw new HTTPError(
+				"Only intended for the staff of this server.",
+				401,
+			);
+		if (features.includes("INVITES_CLOSED"))
+			throw new HTTPError("Sorry, this guild has joins closed.", 403);
 
-	const invite = await Invite.joinGuild(req.user_id, code);
+		const invite = await Invite.joinGuild(req.user_id, code);
 
-	res.json(invite);
-});
+		res.json(invite);
+	},
+);
 
 // * cant use permission of route() function because path doesn't have guild_id/channel_id
 router.delete("/:code", route({}), async (req: Request, res: Response) => {
@@ -36,7 +65,10 @@ router.delete("/:code", route({}), async (req: Request, res: Response) => {
 	const permission = await getPermission(req.user_id, guild_id, channel_id);
 
 	if (!permission.has("MANAGE_GUILD") && !permission.has("MANAGE_CHANNELS"))
-		throw new HTTPError("You missing the MANAGE_GUILD or MANAGE_CHANNELS permission", 401);
+		throw new HTTPError(
+			"You missing the MANAGE_GUILD or MANAGE_CHANNELS permission",
+			401,
+		);
 
 	await Promise.all([
 		Invite.delete({ code }),
@@ -46,9 +78,9 @@ router.delete("/:code", route({}), async (req: Request, res: Response) => {
 			data: {
 				channel_id: channel_id,
 				guild_id: guild_id,
-				code: code
-			}
-		} as InviteDeleteEvent)
+				code: code,
+			},
+		} as InviteDeleteEvent),
 	]);
 
 	res.json({ invite: invite });
diff --git a/src/api/routes/partners/#guild_id/requirements.ts b/src/api/routes/partners/#guild_id/requirements.ts
index 545c5c78..7e63c06b 100644
--- a/src/api/routes/partners/#guild_id/requirements.ts
+++ b/src/api/routes/partners/#guild_id/requirements.ts
@@ -1,4 +1,3 @@
-
 import { Guild, Config } from "@fosscord/util";
 
 import { Router, Request, Response } from "express";
@@ -7,33 +6,33 @@ import { route } from "@fosscord/api";
 const router = Router();
 
 router.get("/", route({}), async (req: Request, res: Response) => {
-	const { guild_id } = req.params;	
-    // TODO:
-    // Load from database
-    // Admin control, but for now it allows anyone to be discoverable
+	const { guild_id } = req.params;
+	// TODO:
+	// Load from database
+	// Admin control, but for now it allows anyone to be discoverable
 
 	res.send({
 		guild_id: guild_id,
 		safe_environment: true,
-        healthy: true,
-        health_score_pending: false,
-        size: true,
-        nsfw_properties: {},
-        protected: true,
-        sufficient: true,
-        sufficient_without_grace_period: true,
-        valid_rules_channel: true,
-        retention_healthy: true,
-        engagement_healthy: true,
-        age: true,
-        minimum_age: 0,
-        health_score: {
-            avg_nonnew_participators: 0,
-            avg_nonnew_communicators: 0,
-            num_intentful_joiners: 0,
-            perc_ret_w1_intentful: 0
-        },
-        minimum_size: 0
+		healthy: true,
+		health_score_pending: false,
+		size: true,
+		nsfw_properties: {},
+		protected: true,
+		sufficient: true,
+		sufficient_without_grace_period: true,
+		valid_rules_channel: true,
+		retention_healthy: true,
+		engagement_healthy: true,
+		age: true,
+		minimum_age: 0,
+		health_score: {
+			avg_nonnew_participators: 0,
+			avg_nonnew_communicators: 0,
+			num_intentful_joiners: 0,
+			perc_ret_w1_intentful: 0,
+		},
+		minimum_size: 0,
 	});
 });
 
diff --git a/src/api/routes/policies/instance/domains.ts b/src/api/routes/policies/instance/domains.ts
index 20cd07ba..f22eac17 100644
--- a/src/api/routes/policies/instance/domains.ts
+++ b/src/api/routes/policies/instance/domains.ts
@@ -1,16 +1,19 @@
 import { Router, Request, Response } from "express";
 import { route } from "@fosscord/api";
 import { Config } from "@fosscord/util";
-import { config } from "dotenv"
+import { config } from "dotenv";
 const router = Router();
 
-router.get("/",route({}), async (req: Request, res: Response) => {
-    const { cdn, gateway } = Config.get();
-    
-    const IdentityForm = {
-        cdn: cdn.endpointPublic || process.env.CDN || "http://localhost:3001",
-        gateway: gateway.endpointPublic || process.env.GATEWAY || "ws://localhost:3002"
-    };
+router.get("/", route({}), async (req: Request, res: Response) => {
+	const { cdn, gateway } = Config.get();
+
+	const IdentityForm = {
+		cdn: cdn.endpointPublic || process.env.CDN || "http://localhost:3001",
+		gateway:
+			gateway.endpointPublic ||
+			process.env.GATEWAY ||
+			"ws://localhost:3002",
+	};
 
 	res.json(IdentityForm);
 });
diff --git a/src/api/routes/policies/instance/index.ts b/src/api/routes/policies/instance/index.ts
index e3da014f..1c1afa09 100644
--- a/src/api/routes/policies/instance/index.ts
+++ b/src/api/routes/policies/instance/index.ts
@@ -3,8 +3,7 @@ import { route } from "@fosscord/api";
 import { Config } from "@fosscord/util";
 const router = Router();
 
-
-router.get("/",route({}), async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
 	const { general } = Config.get();
 	res.json(general);
 });
diff --git a/src/api/routes/policies/instance/limits.ts b/src/api/routes/policies/instance/limits.ts
index 7de1476b..06f14f83 100644
--- a/src/api/routes/policies/instance/limits.ts
+++ b/src/api/routes/policies/instance/limits.ts
@@ -3,7 +3,7 @@ import { route } from "@fosscord/api";
 import { Config } from "@fosscord/util";
 const router = Router();
 
-router.get("/",route({}), async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
 	const { limits } = Config.get();
 	res.json(limits);
 });
diff --git a/src/api/routes/scheduled-maintenances/upcoming_json.ts b/src/api/routes/scheduled-maintenances/upcoming_json.ts
index 83092e44..e42723a1 100644
--- a/src/api/routes/scheduled-maintenances/upcoming_json.ts
+++ b/src/api/routes/scheduled-maintenances/upcoming_json.ts
@@ -2,11 +2,15 @@ import { Router, Request, Response } from "express";
 import { route } from "@fosscord/api";
 const router = Router();
 
-router.get("/scheduled-maintenances/upcoming.json",route({}), async (req: Request, res: Response) => {
-	res.json({
-  "page": {},
-  "scheduled_maintenances": {}
-  });
-});
+router.get(
+	"/scheduled-maintenances/upcoming.json",
+	route({}),
+	async (req: Request, res: Response) => {
+		res.json({
+			page: {},
+			scheduled_maintenances: {},
+		});
+	},
+);
 
 export default router;
diff --git a/src/api/routes/stop.ts b/src/api/routes/stop.ts
index 7f8b78ba..78abb9d7 100644
--- a/src/api/routes/stop.ts
+++ b/src/api/routes/stop.ts
@@ -6,17 +6,19 @@ const router: Router = Router();
 
 router.post("/", route({}), async (req: Request, res: Response) => {
 	//EXPERIMENTAL: have an "OPERATOR" platform permission implemented for this API route
-	const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["rights"] });
-	if((Number(user.rights) << Number(0))%Number(2)==Number(1)) {
+	const user = await User.findOneOrFail({
+		where: { id: req.user_id },
+		select: ["rights"],
+	});
+	if ((Number(user.rights) << Number(0)) % Number(2) == Number(1)) {
 		console.log("user that POSTed to the API was ALLOWED");
 		console.log(user.rights);
-		res.sendStatus(200)
-		process.kill(process.pid, 'SIGTERM')
-	}
-	else {
+		res.sendStatus(200);
+		process.kill(process.pid, "SIGTERM");
+	} else {
 		console.log("operation failed");
 		console.log(user.rights);
-		res.sendStatus(403)
+		res.sendStatus(403);
 	}
 });
 
diff --git a/src/api/routes/store/published-listings/applications.ts b/src/api/routes/store/published-listings/applications.ts
index 060a4c3d..6156f43e 100644
--- a/src/api/routes/store/published-listings/applications.ts
+++ b/src/api/routes/store/published-listings/applications.ts
@@ -41,29 +41,29 @@ router.get("/:id", route({}), async (req: Request, res: Response) => {
 				publishers: [
 					{
 						id: "",
-						name: ""
-					}
+						name: "",
+					},
 				],
 				developers: [
 					{
 						id: "",
-						name: ""
-					}
+						name: "",
+					},
 				],
 				system_requirements: {},
 				show_age_gate: false,
 				price: {
 					amount: 0,
-					currency: "EUR"
+					currency: "EUR",
 				},
-				locales: []
+				locales: [],
 			},
 			tagline: "",
 			description: "",
 			carousel_items: [
 				{
-					asset_id: ""
-				}
+					asset_id: "",
+				},
 			],
 			header_logo_dark_theme: {}, //{id: "", size: 4665, mime_type: "image/gif", width 160, height: 160}
 			header_logo_light_theme: {},
@@ -71,8 +71,8 @@ router.get("/:id", route({}), async (req: Request, res: Response) => {
 			thumbnail: {},
 			header_background: {},
 			hero_background: {},
-			assets: []
-		}
+			assets: [],
+		},
 	}).status(200);
 });
 
diff --git a/src/api/routes/store/published-listings/applications/#id/subscription-plans.ts b/src/api/routes/store/published-listings/applications/#id/subscription-plans.ts
index 54151ae5..845cdfe7 100644
--- a/src/api/routes/store/published-listings/applications/#id/subscription-plans.ts
+++ b/src/api/routes/store/published-listings/applications/#id/subscription-plans.ts
@@ -17,8 +17,8 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 			fallback_currency: "eur",
 			currency: "eur",
 			price: 4199,
-			price_tier: null
-		}
+			price_tier: null,
+		},
 	]).status(200);
 });
 
diff --git a/src/api/routes/store/published-listings/skus.ts b/src/api/routes/store/published-listings/skus.ts
index 060a4c3d..6156f43e 100644
--- a/src/api/routes/store/published-listings/skus.ts
+++ b/src/api/routes/store/published-listings/skus.ts
@@ -41,29 +41,29 @@ router.get("/:id", route({}), async (req: Request, res: Response) => {
 				publishers: [
 					{
 						id: "",
-						name: ""
-					}
+						name: "",
+					},
 				],
 				developers: [
 					{
 						id: "",
-						name: ""
-					}
+						name: "",
+					},
 				],
 				system_requirements: {},
 				show_age_gate: false,
 				price: {
 					amount: 0,
-					currency: "EUR"
+					currency: "EUR",
 				},
-				locales: []
+				locales: [],
 			},
 			tagline: "",
 			description: "",
 			carousel_items: [
 				{
-					asset_id: ""
-				}
+					asset_id: "",
+				},
 			],
 			header_logo_dark_theme: {}, //{id: "", size: 4665, mime_type: "image/gif", width 160, height: 160}
 			header_logo_light_theme: {},
@@ -71,8 +71,8 @@ router.get("/:id", route({}), async (req: Request, res: Response) => {
 			thumbnail: {},
 			header_background: {},
 			hero_background: {},
-			assets: []
-		}
+			assets: [],
+		},
 	}).status(200);
 });
 
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 03162ec8..33151056 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
@@ -17,8 +17,8 @@ const skus = new Map([
 				currency: "usd",
 				price: 0,
 				price_tier: null,
-			}
-		]
+			},
+		],
 	],
 	[
 		"521842865731534868",
@@ -32,7 +32,7 @@ const skus = new Map([
 				sku_id: "521842865731534868",
 				currency: "usd",
 				price: 0,
-				price_tier: null
+				price_tier: null,
 			},
 			{
 				id: "511651860671627264",
@@ -43,9 +43,9 @@ const skus = new Map([
 				sku_id: "521842865731534868",
 				currency: "usd",
 				price: 0,
-				price_tier: null
-			}
-		]
+				price_tier: null,
+			},
+		],
 	],
 	[
 		"521846918637420545",
@@ -59,7 +59,7 @@ const skus = new Map([
 				sku_id: "521846918637420545",
 				currency: "usd",
 				price: 0,
-				price_tier: null
+				price_tier: null,
 			},
 			{
 				id: "511651876987469824",
@@ -70,9 +70,9 @@ const skus = new Map([
 				sku_id: "521846918637420545",
 				currency: "usd",
 				price: 0,
-				price_tier: null
-			}
-		]
+				price_tier: null,
+			},
+		],
 	],
 	[
 		"521847234246082599",
@@ -86,7 +86,7 @@ const skus = new Map([
 				sku_id: "521847234246082599",
 				currency: "usd",
 				price: 0,
-				price_tier: null
+				price_tier: null,
 			},
 			{
 				id: "511651880837840896",
@@ -97,7 +97,7 @@ const skus = new Map([
 				sku_id: "521847234246082599",
 				currency: "usd",
 				price: 0,
-				price_tier: null
+				price_tier: null,
 			},
 			{
 				id: "511651885459963904",
@@ -108,9 +108,9 @@ const skus = new Map([
 				sku_id: "521847234246082599",
 				currency: "usd",
 				price: 0,
-				price_tier: null
-			}
-		]
+				price_tier: null,
+			},
+		],
 	],
 	[
 		"590663762298667008",
@@ -125,7 +125,7 @@ const skus = new Map([
 				discount_price: 0,
 				currency: "usd",
 				price: 0,
-				price_tier: null
+				price_tier: null,
 			},
 			{
 				id: "590665538238152709",
@@ -137,10 +137,10 @@ const skus = new Map([
 				discount_price: 0,
 				currency: "usd",
 				price: 0,
-				price_tier: null
-			}
-		]
-	]
+				price_tier: null,
+			},
+		],
+	],
 ]);
 
 router.get("/", route({}), async (req: Request, res: Response) => {
diff --git a/src/api/routes/updates.ts b/src/api/routes/updates.ts
index 42f77323..8fe6fc2a 100644
--- a/src/api/routes/updates.ts
+++ b/src/api/routes/updates.ts
@@ -7,13 +7,15 @@ const router = Router();
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const { client } = Config.get();
 
-	const release = await Release.findOneOrFail({ where: { name: client.releases.upstreamVersion } });
+	const release = await Release.findOneOrFail({
+		where: { name: client.releases.upstreamVersion },
+	});
 
 	res.json({
 		name: release.name,
 		pub_date: release.pub_date,
 		url: release.url,
-		notes: release.notes
+		notes: release.notes,
 	});
 });
 
diff --git a/src/api/routes/users/#id/profile.ts b/src/api/routes/users/#id/profile.ts
index de96422a..ebea805b 100644
--- a/src/api/routes/users/#id/profile.ts
+++ b/src/api/routes/users/#id/profile.ts
@@ -1,5 +1,12 @@
 import { Router, Request, Response } from "express";
-import { PublicConnectedAccount, PublicUser, User, UserPublic, Member, Guild } from "@fosscord/util";
+import {
+	PublicConnectedAccount,
+	PublicUser,
+	User,
+	UserPublic,
+	Member,
+	Guild,
+} from "@fosscord/util";
 import { route } from "@fosscord/api";
 
 const router: Router = Router();
@@ -11,81 +18,102 @@ export interface UserProfileResponse {
 	premium_since?: Date;
 }
 
-router.get("/", route({ test: { response: { body: "UserProfileResponse" } } }), async (req: Request, res: Response) => {
-	if (req.params.id === "@me") req.params.id = req.user_id;
+router.get(
+	"/",
+	route({ test: { response: { body: "UserProfileResponse" } } }),
+	async (req: Request, res: Response) => {
+		if (req.params.id === "@me") req.params.id = req.user_id;
 
-	const { guild_id, with_mutual_guilds } = req.query;
+		const { guild_id, with_mutual_guilds } = req.query;
 
-	const user = await User.getPublicUser(req.params.id, { relations: ["connected_accounts"] });
+		const user = await User.getPublicUser(req.params.id, {
+			relations: ["connected_accounts"],
+		});
 
-	var mutual_guilds: object[] = [];
-	var premium_guild_since;
+		var mutual_guilds: object[] = [];
+		var premium_guild_since;
 
-	if (with_mutual_guilds == "true") {
-		const requested_member = await Member.find({ where: { id: req.params.id } });
-		const self_member = await Member.find({ where: { id: req.user_id } });
+		if (with_mutual_guilds == "true") {
+			const requested_member = await Member.find({
+				where: { id: req.params.id },
+			});
+			const self_member = await Member.find({
+				where: { id: req.user_id },
+			});
 
-		for (const rmem of requested_member) {
-			if (rmem.premium_since) {
-				if (premium_guild_since) {
-					if (premium_guild_since > rmem.premium_since) {
+			for (const rmem of requested_member) {
+				if (rmem.premium_since) {
+					if (premium_guild_since) {
+						if (premium_guild_since > rmem.premium_since) {
+							premium_guild_since = rmem.premium_since;
+						}
+					} else {
 						premium_guild_since = rmem.premium_since;
 					}
-				} else {
-					premium_guild_since = rmem.premium_since;
 				}
-			}
-			for (const smem of self_member) {
-				if (smem.guild_id === rmem.guild_id) {
-					mutual_guilds.push({ id: rmem.guild_id, nick: rmem.nick });
+				for (const smem of self_member) {
+					if (smem.guild_id === rmem.guild_id) {
+						mutual_guilds.push({
+							id: rmem.guild_id,
+							nick: rmem.nick,
+						});
+					}
 				}
 			}
 		}
-	}
 
-	const guild_member = guild_id && typeof guild_id == "string"
-		? await Member.findOneOrFail({ where: { id: req.params.id, guild_id: guild_id }, relations: ["roles"] })
-		: undefined;
+		const guild_member =
+			guild_id && typeof guild_id == "string"
+				? await Member.findOneOrFail({
+						where: { id: req.params.id, guild_id: guild_id },
+						relations: ["roles"],
+				  })
+				: undefined;
 
-	// TODO: make proper DTO's in util?
+		// TODO: make proper DTO's in util?
 
-	const userDto = {
-		username: user.username,
-		discriminator: user.discriminator,
-		id: user.id,
-		public_flags: user.public_flags,
-		avatar: user.avatar,
-		accent_color: user.accent_color,
-		banner: user.banner,
-		bio: req.user_bot ? null : user.bio,
-		bot: user.bot
-	};
+		const userDto = {
+			username: user.username,
+			discriminator: user.discriminator,
+			id: user.id,
+			public_flags: user.public_flags,
+			avatar: user.avatar,
+			accent_color: user.accent_color,
+			banner: user.banner,
+			bio: req.user_bot ? null : user.bio,
+			bot: user.bot,
+		};
 
-	const guildMemberDto = guild_member ? {
-		avatar: user.avatar,	// TODO
-		banner: user.banner,	// TODO
-		bio: req.user_bot ? null : user.bio, // TODO
-		communication_disabled_until: null,	// TODO
-		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 guildMemberDto = guild_member
+			? {
+					avatar: user.avatar, // TODO
+					banner: user.banner, // TODO
+					bio: req.user_bot ? null : user.bio, // TODO
+					communication_disabled_until: null, // TODO
+					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;
 
-	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,
-		guild_member: guildMemberDto,
-	});
-});
+		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,
+			guild_member: guildMemberDto,
+		});
+	},
+);
 
 export default router;
diff --git a/src/api/routes/users/#id/relationships.ts b/src/api/routes/users/#id/relationships.ts
index de7cb9d3..c6480567 100644
--- a/src/api/routes/users/#id/relationships.ts
+++ b/src/api/routes/users/#id/relationships.ts
@@ -6,36 +6,49 @@ const router: Router = Router();
 
 export interface UserRelationsResponse {
 	object: {
-		id?: string,
-		username?: string,
-		avatar?: string, 
-		discriminator?: string, 
-		public_flags?: number
-	}
+		id?: string;
+		username?: string;
+		avatar?: string;
+		discriminator?: string;
+		public_flags?: number;
+	};
 }
 
+router.get(
+	"/",
+	route({ test: { response: { body: "UserRelationsResponse" } } }),
+	async (req: Request, res: Response) => {
+		var mutual_relations: object[] = [];
+		const requested_relations = await User.findOneOrFail({
+			where: { id: req.params.id },
+			relations: ["relationships"],
+		});
+		const self_relations = await User.findOneOrFail({
+			where: { id: req.user_id },
+			relations: ["relationships"],
+		});
 
-router.get("/", route({ test: { response: { body: "UserRelationsResponse" } } }), async (req: Request, res: Response) => {
-	var mutual_relations: object[] = [];
-    const requested_relations = await User.findOneOrFail({
-		where: { id: req.params.id },
-		relations: ["relationships"]
-	});
-    const self_relations = await User.findOneOrFail({
-		where: { id: req.user_id },
-		relations: ["relationships"]
-	});
-	
-    for(const rmem of requested_relations.relationships) {
-		for(const smem of self_relations.relationships)
-		if (rmem.to_id === smem.to_id && rmem.type === 1 && rmem.to_id !== req.user_id) {
-			var relation_user = await User.getPublicUser(rmem.to_id)
+		for (const rmem of requested_relations.relationships) {
+			for (const smem of self_relations.relationships)
+				if (
+					rmem.to_id === smem.to_id &&
+					rmem.type === 1 &&
+					rmem.to_id !== req.user_id
+				) {
+					var relation_user = await User.getPublicUser(rmem.to_id);
 
-			mutual_relations.push({id: relation_user.id, username: relation_user.username, avatar: relation_user.avatar, discriminator: relation_user.discriminator, public_flags: relation_user.public_flags})
+					mutual_relations.push({
+						id: relation_user.id,
+						username: relation_user.username,
+						avatar: relation_user.avatar,
+						discriminator: relation_user.discriminator,
+						public_flags: relation_user.public_flags,
+					});
+				}
 		}
-	}
 
-	res.json(mutual_relations)
-});
+		res.json(mutual_relations);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/users/@me/channels.ts b/src/api/routes/users/@me/channels.ts
index ad483529..237be102 100644
--- a/src/api/routes/users/@me/channels.ts
+++ b/src/api/routes/users/@me/channels.ts
@@ -1,5 +1,10 @@
 import { Request, Response, Router } from "express";
-import { Recipient, DmChannelDTO, Channel, DmChannelCreateSchema } from "@fosscord/util";
+import {
+	Recipient,
+	DmChannelDTO,
+	Channel,
+	DmChannelCreateSchema,
+} from "@fosscord/util";
 import { route } from "@fosscord/api";
 
 const router: Router = Router();
@@ -7,14 +12,28 @@ const router: Router = Router();
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const recipients = await Recipient.find({
 		where: { user_id: req.user_id, closed: false },
-		relations: ["channel", "channel.recipients"]
+		relations: ["channel", "channel.recipients"],
 	});
-	res.json(await Promise.all(recipients.map((r) => DmChannelDTO.from(r.channel, [req.user_id]))));
+	res.json(
+		await Promise.all(
+			recipients.map((r) => DmChannelDTO.from(r.channel, [req.user_id])),
+		),
+	);
 });
 
-router.post("/", route({ body: "DmChannelCreateSchema" }), async (req: Request, res: Response) => {
-	const body = req.body as DmChannelCreateSchema;
-	res.json(await Channel.createDMChannel(body.recipients, req.user_id, body.name));
-});
+router.post(
+	"/",
+	route({ body: "DmChannelCreateSchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as DmChannelCreateSchema;
+		res.json(
+			await Channel.createDMChannel(
+				body.recipients,
+				req.user_id,
+				body.name,
+			),
+		);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/users/@me/delete.ts b/src/api/routes/users/@me/delete.ts
index c24c3f1e..a9f8167c 100644
--- a/src/api/routes/users/@me/delete.ts
+++ b/src/api/routes/users/@me/delete.ts
@@ -7,7 +7,10 @@ import { HTTPError } from "lambert-server";
 const router = Router();
 
 router.post("/", route({}), async (req: Request, res: Response) => {
-	const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["data"] }); //User object
+	const user = await User.findOneOrFail({
+		where: { id: req.user_id },
+		select: ["data"],
+	}); //User object
 	let correctpass = true;
 
 	if (user.data.hash) {
@@ -21,7 +24,10 @@ router.post("/", route({}), async (req: Request, res: Response) => {
 	// TODO: decrement guild member count
 
 	if (correctpass) {
-		await Promise.all([User.delete({ id: req.user_id }), Member.delete({ id: req.user_id })]);
+		await Promise.all([
+			User.delete({ id: req.user_id }),
+			Member.delete({ id: req.user_id }),
+		]);
 
 		res.sendStatus(204);
 	} else {
diff --git a/src/api/routes/users/@me/disable.ts b/src/api/routes/users/@me/disable.ts
index 4aff3774..313a888f 100644
--- a/src/api/routes/users/@me/disable.ts
+++ b/src/api/routes/users/@me/disable.ts
@@ -6,7 +6,10 @@ import bcrypt from "bcrypt";
 const router = Router();
 
 router.post("/", route({}), async (req: Request, res: Response) => {
-	const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["data"] }); //User object
+	const user = await User.findOneOrFail({
+		where: { id: req.user_id },
+		select: ["data"],
+	}); //User object
 	let correctpass = true;
 
 	if (user.data.hash) {
@@ -19,7 +22,10 @@ router.post("/", route({}), async (req: Request, res: Response) => {
 
 		res.sendStatus(204);
 	} else {
-		res.status(400).json({ message: "Password does not match", code: 50018 });
+		res.status(400).json({
+			message: "Password does not match",
+			code: 50018,
+		});
 	}
 });
 
diff --git a/src/api/routes/users/@me/email-settings.ts b/src/api/routes/users/@me/email-settings.ts
index 3114984e..a2834b89 100644
--- a/src/api/routes/users/@me/email-settings.ts
+++ b/src/api/routes/users/@me/email-settings.ts
@@ -11,9 +11,9 @@ router.get("/", route({}), (req: Request, res: Response) => {
 			communication: true,
 			tips: false,
 			updates_and_announcements: false,
-			recommendations_and_events: false
+			recommendations_and_events: false,
 		},
-		initialized: false
+		initialized: false,
 	}).status(200);
 });
 
diff --git a/src/api/routes/users/@me/guilds.ts b/src/api/routes/users/@me/guilds.ts
index 754a240e..e12bf258 100644
--- a/src/api/routes/users/@me/guilds.ts
+++ b/src/api/routes/users/@me/guilds.ts
@@ -1,12 +1,23 @@
 import { Router, Request, Response } from "express";
-import { Guild, Member, User, GuildDeleteEvent, GuildMemberRemoveEvent, emitEvent, Config } from "@fosscord/util";
+import {
+	Guild,
+	Member,
+	User,
+	GuildDeleteEvent,
+	GuildMemberRemoveEvent,
+	emitEvent,
+	Config,
+} from "@fosscord/util";
 import { HTTPError } from "lambert-server";
 import { route } from "@fosscord/api";
 
 const router: Router = Router();
 
 router.get("/", route({}), async (req: Request, res: Response) => {
-	const members = await Member.find({ relations: ["guild"], where: { id: req.user_id } });
+	const members = await Member.find({
+		relations: ["guild"],
+		where: { id: req.user_id },
+	});
 
 	let guild = members.map((x) => x.guild);
 
@@ -21,11 +32,19 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 router.delete("/:guild_id", route({}), async (req: Request, res: Response) => {
 	const { autoJoin } = Config.get().guild;
 	const { guild_id } = req.params;
-	const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: ["owner_id"] });
+	const guild = await Guild.findOneOrFail({
+		where: { id: guild_id },
+		select: ["owner_id"],
+	});
 
 	if (!guild) throw new HTTPError("Guild doesn't exist", 404);
-	if (guild.owner_id === req.user_id) throw new HTTPError("You can't leave your own guild", 400);
-	if (autoJoin.enabled && autoJoin.guilds.includes(guild_id) && !autoJoin.canLeave) {
+	if (guild.owner_id === req.user_id)
+		throw new HTTPError("You can't leave your own guild", 400);
+	if (
+		autoJoin.enabled &&
+		autoJoin.guilds.includes(guild_id) &&
+		!autoJoin.canLeave
+	) {
 		throw new HTTPError("You can't leave instance auto join guilds", 400);
 	}
 
@@ -34,10 +53,10 @@ router.delete("/:guild_id", route({}), async (req: Request, res: Response) => {
 		emitEvent({
 			event: "GUILD_DELETE",
 			data: {
-				id: guild_id
+				id: guild_id,
 			},
-			user_id: req.user_id
-		} as GuildDeleteEvent)
+			user_id: req.user_id,
+		} as GuildDeleteEvent),
 	]);
 
 	const user = await User.getPublicUser(req.user_id);
@@ -46,9 +65,9 @@ router.delete("/:guild_id", route({}), async (req: Request, res: Response) => {
 		event: "GUILD_MEMBER_REMOVE",
 		data: {
 			guild_id: guild_id,
-			user: user
+			user: user,
 		},
-		guild_id: guild_id
+		guild_id: guild_id,
 	} as GuildMemberRemoveEvent);
 
 	return res.sendStatus(204);
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 f09be25b..4b806cfb 100644
--- a/src/api/routes/users/@me/guilds/#guild_id/settings.ts
+++ b/src/api/routes/users/@me/guilds/#guild_id/settings.ts
@@ -1,39 +1,51 @@
 import { Router, Response, Request } from "express";
-import { Channel, ChannelOverride, Member, UserGuildSettings } from "@fosscord/util";
+import {
+	Channel,
+	ChannelOverride,
+	Member,
+	UserGuildSettings,
+} from "@fosscord/util";
 import { route } from "@fosscord/api";
 
 const router = Router();
 
 // This sucks. I would use a DeepPartial, my own or typeorms, but they both generate inncorect schema
-export interface UserGuildSettingsSchema extends Partial<Omit<UserGuildSettings, 'channel_overrides'>> {
+export interface UserGuildSettingsSchema
+	extends Partial<Omit<UserGuildSettings, "channel_overrides">> {
 	channel_overrides: {
 		[channel_id: string]: Partial<ChannelOverride>;
-	},
+	};
 }
 
 // GET doesn't exist on discord.com
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const user = await Member.findOneOrFail({
 		where: { id: req.user_id, guild_id: req.params.guild_id },
-		select: ["settings"]
+		select: ["settings"],
 	});
 	return res.json(user.settings);
 });
 
-router.patch("/", route({ body: "UserGuildSettingsSchema" }), async (req: Request, res: Response) => {
-	const body = req.body as UserGuildSettings;
+router.patch(
+	"/",
+	route({ body: "UserGuildSettingsSchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as UserGuildSettings;
 
-	if (body.channel_overrides) {
-		for (var channel in body.channel_overrides) {
-			Channel.findOneOrFail({ where: { id: channel } });
+		if (body.channel_overrides) {
+			for (var channel in body.channel_overrides) {
+				Channel.findOneOrFail({ where: { id: channel } });
+			}
 		}
-	}
 
-	const user = await Member.findOneOrFail({ where: { id: req.user_id, guild_id: req.params.guild_id } });
-	user.settings = { ...user.settings, ...body };
-	await user.save();
+		const user = await Member.findOneOrFail({
+			where: { id: req.user_id, guild_id: req.params.guild_id },
+		});
+		user.settings = { ...user.settings, ...body };
+		await user.save();
 
-	res.json(user.settings);
-});
+		res.json(user.settings);
+	},
+);
 
 export default router;
diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts
index e849b72a..5eba4665 100644
--- a/src/api/routes/users/@me/index.ts
+++ b/src/api/routes/users/@me/index.ts
@@ -1,5 +1,15 @@
 import { Router, Request, Response } from "express";
-import { User, PrivateUserProjection, emitEvent, UserUpdateEvent, handleFile, FieldErrors, adjustEmail, Config, UserModifySchema } from "@fosscord/util";
+import {
+	User,
+	PrivateUserProjection,
+	emitEvent,
+	UserUpdateEvent,
+	handleFile,
+	FieldErrors,
+	adjustEmail,
+	Config,
+	UserModifySchema,
+} from "@fosscord/util";
 import { route } from "@fosscord/api";
 import bcrypt from "bcrypt";
 import { HTTPError } from "lambert-server";
@@ -7,79 +17,134 @@ import { HTTPError } from "lambert-server";
 const router: Router = Router();
 
 router.get("/", route({}), async (req: Request, res: Response) => {
-	res.json(await User.findOne({ select: PrivateUserProjection, where: { id: req.user_id } }));
+	res.json(
+		await User.findOne({
+			select: PrivateUserProjection,
+			where: { id: req.user_id },
+		}),
+	);
 });
 
-router.patch("/", route({ body: "UserModifySchema" }), async (req: Request, res: Response) => {
-	const body = req.body as UserModifySchema;
-
-	const user = await User.findOneOrFail({ where: { id: req.user_id }, select: [...PrivateUserProjection, "data"] });
-
-	if (user.email == "demo@maddy.k.vu") throw new HTTPError("Demo user, sorry", 400);
+router.patch(
+	"/",
+	route({ body: "UserModifySchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as UserModifySchema;
+
+		const user = await User.findOneOrFail({
+			where: { id: req.user_id },
+			select: [...PrivateUserProjection, "data"],
+		});
+
+		if (user.email == "demo@maddy.k.vu")
+			throw new HTTPError("Demo user, sorry", 400);
+
+		if (body.avatar)
+			body.avatar = await handleFile(
+				`/avatars/${req.user_id}`,
+				body.avatar as string,
+			);
+		if (body.banner)
+			body.banner = await handleFile(
+				`/banners/${req.user_id}`,
+				body.banner as string,
+			);
+
+		if (body.password) {
+			if (user.data?.hash) {
+				const same_password = await bcrypt.compare(
+					body.password,
+					user.data.hash || "",
+				);
+				if (!same_password) {
+					throw FieldErrors({
+						password: {
+							message: req.t("auth:login.INVALID_PASSWORD"),
+							code: "INVALID_PASSWORD",
+						},
+					});
+				}
+			} else {
+				user.data.hash = await bcrypt.hash(body.password, 12);
+			}
+		}
 
-	if (body.avatar) body.avatar = await handleFile(`/avatars/${req.user_id}`, body.avatar as string);
-	if (body.banner) body.banner = await handleFile(`/banners/${req.user_id}`, body.banner as string);
+		if (body.email) {
+			body.email = adjustEmail(body.email);
+			if (!body.email && Config.get().register.email.required)
+				throw FieldErrors({
+					email: {
+						message: req.t("auth:register.EMAIL_INVALID"),
+						code: "EMAIL_INVALID",
+					},
+				});
+			if (!body.password)
+				throw FieldErrors({
+					password: {
+						message: req.t("auth:register.INVALID_PASSWORD"),
+						code: "INVALID_PASSWORD",
+					},
+				});
+		}
 
-	if (body.password) {
-		if (user.data?.hash) {
-			const same_password = await bcrypt.compare(body.password, user.data.hash || "");
-			if (!same_password) {
-				throw FieldErrors({ password: { message: req.t("auth:login.INVALID_PASSWORD"), code: "INVALID_PASSWORD" } });
+		if (body.new_password) {
+			if (!body.password && !user.email) {
+				throw FieldErrors({
+					password: {
+						code: "BASE_TYPE_REQUIRED",
+						message: req.t("common:field.BASE_TYPE_REQUIRED"),
+					},
+				});
 			}
-		} else {
-			user.data.hash = await bcrypt.hash(body.password, 12);
+			user.data.hash = await bcrypt.hash(body.new_password, 12);
 		}
-	}
-
-	if (body.email) {
-		body.email = adjustEmail(body.email);
-		if (!body.email && Config.get().register.email.required)
-			throw FieldErrors({ email: { message: req.t("auth:register.EMAIL_INVALID"), code: "EMAIL_INVALID" } });
-		if (!body.password)
-			throw FieldErrors({ password: { message: req.t("auth:register.INVALID_PASSWORD"), code: "INVALID_PASSWORD" } });
-	}
-
-	if (body.new_password) {
-		if (!body.password && !user.email) {
-			throw FieldErrors({
-				password: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") }
-			});
-		}
-		user.data.hash = await bcrypt.hash(body.new_password, 12);
-	}
-
-	if (body.username) {
-		var check_username = body?.username?.replace(/\s/g, '');
-		if (!check_username) {
-			throw FieldErrors({
-				username: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") }
-			});
+
+		if (body.username) {
+			var check_username = body?.username?.replace(/\s/g, "");
+			if (!check_username) {
+				throw FieldErrors({
+					username: {
+						code: "BASE_TYPE_REQUIRED",
+						message: req.t("common:field.BASE_TYPE_REQUIRED"),
+					},
+				});
+			}
 		}
-	}
 
-	if (body.discriminator) {
-		if (await User.findOne({ where: { discriminator: body.discriminator, username: body.username || user.username } })) {
-			throw FieldErrors({
-				discriminator: { code: "INVALID_DISCRIMINATOR", message: "This discriminator is already in use." }
-			});
+		if (body.discriminator) {
+			if (
+				await User.findOne({
+					where: {
+						discriminator: body.discriminator,
+						username: body.username || user.username,
+					},
+				})
+			) {
+				throw FieldErrors({
+					discriminator: {
+						code: "INVALID_DISCRIMINATOR",
+						message: "This discriminator is already in use.",
+					},
+				});
+			}
 		}
-	}
 
-	user.assign(body);
-	await user.save();
+		user.assign(body);
+		await user.save();
 
-	// @ts-ignore
-	delete user.data;
+		// @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);
+		// TODO: send update member list event in gateway
+		await emitEvent({
+			event: "USER_UPDATE",
+			user_id: req.user_id,
+			data: user,
+		} as UserUpdateEvent);
 
-	res.json(user);
-});
+		res.json(user);
+	},
+);
 
 export default router;
 // {"message": "Invalid two-factor code", "code": 60008}
diff --git a/src/api/routes/users/@me/mfa/codes-verification.ts b/src/api/routes/users/@me/mfa/codes-verification.ts
index 071c71fa..3411605b 100644
--- a/src/api/routes/users/@me/mfa/codes-verification.ts
+++ b/src/api/routes/users/@me/mfa/codes-verification.ts
@@ -1,41 +1,49 @@
 import { Router, Request, Response } from "express";
 import { route } from "@fosscord/api";
-import { BackupCode, generateMfaBackupCodes, User, CodesVerificationSchema } from "@fosscord/util";
+import {
+	BackupCode,
+	generateMfaBackupCodes,
+	User,
+	CodesVerificationSchema,
+} from "@fosscord/util";
 
 const router = Router();
 
-router.post("/", route({ body: "CodesVerificationSchema" }), async (req: Request, res: Response) => {
-	const { key, nonce, regenerate } = req.body as CodesVerificationSchema;
-
-	// TODO: We don't have email/etc etc, so can't send a verification code.
-	// Once that's done, this route can verify `key`
-
-	const user = await User.findOneOrFail({ where: { id: req.user_id } });
-
-	var codes: BackupCode[];
-	if (regenerate) {
-		await BackupCode.update(
-			{ user: { id: req.user_id } },
-			{ expired: true }
-		);
-
-		codes = generateMfaBackupCodes(req.user_id);
-		await Promise.all(codes.map(x => x.save()));
-	}
-	else {
-		codes = await BackupCode.find({
-			where: {
-				user: {
-					id: req.user_id,
+router.post(
+	"/",
+	route({ body: "CodesVerificationSchema" }),
+	async (req: Request, res: Response) => {
+		const { key, nonce, regenerate } = req.body as CodesVerificationSchema;
+
+		// TODO: We don't have email/etc etc, so can't send a verification code.
+		// Once that's done, this route can verify `key`
+
+		const user = await User.findOneOrFail({ where: { id: req.user_id } });
+
+		var codes: BackupCode[];
+		if (regenerate) {
+			await BackupCode.update(
+				{ user: { id: req.user_id } },
+				{ expired: true },
+			);
+
+			codes = generateMfaBackupCodes(req.user_id);
+			await Promise.all(codes.map((x) => x.save()));
+		} else {
+			codes = await BackupCode.find({
+				where: {
+					user: {
+						id: req.user_id,
+					},
+					expired: false,
 				},
-				expired: false,
-			}
-		});
-	}
+			});
+		}
 
-	return res.json({
-		backup_codes: codes.map(x => ({ ...x, expired: undefined })),
-	});
-});
+		return res.json({
+			backup_codes: codes.map((x) => ({ ...x, expired: undefined })),
+		});
+	},
+);
 
 export default router;
diff --git a/src/api/routes/users/@me/mfa/codes.ts b/src/api/routes/users/@me/mfa/codes.ts
index 58466b9c..33053028 100644
--- a/src/api/routes/users/@me/mfa/codes.ts
+++ b/src/api/routes/users/@me/mfa/codes.ts
@@ -1,45 +1,62 @@
 import { Router, Request, Response } from "express";
 import { route } from "@fosscord/api";
-import { BackupCode, FieldErrors, generateMfaBackupCodes, User, MfaCodesSchema } from "@fosscord/util";
+import {
+	BackupCode,
+	FieldErrors,
+	generateMfaBackupCodes,
+	User,
+	MfaCodesSchema,
+} from "@fosscord/util";
 import bcrypt from "bcrypt";
 
 const router = Router();
 
 // TODO: This route is replaced with users/@me/mfa/codes-verification in newer clients
 
-router.post("/", route({ body: "MfaCodesSchema" }), async (req: Request, res: Response) => {
-	const { password, regenerate } = req.body as MfaCodesSchema;
-
-	const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["data"] });
-
-	if (!await bcrypt.compare(password, user.data.hash || "")) {
-		throw FieldErrors({ password: { message: req.t("auth:login.INVALID_PASSWORD"), code: "INVALID_PASSWORD" } });
-	}
-
-	var codes: BackupCode[];
-	if (regenerate) {
-		await BackupCode.update(
-			{ user: { id: req.user_id } },
-			{ expired: true }
-		);
-
-		codes = generateMfaBackupCodes(req.user_id);
-		await Promise.all(codes.map(x => x.save()));
-	}
-	else {
-		codes = await BackupCode.find({
-			where: {
-				user: {
-					id: req.user_id,
-				},
-				expired: false,
-			}
+router.post(
+	"/",
+	route({ body: "MfaCodesSchema" }),
+	async (req: Request, res: Response) => {
+		const { password, regenerate } = req.body as MfaCodesSchema;
+
+		const user = await User.findOneOrFail({
+			where: { id: req.user_id },
+			select: ["data"],
 		});
-	}
 
-	return res.json({
-		backup_codes: codes.map(x => ({ ...x, expired: undefined })),
-	});
-});
+		if (!(await bcrypt.compare(password, user.data.hash || ""))) {
+			throw FieldErrors({
+				password: {
+					message: req.t("auth:login.INVALID_PASSWORD"),
+					code: "INVALID_PASSWORD",
+				},
+			});
+		}
+
+		var codes: BackupCode[];
+		if (regenerate) {
+			await BackupCode.update(
+				{ user: { id: req.user_id } },
+				{ expired: true },
+			);
+
+			codes = generateMfaBackupCodes(req.user_id);
+			await Promise.all(codes.map((x) => x.save()));
+		} else {
+			codes = await BackupCode.find({
+				where: {
+					user: {
+						id: req.user_id,
+					},
+					expired: false,
+				},
+			});
+		}
+
+		return res.json({
+			backup_codes: codes.map((x) => ({ ...x, expired: undefined })),
+		});
+	},
+);
 
 export default router;
diff --git a/src/api/routes/users/@me/mfa/totp/disable.ts b/src/api/routes/users/@me/mfa/totp/disable.ts
index 2fe9355c..7916e598 100644
--- a/src/api/routes/users/@me/mfa/totp/disable.ts
+++ b/src/api/routes/users/@me/mfa/totp/disable.ts
@@ -1,41 +1,56 @@
 import { Router, Request, Response } from "express";
 import { route } from "@fosscord/api";
-import { verifyToken } from 'node-2fa';
+import { verifyToken } from "node-2fa";
 import { HTTPError } from "lambert-server";
-import { User, generateToken, BackupCode, TotpDisableSchema } from "@fosscord/util";
+import {
+	User,
+	generateToken,
+	BackupCode,
+	TotpDisableSchema,
+} from "@fosscord/util";
 
 const router = Router();
 
-router.post("/", route({ body: "TotpDisableSchema" }), async (req: Request, res: Response) => {
-	const body = req.body as TotpDisableSchema;
-
-	const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["totp_secret"] });
-
-	const backup = await BackupCode.findOne({ where: { code: body.code } });
-	if (!backup) {
-		const ret = verifyToken(user.totp_secret!, body.code);
-		if (!ret || ret.delta != 0)
-			throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
-	}
-
-	await User.update(
-		{ id: req.user_id },
-		{
-			mfa_enabled: false,
-			totp_secret: "",
-		},
-	);
-
-	await BackupCode.update(
-		{ user: { id: req.user_id } },
-		{
-			expired: true,
+router.post(
+	"/",
+	route({ body: "TotpDisableSchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as TotpDisableSchema;
+
+		const user = await User.findOneOrFail({
+			where: { id: req.user_id },
+			select: ["totp_secret"],
+		});
+
+		const backup = await BackupCode.findOne({ where: { code: body.code } });
+		if (!backup) {
+			const ret = verifyToken(user.totp_secret!, body.code);
+			if (!ret || ret.delta != 0)
+				throw new HTTPError(
+					req.t("auth:login.INVALID_TOTP_CODE"),
+					60008,
+				);
 		}
-	);
 
-	return res.json({
-		token: await generateToken(user.id),
-	});
-});
-
-export default router;
\ No newline at end of file
+		await User.update(
+			{ id: req.user_id },
+			{
+				mfa_enabled: false,
+				totp_secret: "",
+			},
+		);
+
+		await BackupCode.update(
+			{ user: { id: req.user_id } },
+			{
+				expired: true,
+			},
+		);
+
+		return res.json({
+			token: await generateToken(user.id),
+		});
+	},
+);
+
+export default router;
diff --git a/src/api/routes/users/@me/mfa/totp/enable.ts b/src/api/routes/users/@me/mfa/totp/enable.ts
index adafe180..75c64425 100644
--- a/src/api/routes/users/@me/mfa/totp/enable.ts
+++ b/src/api/routes/users/@me/mfa/totp/enable.ts
@@ -1,46 +1,62 @@
 import { Router, Request, Response } from "express";
-import { User, generateToken, generateMfaBackupCodes, TotpEnableSchema } from "@fosscord/util";
+import {
+	User,
+	generateToken,
+	generateMfaBackupCodes,
+	TotpEnableSchema,
+} from "@fosscord/util";
 import { route } from "@fosscord/api";
 import bcrypt from "bcrypt";
 import { HTTPError } from "lambert-server";
-import { verifyToken } from 'node-2fa';
+import { verifyToken } from "node-2fa";
 
 const router = Router();
 
-router.post("/", route({ body: "TotpEnableSchema" }), async (req: Request, res: Response) => {
-	const body = req.body as TotpEnableSchema;
-
-	const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["data", "email"] });
-
-	if (user.email == "demo@maddy.k.vu") throw new HTTPError("Demo user, sorry", 400);
-
-	// TODO: Are guests allowed to enable 2fa?
-	if (user.data.hash) {
-		if (!await bcrypt.compare(body.password, user.data.hash)) {
-			throw new HTTPError(req.t("auth:login.INVALID_PASSWORD"));
+router.post(
+	"/",
+	route({ body: "TotpEnableSchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as TotpEnableSchema;
+
+		const user = await User.findOneOrFail({
+			where: { id: req.user_id },
+			select: ["data", "email"],
+		});
+
+		if (user.email == "demo@maddy.k.vu")
+			throw new HTTPError("Demo user, sorry", 400);
+
+		// TODO: Are guests allowed to enable 2fa?
+		if (user.data.hash) {
+			if (!(await bcrypt.compare(body.password, user.data.hash))) {
+				throw new HTTPError(req.t("auth:login.INVALID_PASSWORD"));
+			}
 		}
-	}
-
-	if (!body.secret)
-		throw new HTTPError(req.t("auth:login.INVALID_TOTP_SECRET"), 60005);
-
-	if (!body.code)
-		throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
-
-	if (verifyToken(body.secret, body.code)?.delta != 0)
-		throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
-
-	let backup_codes = generateMfaBackupCodes(req.user_id);
-	await Promise.all(backup_codes.map(x => x.save()));
-	await User.update(
-		{ id: req.user_id },
-		{ mfa_enabled: true, totp_secret: body.secret }
-	);
-
-	res.send({
-		token: await generateToken(user.id),
-		backup_codes: backup_codes.map(x => ({ ...x, expired: undefined })),
-	});
-});
 
-export default router;
\ No newline at end of file
+		if (!body.secret)
+			throw new HTTPError(req.t("auth:login.INVALID_TOTP_SECRET"), 60005);
+
+		if (!body.code)
+			throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
+
+		if (verifyToken(body.secret, body.code)?.delta != 0)
+			throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
+
+		let backup_codes = generateMfaBackupCodes(req.user_id);
+		await Promise.all(backup_codes.map((x) => x.save()));
+		await User.update(
+			{ id: req.user_id },
+			{ mfa_enabled: true, totp_secret: body.secret },
+		);
+
+		res.send({
+			token: await generateToken(user.id),
+			backup_codes: backup_codes.map((x) => ({
+				...x,
+				expired: undefined,
+			})),
+		});
+	},
+);
+
+export default router;
diff --git a/src/api/routes/users/@me/notes.ts b/src/api/routes/users/@me/notes.ts
index f938f088..e54eb897 100644
--- a/src/api/routes/users/@me/notes.ts
+++ b/src/api/routes/users/@me/notes.ts
@@ -11,7 +11,7 @@ router.get("/:id", route({}), async (req: Request, res: Response) => {
 		where: {
 			owner: { id: req.user_id },
 			target: { id: id },
-		}
+		},
 	});
 
 	return res.json({
@@ -24,32 +24,40 @@ router.get("/:id", route({}), async (req: Request, res: Response) => {
 router.put("/:id", route({}), async (req: Request, res: Response) => {
 	const { id } = req.params;
 	const owner = await User.findOneOrFail({ where: { id: req.user_id } });
-	const target = await User.findOneOrFail({ where: { id: id } });		//if noted user does not exist throw
+	const target = await User.findOneOrFail({ where: { id: id } }); //if noted user does not exist throw
 	const { note } = req.body;
 
 	if (note && note.length) {
 		// upsert a note
-		if (await Note.findOne({ where: { owner: { id: owner.id }, target: { id: target.id } } })) {
+		if (
+			await Note.findOne({
+				where: { owner: { id: owner.id }, target: { id: target.id } },
+			})
+		) {
 			Note.update(
 				{ owner: { id: owner.id }, target: { id: target.id } },
-				{ owner, target, content: note }
+				{ owner, target, content: note },
 			);
+		} else {
+			Note.insert({
+				id: Snowflake.generate(),
+				owner,
+				target,
+				content: note,
+			});
 		}
-		else {
-			Note.insert(
-				{ id: Snowflake.generate(), owner, target, content: note }
-			);
-		}
-	}
-	else {
-		await Note.delete({ owner: { id: owner.id }, target: { id: target.id } });
+	} else {
+		await Note.delete({
+			owner: { id: owner.id },
+			target: { id: target.id },
+		});
 	}
 
 	await emitEvent({
 		event: "USER_NOTE_UPDATE",
 		data: {
 			note: note,
-			id: target.id
+			id: target.id,
 		},
 		user_id: owner.id,
 	});
diff --git a/src/api/routes/users/@me/relationships.ts b/src/api/routes/users/@me/relationships.ts
index cd33704d..3eec704b 100644
--- a/src/api/routes/users/@me/relationships.ts
+++ b/src/api/routes/users/@me/relationships.ts
@@ -6,7 +6,7 @@ import {
 	RelationshipRemoveEvent,
 	emitEvent,
 	Relationship,
-	Config
+	Config,
 } from "@fosscord/util";
 import { Router, Response, Request } from "express";
 import { HTTPError } from "lambert-server";
@@ -15,13 +15,16 @@ import { route } from "@fosscord/api";
 
 const router = Router();
 
-const userProjection: (keyof User)[] = ["relationships", ...PublicUserProjection];
+const userProjection: (keyof User)[] = [
+	"relationships",
+	...PublicUserProjection,
+];
 
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const user = await User.findOneOrFail({
 		where: { id: req.user_id },
 		relations: ["relationships", "relationships.to"],
-		select: ["id", "relationships"]
+		select: ["id", "relationships"],
 	});
 
 	//TODO DTO
@@ -30,49 +33,76 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 			id: r.to.id,
 			type: r.type,
 			nickname: null,
-			user: r.to.toPublicUser()
+			user: r.to.toPublicUser(),
 		};
 	});
 
 	return res.json(related_users);
 });
 
-router.put("/:id", route({ body: "RelationshipPutSchema" }), async (req: Request, res: Response) => {
-	return await updateRelationship(
-		req,
-		res,
-		await User.findOneOrFail({ where: { id: req.params.id }, relations: ["relationships", "relationships.to"], select: userProjection }),
-		req.body.type ?? RelationshipType.friends
-	);
-});
+router.put(
+	"/:id",
+	route({ body: "RelationshipPutSchema" }),
+	async (req: Request, res: Response) => {
+		return await updateRelationship(
+			req,
+			res,
+			await User.findOneOrFail({
+				where: { id: req.params.id },
+				relations: ["relationships", "relationships.to"],
+				select: userProjection,
+			}),
+			req.body.type ?? RelationshipType.friends,
+		);
+	},
+);
 
-router.post("/", route({ body: "RelationshipPostSchema" }), async (req: Request, res: Response) => {
-	return await updateRelationship(
-		req,
-		res,
-		await User.findOneOrFail({
-			relations: ["relationships", "relationships.to"],
-			select: userProjection,
-			where: {
-				discriminator: String(req.body.discriminator).padStart(4, "0"), //Discord send the discriminator as integer, we need to add leading zeroes
-				username: req.body.username
-			}
-		}),
-		req.body.type
-	);
-});
+router.post(
+	"/",
+	route({ body: "RelationshipPostSchema" }),
+	async (req: Request, res: Response) => {
+		return await updateRelationship(
+			req,
+			res,
+			await User.findOneOrFail({
+				relations: ["relationships", "relationships.to"],
+				select: userProjection,
+				where: {
+					discriminator: String(req.body.discriminator).padStart(
+						4,
+						"0",
+					), //Discord send the discriminator as integer, we need to add leading zeroes
+					username: req.body.username,
+				},
+			}),
+			req.body.type,
+		);
+	},
+);
 
 router.delete("/:id", route({}), async (req: Request, res: Response) => {
 	const { id } = req.params;
-	if (id === req.user_id) throw new HTTPError("You can't remove yourself as a friend");
+	if (id === req.user_id)
+		throw new HTTPError("You can't remove yourself as a friend");
 
-	const user = await User.findOneOrFail({ where: { id: req.user_id }, select: userProjection, relations: ["relationships"] });
-	const friend = await User.findOneOrFail({ where: { id: id }, select: userProjection, relations: ["relationships"] });
+	const user = await User.findOneOrFail({
+		where: { id: req.user_id },
+		select: userProjection,
+		relations: ["relationships"],
+	});
+	const friend = await User.findOneOrFail({
+		where: { id: id },
+		select: userProjection,
+		relations: ["relationships"],
+	});
 
 	const relationship = user.relationships.find((x) => x.to_id === id);
-	const friendRequest = friend.relationships.find((x) => x.to_id === req.user_id);
+	const friendRequest = friend.relationships.find(
+		(x) => x.to_id === req.user_id,
+	);
 
-	if (!relationship) throw new HTTPError("You are not friends with the user", 404);
+	if (!relationship)
+		throw new HTTPError("You are not friends with the user", 404);
 	if (relationship?.type === RelationshipType.blocked) {
 		// unblock user
 
@@ -81,8 +111,8 @@ router.delete("/:id", route({}), async (req: Request, res: Response) => {
 			emitEvent({
 				event: "RELATIONSHIP_REMOVE",
 				user_id: req.user_id,
-				data: relationship.toPublicRelationship()
-			} as RelationshipRemoveEvent)
+				data: relationship.toPublicRelationship(),
+			} as RelationshipRemoveEvent),
 		]);
 		return res.sendStatus(204);
 	}
@@ -92,8 +122,8 @@ router.delete("/:id", route({}), async (req: Request, res: Response) => {
 			await emitEvent({
 				event: "RELATIONSHIP_REMOVE",
 				data: friendRequest.toPublicRelationship(),
-				user_id: id
-			} as RelationshipRemoveEvent)
+				user_id: id,
+			} as RelationshipRemoveEvent),
 		]);
 	}
 
@@ -102,8 +132,8 @@ router.delete("/:id", route({}), async (req: Request, res: Response) => {
 		emitEvent({
 			event: "RELATIONSHIP_REMOVE",
 			data: relationship.toPublicRelationship(),
-			user_id: req.user_id
-		} as RelationshipRemoveEvent)
+			user_id: req.user_id,
+		} as RelationshipRemoveEvent),
 	]);
 
 	return res.sendStatus(204);
@@ -111,26 +141,40 @@ router.delete("/:id", route({}), async (req: Request, res: Response) => {
 
 export default router;
 
-async function updateRelationship(req: Request, res: Response, friend: User, type: RelationshipType) {
+async function updateRelationship(
+	req: Request,
+	res: Response,
+	friend: User,
+	type: RelationshipType,
+) {
 	const id = friend.id;
-	if (id === req.user_id) throw new HTTPError("You can't add yourself as a friend");
+	if (id === req.user_id)
+		throw new HTTPError("You can't add yourself as a friend");
 
 	const user = await User.findOneOrFail({
 		where: { id: req.user_id },
-		relations: ["relationships", "relationships.to"], select: userProjection
+		relations: ["relationships", "relationships.to"],
+		select: userProjection,
 	});
 
 	var relationship = user.relationships.find((x) => x.to_id === id);
-	const friendRequest = friend.relationships.find((x) => x.to_id === req.user_id);
+	const friendRequest = friend.relationships.find(
+		(x) => x.to_id === req.user_id,
+	);
 
 	// TODO: you can add infinitely many blocked users (should this be prevented?)
 	if (type === RelationshipType.blocked) {
 		if (relationship) {
-			if (relationship.type === RelationshipType.blocked) throw new HTTPError("You already blocked the user");
+			if (relationship.type === RelationshipType.blocked)
+				throw new HTTPError("You already blocked the user");
 			relationship.type = RelationshipType.blocked;
 			await relationship.save();
 		} else {
-			relationship = await Relationship.create({ to_id: id, type: RelationshipType.blocked, from_id: req.user_id }).save();
+			relationship = await Relationship.create({
+				to_id: id,
+				type: RelationshipType.blocked,
+				from_id: req.user_id,
+			}).save();
 		}
 
 		if (friendRequest && friendRequest.type !== RelationshipType.blocked) {
@@ -139,43 +183,56 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ
 				emitEvent({
 					event: "RELATIONSHIP_REMOVE",
 					data: friendRequest.toPublicRelationship(),
-					user_id: id
-				} as RelationshipRemoveEvent)
+					user_id: id,
+				} as RelationshipRemoveEvent),
 			]);
 		}
 
 		await emitEvent({
 			event: "RELATIONSHIP_ADD",
 			data: relationship.toPublicRelationship(),
-			user_id: req.user_id
+			user_id: req.user_id,
 		} as RelationshipAddEvent);
 
 		return res.sendStatus(204);
 	}
 
 	const { maxFriends } = Config.get().limits.user;
-	if (user.relationships.length >= maxFriends) throw DiscordApiErrors.MAXIMUM_FRIENDS.withParams(maxFriends);
+	if (user.relationships.length >= maxFriends)
+		throw DiscordApiErrors.MAXIMUM_FRIENDS.withParams(maxFriends);
 
-	var incoming_relationship = Relationship.create({ nickname: undefined, type: RelationshipType.incoming, to: user, from: friend });
+	var incoming_relationship = Relationship.create({
+		nickname: undefined,
+		type: RelationshipType.incoming,
+		to: user,
+		from: friend,
+	});
 	var outgoing_relationship = Relationship.create({
 		nickname: undefined,
 		type: RelationshipType.outgoing,
 		to: friend,
-		from: user
+		from: user,
 	});
 
 	if (friendRequest) {
-		if (friendRequest.type === RelationshipType.blocked) throw new HTTPError("The user blocked you");
-		if (friendRequest.type === RelationshipType.friends) throw new HTTPError("You are already friends with the user");
+		if (friendRequest.type === RelationshipType.blocked)
+			throw new HTTPError("The user blocked you");
+		if (friendRequest.type === RelationshipType.friends)
+			throw new HTTPError("You are already friends with the user");
 		// accept friend request
 		incoming_relationship = friendRequest;
 		incoming_relationship.type = RelationshipType.friends;
 	}
 
 	if (relationship) {
-		if (relationship.type === RelationshipType.outgoing) throw new HTTPError("You already sent a friend request");
-		if (relationship.type === RelationshipType.blocked) throw new HTTPError("Unblock the user before sending a friend request");
-		if (relationship.type === RelationshipType.friends) throw new HTTPError("You are already friends with the user");
+		if (relationship.type === RelationshipType.outgoing)
+			throw new HTTPError("You already sent a friend request");
+		if (relationship.type === RelationshipType.blocked)
+			throw new HTTPError(
+				"Unblock the user before sending a friend request",
+			);
+		if (relationship.type === RelationshipType.friends)
+			throw new HTTPError("You are already friends with the user");
 		outgoing_relationship = relationship;
 		outgoing_relationship.type = RelationshipType.friends;
 	}
@@ -186,16 +243,16 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ
 		emitEvent({
 			event: "RELATIONSHIP_ADD",
 			data: outgoing_relationship.toPublicRelationship(),
-			user_id: req.user_id
+			user_id: req.user_id,
 		} as RelationshipAddEvent),
 		emitEvent({
 			event: "RELATIONSHIP_ADD",
 			data: {
 				...incoming_relationship.toPublicRelationship(),
-				should_notify: true
+				should_notify: true,
 			},
-			user_id: id
-		} as RelationshipAddEvent)
+			user_id: id,
+		} as RelationshipAddEvent),
 	]);
 
 	return res.sendStatus(204);
diff --git a/src/api/routes/users/@me/settings.ts b/src/api/routes/users/@me/settings.ts
index 9060baf7..30e5969c 100644
--- a/src/api/routes/users/@me/settings.ts
+++ b/src/api/routes/users/@me/settings.ts
@@ -4,25 +4,31 @@ import { route } from "@fosscord/api";
 
 const router = Router();
 
-export interface UserSettingsSchema extends Partial<UserSettings> { }
+export interface UserSettingsSchema extends Partial<UserSettings> {}
 
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const user = await User.findOneOrFail({
 		where: { id: req.user_id },
-		select: ["settings"]
+		select: ["settings"],
 	});
 	return res.json(user.settings);
 });
 
-router.patch("/", route({ body: "UserSettingsSchema" }), async (req: Request, res: Response) => {
-	const body = req.body as UserSettings;
-	if (body.locale === "en") body.locale = "en-US"; // fix discord client crash on unkown locale
+router.patch(
+	"/",
+	route({ body: "UserSettingsSchema" }),
+	async (req: Request, res: Response) => {
+		const body = req.body as UserSettings;
+		if (body.locale === "en") body.locale = "en-US"; // fix discord client crash on unkown locale
 
-	const user = await User.findOneOrFail({ where: { id: req.user_id, bot: false } });
-	user.settings = { ...user.settings, ...body };
-	await user.save();
+		const user = await User.findOneOrFail({
+			where: { id: req.user_id, bot: false },
+		});
+		user.settings = { ...user.settings, ...body };
+		await user.save();
 
-	res.json(user.settings);
-});
+		res.json(user.settings);
+	},
+);
 
 export default router;
diff --git a/src/api/start.ts b/src/api/start.ts
index ccb4d108..fa120e59 100644
--- a/src/api/start.ts
+++ b/src/api/start.ts
@@ -11,7 +11,7 @@ var cores = 1;
 try {
 	cores = Number(process.env.THREADS) || os.cpus().length;
 } catch {
-	console.log("[API] Failed to get thread count! Using 1...")
+	console.log("[API] Failed to get thread count! Using 1...");
 }
 
 if (cluster.isMaster && process.env.NODE_ENV == "production") {
diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts
index 0d29c2e6..d44b368f 100644
--- a/src/api/util/handlers/Message.ts
+++ b/src/api/util/handlers/Message.ts
@@ -32,24 +32,32 @@ const allow_empty = false;
 // TODO: check webhook, application, system author, stickers
 // TODO: embed gifs/videos/images
 
-const LINK_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
+const LINK_REGEX =
+	/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
 
 const DEFAULT_FETCH_OPTIONS: any = {
 	redirect: "follow",
 	follow: 1,
 	headers: {
-		"user-agent": "Mozilla/5.0 (compatible; Fosscord/1.0; +https://github.com/fosscord/fosscord)"
+		"user-agent":
+			"Mozilla/5.0 (compatible; Fosscord/1.0; +https://github.com/fosscord/fosscord)",
 	},
 	// size: 1024 * 1024 * 5, 	// grabbed from config later
 	compress: true,
-	method: "GET"
+	method: "GET",
 };
 
 export async function handleMessage(opts: MessageOptions): Promise<Message> {
-	const channel = await Channel.findOneOrFail({ where: { id: opts.channel_id }, relations: ["recipients"] });
-	if (!channel || !opts.channel_id) throw new HTTPError("Channel not found", 404);
+	const channel = await Channel.findOneOrFail({
+		where: { id: opts.channel_id },
+		relations: ["recipients"],
+	});
+	if (!channel || !opts.channel_id)
+		throw new HTTPError("Channel not found", 404);
 
-	const stickers = opts.sticker_ids ? await Sticker.find({ where: { id: In(opts.sticker_ids) } }) : undefined;
+	const stickers = opts.sticker_ids
+		? await Sticker.find({ where: { id: In(opts.sticker_ids) } })
+		: undefined;
 	const message = Message.create({
 		...opts,
 		id: Snowflake.generate(),
@@ -58,11 +66,14 @@ 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,
 	});
 
-	if (message.content && message.content.length > Config.get().limits.message.maxCharacters) {
+	if (
+		message.content &&
+		message.content.length > Config.get().limits.message.maxCharacters
+	) {
 		throw new HTTPError("Content length over max character limit");
 	}
 
@@ -72,13 +83,21 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
 		rights.hasThrow("SEND_MESSAGES");
 	}
 	if (opts.application_id) {
-		message.application = await Application.findOneOrFail({ where: { id: opts.application_id } });
+		message.application = await Application.findOneOrFail({
+			where: { id: opts.application_id },
+		});
 	}
 	if (opts.webhook_id) {
-		message.webhook = await Webhook.findOneOrFail({ where: { id: opts.webhook_id } });
+		message.webhook = await Webhook.findOneOrFail({
+			where: { id: opts.webhook_id },
+		});
 	}
 
-	const permission = await getPermission(opts.author_id, channel.guild_id, opts.channel_id);
+	const permission = await getPermission(
+		opts.author_id,
+		channel.guild_id,
+		opts.channel_id,
+	);
 	permission.hasThrow("SEND_MESSAGES");
 	if (permission.cache.member) {
 		message.member = permission.cache.member;
@@ -89,10 +108,18 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
 		permission.hasThrow("READ_MESSAGE_HISTORY");
 		// code below has to be redone when we add custom message routing
 		if (message.guild_id !== null) {
-			const guild = await Guild.findOneOrFail({ where: { id: channel.guild_id } });
+			const guild = await Guild.findOneOrFail({
+				where: { id: channel.guild_id },
+			});
 			if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) {
-				if (opts.message_reference.guild_id !== channel.guild_id) throw new HTTPError("You can only reference messages from this guild");
-				if (opts.message_reference.channel_id !== opts.channel_id) throw new HTTPError("You can only reference messages from this channel");
+				if (opts.message_reference.guild_id !== channel.guild_id)
+					throw new HTTPError(
+						"You can only reference messages from this guild",
+					);
+				if (opts.message_reference.channel_id !== opts.channel_id)
+					throw new HTTPError(
+						"You can only reference messages from this channel",
+					);
 			}
 		}
 		/** Q: should be checked if the referenced message exists? ANSWER: NO
@@ -102,7 +129,13 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
 	}
 
 	// TODO: stickers/activity
-	if (!allow_empty && (!opts.content && !opts.embeds?.length && !opts.attachments?.length && !opts.sticker_ids?.length)) {
+	if (
+		!allow_empty &&
+		!opts.content &&
+		!opts.embeds?.length &&
+		!opts.attachments?.length &&
+		!opts.sticker_ids?.length
+	) {
 		throw new HTTPError("Empty messages are not allowed", 50006);
 	}
 
@@ -112,31 +145,42 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
 	var mention_user_ids = [] as string[];
 	var mention_everyone = false;
 
-	if (content) { // TODO: explicit-only mentions
+	if (content) {
+		// TODO: explicit-only mentions
 		message.content = content.trim();
 		for (const [_, mention] of content.matchAll(CHANNEL_MENTION)) {
-			if (!mention_channel_ids.includes(mention)) mention_channel_ids.push(mention);
+			if (!mention_channel_ids.includes(mention))
+				mention_channel_ids.push(mention);
 		}
 
 		for (const [_, mention] of content.matchAll(USER_MENTION)) {
-			if (!mention_user_ids.includes(mention)) mention_user_ids.push(mention);
+			if (!mention_user_ids.includes(mention))
+				mention_user_ids.push(mention);
 		}
 
 		await Promise.all(
-			Array.from(content.matchAll(ROLE_MENTION)).map(async ([_, mention]) => {
-				const role = await Role.findOneOrFail({ where: { id: mention, guild_id: channel.guild_id } });
-				if (role.mentionable || permission.has("MANAGE_ROLES")) {
-					mention_role_ids.push(mention);
-				}
-			})
+			Array.from(content.matchAll(ROLE_MENTION)).map(
+				async ([_, mention]) => {
+					const role = await Role.findOneOrFail({
+						where: { id: mention, guild_id: channel.guild_id },
+					});
+					if (role.mentionable || permission.has("MANAGE_ROLES")) {
+						mention_role_ids.push(mention);
+					}
+				},
+			),
 		);
 
 		if (permission.has("MENTION_EVERYONE")) {
-			mention_everyone = !!content.match(EVERYONE_MENTION) || !!content.match(HERE_MENTION);
+			mention_everyone =
+				!!content.match(EVERYONE_MENTION) ||
+				!!content.match(HERE_MENTION);
 		}
 	}
 
-	message.mention_channels = mention_channel_ids.map((x) => Channel.create({ id: x }));
+	message.mention_channels = mention_channel_ids.map((x) =>
+		Channel.create({ id: x }),
+	);
 	message.mention_roles = mention_role_ids.map((x) => Role.create({ id: x }));
 	message.mentions = mention_user_ids.map((x) => User.create({ id: x }));
 	message.mention_everyone = mention_everyone;
@@ -156,7 +200,8 @@ export async function postHandleMessage(message: Message) {
 
 	links = links.slice(0, 20) as RegExpMatchArray; // embed max 20 links — TODO: make this configurable with instance policies
 
-	const { endpointPublic, resizeWidthMax, resizeHeightMax } = Config.get().cdn;
+	const { endpointPublic, resizeWidthMax, resizeHeightMax } =
+		Config.get().cdn;
 
 	for (const link of links) {
 		try {
@@ -176,45 +221,64 @@ export async function postHandleMessage(message: Message) {
 					},
 					image: {
 						// can't be bothered rn
-						proxy_url: `${endpointPublic}/external/resize/${encodeURIComponent(link)}?width=500&height=400`,
+						proxy_url: `${endpointPublic}/external/resize/${encodeURIComponent(
+							link,
+						)}?width=500&height=400`,
 						url: link,
 						width: 500,
-						height: 400
-					}
+						height: 400,
+					},
 				};
 				data.embeds.push(embed);
-			}
-			else {
+			} else {
 				const text = await request.text();
 				const $ = cheerio.load(text);
 
 				const title = $('meta[property="og:title"]').attr("content");
 				const provider_name = $('meta[property="og:site_name"]').text();
-				const author_name = $('meta[property="article:author"]').attr("content");
-				const description = $('meta[property="og:description"]').attr("content") || $('meta[property="description"]').attr("content");
+				const author_name = $('meta[property="article:author"]').attr(
+					"content",
+				);
+				const description =
+					$('meta[property="og:description"]').attr("content") ||
+					$('meta[property="description"]').attr("content");
 
 				const image = $('meta[property="og:image"]').attr("content");
-				const width = parseInt($('meta[property="og:image:width"]').attr("content") || "") || undefined;
-				const height = parseInt($('meta[property="og:image:height"]').attr("content") || "") || undefined;
+				const width =
+					parseInt(
+						$('meta[property="og:image:width"]').attr("content") ||
+							"",
+					) || undefined;
+				const height =
+					parseInt(
+						$('meta[property="og:image:height"]').attr("content") ||
+							"",
+					) || undefined;
 
 				const url = $('meta[property="og:url"]').attr("content");
 				// TODO: color
 				embed = {
 					provider: {
 						url: link,
-						name: provider_name
-					}
+						name: provider_name,
+					},
 				};
 
 				const resizeWidth = Math.min(resizeWidthMax ?? 1, width ?? 100);
-				const resizeHeight = Math.min(resizeHeightMax ?? 1, height ?? 100);
+				const resizeHeight = Math.min(
+					resizeHeightMax ?? 1,
+					height ?? 100,
+				);
 				if (author_name) embed.author = { name: author_name };
-				if (image) embed.thumbnail = {
-					proxy_url: `${endpointPublic}/external/resize/${encodeURIComponent(image)}?width=${resizeWidth}&height=${resizeHeight}`,
-					url: image,
-					width: width,
-					height: height
-				};
+				if (image)
+					embed.thumbnail = {
+						proxy_url: `${endpointPublic}/external/resize/${encodeURIComponent(
+							image,
+						)}?width=${resizeWidth}&height=${resizeHeight}`,
+						url: image,
+						width: width,
+						height: height,
+					};
 				if (title) embed.title = title;
 				if (url) embed.url = url;
 				if (description) embed.description = description;
@@ -227,18 +291,25 @@ export async function postHandleMessage(message: Message) {
 
 				// very bad code below
 				// don't care lol
-				if (embed?.thumbnail?.url && approvedProviders.indexOf(new URL(embed.thumbnail.url).hostname) !== -1) {
+				if (
+					embed?.thumbnail?.url &&
+					approvedProviders.indexOf(
+						new URL(embed.thumbnail.url).hostname,
+					) !== -1
+				) {
 					embed = {
 						provider: {
 							url: link,
 							name: new URL(link).hostname,
 						},
 						image: {
-							proxy_url: `${endpointPublic}/external/resize/${encodeURIComponent(image!)}?width=${resizeWidth}&height=${resizeHeight}`,
+							proxy_url: `${endpointPublic}/external/resize/${encodeURIComponent(
+								image!,
+							)}?width=${resizeWidth}&height=${resizeHeight}`,
 							url: image,
 							width: width,
-							height: height
-						}
+							height: height,
+						},
 					};
 				}
 
@@ -246,16 +317,19 @@ export async function postHandleMessage(message: Message) {
 					data.embeds.push(embed);
 				}
 			}
-		} catch (error) { }
+		} catch (error) {}
 	}
 
 	await Promise.all([
 		emitEvent({
 			event: "MESSAGE_UPDATE",
 			channel_id: message.channel_id,
-			data
+			data,
 		} as MessageUpdateEvent),
-		Message.update({ id: message.id, channel_id: message.channel_id }, { embeds: data.embeds })
+		Message.update(
+			{ id: message.id, channel_id: message.channel_id },
+			{ embeds: data.embeds },
+		),
 	]);
 }
 
@@ -264,10 +338,14 @@ export async function sendMessage(opts: MessageOptions) {
 
 	await Promise.all([
 		Message.insert(message),
-		emitEvent({ event: "MESSAGE_CREATE", channel_id: opts.channel_id, data: message.toJSON() } as MessageCreateEvent)
+		emitEvent({
+			event: "MESSAGE_CREATE",
+			channel_id: opts.channel_id,
+			data: message.toJSON(),
+		} 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/handlers/Voice.ts b/src/api/util/handlers/Voice.ts
index 4d60eb91..88e266a1 100644
--- a/src/api/util/handlers/Voice.ts
+++ b/src/api/util/handlers/Voice.ts
@@ -3,7 +3,9 @@ import { distanceBetweenLocations, IPAnalysis } from "../utility/ipAddress";
 
 export async function getVoiceRegions(ipAddress: string, vip: boolean) {
 	const regions = Config.get().regions;
-	const availableRegions = regions.available.filter((ar) => (vip ? true : !ar.vip));
+	const availableRegions = regions.available.filter((ar) =>
+		vip ? true : !ar.vip,
+	);
 	let optimalId = regions.default;
 
 	if (!regions.useDefaultAsOptimal) {
@@ -13,7 +15,10 @@ export async function getVoiceRegions(ipAddress: string, vip: boolean) {
 
 		for (let ar of availableRegions) {
 			//TODO the endpoint location should be saved in the database if not already present to prevent IPAnalysis call
-			const dist = distanceBetweenLocations(clientIpAnalysis, ar.location || (await IPAnalysis(ar.endpoint)));
+			const dist = distanceBetweenLocations(
+				clientIpAnalysis,
+				ar.location || (await IPAnalysis(ar.endpoint)),
+			);
 
 			if (dist < min) {
 				min = dist;
@@ -27,6 +32,6 @@ export async function getVoiceRegions(ipAddress: string, vip: boolean) {
 		name: ar.name,
 		custom: ar.custom,
 		deprecated: ar.deprecated,
-		optimal: ar.id === optimalId
+		optimal: ar.id === optimalId,
 	}));
 }
diff --git a/src/api/util/handlers/route.ts b/src/api/util/handlers/route.ts
index c245b411..5dcae953 100644
--- a/src/api/util/handlers/route.ts
+++ b/src/api/util/handlers/route.ts
@@ -10,7 +10,7 @@ import {
 	PermissionResolvable,
 	Permissions,
 	RightResolvable,
-	Rights
+	Rights,
 } from "@fosscord/util";
 import { NextFunction, Request, Response } from "express";
 import { AnyValidateFunction } from "ajv/dist/core";
@@ -23,7 +23,11 @@ declare global {
 	}
 }
 
-export type RouteResponse = { status?: number; body?: `${string}Response`; headers?: Record<string, string> };
+export type RouteResponse = {
+	status?: number;
+	body?: `${string}Response`;
+	headers?: Record<string, string>;
+};
 
 export interface RouteOptions {
 	permission?: PermissionResolvable;
@@ -48,11 +52,17 @@ export function route(opts: RouteOptions) {
 	return async (req: Request, res: Response, next: NextFunction) => {
 		if (opts.permission) {
 			const required = new Permissions(opts.permission);
-			req.permission = await getPermission(req.user_id, req.params.guild_id, req.params.channel_id);
+			req.permission = await getPermission(
+				req.user_id,
+				req.params.guild_id,
+				req.params.channel_id,
+			);
 
 			// bitfield comparison: check if user lacks certain permission
 			if (!req.permission.has(required)) {
-				throw DiscordApiErrors.MISSING_PERMISSIONS.withParams(opts.permission as string);
+				throw DiscordApiErrors.MISSING_PERMISSIONS.withParams(
+					opts.permission as string,
+				);
 			}
 		}
 
@@ -61,15 +71,26 @@ export function route(opts: RouteOptions) {
 			req.rights = await getRights(req.user_id);
 
 			if (!req.rights || !req.rights.has(required)) {
-				throw FosscordApiErrors.MISSING_RIGHTS.withParams(opts.right as string);
+				throw FosscordApiErrors.MISSING_RIGHTS.withParams(
+					opts.right as string,
+				);
 			}
 		}
 
 		if (validate) {
 			const valid = validate(normalizeBody(req.body));
 			if (!valid) {
-				const fields: Record<string, { code?: string; message: string }> = {};
-				validate.errors?.forEach((x) => (fields[x.instancePath.slice(1)] = { code: x.keyword, message: x.message || "" }));
+				const fields: Record<
+					string,
+					{ code?: string; message: string }
+				> = {};
+				validate.errors?.forEach(
+					(x) =>
+						(fields[x.instancePath.slice(1)] = {
+							code: x.keyword,
+							message: x.message || "",
+						}),
+				);
 				throw FieldErrors(fields);
 			}
 		}
diff --git a/src/api/util/index.ts b/src/api/util/index.ts
index de6b6064..9f375f72 100644
--- a/src/api/util/index.ts
+++ b/src/api/util/index.ts
@@ -6,4 +6,4 @@ export * from "./utility/RandomInviteID";
 export * from "./handlers/route";
 export * from "./utility/String";
 export * from "./handlers/Voice";
-export * from "./utility/captcha";
\ No newline at end of file
+export * from "./utility/captcha";
diff --git a/src/api/util/utility/Base64.ts b/src/api/util/utility/Base64.ts
index 46cff77a..c10176f2 100644
--- a/src/api/util/utility/Base64.ts
+++ b/src/api/util/utility/Base64.ts
@@ -1,4 +1,5 @@
-const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+";
+const alphabet =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+";
 
 // binary to string lookup table
 const b2s = alphabet.split("");
diff --git a/src/api/util/utility/RandomInviteID.ts b/src/api/util/utility/RandomInviteID.ts
index 7ea344e0..bfed65bb 100644
--- a/src/api/util/utility/RandomInviteID.ts
+++ b/src/api/util/utility/RandomInviteID.ts
@@ -2,7 +2,8 @@ import { Snowflake } from "@fosscord/util";
 
 export function random(length = 6) {
 	// Declare all characters
-	let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+	let chars =
+		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 
 	// Pick characers randomly
 	let str = "";
@@ -15,18 +16,18 @@ export function random(length = 6) {
 
 export function snowflakeBasedInvite() {
 	// Declare all characters
-	let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+	let chars =
+		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 	let base = BigInt(chars.length);
 	let snowflake = Snowflake.generateWorkerProcess();
 
 	// snowflakes hold ~10.75 characters worth of entropy;
 	// safe to generate a 8-char invite out of them
 	let str = "";
-	for (let i=0; i < 10; i++) {
-		
+	for (let i = 0; i < 10; i++) {
 		str.concat(chars.charAt(Number(snowflake % base)));
 		snowflake = snowflake / base;
 	}
-	
-	return str.substr(3,8).split("").reverse().join("");
+
+	return str.substr(3, 8).split("").reverse().join("");
 }
diff --git a/src/api/util/utility/String.ts b/src/api/util/utility/String.ts
index 982b7e11..0913013e 100644
--- a/src/api/util/utility/String.ts
+++ b/src/api/util/utility/String.ts
@@ -2,13 +2,21 @@ import { Request } from "express";
 import { ntob } from "./Base64";
 import { FieldErrors } from "@fosscord/util";
 
-export function checkLength(str: string, min: number, max: number, key: string, req: Request) {
+export function checkLength(
+	str: string,
+	min: number,
+	max: number,
+	key: string,
+	req: Request,
+) {
 	if (str.length < min || str.length > max) {
 		throw FieldErrors({
 			[key]: {
 				code: "BASE_TYPE_BAD_LENGTH",
-				message: req.t("common:field.BASE_TYPE_BAD_LENGTH", { length: `${min} - ${max}` })
-			}
+				message: req.t("common:field.BASE_TYPE_BAD_LENGTH", {
+					length: `${min} - ${max}`,
+				}),
+			},
 		});
 	}
 }
diff --git a/src/api/util/utility/captcha.ts b/src/api/util/utility/captcha.ts
index 739647d2..50e2c91a 100644
--- a/src/api/util/utility/captcha.ts
+++ b/src/api/util/utility/captcha.ts
@@ -7,8 +7,8 @@ export interface hcaptchaResponse {
 	hostname: string;
 	credit: boolean;
 	"error-codes": string[];
-	score: number;	// enterprise only
-	score_reason: string[];	// enterprise only
+	score: number; // enterprise only
+	score_reason: string[]; // enterprise only
 }
 
 export interface recaptchaResponse {
@@ -23,7 +23,7 @@ export interface recaptchaResponse {
 const verifyEndpoints = {
 	hcaptcha: "https://hcaptcha.com/siteverify",
 	recaptcha: "https://www.google.com/recaptcha/api/siteverify",
-}
+};
 
 export async function verifyCaptcha(response: string, ip?: string) {
 	const { security } = Config.get();
@@ -36,11 +36,12 @@ export async function verifyCaptcha(response: string, ip?: string) {
 		headers: {
 			"Content-Type": "application/x-www-form-urlencoded",
 		},
-		body: `response=${encodeURIComponent(response)}`
-			+ `&secret=${encodeURIComponent(secret!)}`
-			+ `&sitekey=${encodeURIComponent(sitekey!)}`
-			+ (ip ? `&remoteip=${encodeURIComponent(ip!)}` : ""),
+		body:
+			`response=${encodeURIComponent(response)}` +
+			`&secret=${encodeURIComponent(secret!)}` +
+			`&sitekey=${encodeURIComponent(sitekey!)}` +
+			(ip ? `&remoteip=${encodeURIComponent(ip!)}` : ""),
 	});
 
-	return await res.json() as hcaptchaResponse | recaptchaResponse;
-}
\ No newline at end of file
+	return (await res.json()) as hcaptchaResponse | recaptchaResponse;
+}
diff --git a/src/api/util/utility/ipAddress.ts b/src/api/util/utility/ipAddress.ts
index f17b145e..d166ebc5 100644
--- a/src/api/util/utility/ipAddress.ts
+++ b/src/api/util/utility/ipAddress.ts
@@ -25,27 +25,27 @@ const exampleData = {
 		name: "",
 		domain: "",
 		route: "",
-		type: "isp"
+		type: "isp",
 	},
 	languages: [
 		{
 			name: "",
-			native: ""
-		}
+			native: "",
+		},
 	],
 	currency: {
 		name: "",
 		code: "",
 		symbol: "",
 		native: "",
-		plural: ""
+		plural: "",
 	},
 	time_zone: {
 		name: "",
 		abbr: "",
 		offset: "",
 		is_dst: true,
-		current_time: ""
+		current_time: "",
 	},
 	threat: {
 		is_tor: false,
@@ -54,10 +54,10 @@ const exampleData = {
 		is_known_attacker: false,
 		is_known_abuser: false,
 		is_threat: false,
-		is_bogon: false
+		is_bogon: false,
 	},
 	count: 0,
-	status: 200
+	status: 200,
 };
 
 //TODO add function that support both ip and domain names
@@ -65,7 +65,9 @@ export async function IPAnalysis(ip: string): Promise<typeof exampleData> {
 	const { ipdataApiKey } = Config.get().security;
 	if (!ipdataApiKey) return { ...exampleData, ip };
 
-	return (await fetch(`https://api.ipdata.co/${ip}?api-key=${ipdataApiKey}`)).json() as any; // TODO: types
+	return (
+		await fetch(`https://api.ipdata.co/${ip}?api-key=${ipdataApiKey}`)
+	).json() as any; // TODO: types
 }
 
 export function isProxy(data: typeof exampleData) {
@@ -77,19 +79,35 @@ export function isProxy(data: typeof exampleData) {
 }
 
 export function getIpAdress(req: Request): string {
-	// @ts-ignore
-	return req.headers[Config.get().security.forwadedFor] || req.socket.remoteAddress;
+	return (
+		// @ts-ignore
+		req.headers[Config.get().security.forwadedFor] ||
+		req.socket.remoteAddress
+	);
 }
 
 export function distanceBetweenLocations(loc1: any, loc2: any): number {
-	return distanceBetweenCoords(loc1.latitude, loc1.longitude, loc2.latitude, loc2.longitude);
+	return distanceBetweenCoords(
+		loc1.latitude,
+		loc1.longitude,
+		loc2.latitude,
+		loc2.longitude,
+	);
 }
 
 //Haversine function
-function distanceBetweenCoords(lat1: number, lon1: number, lat2: number, lon2: number) {
+function distanceBetweenCoords(
+	lat1: number,
+	lon1: number,
+	lat2: number,
+	lon2: number,
+) {
 	const p = 0.017453292519943295; // Math.PI / 180
 	const c = Math.cos;
-	const a = 0.5 - c((lat2 - lat1) * p) / 2 + (c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p))) / 2;
+	const a =
+		0.5 -
+		c((lat2 - lat1) * p) / 2 +
+		(c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p))) / 2;
 
 	return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
 }
diff --git a/src/api/util/utility/passwordStrength.ts b/src/api/util/utility/passwordStrength.ts
index 439700d0..35c55999 100644
--- a/src/api/util/utility/passwordStrength.ts
+++ b/src/api/util/utility/passwordStrength.ts
@@ -18,7 +18,8 @@ const blocklist: string[] = []; // TODO: update ones passwordblocklist is stored
  * Returns: 0 > pw > 1
  */
 export function checkPassword(password: string): number {
-	const { minLength, minNumbers, minUpperCase, minSymbols } = Config.get().register.password;
+	const { minLength, minNumbers, minUpperCase, minSymbols } =
+		Config.get().register.password;
 	var strength = 0;
 
 	// checks for total password len
@@ -42,19 +43,24 @@ export function checkPassword(password: string): number {
 	}
 
 	// checks if password only consists of numbers or only consists of chars
-	if (password.length == password.count(reNUMBER) || password.length === password.count(reUPPERCASELETTER)) {
+	if (
+		password.length == password.count(reNUMBER) ||
+		password.length === password.count(reUPPERCASELETTER)
+	) {
 		strength = 0;
 	}
-	
+
 	let entropyMap: { [key: string]: number } = {};
 	for (let i = 0; i < password.length; i++) {
 		if (entropyMap[password[i]]) entropyMap[password[i]]++;
 		else entropyMap[password[i]] = 1;
 	}
-	
+
 	let entropies = Object.values(entropyMap);
-	
-	entropies.map(x => (x / entropyMap.length));
-	strength += entropies.reduceRight((a: number, x: number) => a - (x * Math.log2(x))) / Math.log2(password.length);	
+
+	entropies.map((x) => x / entropyMap.length);
+	strength +=
+		entropies.reduceRight((a: number, x: number) => a - x * Math.log2(x)) /
+		Math.log2(password.length);
 	return strength;
 }