diff --git a/src/config.ts b/src/config.ts
index 584cc0f..38de4bb 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -3,7 +3,27 @@ export default {
     chat_role_id: "823595534567866428",
     restrict_role_id: "895972446992228382",
     admin_role_id: "686489711719612502",
+    staff_role_id: "913986523500777482",
     infra_role_id: "1291763746657669211",
+    update_requests_channel: "1310730413211254905",
+
+    lockdownChannels: [
+      "471385416595931176",  // #command-spam
+      "468104786235883530",  // #tupperbox-import
+
+      "667795132971614229",  // #bot-support
+      "1310730413211254905", // #update-requests
+      "961622977458360373",  // #website
+      "468821582794588160",  // #suggestions-feedback
+
+      "912804952953479171",  // #third-party-discussion
+
+      "710861135842115931",  // #internal-stuffs
+      "635146116144431149",  // #bug-reports-and-errors
+      "557321432342855691",  // #api-support
+      "598555595808702473",  // #beta-testing
+    ],
+
     // 2 weeks
     newAccountDuration:
           60 // seconds
diff --git a/src/evt/messageCreate.ts b/src/evt/messageCreate.ts
index 0b37eda..6ccedd1 100644
--- a/src/evt/messageCreate.ts
+++ b/src/evt/messageCreate.ts
@@ -12,7 +12,28 @@ const isNewAccount = (createdAt: number) => {
 
 const token: string = process.env.token!;
 
-let allowChatAccess: boolean = true;
+const pinnedMessageText = `<#${config.update_requests_channel}>: ask for limit updates or ID re-rolls here.
+
+Please keep your requests to **one message**, feel free to edit it afterward. Staff may create a thread in case they need more information.
+
+Any other messages **will be deleted with no warning**. If you are not sure which channel to use, please read <#641807196056715294>.`;
+
+const updatePinnedMessage = async (ctx: Context) => {
+	const key = "_pinnedMessageId";
+	let currentMessageId = await ctx.db.maybeGetString(key);
+	let newMessage = await ctx.rest.createMessage(config.update_requests_channel, pinnedMessageText);
+	if (currentMessageId) ctx.rest.deleteMessage(config.update_requests_channel, currentMessageId);
+	await ctx.db.level.put(key, newMessage.id);
+}
+
+const lockUnlockChannel = async (ctx: Context, guildId: string, channelId: string, lock: boolean) => {
+	let flags = await ctx.rest.fetchChannel(channelId).then((x: any) => x.permission_overwrites.find((o: any) => o.id == guildId).deny);
+	let operator = lock ? " | " : " & ~";
+	let deny = eval(`String(BigInt(flags) ${operator}BigInt(2048))`);
+	await ctx.rest.editChannelOverwrite(channelId, guildId, { deny });
+}
+
+const offtopicLockKey = "_offtopicLock";
 
 export default async (evt: any, ctx: Context) => {
 	if (!evt.content) return;
@@ -25,6 +46,8 @@ export default async (evt: any, ctx: Context) => {
 	
 	if (evt.guild_id != config.guild_id) return;
 
+	if (evt.channel_id == config.update_requests_channel) await updatePinnedMessage(ctx);
+
 	if (["?tags", "?tag list", "?taglist"].includes(content))
 		return await ctx.rest.createMessage(evt.channel_id, {
 			content: `available tags: ${Object.keys(tags).join(', ')}`,
@@ -76,7 +99,7 @@ export default async (evt: any, ctx: Context) => {
 				},
 			});
 		} else {
-			if (!allowChatAccess) {
+			if (await ctx.db.maybeGetString(offtopicLockKey)) {
 				await ctx.rest.createMessage(evt.channel_id, {
 					content: `\u274c This command is currently disabled, please try again later.`,
 					allowedMentions: { parse: [] },
@@ -101,6 +124,34 @@ export default async (evt: any, ctx: Context) => {
 		}
 	}
 
+	if (content == ".lockchat" && evt.member.roles.includes(config.staff_role_id)) {
+		await ctx.db.level.put(offtopicLockKey, "true");
+		await ctx.rest.createMessage(evt.channel_id, "ok");
+	}
+
+	if (content == ".unlockchat" && evt.member.roles.includes(config.staff_role_id)) {
+		await ctx.db.level.del(offtopicLockKey);
+		await ctx.rest.createMessage(evt.channel_id, "ok");
+	}
+
+	if (content == ".lockchannels" && evt.member.roles.includes(config.staff_role_id)) {
+		await ctx.rest.createReaction(evt.channel_id, evt.id, "\u23f3");
+		for (let channel of config.lockdownChannels) {
+			await lockUnlockChannel(ctx, evt.guild_id, channel, true);
+			console.log("locked", channel);
+		}
+		await ctx.rest.createMessage(evt.channel_id, "ok");
+	}
+
+	if (content == ".unlockchannels" && evt.member.roles.includes(config.staff_role_id)) {
+		await ctx.rest.createReaction(evt.channel_id, evt.id, "\u23f3");
+		for (let channel of config.lockdownChannels) {
+			await lockUnlockChannel(ctx, evt.guild_id, channel, false);
+			console.log("locked", channel);
+		}
+		await ctx.rest.createMessage(evt.channel_id, "ok");
+	}
+
 	if (content?.startsWith(".eval ") && evt.member.roles.includes(config.admin_role_id)) {
 		let res;
 		try {
diff --git a/src/index.ts b/src/index.ts
index 41d9dfa..44ea78b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -20,6 +20,8 @@ const level = new Level<string[]>('bot.db')
 
 const ctx = { socket, rest, db: {
     level,
+    maybeGetString: async (id: string): Promise<string | null> =>
+        await level.get(id).catch(x => {console.log(x); return null;}) as string|null,
     get: async (id: string): Promise<string[]> => {
         if (!await level.exists(id))
             await level.put(id, []);