summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--api/LICENSE14
-rw-r--r--api/client_test/index.html12
-rw-r--r--api/crowdin.yml3
-rw-r--r--api/locales/he/auth.json20
-rw-r--r--api/locales/he/common.json22
-rw-r--r--api/package.json2
-rw-r--r--api/src/middlewares/RateLimit.ts22
-rw-r--r--api/src/routes/auth/register.ts2
-rw-r--r--api/src/routes/channels/#channel_id/messages/#message_id/index.ts29
-rw-r--r--api/src/routes/channels/#channel_id/messages/bulk-delete.ts20
-rw-r--r--api/src/routes/channels/#channel_id/messages/index.ts49
-rw-r--r--api/src/routes/channels/#channel_id/purge.ts84
-rw-r--r--api/src/routes/guilds/#guild_id/prune.ts8
-rw-r--r--bundle/package-lock.json6
-rw-r--r--cdn/CONTRIBUTE.md18
-rw-r--r--cdn/package.json4
-rw-r--r--dashboard/LICENSE14
-rw-r--r--dashboard/package.json4
-rw-r--r--gateway/LICENSE14
-rw-r--r--gateway/client.js51
-rw-r--r--gateway/package.json2
-rw-r--r--package.json17
-rw-r--r--rtc/LICENSE14
-rw-r--r--util/LICENSE14
-rw-r--r--util/package.json2
-rw-r--r--util/src/entities/AuditLog.ts60
-rw-r--r--util/src/entities/Channel.ts2
-rw-r--r--util/src/entities/Config.ts2
-rw-r--r--util/src/entities/Group.ts33
-rw-r--r--util/src/entities/Message.ts4
-rw-r--r--util/src/entities/User.ts5
-rw-r--r--util/src/util/Constants.ts17
-rw-r--r--util/src/util/Intents.ts2
-rw-r--r--util/src/util/MessageFlags.ts12
-rw-r--r--util/src/util/Token.ts7
-rw-r--r--webrtc/LICENSE14
-rw-r--r--webrtc/package.json4
37 files changed, 359 insertions, 250 deletions
diff --git a/api/LICENSE b/api/LICENSE
deleted file mode 100644
index f19bf520..00000000
--- a/api/LICENSE
+++ /dev/null
@@ -1,14 +0,0 @@
-Copyright (C) 2021 Fosscord and contributors
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <https://www.gnu.org/licenses/>.
\ No newline at end of file
diff --git a/api/client_test/index.html b/api/client_test/index.html
index 39ff346d..b438b492 100644
--- a/api/client_test/index.html
+++ b/api/client_test/index.html
@@ -24,20 +24,20 @@
 				ASSET_ENDPOINT: "",
 				MEDIA_PROXY_ENDPOINT: "https://media.discordapp.net",
 				WIDGET_ENDPOINT: `//${location.host}/widget`,
-				INVITE_HOST: `${location.host}/invite`,
-				GUILD_TEMPLATE_HOST: "discord.new",
-				GIFT_CODE_HOST: "discord.gift",
+				INVITE_HOST: `${location.hostname}/invite`,
+				GUILD_TEMPLATE_HOST: "${location.host}",
+				GIFT_CODE_HOST: "${location.hostname}",
 				RELEASE_CHANNEL: "stable",
 				MARKETING_ENDPOINT: "//discord.com",
 				BRAINTREE_KEY: "production_5st77rrc_49pp2rp4phym7387",
 				STRIPE_KEY: "pk_live_CUQtlpQUF0vufWpnpUmQvcdi",
 				NETWORKING_ENDPOINT: "//router.discordapp.net",
-				RTC_LATENCY_ENDPOINT: "//latency.discord.media/rtc",
+				RTC_LATENCY_ENDPOINT: "//${location.hostname}/rtc",
 				PROJECT_ENV: "production",
 				REMOTE_AUTH_ENDPOINT: "//localhost:3020",
 				SENTRY_TAGS: { buildId: "75e36d9", buildType: "normal" },
-				MIGRATION_SOURCE_ORIGIN: "https://discordapp.com",
-				MIGRATION_DESTINATION_ORIGIN: "https://discord.com",
+				MIGRATION_SOURCE_ORIGIN: "https://${location.hostname}",
+				MIGRATION_DESTINATION_ORIGIN: "https://${location.hostname}",
 				HTML_TIMESTAMP: Date.now(),
 				ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0"
 			};
