From 0b3f5ef88b11999287cd0318f45418de75069b24 Mon Sep 17 00:00:00 2001
From: Simon Ser <contact@emersion.fr>
Date: Fri, 11 Jun 2021 12:54:42 +0200
Subject: [PATCH] Add irc.forEachChannelModeUpdate helper

---
 lib/irc.js | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 state.js   | 40 ++--------------------------------------
 2 files changed, 49 insertions(+), 38 deletions(-)

diff --git a/lib/irc.js b/lib/irc.js
index e56fef2..e485140 100644
--- a/lib/irc.js
+++ b/lib/irc.js
@@ -571,3 +571,50 @@ export function getMessageLabel(msg) {
 
 	return null;
 }
+
+export function forEachChannelModeUpdate(msg, isupport, callback) {
+	let chanmodes = isupport.get("CHANMODES") || STD_CHANMODES;
+	let prefix = isupport.get("PREFIX") || "";
+
+	let typeByMode = new Map();
+	let [a, b, c, d] = chanmodes.split(",");
+	Array.from(a).forEach((mode) => typeByMode.set(mode, "A"));
+	Array.from(b).forEach((mode) => typeByMode.set(mode, "B"));
+	Array.from(c).forEach((mode) => typeByMode.set(mode, "C"));
+	Array.from(d).forEach((mode) => typeByMode.set(mode, "D"));
+	parseMembershipModes(prefix).forEach((membership) => typeByMode.set(membership.mode, "B"));
+
+	if (msg.command !== "MODE") {
+		throw new Error("Expected a MODE message");
+	}
+	let change = msg.params[1];
+	let args = msg.params.slice(2);
+
+	let plusMinus = null;
+	let j = 0;
+	for (let i = 0; i < change.length; i++) {
+		if (change[i] === "+" || change[i] === "-") {
+			plusMinus = change[i];
+			continue;
+		}
+		if (!plusMinus) {
+			throw new Error("malformed mode string: missing plus/minus");
+		}
+
+		let mode = change[i];
+		let add = plusMinus === "+";
+
+		let modeType = typeByMode.get(mode);
+		if (!modeType) {
+			continue;
+		}
+
+		let arg = null;
+		if (modeType === "A" || modeType === "B" || (modeType === "C" && add)) {
+			arg = args[j];
+			j++;
+		}
+
+		callback(mode, add, arg);
+	}
+}
diff --git a/state.js b/state.js
index f823e1c..c0f70d4 100644
--- a/state.js
+++ b/state.js
@@ -413,63 +413,27 @@ export const State = {
 			return updateBuffer(channel, { topic });
 		case "MODE":
 			target = msg.params[0];
-			let change = msg.params[1];
-			let args = msg.params.slice(2);
 
 			if (!client.isChannel(target)) {
 				return; // TODO: handle user mode changes too
 			}
 
-			let chanmodes = client.isupport.get("CHANMODES") || irc.STD_CHANMODES;
 			let prefix = client.isupport.get("PREFIX") || "";
-
 			let prefixByMode = new Map(irc.parseMembershipModes(prefix).map((membership) => {
 				return [membership.mode, membership.prefix];
 			}));
 
-			let typeByMode = new Map();
-			let [a, b, c, d] = chanmodes.split(",");
-			Array.from(a).forEach((mode) => typeByMode.set(mode, "A"));
-			Array.from(b).forEach((mode) => typeByMode.set(mode, "B"));
-			Array.from(c).forEach((mode) => typeByMode.set(mode, "C"));
-			Array.from(d).forEach((mode) => typeByMode.set(mode, "D"));
-			prefixByMode.forEach((prefix, mode) => typeByMode.set(mode, "B"));
-
 			return updateBuffer(target, (buf) => {
 				let members = new irc.CaseMapMap(buf.members);
 
-				let plusMinus = null;
-				let j = 0;
-				for (let i = 0; i < change.length; i++) {
-					if (change[i] === "+" || change[i] === "-") {
-						plusMinus = change[i];
-						continue;
-					}
-					if (!plusMinus) {
-						throw new Error("malformed mode string: missing plus/minus");
-					}
-
-					let mode = change[i];
-					let add = plusMinus === "+";
-
-					let modeType = typeByMode.get(mode);
-					if (!modeType) {
-						continue;
-					}
-
-					let arg = null;
-					if (modeType === "A" || modeType === "B" || (modeType === "C" && add)) {
-						arg = args[j];
-						j++;
-					}
-
+				irc.forEachChannelModeUpdate(msg, client.isupport, (mode, add, arg) => {
 					if (prefixByMode.has(mode)) {
 						let nick = arg;
 						let membership = members.get(nick);
 						let letter = prefixByMode.get(mode);
 						members.set(nick, updateMembership(membership, letter, add, client));
 					}
-				}
+				});
 
 				return { members };
 			});