summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--catgirl.132
-rw-r--r--chat.h2
-rw-r--r--command.c70
-rw-r--r--handle.c48
4 files changed, 134 insertions, 18 deletions
diff --git a/catgirl.1 b/catgirl.1
index fd8d40a..b1b72de 100644
--- a/catgirl.1
+++ b/catgirl.1
@@ -1,4 +1,4 @@
-.Dd February 19, 2020
+.Dd February 23, 2020
 .Dt CATGIRL 1
 .Os
 .
@@ -242,16 +242,12 @@ can be typed
 .Bl -tag -width Ds
 .It Ic /away Op Ar message
 Set or clear your away status.
-.It Ic /ban Op Ar mask ...
-List or ban masks from the channel.
 .It Ic /cs Ar command
 Send a command to ChanServ.
 .It Ic /invite Ar nick
 Invite a user to the channel.
 .It Ic /join Ar channel
 Join a channel.
-.It Ic /kick Ar nick Op Ar message
-Kick a user from the channel.
 .It Ic /list Op Ar channel
 List channels.
 .It Ic /me Op Ar action
@@ -276,8 +272,6 @@ Quit IRC.
 Send a raw IRC command.
 .It Ic /topic Op Ar topic
 Show or set the topic of the channel.
-.It Ic /unban Ar mask ...
-Unban masks from the channel.
 .It Ic /whois Ar nick
 Query information about a user.
 .El
@@ -326,6 +320,30 @@ Switch to window by name.
 Switch to window by number.
 .El
 .
+.Ss Operator Commands
+.Bl -tag -width Ds
+.It Ic /ban Op Ar mask ...
+List or ban masks from the channel.
+.It Ic /except Op Ar mask ...
+List or add masks to the channel ban exception list.
+.It Ic /invex Op Ar mask ...
+List or add masks to the channel invite list.
+.It Ic /kick Ar nick Op Ar message
+Kick a user from the channel.
+.It Ic /mode Oo Ar modes Oc Op Ar param ...
+Show or set channel modes.
+In the
+.Sy <network>
+window,
+show or set user modes.
+.It Ic /unban Ar mask ...
+Unban masks from the channel.
+.It Ic /unexcept Ar mask ...
+Remove masks from the channel ban exception list.
+.It Ic /uninvex Ar mask ...
+Remove masks from the channel invite list.
+.El
+.
 .Sh KEY BINDINGS
 The
 .Nm
