summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--api/src/routes/channels/#channel_id/pins.ts3
-rw-r--r--api/src/routes/channels/#channel_id/webhooks.ts9
-rw-r--r--api/src/routes/guilds/#guild_id/roles.ts45
-rw-r--r--api/src/routes/guilds/index.ts3
-rw-r--r--api/src/routes/guilds/templates/index.ts3
-rw-r--r--api/src/routes/users/@me/relationships.ts8
-rw-r--r--api/src/util/ApiError.ts38
-rw-r--r--api/src/util/Constants.ts114
-rw-r--r--gateway/src/schema/Identify.ts53
9 files changed, 132 insertions, 144 deletions
diff --git a/api/src/routes/channels/#channel_id/pins.ts b/api/src/routes/channels/#channel_id/pins.ts
index 96a3fdbf..fafb789f 100644
--- a/api/src/routes/channels/#channel_id/pins.ts
+++ b/api/src/routes/channels/#channel_id/pins.ts
@@ -1,6 +1,7 @@
 import { Channel, ChannelPinsUpdateEvent, Config, emitEvent, getPermission, Message, MessageUpdateEvent } from "@fosscord/util";
 import { Router, Request, Response } from "express";
 import { HTTPError } from "lambert-server";
+import { DiscordApiErrors } from "../../../util/Constants";
 
 const router: Router = Router();
 