diff --git a/api/crowdin.yml b/api/crowdin.yml
deleted file mode 100644
index 7228117f..00000000
--- a/api/crowdin.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-files:
-    - source: /locales/en/*.json
-      translation: /locales/%two_letters_code%/%original_file_name%
diff --git a/api/locales/he/auth.json b/api/locales/he/auth.json
index e19547a0..b7296868 100644
--- a/api/locales/he/auth.json
+++ b/api/locales/he/auth.json
@@ -1,16 +1,16 @@
 {
 	"login": {
-		"INVALID_LOGIN": "E-Mail or Phone not found",
-		"INVALID_PASSWORD": "Invalid Password",
-		"ACCOUNT_DISABLED": "This account is disabled"
+		"INVALID_LOGIN": "מייל או מספר טלפון לא נמצאים במאגר",
+		"INVALID_PASSWORD": "סיסמא שגויה",
+		"ACCOUNT_DISABLED": "משתמש זה חסום / מבוטל"
 	},
 	"register": {
-		"REGISTRATION_DISABLED": "New user registration is disabled",
-		"INVITE_ONLY": "You must be invited to register",
-		"EMAIL_INVALID": "Invalid Email",
-		"EMAIL_ALREADY_REGISTERED": "Email is already registered",
-		"DATE_OF_BIRTH_UNDERAGE": "You need to be {{years}} years or older",
-		"CONSENT_REQUIRED": "You must agree to the Terms of Service and Privacy Policy.",
-		"USERNAME_TOO_MANY_USERS": "Too many users have this username, please try another"
+		"REGISTRATION_DISABLED": "לא ניתן לאפשר רישום משתמשים חדשים",
+		"INVITE_ONLY": "עליך להיות מוזמן בכדי להרשם",
+		"EMAIL_INVALID": "מייל שגוי",
+		"EMAIL_ALREADY_REGISTERED": "מייל זה כבר רשום",
+		"DATE_OF_BIRTH_UNDERAGE": "{{years}} עלייך להיות מעל גיל",
+		"CONSENT_REQUIRED": ".עליך להסכים לתנאי השירות ולמדיניות הפרטיות",
+		"USERNAME_TOO_MANY_USERS": "ליותר מדי משתמשים יש שם משתמש זהה, אנא נסה אחר"
 	}
 }
diff --git a/api/locales/he/common.json b/api/locales/he/common.json
index 9e72e941..4101eac4 100644
--- a/api/locales/he/common.json
+++ b/api/locales/he/common.json
@@ -1,18 +1,18 @@
 {
 	"field": {
-		"BASE_TYPE_REQUIRED": "This field is required",
-		"BASE_TYPE_STRING": "This field must be a string",
-		"BASE_TYPE_NUMBER": "This field must be a number",
-		"BASE_TYPE_BIGINT": "This field must be a bigint",
-		"BASE_TYPE_BOOLEAN": "This field must be a boolean",
-		"BASE_TYPE_CHOICES": "This field must be one of ({{types}})",
-		"BASE_TYPE_CLASS": "This field must be an instance of {{type}}",
+		"BASE_TYPE_REQUIRED": "שדה זה חובה",
+		"BASE_TYPE_STRING": "שדה זה חייב להיות כטקסט",
+		"BASE_TYPE_NUMBER": "שדה זה חייב להיות מספר",
+		"BASE_TYPE_BIGINT": "השדה הזה חייב להיות ביגינט",
+		"BASE_TYPE_BOOLEAN": "השדה הזה חייב להיות בוליאני",
+		"BASE_TYPE_CHOICES": "({{types}}) שדה זה חייב להיות אחד מ",
+		"BASE_TYPE_CLASS": "{{type}} מסוג instance שדה זה חייב להיות",
 		"BASE_TYPE_OBJECT": "שדה זה חייב להיות אובייקט",
 		"BASE_TYPE_ARRAY": "שדה זה חייב להיות מערך",
-		"UNKOWN_FIELD": "מפתח לא ידוע: {{key}}",
-		"BASE_TYPE_CONSTANT": "שדה זה להיות {{value}}",
+		"UNKOWN_FIELD": "{{key}} :מפתח לא ידוע",
+		"BASE_TYPE_CONSTANT": "{{value}} שדה זה חייב להיות",
 		"EMAIL_TYPE_INVALID_EMAIL": "כתובת דואר אלקטרוני לא חוקית",
-		"DATE_TYPE_PARSE": "לא ניתן לנתח {{date}}. צריך להיות ISO8601",
-		"BASE_TYPE_BAD_LENGTH": "האורך חייב להיות בין {{length}}"
+		"DATE_TYPE_PARSE": "ISO8601 אמור להיות {{date}} לא ניתן לאתר",
+		"BASE_TYPE_BAD_LENGTH": "{{length}} האורך חייב להיות בין"
 	}
 }
diff --git a/api/package.json b/api/package.json
index c586c9fe..65472522 100644
--- a/api/package.json
+++ b/api/package.json
@@ -30,7 +30,7 @@
 		"discord-open-source"
 	],
 	"author": "Fosscord",
-	"license": "GPLV3",
+	"license": "AGPLV3",
 	"bugs": {
 		"url": "https://github.com/fosscord/fosscord-server/issues"
 	},
diff --git a/api/src/middlewares/RateLimit.ts b/api/src/middlewares/RateLimit.ts
index 1a38cfcf..ca6de98f 100644
--- a/api/src/middlewares/RateLimit.ts
+++ b/api/src/middlewares/RateLimit.ts
@@ -1,4 +1,4 @@
-import { Config, listenEvent } from "@fosscord/util";
+import { Config, getRights, listenEvent, Rights } from "@fosscord/util";
 import { NextFunction, Request, Response, Router } from "express";
 import { getIpAdress } from "@fosscord/api";
 import { API_PREFIX_TRAILING_SLASH } from "./Authentication";
@@ -9,6 +9,7 @@ import { API_PREFIX_TRAILING_SLASH } from "./Authentication";
 
 /*
 ? bucket limit? Max actions/sec per bucket?
+(ANSWER: a small fosscord instance might not need a complex rate limiting system)
 
 TODO: delay database requests to include multiple queries
 TODO: different for methods (GET/POST)
@@ -44,21 +45,25 @@ export default function rateLimit(opts: {
 	onlyIp?: boolean;
 }): any {
 	return async (req: Request, res: Response, next: NextFunction): Promise<any> => {
+		// exempt user? if so, immediately short circuit
+		const rights = await getRights(req.user_id);
+		if (rights.has("BYPASS_RATE_LIMITS")) return;
+		
 		const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, "");
 		var executor_id = getIpAdress(req);
-		if (!opts.onlyIp && req.user_id) executor_id = req.user_id;
+		if (!opts.onlyIp && req.user_id) executor_id = req.user_id;		
 
 		var 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;
 
-		const offender = Cache.get(executor_id + bucket_id);
+		let offender = Cache.get(executor_id + bucket_id);
 
 		if (offender) {
-			const reset = offender.expires_at.getTime();
-			const resetAfterMs = reset - Date.now();
-			const resetAfterSec = resetAfterMs / 1000;
+			let reset = offender.expires_at.getTime();
+			let resetAfterMs = reset - Date.now();
+			let resetAfterSec = Math.ceil(resetAfterMs / 1000);
 
 			if (resetAfterMs <= 0) {
 				offender.hits = 0;
@@ -70,6 +75,11 @@ export default function rateLimit(opts: {
 
 			if (offender.blocked) {
 				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);
+				resetAfterMs = reset - Date.now();
+				resetAfterSec = Math.ceil(resetAfterMs / 1000);
 
 				console.log("blocked bucket: " + bucket_id, { resetAfterMs });
 				return (
diff --git a/api/src/routes/auth/register.ts b/api/src/routes/auth/register.ts
index cd1bcb72..94dd6502 100644
--- a/api/src/routes/auth/register.ts
+++ b/api/src/routes/auth/register.ts
@@ -128,7 +128,7 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
 		throw FieldErrors({
 			date_of_birth: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") }
 		});
-	} else if (register.dateOfBirth.minimum) {
+	} 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);
diff --git a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts
index 6d2bf185..63fee9b9 100644
--- a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts
+++ b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts
@@ -2,13 +2,16 @@ import {
 	Attachment,
 	Channel,
 	Embed,
+	DiscordApiErrors,
 	emitEvent,
+	FosscordApiErrors,
 	getPermission,
 	getRights,
  	Message,
 	MessageCreateEvent,
 	MessageDeleteEvent,
 	MessageUpdateEvent,
+	Snowflake,
 	uploadFile 
 } from "@fosscord/util";
 import { Router, Response, Request } from "express";
@@ -16,6 +19,7 @@ import multer from "multer";
 import { route } from "@fosscord/api";
 import { handleMessage, postHandleMessage } from "@fosscord/api";
 import { MessageCreateSchema } from "../index";
+import { HTTPError } from "lambert-server";
 
 const router = Router();
 // TODO: message content/embed string length limit
@@ -90,6 +94,25 @@ router.put(
 		const { channel_id, message_id } = req.params;
 		var body = req.body as MessageCreateSchema;
 		const attachments: Attachment[] = [];
+		
+		const rights = await getRights(req.user_id);
+		rights.hasThrow("SEND_MESSAGES");
+
+		// regex to check if message contains anything other than numerals ( also no decimals )
+		if (!message_id.match(/^\+?\d+$/)) {
+			throw new HTTPError("Message IDs must be positive integers", 400);
+		}
+
+		const snowflake = Snowflake.deconstruct(message_id)
+		if (Date.now() < snowflake.timestamp) {
+			// message is in the future
+			throw FosscordApiErrors.CANNOT_BACKFILL_TO_THE_FUTURE;
+		}
+
+		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 {
@@ -100,8 +123,6 @@ router.put(
 			}
 		}
 		const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients", "recipients.user"] });
-		
-		// TODO: check the ID is not from the future, to prevent future-faking of channel histories
 
 		const embeds = body.embeds || [];
 		if (body.embed) embeds.push(body.embed);
@@ -115,11 +136,9 @@ router.put(
 			channel_id,
 			attachments,
 			edited_timestamp: undefined,
-			timestamp: undefined, // FIXME: calculate timestamp from snowflake
+			timestamp: new Date(snowflake.timestamp),
 		});
 
-		channel.last_message_id = message.id;
-
 		//Fix for the client bug
 		delete message.member
 		
diff --git a/api/src/routes/channels/#channel_id/messages/bulk-delete.ts b/api/src/routes/channels/#channel_id/messages/bulk-delete.ts
index 7a711cb0..6eacf249 100644
--- a/api/src/routes/channels/#channel_id/messages/bulk-delete.ts
+++ b/api/src/routes/channels/#channel_id/messages/bulk-delete.ts
@@ -1,5 +1,5 @@
 import { Router, Response, Request } from "express";
-import { Channel, Config, emitEvent, getPermission, 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";
 import { In } from "typeorm";
@@ -12,22 +12,28 @@ export interface BulkDeleteSchema {
 	messages: string[];
 }
 
-// TODO: should users be able to bulk delete messages or only bots?
-// TODO: should this request fail, if you provide messages older than 14 days/invalid ids?
+// 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({ 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);
-	permission.hasThrow("MANAGE_MESSAGES");
-
+		
 	const { maxBulkDelete } = Config.get().limits.message;
 
 	const { messages } = req.body as { messages: string[] };
-	if (messages.length < 2) throw new HTTPError("You must at least specify 2 messages to bulk delete");
-	if (messages.length > maxBulkDelete) throw new HTTPError(`You cannot delete more than ${maxBulkDelete} messages`);
+	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.map((x) => ({ id: x })));
 
diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts
index 34cc5ff8..2d6a2977 100644
--- a/api/src/routes/channels/#channel_id/messages/index.ts
+++ b/api/src/routes/channels/#channel_id/messages/index.ts
@@ -11,6 +11,7 @@ import {
 	getRights,
 	Message,
 	MessageCreateEvent,
+	Snowflake,
 	uploadFile,
 	Member
 } from "@fosscord/util";
@@ -30,6 +31,8 @@ export function isTextChannel(type: ChannelType): boolean {
 		case ChannelType.GUILD_VOICE:
 		case ChannelType.GUILD_STAGE_VOICE:
 		case ChannelType.GUILD_CATEGORY:
+		case ChannelType.GUILD_FORUM:
+		case ChannelType.DIRECTORY:
 			throw new HTTPError("not a text channel", 400);
 		case ChannelType.DM:
 		case ChannelType.GROUP_DM:
@@ -68,7 +71,11 @@ export interface MessageCreateSchema {
 	};
 	payload_json?: string;
 	file?: any;
-	attachments?: any[]; //TODO we should create an interface for attachments
+	/**
+	TODO: we should create an interface for attachments
+	TODO: OpenWAAO<-->attachment-style metadata conversion
+	**/
+	attachments?: any[];
 	sticker_ids?: string[];
 }
 