diff --git a/chat.h b/chat.h
index e42cf7b..5b4e14c 100644
--- a/chat.h
+++ b/chat.h
@@ -176,6 +176,8 @@ static inline void utilPush(struct Util *util, const char *arg) {
 extern struct Replies {
 	uint away;
 	uint ban;
+	uint excepts;
+	uint invex;
 	uint join;
 	uint list;
 	uint names;
diff --git a/command.c b/command.c
index 1d19a91..c2ebab7 100644
--- a/command.c
+++ b/command.c
@@ -143,14 +143,34 @@ static void commandKick(uint id, char *params) {
 	}
 }
 
+static void commandMode(uint id, char *params) {
+	if (id == Network) {
+		if (params) {
+			ircFormat("MODE %s %s\r\n", self.nick, params);
+		} else {
+			ircFormat("MODE %s\r\n", self.nick);
+		}
+	} else {
+		if (params) {
+			ircFormat("MODE %s %s\r\n", idNames[id], params);
+		} else {
+			ircFormat("MODE %s\r\n", idNames[id]);
+		}
+	}
+}
+
+static void channelListMode(uint id, char pm, char l, char *params) {
+	int count = 1;
+	for (char *ch = params; *ch; ++ch) {
+		if (*ch == ' ') count++;
+	}
+	char modes[ParamCap - 2] = { l, l, l, l, l, l, l, l, l, l, l, l, l };
+	ircFormat("MODE %s %c%.*s %s\r\n", idNames[id], pm, count, modes, params);
+}
+
 static void commandBan(uint id, char *params) {
 	if (params) {
-		int count = 1;
-		for (char *ch = params; *ch; ++ch) {
-			if (*ch == ' ') count++;
-		}
-		char b[ParamCap - 2] = "bbbbbbbbbbbbb";
-		ircFormat("MODE %s +%.*s %s\r\n", idNames[id], count, b, params);
+		channelListMode(id, '+', 'b', params);
 	} else {
 		ircFormat("MODE %s b\r\n", idNames[id]);
 		replies.ban++;
@@ -159,12 +179,35 @@ static void commandBan(uint id, char *params) {
 
 static void commandUnban(uint id, char *params) {
 	if (!params) return;
-	int count = 1;
-	for (char *ch = params; *ch; ++ch) {
-		if (*ch == ' ') count++;
+	channelListMode(id, '-', 'b', params);
+}
+
+static void commandExcept(uint id, char *params) {
+	if (params) {
+		channelListMode(id, '+', network.excepts, params);
+	} else {
+		ircFormat("MODE %s %c\r\n", idNames[id], network.excepts);
+		replies.excepts++;
 	}
-	char b[ParamCap - 2] = "bbbbbbbbbbbbb";
-	ircFormat("MODE %s -%.*s %s\r\n", idNames[id], count, b, params);
+}
+
+static void commandUnexcept(uint id, char *params) {
+	if (!params) return;
+	channelListMode(id, '-', network.excepts, params);
+}
+
+static void commandInvex(uint id, char *params) {
+	if (params) {
+		channelListMode(id, '+', network.invex, params);
+	} else {
+		ircFormat("MODE %s %c\r\n", idNames[id], network.invex);
+		replies.invex++;
+	}
+}
+
+static void commandUninvex(uint id, char *params) {
+	if (!params) return;
+	channelListMode(id, '-', network.invex, params);
 }
 
 static void commandList(uint id, char *params) {
@@ -293,13 +336,16 @@ static const struct Handler {
 	{ "/copy", .fn = commandCopy, .restricted = true },
 	{ "/cs", .fn = commandCS },
 	{ "/debug", .fn = commandDebug, .restricted = true },
+	{ "/except", .fn = commandExcept },
 	{ "/exec", .fn = commandExec, .restricted = true },
 	{ "/help", .fn = commandHelp },
+	{ "/invex", .fn = commandInvex },
 	{ "/invite", .fn = commandInvite },
 	{ "/join", .fn = commandJoin, .restricted = true },
 	{ "/kick", .fn = commandKick },
 	{ "/list", .fn = commandList },
 	{ "/me", .fn = commandMe },
+	{ "/mode", .fn = commandMode },
 	{ "/move", .fn = commandMove },
 	{ "/msg", .fn = commandMsg, .restricted = true },
 	{ "/names", .fn = commandNames },
@@ -313,6 +359,8 @@ static const struct Handler {
 	{ "/quote", .fn = commandQuote, .restricted = true },
 	{ "/topic", .fn = commandTopic },
 	{ "/unban", .fn = commandUnban },
+	{ "/unexcept", .fn = commandUnexcept },
+	{ "/uninvex", .fn = commandUninvex },
 	{ "/whois", .fn = commandWhois },
 	{ "/window", .fn = commandWindow },
 };
diff --git a/handle.c b/handle.c
index 2dc4654..f1fc75d 100644
--- a/handle.c
+++ b/handle.c
@@ -606,6 +606,50 @@ static void handleReplyEndOfBanList(struct Message *msg) {
 	if (replies.ban) replies.ban--;
 }
 
+static void onList(const char *list, struct Message *msg) {
+	uint id = idFor(msg->params[1]);
+	if (msg->params[3] && msg->params[4]) {
+		char since[sizeof("0000-00-00 00:00:00")];
+		time_t time = strtol(msg->params[4], NULL, 10);
+		strftime(since, sizeof(since), "%F %T", localtime(&time));
+		uiFormat(
+			id, Cold, tagTime(msg),
+			"On the \3%02d%s\3 %s list since %s by \3%02d%s\3: %s",
+			hash(msg->params[1]), msg->params[1], list,
+			since, completeColor(id, msg->params[3]), msg->params[3],
+			msg->params[2]
+		);
+	} else {
+		uiFormat(
+			id, Cold, tagTime(msg),
+			"On the \3%02d%s\3 %s list: %s",
+			hash(msg->params[1]), msg->params[1], list, msg->params[2]
+		);
+	}
+}
+
+static void handleReplyExceptList(struct Message *msg) {
+	require(msg, false, 3);
+	if (!replies.excepts) return;
+	onList("except", msg);
+}
+
+static void handleReplyEndOfExceptList(struct Message *msg) {
+	(void)msg;
+	if (replies.excepts) replies.excepts--;
+}
+
+static void handleReplyInviteList(struct Message *msg) {
+	require(msg, false, 3);
+	if (!replies.invex) return;
+	onList("invite", msg);
+}
+
+static void handleReplyEndOfInviteList(struct Message *msg) {
+	(void)msg;
+	if (replies.invex) replies.invex--;
+}
+
 static void handleReplyList(struct Message *msg) {
 	require(msg, false, 4);
 	if (!replies.list) return;
@@ -881,6 +925,10 @@ static const struct Handler {
 	{ "331", handleReplyNoTopic },
 	{ "332", handleReplyTopic },
 	{ "341", handleReplyInviting },
+	{ "346", handleReplyInviteList },
+	{ "347", handleReplyEndOfInviteList },
+	{ "348", handleReplyExceptList },
+	{ "349", handleReplyEndOfExceptList },
 	{ "353", handleReplyNames },
 	{ "366", handleReplyEndOfNames },
 	{ "367", handleReplyBanList },