@@ -16,7 +17,7 @@ router.put("/:message_id", async (req: Request, res: Response) => {
 
 	const pinned_count = await Message.count({ channel: { id: channel_id }, pinned: true });
 	const { maxPins } = Config.get().limits.channel;
-	if (pinned_count >= maxPins) throw new HTTPError("Max pin count reached: " + maxPins);
+	if (pinned_count >= maxPins) throw DiscordApiErrors.MAXIMUM_PINS.withParams(maxPins);
 
 	await Promise.all([
 		Message.update({ id: message_id }, { pinned: true }),
diff --git a/api/src/routes/channels/#channel_id/webhooks.ts b/api/src/routes/channels/#channel_id/webhooks.ts
index 775053ba..d2a4b9a0 100644
--- a/api/src/routes/channels/#channel_id/webhooks.ts
+++ b/api/src/routes/channels/#channel_id/webhooks.ts
@@ -1,11 +1,12 @@
 import { Router, Response, Request } from "express";
 import { check, Length } from "../../../util/instanceOf";
-import { Channel, getPermission, trimSpecial } from "@fosscord/util";
+import { Channel, Config, getPermission, trimSpecial, Webhook } from "@fosscord/util";
 import { HTTPError } from "lambert-server";
 import { isTextChannel } from "./messages/index";
+import { DiscordApiErrors } from "../../../util/Constants";
 
 const router: Router = Router();
-// TODO:
+// TODO: webhooks
 
 // TODO: use Image Data Type for avatar instead of String
 router.post("/", check({ name: new Length(String, 1, 80), $avatar: String }), async (req: Request, res: Response) => {
@@ -15,6 +16,10 @@ router.post("/", check({ name: new Length(String, 1, 80), $avatar: String }), as
 	isTextChannel(channel.type);
 	if (!channel.guild_id) throw new HTTPError("Not a guild channel", 400);
 
+	const webhook_count = await Webhook.count({ channel_id });
+	const { maxWebhooks } = Config.get().limits.channel;
+	if (webhook_count > maxWebhooks) throw DiscordApiErrors.MAXIMUM_WEBHOOKS.withParams(maxWebhooks);
+
 	const permission = await getPermission(req.user_id, channel.guild_id);
 	permission.hasThrow("MANAGE_WEBHOOKS");
 
diff --git a/api/src/routes/guilds/#guild_id/roles.ts b/api/src/routes/guilds/#guild_id/roles.ts
index f6ac8caa..c3dd92dc 100644
--- a/api/src/routes/guilds/#guild_id/roles.ts
+++ b/api/src/routes/guilds/#guild_id/roles.ts
@@ -7,12 +7,14 @@ import {
 	GuildRoleCreateEvent,
 	GuildRoleUpdateEvent,
 	GuildRoleDeleteEvent,
-	emitEvent
+	emitEvent,
+	Config
 } from "@fosscord/util";
 import { HTTPError } from "lambert-server";
 
 import { check } from "../../../util/instanceOf";
 import { RoleModifySchema } from "../../../schema/Roles";
+import { DiscordApiErrors } from "../../../util/Constants";
 
 const router: Router = Router();
 
@@ -33,24 +35,34 @@ router.post("/", check(RoleModifySchema), async (req: Request, res: Response) =>
 	const perms = await getPermission(req.user_id, guild_id);
 	perms.hasThrow("MANAGE_ROLES");
 
-	const role = await new Role({
+	const role_count = await Role.count({ guild_id });
+	const { maxRoles } = Config.get().limits.guild;
+
+	if (role_count > maxRoles) throw DiscordApiErrors.MAXIMUM_ROLES.withParams(maxRoles);
+
+	const role = {
+		position: 0,
+		hoist: false,
+		color: 0, // default value
 		...body,
 		id: Snowflake.generate(),
 		guild_id: guild_id,
 		managed: false,
-		position: 0,
-		tags: null,
-		permissions: String(perms.bitfield & (body.permissions || 0n))
-	}).save();
-
-	await emitEvent({
-		event: "GUILD_ROLE_CREATE",
-		guild_id,
-		data: {
+		permissions: String(perms.bitfield & (body.permissions || 0n)),
+		tags: undefined
+	};
+
+	await Promise.all([
+		Role.insert(role),
+		emitEvent({
+			event: "GUILD_ROLE_CREATE",
 			guild_id,
-			role: role
-		}
-	} as GuildRoleCreateEvent);
+			data: {
+				guild_id,
+				role: role
+			}
+		} as GuildRoleCreateEvent)
+	]);
 
 	res.json(role);
 });
@@ -84,14 +96,13 @@ router.delete("/:role_id", async (req: Request, res: Response) => {
 // TODO: check role hierarchy
 
 router.patch("/:role_id", check(RoleModifySchema), async (req: Request, res: Response) => {
-	const guild_id = req.params.guild_id;
-	const { role_id } = req.params;
+	const { role_id, guild_id } = req.params;
 	const body = req.body as RoleModifySchema;
 
 	const perms = await getPermission(req.user_id, guild_id);
 	perms.hasThrow("MANAGE_ROLES");
 
-	const role = new Role({ ...body, id: role_id, guild_id, permissions: perms.bitfield & (body.permissions || 0n) });
+	const role = new Role({ ...body, id: role_id, guild_id, permissions: String(perms.bitfield & (body.permissions || 0n)) });
 
 	await Promise.all([
 		role.save(),
diff --git a/api/src/routes/guilds/index.ts b/api/src/routes/guilds/index.ts
index e4157384..a54b83ba 100644
--- a/api/src/routes/guilds/index.ts
+++ b/api/src/routes/guilds/index.ts
@@ -3,6 +3,7 @@ import { Role, Guild, Snowflake, Config, User, Member, Channel } from "@fosscord
 import { HTTPError } from "lambert-server";
 import { check } from "./../../util/instanceOf";
 import { GuildCreateSchema } from "../../schema/Guild";
+import { DiscordApiErrors } from "../../util/Constants";
 
 const router: Router = Router();
 
@@ -14,7 +15,7 @@ router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) =
 	const { maxGuilds } = Config.get().limits.user;
 	const guild_count = await Member.count({ id: req.user_id });
 	if (guild_count >= maxGuilds) {
-		throw new HTTPError(`Maximum number of guilds reached ${maxGuilds}`, 403);
+		throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds);
 	}
 
 	const guild_id = Snowflake.generate();
diff --git a/api/src/routes/guilds/templates/index.ts b/api/src/routes/guilds/templates/index.ts
index 7a8ac886..3a619278 100644
--- a/api/src/routes/guilds/templates/index.ts
+++ b/api/src/routes/guilds/templates/index.ts
@@ -4,6 +4,7 @@ import { Template, Guild, Role, Snowflake, Config, User, Member } from "@fosscor
 import { HTTPError } from "lambert-server";
 import { GuildTemplateCreateSchema } from "../../../schema/Guild";
 import { check } from "../../../util/instanceOf";
+import { DiscordApiErrors } from "../../../util/Constants";
 
 router.get("/:code", async (req: Request, res: Response) => {
 	const { code } = req.params;
@@ -21,7 +22,7 @@ router.post("/:code", check(GuildTemplateCreateSchema), async (req: Request, res
 
 	const guild_count = await Member.count({ id: req.user_id });
 	if (guild_count >= maxGuilds) {
-		throw new HTTPError(`Maximum number of guilds reached ${maxGuilds}`, 403);
+		throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds);
 	}
 
 	const template = await Template.findOneOrFail({ code: code });
diff --git a/api/src/routes/users/@me/relationships.ts b/api/src/routes/users/@me/relationships.ts
index 0b864d88..2bd9c819 100644
--- a/api/src/routes/users/@me/relationships.ts
+++ b/api/src/routes/users/@me/relationships.ts
@@ -5,10 +5,12 @@ import {
 	RelationshipType,
 	RelationshipRemoveEvent,
 	emitEvent,
-	Relationship
+	Relationship,
+	Config
 } from "@fosscord/util";
 import { Router, Response, Request } from "express";
 import { HTTPError } from "lambert-server";
+import { DiscordApiErrors } from "../../../util/Constants";
 
 import { check, Length } from "../../../util/instanceOf";
 
@@ -31,6 +33,7 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ
 	var relationship = user.relationships.find((x) => x.id === id);
 	const friendRequest = friend.relationships.find((x) => x.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");
@@ -67,6 +70,9 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ
 		return res.sendStatus(204);
 	}
 
+	const { maxFriends } = Config.get().limits.user;
+	if (user.relationships.length >= maxFriends) throw DiscordApiErrors.MAXIMUM_FRIENDS.withParams(maxFriends);
+
 	var incoming_relationship = new Relationship({ nickname: undefined, type: RelationshipType.incoming, id: req.user_id });
 	var outgoing_relationship = new Relationship({ nickname: undefined, type: RelationshipType.outgoing, id });
 
diff --git a/api/src/util/ApiError.ts b/api/src/util/ApiError.ts
index 2316cd71..c133e6e7 100644
--- a/api/src/util/ApiError.ts
+++ b/api/src/util/ApiError.ts
@@ -1,23 +1,27 @@
 export class ApiError extends Error {
-    constructor(readonly message: string, public readonly code: number, public readonly httpStatus: number = 400, public readonly defaultParams?: string[]) {
-        super(message);
-    }
+	constructor(
+		readonly message: string,
+		public readonly code: number,
+		public readonly httpStatus: number = 400,
+		public readonly defaultParams?: string[]
+	) {
+		super(message);
+	}
 
-    withDefaultParams(): ApiError {
-        if(this.defaultParams)
-            return new ApiError(applyParamsToString(this.message, this.defaultParams), this.code, this.httpStatus)
-        return this
-    }
+	withDefaultParams(): ApiError {
+		if (this.defaultParams) return new ApiError(applyParamsToString(this.message, this.defaultParams), this.code, this.httpStatus);
+		return this;
+	}
 
-    withParams(...params: string[]): ApiError {
-        return new ApiError(applyParamsToString(this.message, params), this.code, this.httpStatus)
-    }
+	withParams(...params: (string | number)[]): ApiError {
+		return new ApiError(applyParamsToString(this.message, params), this.code, this.httpStatus);
+	}
 }
 
-export function applyParamsToString(s: string, params: string[]): string {
-    let newString = s
-    params.forEach(a => {
-        newString = newString.replace("{}", a)
-    })
-    return newString
+export function applyParamsToString(s: string, params: (string | number)[]): string {
+	let newString = s;
+	params.forEach((a) => {
+		newString = newString.replace("{}", "" + a);
+	});
+	return newString;
 }
diff --git a/api/src/util/Constants.ts b/api/src/util/Constants.ts
index 15fdc519..f06b3d56 100644
--- a/api/src/util/Constants.ts
+++ b/api/src/util/Constants.ts
@@ -1,4 +1,4 @@
-import {ApiError} from "./ApiError";
+import { ApiError } from "./ApiError";
 
 export const WSCodes = {
 	1000: "WS_CLOSE_REQUESTED",
@@ -6,60 +6,7 @@ export const WSCodes = {
 	4010: "SHARDING_INVALID",
 	4011: "SHARDING_REQUIRED",
 	4013: "INVALID_INTENTS",
-	4014: "DISALLOWED_INTENTS",
-};
-
-const AllowedImageFormats = ["webp", "png", "jpg", "jpeg", "gif"];
-
-const AllowedImageSizes = Array.from({ length: 9 }, (e, i) => 2 ** (i + 4));
-
-function makeImageUrl(root: string, { format = "webp", size = 512 } = {}) {
-	if (format && !AllowedImageFormats.includes(format)) throw new Error("IMAGE_FORMAT: " + format);
-	if (size && !AllowedImageSizes.includes(size)) throw new RangeError("IMAGE_SIZE: " + size);
-	return `${root}.${format}${size ? `?size=${size}` : ""}`;
-}
-/**
- * Options for Image URLs.
- * @typedef {Object} ImageURLOptions
- * @property {string} [format] One of `webp`, `png`, `jpg`, `jpeg`, `gif`. If no format is provided,
- * defaults to `webp`.
- * @property {boolean} [dynamic] If true, the format will dynamically change to `gif` for
- * animated avatars; the default is false.
- * @property {number} [size] One of `16`, `32`, `64`, `128`, `256`, `512`, `1024`, `2048`, `4096`
- */
-
-export const Endpoints = {
-	CDN(root: string) {
-		return {
-			Emoji: (emojiID: string, format = "png") => `${root}/emojis/${emojiID}.${format}`,
-			Asset: (name: string) => `${root}/assets/${name}`,
-			DefaultAvatar: (discriminator: string) => `${root}/embed/avatars/${discriminator}.png`,
-			Avatar: (user_id: string, hash: string, format = "webp", size: number, dynamic = false) => {
-				if (dynamic) format = hash.startsWith("a_") ? "gif" : format;
-				return makeImageUrl(`${root}/avatars/${user_id}/${hash}`, { format, size });
-			},
-			Banner: (guildID: string, hash: string, format = "webp", size: number) =>
-				makeImageUrl(`${root}/banners/${guildID}/${hash}`, { format, size }),
-			Icon: (guildID: string, hash: string, format = "webp", size: number, dynamic = false) => {
-				if (dynamic) format = hash.startsWith("a_") ? "gif" : format;
-				return makeImageUrl(`${root}/icons/${guildID}/${hash}`, { format, size });
-			},
-			AppIcon: (clientID: string, hash: string, { format = "webp", size }: { format?: string; size?: number } = {}) =>
-				makeImageUrl(`${root}/app-icons/${clientID}/${hash}`, { size, format }),
-			AppAsset: (clientID: string, hash: string, { format = "webp", size }: { format?: string; size?: number } = {}) =>
-				makeImageUrl(`${root}/app-assets/${clientID}/${hash}`, { size, format }),
-			GDMIcon: (channelID: string, hash: string, format = "webp", size: number) =>
-				makeImageUrl(`${root}/channel-icons/${channelID}/${hash}`, { size, format }),
-			Splash: (guildID: string, hash: string, format = "webp", size: number) =>
-				makeImageUrl(`${root}/splashes/${guildID}/${hash}`, { size, format }),
-			DiscoverySplash: (guildID: string, hash: string, format = "webp", size: number) =>
-				makeImageUrl(`${root}/discovery-splashes/${guildID}/${hash}`, { size, format }),
-			TeamIcon: (teamID: string, hash: string, { format = "webp", size }: { format?: string; size?: number } = {}) =>
-				makeImageUrl(`${root}/team-icons/${teamID}/${hash}`, { size, format }),
-		};
-	},
-	invite: (root: string, code: string) => `${root}/${code}`,
-	botGateway: "/gateway/bot",
+	4014: "DISALLOWED_INTENTS"
 };
 
 /**
@@ -84,7 +31,7 @@ export const Status = {
 	DISCONNECTED: 5,
 	WAITING_FOR_GUILDS: 6,
 	IDENTIFYING: 7,
-	RESUMING: 8,
+	RESUMING: 8
 };
 
 /**
@@ -101,7 +48,7 @@ export const VoiceStatus = {
 	CONNECTING: 1,
 	AUTHENTICATING: 2,
 	RECONNECTING: 3,
-	DISCONNECTED: 4,
+	DISCONNECTED: 4
 };
 
 export const OPCodes = {
@@ -116,7 +63,7 @@ export const OPCodes = {
 	REQUEST_GUILD_MEMBERS: 8,
 	INVALID_SESSION: 9,
 	HELLO: 10,
-	HEARTBEAT_ACK: 11,
+	HEARTBEAT_ACK: 11
 };
 
 export const VoiceOPCodes = {
@@ -128,7 +75,7 @@ export const VoiceOPCodes = {
 	SPEAKING: 5,
 	HELLO: 8,
 	CLIENT_CONNECT: 12,
-	CLIENT_DISCONNECT: 13,
+	CLIENT_DISCONNECT: 13
 };
 
 export const Events = {
@@ -186,7 +133,7 @@ export const Events = {
 	SHARD_READY: "shardReady",
 	SHARD_RESUME: "shardResume",
 	INVALIDATED: "invalidated",
-	RAW: "raw",
+	RAW: "raw"
 };
 
 export const ShardEvents = {
@@ -195,7 +142,7 @@ export const ShardEvents = {
 	INVALID_SESSION: "invalidSession",
 	READY: "ready",
 	RESUMED: "resumed",
-	ALL_READY: "allReady",
+	ALL_READY: "allReady"
 };
 
 /**
@@ -287,7 +234,7 @@ export const WSEvents = keyMirror([
 	"TYPING_START",
 	"VOICE_STATE_UPDATE",
 	"VOICE_SERVER_UPDATE",
-	"WEBHOOKS_UPDATE",
+	"WEBHOOKS_UPDATE"
 ]);
 
 /**
@@ -330,7 +277,7 @@ export const MessageTypes = [
 	null,
 	null,
 	null,
-	"REPLY",
+	"REPLY"
 ];
 
 /**
@@ -361,12 +308,12 @@ export const ChannelTypes = {
 	GROUP: 3,
 	CATEGORY: 4,
 	NEWS: 5,
-	STORE: 6,
+	STORE: 6
 };
 
 export const ClientApplicationAssetTypes = {
 	SMALL: 1,
-	BIG: 2,
+	BIG: 2
 };
 
 export const Colors = {
@@ -398,7 +345,7 @@ export const Colors = {
 	BLURPLE: 0x7289da,
 	GREYPLE: 0x99aab5,
 	DARK_BUT_NOT_BLACK: 0x2c2f33,
-	NOT_QUITE_BLACK: 0x23272a,
+	NOT_QUITE_BLACK: 0x23272a
 };
 
 /**
@@ -613,7 +560,10 @@ export const DiscordApiErrors = {
 	ONLY_OWNER: new ApiError("Only the owner of this account can perform this action", 20018),
 	ANNOUNCEMENT_RATE_LIMITS: new ApiError("This message cannot be edited due to announcement rate limits", 20022),
 	CHANNEL_WRITE_RATELIMIT: new ApiError("The channel you are writing has hit the write rate limit", 20028),
-	WORDS_NOT_ALLOWED: new ApiError("Your Stage topic, server name, server description, or channel names contain words that are not allowed", 20031),
+	WORDS_NOT_ALLOWED: new ApiError(
+		"Your Stage topic, server name, server description, or channel names contain words that are not allowed",
+		20031
+	),
 	GUILD_PREMIUM_LEVEL_TOO_LOW: new ApiError("Guild premium subscription level too low", 20035),
 	MAXIMUM_GUILDS: new ApiError("Maximum number of guilds reached ({})", 30001, undefined, ["100"]),
 	MAXIMUM_FRIENDS: new ApiError("Maximum number of friends reached ({})", 30002, undefined, ["1000"]),
@@ -659,7 +609,12 @@ export const DiscordApiErrors = {
 	MISSING_PERMISSIONS: new ApiError("You lack permissions to perform that action", 50013),
 	INVALID_AUTHENTICATION_TOKEN: new ApiError("Invalid authentication token provided", 50014),
 	NOTE_TOO_LONG: new ApiError("Note was too long", 50015),
-	INVALID_BULK_DELETE_QUANTITY: new ApiError("Provided too few or too many messages to delete. Must provide at least {} and fewer than {} messages to delete", 50016, undefined, ["2","100"]),
+	INVALID_BULK_DELETE_QUANTITY: new ApiError(
+		"Provided too few or too many messages to delete. Must provide at least {} and fewer than {} messages to delete",
+		50016,
+		undefined,
+		["2", "100"]
+	),
 	CANNOT_PIN_MESSAGE_IN_OTHER_CHANNEL: new ApiError("A message can only be pinned to the channel it was sent in", 50019),
 	INVALID_OR_TAKEN_INVITE_CODE: new ApiError("Invite code was either invalid or taken", 50020),
 	CANNOT_EXECUTE_ON_SYSTEM_MESSAGE: new ApiError("Cannot execute action on a system message", 50021),
@@ -670,7 +625,10 @@ export const DiscordApiErrors = {
 	INVALID_ROLE: new ApiError("Invalid role", 50028),
 	INVALID_RECIPIENT: new ApiError("Invalid Recipient(s)", 50033),
 	BULK_DELETE_MESSAGE_TOO_OLD: new ApiError("A message provided was too old to bulk delete", 50034),
-	INVALID_FORM_BODY: new ApiError("Invalid form body (returned for both application/json and multipart/form-data bodies), or invalid Content-Type provided", 50035),
+	INVALID_FORM_BODY: new ApiError(
+		"Invalid form body (returned for both application/json and multipart/form-data bodies), or invalid Content-Type provided",
+		50035
+	),
 	INVITE_ACCEPTED_TO_GUILD_NOT_CONTAINING_BOT: new ApiError("An invite was accepted to a guild the application's bot is not in", 50036),
 	INVALID_API_VERSION: new ApiError("Invalid API version provided", 50041),
 	FILE_EXCEEDS_MAXIMUM_SIZE: new ApiError("File uploaded exceeds the maximum size", 50045),
@@ -679,7 +637,10 @@ export const DiscordApiErrors = {
 	PAYMENT_SOURCE_REQUIRED: new ApiError("Payment source required to redeem gift", 50070),
 	CANNOT_DELETE_COMMUNITY_REQUIRED_CHANNEL: new ApiError("Cannot delete a channel required for Community guilds", 50074),
 	INVALID_STICKER_SENT: new ApiError("Invalid sticker sent", 50081),
-	CANNOT_EDIT_ARCHIVED_THREAD: new ApiError("Tried to perform an operation on an archived thread, such as editing a message or adding a user to the thread", 50083),
+	CANNOT_EDIT_ARCHIVED_THREAD: new ApiError(
+		"Tried to perform an operation on an archived thread, such as editing a message or adding a user to the thread",
+		50083
+	),
 	INVALID_THREAD_NOTIFICATION_SETTINGS: new ApiError("Invalid thread notification settings", 50084),
 	BEFORE_EARLIER_THAN_THREAD_CREATION_DATE: new ApiError("before value is earlier than the thread creation date", 50085),
 	SERVER_NOT_AVAILABLE_IN_YOUR_LOCATION: new ApiError("This server is not available in your location", 50095),
@@ -701,17 +662,14 @@ export const DiscordApiErrors = {
 	STICKER_FRAME_RATE_TOO_SMALL_OR_TOO_LARGE: new ApiError("Sticker frame rate is either too small or too large", 170006),
 	STICKER_ANIMATION_DURATION_MAXIMUM: new ApiError("Sticker animation duration exceeds maximum of {} seconds", 170007, undefined, ["5"]),
 
-
 	//Other errors
-	UNKNOWN_VOICE_STATE: new ApiError("Unknown Voice State", 10065, 404),
-}
+	UNKNOWN_VOICE_STATE: new ApiError("Unknown Voice State", 10065, 404)
+};
 
 /**
  * An error encountered while performing an API request (Fosscord only). Here are the potential errors:
  */
-export const FosscordApiErrors = {
-
-}
+export const FosscordApiErrors = {};
 
 /**
  * The value set for a guild's default message notifications, e.g. `ALL`. Here are the available types:
@@ -731,7 +689,7 @@ export const MembershipStates = [
 	// They start at 1
 	null,
 	"INVITED",
-	"ACCEPTED",
+	"ACCEPTED"
 ];
 
 /**
@@ -744,7 +702,7 @@ export const WebhookTypes = [
 	// They start at 1
 	null,
 	"Incoming",
-	"Channel Follower",
+	"Channel Follower"
 ];
 
 function keyMirror(arr: string[]) {
diff --git a/gateway/src/schema/Identify.ts b/gateway/src/schema/Identify.ts
index f6d95204..cbd6630a 100644
--- a/gateway/src/schema/Identify.ts
+++ b/gateway/src/schema/Identify.ts
@@ -3,32 +3,33 @@ import { ActivitySchema } from "./Activity";
 export const IdentifySchema = {
 	token: String,
 	$intents: BigInt, // discord uses a Integer for bitfields we use bigints tho. | instanceOf will automatically convert the Number to a BigInt
-	$properties: {
-		// bruh discord really uses $ in the property key for bots, so we need to double prefix it, because instanceOf treats $ (prefix) as a optional key
-		$os: String,
-		$os_arch: String,
-		$browser: String,
-		$device: String,
-		$$os: String,
-		$$browser: String,
-		$$device: String,
-		$browser_user_agent: String,
-		$browser_version: String,
-		$os_version: String,
-		$referrer: String,
-		$$referrer: String,
-		$referring_domain: String,
-		$$referring_domain: String,
-		$referrer_current: String,
-		$referring_domain_current: String,
-		$release_channel: String,
-		$client_build_number: Number,
-		$client_event_source: String,
-		$client_version: String,
-		$system_locale: String,
-		$window_manager: String,
-		$distro: String,
-	},
+	$properties: Object,
+	// {
+	// 	// bruh discord really uses $ in the property key for bots, so we need to double prefix it, because instanceOf treats $ (prefix) as a optional key
+	// 	$os: String,
+	// 	$os_arch: String,
+	// 	$browser: String,
+	// 	$device: String,
+	// 	$$os: String,
+	// 	$$browser: String,
+	// 	$$device: String,
+	// 	$browser_user_agent: String,
+	// 	$browser_version: String,
+	// 	$os_version: String,
+	// 	$referrer: String,
+	// 	$$referrer: String,
+	// 	$referring_domain: String,
+	// 	$$referring_domain: String,
+	// 	$referrer_current: String,
+	// 	$referring_domain_current: String,
+	// 	$release_channel: String,
+	// 	$client_build_number: Number,
+	// 	$client_event_source: String,
+	// 	$client_version: String,
+	// 	$system_locale: String,
+	// 	$window_manager: String,
+	// 	$distro: String,
+	// },
 	$presence: ActivitySchema,
 	$compress: Boolean,
 	$large_threshold: Number,