diff lib/irccd/irccd.c @ 954:30643d18a635

irccd: add onCommand support
author David Demelier <markand@malikania.fr>
date Wed, 20 Jan 2021 14:08:14 +0100
parents ab43ba409f9d
children 9b167c5c4b78
line wrap: on
line diff
--- a/lib/irccd/irccd.c	Wed Jan 20 12:32:59 2021 +0100
+++ b/lib/irccd/irccd.c	Wed Jan 20 14:08:14 2021 +0100
@@ -17,6 +17,7 @@
  */
 
 #include <assert.h>
+#include <ctype.h>
 #include <err.h>
 #include <errno.h>
 #include <poll.h>
@@ -43,7 +44,7 @@
 
 struct defer {
 	void (*exec)(void *);
-	void (*data);
+	void *data;
 };
 
 struct irc irc;
@@ -62,6 +63,39 @@
 	return p1->fd - p2->fd;
 }
 
+static bool
+is_command(const struct irc_plugin *p, const struct irc_event *ev)
+{
+	const char *cc;
+	size_t ccsz;
+
+	if (ev->type != IRC_EVENT_MESSAGE)
+		return false;
+
+	/* Get the command prefix (e.g !)*/
+	cc = ev->server->commandchar;
+	ccsz = strlen(cc);
+
+	return strncmp(ev->msg.args[1], cc, ccsz) == 0 &&
+	       strncmp(ev->msg.args[1] + ccsz, p->name, strlen(p->name)) == 0;
+}
+
+static struct irc_event *
+to_command(const struct irc_plugin *p, struct irc_event *ev)
+{
+	char *s;
+
+	ev->type = IRC_EVENT_COMMAND;
+	ev->msg.args[1] = ev->msg.args[1] + strlen(ev->server->commandchar) + strlen(p->name);
+
+	for (s = ev->msg.args[1]; *s && isspace(*s); )
+		++s;
+
+	ev->msg.args[1] = s;
+
+	return ev;
+}
+
 static struct pkg
 prepare(void)
 {
@@ -103,11 +137,34 @@
 			irc_peer_send(&irc.peers[i], buf);
 }
 
-static inline void
-invoke(const struct irc_event *ev)
+static void
+invoke(struct irc_event *ev)
 {
-	for (size_t i = 0; i < irc.pluginsz; ++i)
-		irc_plugin_handle(&irc.plugins[i], ev);
+	struct irc_plugin *plgcmd = NULL;
+
+	/*
+	 * Invoke for every plugin the event verbatim. Then, the event may match
+	 * a plugin name command in that case we need to modify the event but
+	 * only one plugin can match by its identifier. For example, the
+	 * following plugins are loaded:
+	 *
+	 * - ask
+	 * - hangman
+	 * - logger
+	 *
+	 * If the message is "!ask will I be reach?" then it will invoke
+	 * onMessage for hangman and logger but onCommand for ask. As such call
+	 * hangman and logger first and modify event before ask.
+	 */
+	for (size_t i = 0; i < irc.pluginsz; ++i) {
+		if (is_command(&irc.plugins[i], ev))
+			plgcmd = &irc.plugins[i];
+		else
+			irc_plugin_handle(&irc.plugins[i], ev);
+	}
+
+	if (plgcmd)
+		irc_plugin_handle(plgcmd, to_command(plgcmd, ev));
 }
 
 static void
@@ -222,7 +279,7 @@
 	irc_server_disconnect(s);
 
 	/* Don't forget to notify plugins. */
-	invoke(&(const struct irc_event) {
+	invoke(&(struct irc_event) {
 		.type = IRC_EVENT_DISCONNECT,
 		.server = s
 	});