@@ -84,7 +91,7 @@ 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");
+	if (limit < 1 || limit > 100) throw new HTTPError("limit must be between 1 and 100", 422);
 
 	var halfLimit = Math.floor(limit / 2);
 
@@ -98,9 +105,16 @@ router.get("/", async (req: Request, res: Response) => {
 		where: { channel_id },
 		relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"]
 	};
+	
 
-	if (after) query.where.id = MoreThan(after);
-	else if (before) query.where.id = LessThan(before);
+	if (after) {
+		if (after > new Snowflake()) return res.status(422);
+		query.where.id = MoreThan(after);
+	}
+	else if (before) { 
+		if (before < req.params.channel_id) return res.status(422);
+		query.where.id = LessThan(before);
+	}
 	else if (around) {
 		query.where.id = [
 			MoreThan((BigInt(around) - BigInt(halfLimit)).toString()),
@@ -126,9 +140,12 @@ router.get("/", async (req: Request, res: Response) => {
 				const uri = y.proxy_url.startsWith("http") ? y.proxy_url : `https://example.org${y.proxy_url}`;
 				y.proxy_url = `${endpoint == null ? "" : endpoint}${new URL(uri).pathname}`;
 			});
-
-			//Some clients ( discord.js ) only check if a property exists within the response,
-			//which causes erorrs when, say, the `application` property is `null`.
+			
+			/**
+			Some clients ( discord.js ) only check if a property exists within the response,
+			which causes erorrs when, say, the `application` property is `null`.
+			**/
+			
 			for (var curr in x) {
 				if (x[curr] === null)
 					delete x[curr];
@@ -148,15 +165,14 @@ const messageUpload = multer({
 	},
 	storage: multer.memoryStorage()
 }); // max upload 50 mb
+/**
+ TODO: dynamically change limit of MessageCreateSchema with config
 
-// TODO: dynamically change limit of MessageCreateSchema with config
-// TODO: check: sum of all characters in an embed structure must not exceed instance limits
-
-// https://discord.com/developers/docs/resources/channel#create-message
-// TODO: text channel slowdown
-// TODO: trim and replace message content and every embed field
-// TODO: check allowed_mentions
-
+ https://discord.com/developers/docs/resources/channel#create-message
+ TODO: text channel slowdown (per-user and across-users)
+ Q: trim and replace message content and every embed field A: NO, given this cannot be implemented in E2EE channels
+ TODO: only dispatch notifications for mentions denoted in allowed_mentions
+**/
 // Send message
 router.post(
 	"/",
@@ -223,8 +239,6 @@ router.post(
 				})
 			);
 		}
-
-
 	
 		//Fix for the client bug
 		delete message.member
@@ -241,3 +255,4 @@ router.post(
 		return res.json(message);
 	}
 );
+
diff --git a/api/src/routes/channels/#channel_id/purge.ts b/api/src/routes/channels/#channel_id/purge.ts
new file mode 100644
index 00000000..28b52b50
--- /dev/null
+++ b/api/src/routes/channels/#channel_id/purge.ts
@@ -0,0 +1,84 @@
+import { HTTPError } from "lambert-server";
+import { route } from "@fosscord/api";
+import { isTextChannel } from "./messages";
+import { FindManyOptions, Between, Not } from "typeorm";
+import {
+	Attachment,
+	Channel,
+	Config,
+	Embed,
+	DiscordApiErrors,
+	emitEvent,
+	FosscordApiErrors,
+	getPermission,
+	getRights,
+ 	Message,
+	MessageDeleteBulkEvent,
+	Snowflake,
+	uploadFile 
+} from "@fosscord/util";
+import { Router, Response, Request } from "express";
+import multer from "multer";
+import { handleMessage, postHandleMessage } from "@fosscord/api";
+
+const router: Router = Router();
+
+export default router;
+
+export interface PurgeSchema {
+	before: string;
+	after: string
+}
+
+/**
+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({ id: channel_id });
+	
+	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 { 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"]
+	};
+	
+
+	const messages = await Message.find(query);
+	const endpoint = Config.get().cdn.endpointPublic;
+			
+	if (messages.length == 0) { 
+		res.sendStatus(304);
+		return;
+	}
+
+	await Message.delete(messages.map((x) => ({ id: x })));
+	
+	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);
+});
diff --git a/api/src/routes/guilds/#guild_id/prune.ts b/api/src/routes/guilds/#guild_id/prune.ts
index 0dd4d610..0e587d22 100644
--- a/api/src/routes/guilds/#guild_id/prune.ts
+++ b/api/src/routes/guilds/#guild_id/prune.ts
@@ -11,6 +11,10 @@ export const inactiveMembers = async (guild_id: string, user_id: string, days: n
 	//Snowflake should have `generateFromTime` method? Or similar?
 	var minId = BigInt(date.valueOf() - Snowflake.EPOCH) << BigInt(22);
 
+	/**
+	idea: ability to customise the cutoff variable
+	possible candidates: public read receipt, last presence, last VC leave
+	**/
 	var members = await Member.find({
 		where: [
 			{
@@ -47,7 +51,7 @@ export const inactiveMembers = async (guild_id: string, user_id: string, days: n
 	return members;
 };
 
-router.get("/", route({ permission: "KICK_MEMBERS" }), async (req: Request, res: Response) => {
+router.get("/", route({}), async (req: Request, res: Response) => {
 	const days = parseInt(req.query.days as string);
 
 	var roles = req.query.include_roles;
@@ -65,7 +69,7 @@ export interface PruneSchema {
 	days: number;
 }
 
-router.post("/", route({ permission: "KICK_MEMBERS" }), async (req: Request, res: Response) => {
+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;
diff --git a/bundle/package-lock.json b/bundle/package-lock.json
index 8e620582..2434f250 100644
--- a/bundle/package-lock.json
+++ b/bundle/package-lock.json
@@ -102,7 +102,7 @@
 			"name": "@fosscord/api",
 			"version": "1.0.0",
 			"hasInstallScript": true,
-			"license": "GPLV3",
+			"license": "AGPLV3",
 			"dependencies": {
 				"@babel/preset-env": "^7.15.8",
 				"@babel/preset-typescript": "^7.15.0",
@@ -165,7 +165,7 @@
 		"../cdn": {
 			"name": "@fosscord/cdn",
 			"version": "1.0.0",
-			"license": "GPLV3",
+			"license": "AGPLV3",
 			"dependencies": {
 				"@aws-sdk/client-s3": "^3.36.1",
 				"@aws-sdk/node-http-handler": "^3.36.0",
@@ -209,7 +209,7 @@
 			"name": "@fosscord/gateway",
 			"version": "1.0.0",
 			"hasInstallScript": true,
-			"license": "GPLV3",
+			"license": "AGPLV3",
 			"dependencies": {
 				"@fosscord/util": "file:../util",
 				"amqplib": "^0.8.0",
diff --git a/cdn/CONTRIBUTE.md b/cdn/CONTRIBUTE.md
deleted file mode 100644
index 7cc673d9..00000000
--- a/cdn/CONTRIBUTE.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# CONTRIBUTE
-
-### Setup:
-
-```
-npm i
-npm start
-```
-
-### Run tests:
-
-```
-npm test
-```
-
-#### common errors:
-
--   db not connecting --> start mongod in a seperate terminal window
diff --git a/cdn/package.json b/cdn/package.json
index 7a1f43c9..f1d12ba5 100644
--- a/cdn/package.json
+++ b/cdn/package.json
@@ -14,8 +14,8 @@
 		"url": "git+https://github.com/fosscord/fosscord-server.git"
 	},
 	"keywords": [],
-	"author": "",
-	"license": "GPLV3",
+	"author": "Fosscord",
+	"license": "AGPLV3",
 	"bugs": {
 		"url": "https://github.com/fosscord/fosscord-server/issues"
 	},
diff --git a/dashboard/LICENSE b/dashboard/LICENSE
deleted file mode 100644
index f19bf520..00000000
--- a/dashboard/LICENSE
+++ /dev/null
@@ -1,14 +0,0 @@
-Copyright (C) 2021 Fosscord and contributors
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <https://www.gnu.org/licenses/>.
\ No newline at end of file
diff --git a/dashboard/package.json b/dashboard/package.json
index 1009d658..e71de793 100644
--- a/dashboard/package.json
+++ b/dashboard/package.json
@@ -14,8 +14,8 @@
 		"url": "git+https://github.com/fosscord/fosscord-server.git"
 	},
 	"keywords": [],
-	"author": "",
-	"license": "GPLV3",
+	"author": "Fosscord",
+	"license": "AGPLV3",
 	"bugs": {
 		"url": "https://github.com/fosscord/fosscord-server/issues"
 	},
diff --git a/gateway/LICENSE b/gateway/LICENSE
deleted file mode 100644
index f19bf520..00000000
--- a/gateway/LICENSE
+++ /dev/null
@@ -1,14 +0,0 @@
-Copyright (C) 2021 Fosscord and contributors
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <https://www.gnu.org/licenses/>.
\ No newline at end of file
diff --git a/gateway/client.js b/gateway/client.js
deleted file mode 100644
index c841c6a0..00000000
--- a/gateway/client.js
+++ /dev/null
@@ -1,51 +0,0 @@
-require("missing-native-js-functions");
-const WebSocket = require("ws");
-const Constants = require("./dist/util/Constants");
-
-// const ws = new WebSocket("ws://127.0.0.1:8080");
-const ws = new WebSocket("wss://dev.fosscord.com");
-
-ws.on("open", () => {
-	// ws.send(JSON.stringify({ req_type: "new_auth" }));
-	// ws.send(JSON.stringify({ req_type: "check_auth", token: "" }));
-	// op: 0,
-	// d: {},
-	// s: 42,
-	// t: "GATEWAY_EVENT_NAME",
-});
-
-function send(data) {
-	ws.send(JSON.stringify(data));
-}
-
-ws.on("message", (buffer) => {
-	let data = JSON.parse(buffer.toString());
-	console.log(data);
-
-	switch (data.op) {
-		case 10:
-			setIntervalNow(() => {
-				send({ op: 1 });
-			}, data.d.heartbeat_interval);
-
-			// send({
-			// 	op: 2,
-			// 	d: {
-			// 		token:
-			// 			"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjgxMTY0MjkxNzQzMjA2NjA0OCIsImlhdCI6MTYxMzU4MTE1MX0.7Qj_z2lYIgJ0rc7NfGtpW5DKGqecQfv1mLpoBUQHKDc",
-			// 		intents: 0n,
-			// 		properties: {},
-			// 	},
-			// });
-
-			send({
-				op: 6,
-			});
-
-			break;
-	}
-});
-
-ws.on("close", (code, reason) => {
-	console.log(code, reason, Constants.CLOSECODES[code]);
-});
diff --git a/gateway/package.json b/gateway/package.json
index 6d0d2d1c..92d812b4 100644
--- a/gateway/package.json
+++ b/gateway/package.json
@@ -13,7 +13,7 @@
 	},
 	"keywords": [],
 	"author": "Fosscord",
-	"license": "GPLV3",
+	"license": "AGPLV3",
 	"devDependencies": {
 		"@types/amqplib": "^0.8.1",
 		"@types/jsonwebtoken": "^8.5.0",
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..3395122e
--- /dev/null
+++ b/package.json
@@ -0,0 +1,17 @@
+{
+	"name": "fosscord-server",
+	"version": "1.0.0",
+	"description": "A Fosscord server written in Node.js",
+	"workspaces": ["api", "cdn", "gateway"],
+	"scripts": {},
+	"repository": {
+		"type": "git",
+		"url": "git+https://github.com/fosscord/fosscord-server.git"
+	},
+	"author": "Fosscord",
+	"license": "AGPLV3",
+	"bugs": {
+		"url": "https://github.com/fosscord/fosscord-server/issues"
+	},
+	"homepage": "https://fosscord.com"
+}
diff --git a/rtc/LICENSE b/rtc/LICENSE
deleted file mode 100644
index f19bf520..00000000
--- a/rtc/LICENSE
+++ /dev/null
@@ -1,14 +0,0 @@
-Copyright (C) 2021 Fosscord and contributors
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <https://www.gnu.org/licenses/>.
\ No newline at end of file
diff --git a/util/LICENSE b/util/LICENSE
deleted file mode 100644
index f19bf520..00000000
--- a/util/LICENSE
+++ /dev/null
@@ -1,14 +0,0 @@
-Copyright (C) 2021 Fosscord and contributors
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <https://www.gnu.org/licenses/>.
\ No newline at end of file
diff --git a/util/package.json b/util/package.json
index d7baed9a..7251d3e4 100644
--- a/util/package.json
+++ b/util/package.json
@@ -23,7 +23,7 @@
 		"discord-open-source"
 	],
 	"author": "Fosscord",
-	"license": "GPLV3",
+	"license": "AGPLV3",
 	"bugs": {
 		"url": "https://github.com/fosscord/fosscord-server/issues"
 	},
diff --git a/util/src/entities/AuditLog.ts b/util/src/entities/AuditLog.ts
index 4b81ed6a..b003e7ba 100644
--- a/util/src/entities/AuditLog.ts
+++ b/util/src/entities/AuditLog.ts
@@ -4,41 +4,93 @@ import { ChannelPermissionOverwrite } from "./Channel";
 import { User } from "./User";
 
 export enum AuditLogEvents {
-	GUILD_UPDATE = 1,
-	CHANNEL_CREATE = 10,
+	// guild level
+	GUILD_UPDATE = 1, 
+	GUILD_IMPORT = 2,
+	GUILD_EXPORTED = 3,
+	GUILD_ARCHIVE = 4,
+	GUILD_UNARCHIVE = 5,
+	// join-leave
+	USER_JOIN = 6, 
+	USER_LEAVE = 7,
+	// channels
+	CHANNEL_CREATE = 10, 
 	CHANNEL_UPDATE = 11,
 	CHANNEL_DELETE = 12,
-	CHANNEL_OVERWRITE_CREATE = 13,
+	// permission overrides
+	CHANNEL_OVERWRITE_CREATE = 13, 
 	CHANNEL_OVERWRITE_UPDATE = 14,
 	CHANNEL_OVERWRITE_DELETE = 15,
-	MEMBER_KICK = 20,
+	// kick and ban
+	MEMBER_KICK = 20, 
 	MEMBER_PRUNE = 21,
 	MEMBER_BAN_ADD = 22,
 	MEMBER_BAN_REMOVE = 23,
+	// member updates
 	MEMBER_UPDATE = 24,
 	MEMBER_ROLE_UPDATE = 25,
 	MEMBER_MOVE = 26,
 	MEMBER_DISCONNECT = 27,
 	BOT_ADD = 28,
+	// roles
 	ROLE_CREATE = 30,
 	ROLE_UPDATE = 31,
 	ROLE_DELETE = 32,
+	ROLE_SWAP = 33,
+	// invites
 	INVITE_CREATE = 40,
 	INVITE_UPDATE = 41,
 	INVITE_DELETE = 42,
+	// webhooks
 	WEBHOOK_CREATE = 50,
 	WEBHOOK_UPDATE = 51,
 	WEBHOOK_DELETE = 52,
+	WEBHOOK_SWAP = 53,
+	// custom emojis
 	EMOJI_CREATE = 60,
 	EMOJI_UPDATE = 61,
 	EMOJI_DELETE = 62,
+	EMOJI_SWAP = 63,
+	// deletion
+	MESSAGE_CREATE = 70, // messages sent using non-primary seat of the user only
+	MESSAGE_EDIT = 71, // non-self edits only
 	MESSAGE_DELETE = 72,
 	MESSAGE_BULK_DELETE = 73,
+	// pinning
 	MESSAGE_PIN = 74,
 	MESSAGE_UNPIN = 75,
+	// integrations
 	INTEGRATION_CREATE = 80,
 	INTEGRATION_UPDATE = 81,
 	INTEGRATION_DELETE = 82,
+	// stage actions
+	STAGE_INSTANCE_CREATE = 83,
+	STAGE_INSTANCE_UPDATE = 84,
+	STAGE_INSTANCE_DELETE = 85,
+	// stickers
+	STICKER_CREATE = 90,
+	STICKER_UPDATE = 91,
+	STICKER_DELETE = 92,
+	STICKER_SWAP = 93,
+	// threads
+	THREAD_CREATE = 110,
+	THREAD_UPDATE = 111,
+	THREAD_DELETE = 112,
+	// application commands
+	APPLICATION_COMMAND_PERMISSION_UPDATE = 121,
+	// automod
+	POLICY_CREATE = 140, 
+	POLICY_UPDATE = 141,
+	POLICY_DELETE = 142,
+	MESSAGE_BLOCKED_BY_POLICIES = 143,  // in fosscord, blocked messages are stealth-dropped
+	// instance policies affecting the guild
+	GUILD_AFFECTED_BY_POLICIES = 216,
+	// message moves
+	IN_GUILD_MESSAGE_MOVE = 223,
+	CROSS_GUILD_MESSAGE_MOVE = 224,
+	// message routing
+	ROUTE_CREATE = 225, 
+	ROUTE_UPDATE = 226,
 }
 
 @Entity("audit_logs")
diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts
index bf72994f..f3c40c83 100644
--- a/util/src/entities/Channel.ts
+++ b/util/src/entities/Channel.ts
@@ -28,6 +28,8 @@ export enum ChannelType {
 	GUILD_PUBLIC_THREAD = 11, // a temporary sub-channel within a GUILD_TEXT channel

 	GUILD_PRIVATE_THREAD = 12, // a temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission

 	GUILD_STAGE_VOICE = 13, // a voice channel for hosting events with an audience

+	DIRECTORY = 14, // guild directory listing channel

+	GUILD_FORUM = 15, // forum composed of IM threads

 	TICKET_TRACKER = 33, // ticket tracker, individual ticket items shall have type 12

 	KANBAN = 34, // confluence like kanban board

 	VOICELESS_WHITEBOARD = 35, // whiteboard but without voice (whiteboard + voice is the same as stage)

diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts
index 8d29b387..063a4d4d 100644
--- a/util/src/entities/Config.ts
+++ b/util/src/entities/Config.ts
@@ -324,7 +324,7 @@ export const DefaultConfigOptions: ConfigValue = {
 			// domains: fs.readFileSync(__dirname + "/blockedEmailDomains.txt", { encoding: "utf8" }).split("\n"),
 		},
 		dateOfBirth: {
-			required: false,
+			required: true,
 			minimum: 13,
 		},
 		disabled: false,
diff --git a/util/src/entities/Group.ts b/util/src/entities/Group.ts
new file mode 100644
index 00000000..b24d38cf
--- /dev/null
+++ b/util/src/entities/Group.ts
@@ -0,0 +1,33 @@
+import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
+
+import { BaseClass } from "./BaseClass";
+
+@Entity("groups")
+export class UserGroup extends BaseClass {
+  @Column({ nullable: true })
+  parent?: BigInt;
+
+	@Column()
+	color: number;
+
+	@Column()
+	hoist: boolean;
+
+ 	@Column()
+	mentionable: boolean;
+
+	@Column()
+	name: string;
+
+	@Column()
+	rights: BigInt;
+
+	@Column()
+	position: number;
+
+	@Column({ nullable: true })
+	icon: BigInt;
+
+	@Column({ nullable: true })
+	unicode_emoji: BigInt;
+}
diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts
index b32bbd94..e18cf691 100644
--- a/util/src/entities/Message.ts
+++ b/util/src/entities/Message.ts
@@ -39,13 +39,15 @@ export enum MessageType {
 	USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 10,
 	USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 11,
 	CHANNEL_FOLLOW_ADD = 12,
+	ACTION = 13, // /me messages
 	GUILD_DISCOVERY_DISQUALIFIED = 14,
 	GUILD_DISCOVERY_REQUALIFIED = 15,
 	ENCRYPTED = 16,
 	REPLY = 19,
-	APPLICATION_COMMAND = 20,
+	APPLICATION_COMMAND = 20, // application command or self command invocation
 	ROUTE_ADDED = 41, // custom message routing: new route affecting that channel
 	ROUTE_DISABLED = 42, // custom message routing: given route no longer affecting that channel
+	SELF_COMMAND_SCRIPT = 43, // self command scripts
 	ENCRYPTION = 50,
 	CUSTOM_START = 63,
 	UNHANDLED = 255
diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts
index a5c4c136..9b1c494e 100644
--- a/util/src/entities/User.ts
+++ b/util/src/entities/User.ts
@@ -163,6 +163,10 @@ export class User extends BaseClass {
 
 	@Column({ type: "simple-json", select: false })
 	settings: UserSettings;
+		
+	// workaround to prevent fossord-unaware clients from deleting settings not used by them
+	@Column({ type: "simple-json", select: false })
+	extended_settings: string;
 
 	@Column({ type: "simple-json" })
 	notes: { [key: string]: string };	//key is ID of user
@@ -273,6 +277,7 @@ export class User extends BaseClass {
 				valid_tokens_since: new Date(),
 			},
 			settings: { ...defaultSettings, locale: language },
+			extended_settings: {},
 			fingerprints: [],
 			notes: {},
 		});
diff --git a/util/src/util/Constants.ts b/util/src/util/Constants.ts
index 42a2c274..81a7165d 100644
--- a/util/src/util/Constants.ts
+++ b/util/src/util/Constants.ts
@@ -731,21 +731,23 @@ export const DiscordApiErrors = {
  * An error encountered while performing an API request (Fosscord only). Here are the potential errors:
  */
 export const FosscordApiErrors = {
-	MANUALLY_TRIGGERED_ERROR: new ApiError("This is an artificial error", 1),
+	MANUALLY_TRIGGERED_ERROR: new ApiError("This is an artificial error", 1, 500),
 	PREMIUM_DISABLED_FOR_GUILD: new ApiError("This guild cannot be boosted", 25001),
 	NO_FURTHER_PREMIUM: new ApiError("This guild does not receive further boosts", 25002),
-	GUILD_PREMIUM_DISABLED_FOR_YOU: new ApiError("This guild cannot be boosted by you", 25003),
+	GUILD_PREMIUM_DISABLED_FOR_YOU: new ApiError("This guild cannot be boosted by you", 25003, 403),
 	CANNOT_FRIEND_SELF: new ApiError("Cannot friend oneself", 25009),
 	USER_SPECIFIC_INVITE_WRONG_RECIPIENT: new ApiError("This invite is not meant for you", 25010),
 	USER_SPECIFIC_INVITE_FAILED: new ApiError("Failed to invite user", 25011),
-	CANNOT_MODIFY_USER_GROUP: new ApiError("This user cannot manipulate this group", 25050),
+	CANNOT_MODIFY_USER_GROUP: new ApiError("This user cannot manipulate this group", 25050, 403),
 	CANNOT_REMOVE_SELF_FROM_GROUP: new ApiError("This user cannot remove oneself from user group", 25051),
 	CANNOT_BAN_OPERATOR: new ApiError("Non-OPERATOR cannot ban OPERATOR from instance", 25052),
-	CANNOT_LEAVE_GUILD: new ApiError("You are not allowed to leave guilds that you joined by yourself", 25059),
-	EDITS_DISABLED: new ApiError("You are not allowed to edit your own messages", 25060),
-	DELETE_MESSAGE_DISABLED: new ApiError("You are not allowed to delete your own messages", 25061),
-	FEATURE_PERMANENTLY_DISABLED: new ApiError("This feature has been disabled server-side", 45006),
+	CANNOT_LEAVE_GUILD: new ApiError("You are not allowed to leave guilds that you joined by yourself", 25059, 403),
+	EDITS_DISABLED: new ApiError("You are not allowed to edit your own messages", 25060, 403),
+	DELETE_MESSAGE_DISABLED: new ApiError("You are not allowed to delete your own messages", 25061, 403),
+	FEATURE_PERMANENTLY_DISABLED: new ApiError("This feature has been disabled server-side", 45006, 501),
 	MISSING_RIGHTS: new ApiError("You lack rights to perform that action ({})", 50013, undefined, [""]),
+	CANNOT_REPLACE_BY_BACKFILL: new ApiError("Cannot backfill to message ID that already exists", 55002, 409),
+	CANNOT_BACKFILL_TO_THE_FUTURE: new ApiError("You cannot backfill messages in the future", 55003),
 	CANNOT_GRANT_PERMISSIONS_EXCEEDING_RIGHTS: new ApiError("You cannot grant permissions exceeding your own rights", 50050),
 	ROUTES_LOOPING: new ApiError("Loops in the route definition ({})", 50060, undefined, [""]),
 	CANNOT_REMOVE_ROUTE: new ApiError("Cannot remove message route while it is in effect and being used", 50061),
@@ -791,3 +793,4 @@ function keyMirror(arr: string[]) {
 	for (const value of arr) tmp[value] = value;
 	return tmp;
 }
+
diff --git a/util/src/util/Intents.ts b/util/src/util/Intents.ts
index d9a60e4a..1e840b76 100644
--- a/util/src/util/Intents.ts
+++ b/util/src/util/Intents.ts
@@ -18,6 +18,8 @@ export class Intents extends BitField {
 		DIRECT_MESSAGE_REACTIONS: BigInt(1) << BigInt(13), // DM or orphan channel message reactions
 		DIRECT_MESSAGE_TYPING: BigInt(1) << BigInt(14), // DM typing notifications
 		GUILD_MESSAGES_CONTENT: BigInt(1) << BigInt(15), // guild message content
+		GUILD_POLICIES: BigInt(1) << BigInt(20), // guild policies
+		GUILD_POLICY_EXECUTION: BigInt(1) << BigInt(21), // guild policy execution
 		LIVE_MESSAGE_COMPOSITION: BigInt(1) << BigInt(32), // allow composing messages using the gateway
 		GUILD_ROUTES: BigInt(1) << BigInt(41), // message routes affecting the guild
 		DIRECT_MESSAGES_THREADS: BigInt(1) << BigInt(42),  // direct message threads
diff --git a/util/src/util/MessageFlags.ts b/util/src/util/MessageFlags.ts
index c76be4c8..b59295c4 100644
--- a/util/src/util/MessageFlags.ts
+++ b/util/src/util/MessageFlags.ts
@@ -1,5 +1,5 @@
-// https://github.com/discordjs/discord.js/blob/master/src/util/MessageFlags.js
-// Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah
+// based on https://github.com/discordjs/discord.js/blob/master/src/util/MessageFlags.js
+// Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah, 2022 Erkin Alp Güney
 
 import { BitField } from "./BitField";
 
@@ -8,7 +8,13 @@ export class MessageFlags extends BitField {
 		CROSSPOSTED: BigInt(1) << BigInt(0),
 		IS_CROSSPOST: BigInt(1) << BigInt(1),
 		SUPPRESS_EMBEDS: BigInt(1) << BigInt(2),
-		SOURCE_MESSAGE_DELETED: BigInt(1) << BigInt(3),
+		// SOURCE_MESSAGE_DELETED: BigInt(1) << BigInt(3), // fosscord will delete them from destination too, making this redundant
 		URGENT: BigInt(1) << BigInt(4),
+		// HAS_THREAD: BigInt(1) << BigInt(5) // does not apply to fosscord due to infrastructural differences
+		PRIVATE_ROUTE: BigInt(1) << BigInt(6), // it that has been routed to only some of the users that can see the channel
+		INTERACTION_WAIT: BigInt(1) << BigInt(7), // discord.com calls this LOADING
+		// FAILED_TO_MENTION_SOME_ROLES_IN_THREAD: BigInt(1) << BigInt(8)
+		SCRIPT_WAIT: BigInt(1) << BigInt(24), // waiting for the self command to complete
+		IMPORT_WAIT: BigInt(1) << BigInt(25), // latest message of a bulk import, waiting for the rest of the channel to be backfilled
 	};
 }
diff --git a/util/src/util/Token.ts b/util/src/util/Token.ts
index 7c4cc61d..500ace45 100644
--- a/util/src/util/Token.ts
+++ b/util/src/util/Token.ts
@@ -6,7 +6,12 @@ export const JWTOptions: VerifyOptions = { algorithms: ["HS256"] };
 
 export function checkToken(token: string, jwtSecret: string): Promise<any> {
 	return new Promise((res, rej) => {
-		token = token.replace("Bot ", ""); // TODO: proper bot support
+		token = token.replace("Bot ", "");
+		/**
+		in fosscord, even with instances that have bot distinction; we won't enforce "Bot" prefix,
+		as we don't really have separate pathways for bots 
+		**/
+		
 		jwt.verify(token, jwtSecret, JWTOptions, async (err, decoded: any) => {
 			if (err || !decoded) return rej("Invalid Token");
 
diff --git a/webrtc/LICENSE b/webrtc/LICENSE
deleted file mode 100644
index f19bf520..00000000
--- a/webrtc/LICENSE
+++ /dev/null
@@ -1,14 +0,0 @@
-Copyright (C) 2021 Fosscord and contributors
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <https://www.gnu.org/licenses/>.
\ No newline at end of file
diff --git a/webrtc/package.json b/webrtc/package.json
index b9bac356..2a4705ab 100644
--- a/webrtc/package.json
+++ b/webrtc/package.json
@@ -9,8 +9,8 @@
 		"start": "npm run build && node dist/start.js"
 	},
 	"keywords": [],
-	"author": "",
-	"license": "ISC",
+	"author": "Fosscord",
+	"license": "AGPLV3",
 	"devDependencies": {
 		"@types/node": "^15.6.1",
 		"@types/sdp-transform": "^2.4.5",