diff --git a/ibis/ibisclient.h b/ibis/ibisclient.h
--- a/ibis/ibisclient.h
+++ b/ibis/ibisclient.h
@@ -33,7 +33,6 @@
 #include <gio/gio.h>
 
 #include "ibismessage.h"
-#include "ibisparser.h"
 #include "ibisversion.h"
 
 G_BEGIN_DECLS
@@ -148,19 +147,6 @@
 GError *ibis_client_get_error(IbisClient *client);
 
 /**
- * ibis_client_get_parser:
- * @client: The instance.
- *
- * Gets the [class@Parser] that @client is using.
- *
- * Returns: (transfer none): The parser.
- *
- * Since: 0.1
- */
-IBIS_AVAILABLE_IN_0_1
-IbisParser *ibis_client_get_parser(IbisClient *client);
-
-/**
  * ibis_client_new:
  *
  * Creates a new client.
# HG changeset patch
# Node ID 7096cdb9db37
# Parent  1b04f0475375
diff --git a/ibis/ibisclient.c b/ibis/ibisclient.c
--- a/ibis/ibisclient.c
+++ b/ibis/ibisclient.c
@@ -34,7 +34,6 @@
 	PROP_CAPABILITIES,
 	PROP_CONNECTED,
 	PROP_ERROR,
-	PROP_PARSER,
 	N_PROPERTIES,
 };
 static GParamSpec *properties[N_PROPERTIES] = {NULL, };
@@ -52,7 +51,6 @@
 	IbisCapabilities *capabilities;
 	gboolean connected;
 	GError *error;
-	IbisParser *parser;
 
 	GIOStream *stream;
 	GDataInputStream *input;
@@ -150,7 +148,7 @@
 		return;
 	}
 
-	message = ibis_parser_parse(client->parser, line, &error);
+	message = ibis_message_parse(line, &error);
 	if(error != NULL) {
 		g_warning("failed to handle '%s': %s", line,
 		          error != NULL ? error->message : "unknown error");
@@ -226,7 +224,6 @@
 	g_clear_object(&client->cancellable);
 	g_clear_object(&client->capabilities);
 	g_clear_error(&client->error);
-	g_clear_object(&client->parser);
 
 	G_OBJECT_CLASS(ibis_client_parent_class)->finalize(obj);
 }
@@ -250,9 +247,6 @@
 	case PROP_ERROR:
 		g_value_set_boxed(value, ibis_client_get_error(client));
 		break;
-	case PROP_PARSER:
-		g_value_set_object(value, ibis_client_get_parser(client));
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
 		break;
@@ -262,7 +256,6 @@
 static void
 ibis_client_init(IbisClient *client) {
 	client->capabilities = ibis_capabilities_new(client);
-	client->parser = ibis_parser_new();
 }
 
 static void
@@ -324,19 +317,6 @@
 		G_TYPE_ERROR,
 		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
-	/**
-	 * IbisClient:parser:
-	 *
-	 * The [class@Parser] that this client is using.
-	 *
-	 * Since: 0.1
-	 */
-	properties[PROP_PARSER] = g_param_spec_object(
-		"parser", "parser",
-		"The parser for this client.",
-		IBIS_TYPE_PARSER,
-		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-
 	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
 
 	/**
@@ -473,13 +453,6 @@
 	return client->error;
 }
 
-IbisParser *
-ibis_client_get_parser(IbisClient *client) {
-	g_return_val_if_fail(IBIS_IS_CLIENT(client), NULL);
-
-	return client->parser;
-}
-
 IbisClient *
 ibis_client_new(void) {
 	return g_object_new(IBIS_TYPE_CLIENT, NULL);
diff --git a/ibis/ibismessage.h b/ibis/ibismessage.h
--- a/ibis/ibismessage.h
+++ b/ibis/ibismessage.h
@@ -35,6 +35,10 @@
 
 G_BEGIN_DECLS
 
+#define IBIS_MESSAGE_PARSE_ERROR \
+	(g_quark_from_static_string("ibis-message-parse-error")) \
+	IBIS_AVAILABLE_MACRO_IN_0_1
+
 #define IBIS_TYPE_MESSAGE (ibis_message_get_type())
 
 /**
@@ -232,6 +236,23 @@
 IBIS_AVAILABLE_IN_0_1
 void ibis_message_take_error(IbisMessage *message, GError *error);
 
+/**
+ * ibis_message_parse:
+ * @buffer: The raw IRC message.
+ * @error: (nullable): Return address for a #GError.
+ *
+ * Attempts to parse a raw IRC message from @buffer.
+ *
+ * @buffer should not have the trailing `\r\n`.
+ *
+ * Returns: (transfer full): The new message if one was parsed, otherwise %NULL
+ *          with @error set.
+ *
+ * Since: 0.1
+ */
+IBIS_AVAILABLE_IN_0_1
+IbisMessage *ibis_message_parse(const char *buffer, GError **error);
+
 G_END_DECLS
 
 #endif /* IBIS_MESSAGE_H */
diff --git a/ibis/ibismessage.c b/ibis/ibismessage.c
--- a/ibis/ibismessage.c
+++ b/ibis/ibismessage.c
@@ -141,6 +141,69 @@
 	}
 }
 
+static guint
+ibis_message_extract_params(GStrvBuilder *builder, const char *str) {
+	char *ptr = NULL;
+	guint count = 0;
+
+	/* Loop through str finding each space separated string. */
+	while(str != NULL && *str != '\0') {
+		/* Look for a space. */
+		ptr = strchr(str, ' ');
+
+		/* If we found one, set it to null terminator and add the string to our
+		 * builder.
+		 */
+		if(ptr != NULL) {
+			*ptr = '\0';
+			g_strv_builder_add(builder, str);
+
+			/* Move str to the next character as we know there's another
+			 * character which might be another null terminator.
+			 */
+			str = ptr + 1;
+
+			/* And don't forget to increment the count... ah ah ah! */
+			count++;
+		} else {
+			/* Add the remaining string. */
+			g_strv_builder_add(builder, str);
+
+			/* Give the count another one, ah ah ah! */
+			count++;
+
+			/* Finally break out of the loop. */
+			break;
+		}
+	}
+
+	return count;
+}
+
+static GStrv
+ibis_message_build_params(const char *middle, const char *coda,
+                          const char *trailing, guint *n_params)
+{
+	GStrvBuilder *builder = g_strv_builder_new();
+	GStrv result = NULL;
+
+	ibis_message_extract_params(builder, middle);
+
+	if(*coda != '\0') {
+		g_strv_builder_add(builder, trailing);
+	}
+
+	result = g_strv_builder_end(builder);
+
+	g_strv_builder_unref(builder);
+
+	if(result != NULL && n_params != NULL) {
+		*n_params = g_strv_length(result);
+	}
+
+	return result;
+}
+
 /******************************************************************************
  * GObject Implementation
  *****************************************************************************/
@@ -443,3 +506,98 @@
 
 	g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_ERROR]);
 }
+
+IbisMessage *
+ibis_message_parse(const char *buffer, GError **error) {
+	IbisMessage *message = NULL;
+	IbisTags *tags = NULL;
+	GError *local_error = NULL;
+	GMatchInfo *info = NULL;
+	GRegex *regex = NULL;
+	GStrv params = NULL;
+	char *coda = NULL;
+	char *command = NULL;
+	char *middle = NULL;
+	char *source = NULL;
+	char *tags_string = NULL;
+	char *trailing = NULL;
+
+	g_return_val_if_fail(buffer != NULL, FALSE);
+
+	regex =  g_regex_new("(?:@(?<tags>[^ ]+) )?"
+	                     "(?::(?<source>[^ ]+) +)?"
+	                     "(?<command>[^ :]+)"
+	                     "(?: +(?<middle>(?:[^ :]+(?: +[^ :]+)*)))*"
+	                     "(?<coda> +:(?<trailing>.*)?)?",
+	                     0, 0, NULL);
+
+	/* Check if the buffer matches our regex for messages. */
+	if(!g_regex_match(regex, buffer, 0, &info)) {
+		g_match_info_unref(info);
+
+		g_set_error(error, IBIS_MESSAGE_PARSE_ERROR, 0,
+		            "failed to parser buffer '%s'", buffer);
+
+		g_clear_pointer(&regex, g_regex_unref);
+
+		return NULL;
+	}
+
+	/* Extract the command from the buffer, so we can find the handler. */
+	command = g_match_info_fetch_named(info, "command");
+
+	/* If we made it this far, we have our handler, so lets get the rest of the
+	 * parameters and call the handler.
+	 */
+	message = ibis_message_new(command);
+
+	tags_string = g_match_info_fetch_named(info, "tags");
+	if(!ibis_str_is_empty(tags_string)) {
+		tags = ibis_tags_parse(tags_string, &local_error);
+		if(local_error != NULL) {
+			g_propagate_error(error, local_error);
+
+			g_free(tags_string);
+			g_free(command);
+			g_clear_object(&tags);
+			g_match_info_unref(info);
+
+			g_clear_pointer(&regex, g_regex_unref);
+
+			return NULL;
+		}
+	}
+	g_free(tags_string);
+
+	if(tags != NULL) {
+		ibis_message_set_tags(message, tags);
+		g_clear_object(&tags);
+	}
+
+	source = g_match_info_fetch_named(info, "source");
+	if(!ibis_str_is_empty(source)) {
+		ibis_message_set_source(message, source);
+	}
+	g_free(source);
+
+	middle = g_match_info_fetch_named(info, "middle");
+	coda = g_match_info_fetch_named(info, "coda");
+	trailing = g_match_info_fetch_named(info, "trailing");
+	params = ibis_message_build_params(middle, coda, trailing, NULL);
+	if(params != NULL) {
+		ibis_message_set_paramsv(message, params);
+	}
+
+	/* Clean up everything */
+	g_free(command);
+	g_free(middle);
+	g_free(coda);
+	g_free(trailing);
+	g_strfreev(params);
+
+	/* Cleanup the left overs. */
+	g_match_info_unref(info);
+	g_clear_pointer(&regex, g_regex_unref);
+
+	return message;
+}
diff --git a/ibis/ibisparser.h b/ibis/ibisparser.h
deleted file mode 100644
--- a/ibis/ibisparser.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Ibis - IRCv3 Library
- * Copyright (C) 2022-2024 Ibis Developers
- *
- * This library is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This library 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this library; if not, see <https://www.gnu.org/licenses/>.
- */
-
-#if !defined(IBIS_GLOBAL_HEADER_INSIDE) && !defined(IBIS_COMPILATION)
-# error "only <ibis.h> may be included directly"
-#endif
-
-#ifndef IBIS_PARSER_H
-#define IBIS_PARSER_H
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include "ibismessage.h"
-#include "ibisversion.h"
-
-G_BEGIN_DECLS
-
-/**
- * IBIS_PARSER_ERROR:
- *
- * A GError domain for the Ibis parser.
- *
- * Since: 0.1
- */
-#define IBIS_PARSER_ERROR \
-	(g_quark_from_static_string("ibis-parser")) \
-	IBIS_AVAILABLE_MACRO_IN_0_1
-
-#define IBIS_TYPE_PARSER (ibis_parser_get_type())
-
-/**
- * IbisParser:
- *
- * A parser object that processes the raw IRC messages and calls handlers for
- * the commands.
- *
- * Since: 0.1
- */
-IBIS_AVAILABLE_IN_0_1
-G_DECLARE_FINAL_TYPE(IbisParser, ibis_parser, IBIS, PARSER, GObject)
-
-/**
- * ibis_parser_new:
- *
- * Creates a new instance.
- *
- * Returns: (transfer full): The new parser.
- *
- * Since: 0.1
- */
-IBIS_AVAILABLE_IN_0_1
-IbisParser *ibis_parser_new(void);
-
-/**
- * ibis_parser_parse:
- * @parser: The instance.
- * @buffer: The buffer to parse.
- * @error: Return address for a #GError, or %NULL.
- *
- * Parses @buffer with @parser.
- *
- * @buffer should just be a single %NULL terminated string with the `\r\n`
- * already removed.
- *
- * If an error is encountered @error will be set and %NULL will be returned.
- *
- * Returns: (transfer full) (nullable): The parsed message or %NULL if an error
- *          occurred.
- *
- * Since: 0.1
- */
-IBIS_AVAILABLE_IN_0_1
-IbisMessage *ibis_parser_parse(IbisParser *parser, const gchar *buffer, GError **error);
-
-G_END_DECLS
-
-#endif /* IBIS_PARSER_H */
diff --git a/ibis/ibisparser.c b/ibis/ibisparser.c
deleted file mode 100644
--- a/ibis/ibisparser.c
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Ibis - IRCv3 Library
- * Copyright (C) 2022-2024 Ibis Developers
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This library is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This library 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this library; if not, see <https://www.gnu.org/licenses/>.
- */
-
-#include "ibisparser.h"
-
-#include "ibiscore.h"
-#include "ibismessage.h"
-#include "ibisstring.h"
-#include "ibistags.h"
-
-struct _IbisParser {
-	GObject parent;
-
-	GRegex *regex_message;
-};
-
-G_DEFINE_FINAL_TYPE(IbisParser, ibis_parser, G_TYPE_OBJECT)
-
-/******************************************************************************
- * Helpers
- *****************************************************************************/
-static guint
-ibis_parser_extract_params(G_GNUC_UNUSED IbisParser *parser,
-                           GStrvBuilder *builder, const char *str)
-{
-	char *ptr = NULL;
-	guint count = 0;
-
-	/* Loop through str finding each space separated string. */
-	while(str != NULL && *str != '\0') {
-		/* Look for a space. */
-		ptr = strchr(str, ' ');
-
-		/* If we found one, set it to null terminator and add the string to our
-		 * builder.
-		 */
-		if(ptr != NULL) {
-			*ptr = '\0';
-			g_strv_builder_add(builder, str);
-
-			/* Move str to the next character as we know there's another
-			 * character which might be another null terminator.
-			 */
-			str = ptr + 1;
-
-			/* And don't forget to increment the count... ah ah ah! */
-			count++;
-		} else {
-			/* Add the remaining string. */
-			g_strv_builder_add(builder, str);
-
-			/* Give the count another one, ah ah ah! */
-			count++;
-
-			/* Finally break out of the loop. */
-			break;
-		}
-	}
-
-	return count;
-}
-
-static GStrv
-ibis_parser_build_params(IbisParser *parser, const char *middle,
-                         const char *coda, const char *trailing,
-                         guint *n_params)
-{
-	GStrvBuilder *builder = g_strv_builder_new();
-	GStrv result = NULL;
-
-	ibis_parser_extract_params(parser, builder, middle);
-
-	if(*coda != '\0') {
-		g_strv_builder_add(builder, trailing);
-	}
-
-	result = g_strv_builder_end(builder);
-
-	g_strv_builder_unref(builder);
-
-	if(result != NULL && n_params != NULL) {
-		*n_params = g_strv_length(result);
-	}
-
-	return result;
-}
-
-/******************************************************************************
- * GObject Implementation
- *****************************************************************************/
-static void
-ibis_parser_finalize(GObject *obj) {
-	IbisParser *parser = IBIS_PARSER(obj);
-
-	g_clear_pointer(&parser->regex_message, g_regex_unref);
-
-	G_OBJECT_CLASS(ibis_parser_parent_class)->finalize(obj);
-}
-
-static void
-ibis_parser_init(IbisParser *parser) {
-	parser->regex_message =  g_regex_new("(?:@(?<tags>[^ ]+) )?"
-	                                     "(?::(?<source>[^ ]+) +)?"
-	                                     "(?<command>[^ :]+)"
-	                                     "(?: +(?<middle>(?:[^ :]+(?: +[^ :]+)*)))*"
-	                                     "(?<coda> +:(?<trailing>.*)?)?",
-	                                     0, 0, NULL);
-	g_assert(parser->regex_message != NULL);
-}
-
-static void
-ibis_parser_class_init(IbisParserClass *klass) {
-	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
-
-	obj_class->finalize = ibis_parser_finalize;
-}
-
-/******************************************************************************
- * Public API
- *****************************************************************************/
-IbisParser *
-ibis_parser_new(void) {
-	return g_object_new(IBIS_TYPE_PARSER, NULL);
-}
-
-IbisMessage *
-ibis_parser_parse(IbisParser *parser, const char *buffer, GError **error) {
-	IbisMessage *message = NULL;
-	IbisTags *tags = NULL;
-	GError *local_error = NULL;
-	GMatchInfo *info = NULL;
-	GStrv params = NULL;
-	char *coda = NULL;
-	char *command = NULL;
-	char *middle = NULL;
-	char *source = NULL;
-	char *tags_string = NULL;
-	char *trailing = NULL;
-	gboolean matches = FALSE;
-
-	g_return_val_if_fail(IBIS_IS_PARSER(parser), FALSE);
-	g_return_val_if_fail(buffer != NULL, FALSE);
-
-	/* Check if the buffer matches our regex for messages. */
-	matches = g_regex_match(parser->regex_message, buffer, 0, &info);
-	if(!matches) {
-		g_match_info_unref(info);
-
-		g_set_error(error, IBIS_PARSER_ERROR, 0,
-		            "failed to parser buffer '%s'", buffer);
-
-		return NULL;
-	}
-
-	/* Extract the command from the buffer, so we can find the handler. */
-	command = g_match_info_fetch_named(info, "command");
-
-	/* If we made it this far, we have our handler, so lets get the rest of the
-	 * parameters and call the handler.
-	 */
-	message = ibis_message_new(command);
-
-	tags_string = g_match_info_fetch_named(info, "tags");
-	if(!ibis_str_is_empty(tags_string)) {
-		tags = ibis_tags_parse(tags_string, &local_error);
-		if(local_error != NULL) {
-			g_propagate_error(error, local_error);
-
-			g_free(tags_string);
-			g_free(command);
-			g_clear_object(&tags);
-			g_match_info_unref(info);
-
-			return NULL;
-		}
-	}
-	g_free(tags_string);
-
-	if(tags != NULL) {
-		ibis_message_set_tags(message, tags);
-		g_clear_object(&tags);
-	}
-
-	source = g_match_info_fetch_named(info, "source");
-	if(!ibis_str_is_empty(source)) {
-		ibis_message_set_source(message, source);
-	}
-	g_free(source);
-
-	middle = g_match_info_fetch_named(info, "middle");
-	coda = g_match_info_fetch_named(info, "coda");
-	trailing = g_match_info_fetch_named(info, "trailing");
-	params = ibis_parser_build_params(parser, middle, coda, trailing,
-	                                          NULL);
-	if(params != NULL) {
-		ibis_message_set_paramsv(message, params);
-	}
-
-	/* Clean up everything */
-	g_free(command);
-	g_free(middle);
-	g_free(coda);
-	g_free(trailing);
-	g_strfreev(params);
-
-	/* Cleanup the left overs. */
-	g_match_info_unref(info);
-
-	return message;
-}
diff --git a/ibis/meson.build b/ibis/meson.build
--- a/ibis/meson.build
+++ b/ibis/meson.build
@@ -3,7 +3,6 @@
   'ibisclient.c',
   'ibisformatting.c',
   'ibismessage.c',
-  'ibisparser.c',
   'ibissource.c',
   'ibistags.c',
   'ibisversion.c',
@@ -16,7 +15,6 @@
   'ibiscore.h',
   'ibisformatting.h',
   'ibismessage.h',
-  'ibisparser.h',
   'ibissource.h',
   'ibisstring.h',
   'ibistags.h',
diff --git a/ibis/tests/test_parser.c b/ibis/tests/test_parser.c
--- a/ibis/tests/test_parser.c
+++ b/ibis/tests/test_parser.c
@@ -36,16 +36,13 @@
 static void
 test_ibis_parser(const gchar *raw, TestIbisParserData *d) {
 	IbisMessage *message = NULL;
-	IbisParser *parser = NULL;
 	GError *error = NULL;
 	GStrv params = NULL;
 	const char *command = NULL;
 	const char *source = NULL;
 	guint n_params = 0;
 
-	parser = ibis_parser_new();
-
-	message = ibis_parser_parse(parser, raw, &error);
+	message = ibis_message_parse(raw, &error);
 	g_assert_no_error(error);
 	g_assert_true(IBIS_IS_MESSAGE(message));
 
@@ -82,7 +79,6 @@
 	g_clear_object(&d->tags);
 
 	g_assert_finalize_object(message);
-	g_assert_finalize_object(parser);
 }
 
 /******************************************************************************
@@ -91,18 +87,13 @@
 static void
 test_ibis_parser_error(void) {
 	IbisMessage *message = NULL;
-	IbisParser *parser = NULL;
 	GError *error = NULL;
 
-	parser = ibis_parser_new();
-
-	message = ibis_parser_parse(parser, "", &error);
-	g_assert_error(error, IBIS_PARSER_ERROR, 0);
+	message = ibis_message_parse("", &error);
+	g_assert_error(error, IBIS_MESSAGE_PARSE_ERROR, 0);
 	g_clear_error(&error);
 
 	g_assert_null(message);
-
-	g_assert_finalize_object(parser);
 }
 
 static void
diff --git a/po/POTFILES b/po/POTFILES
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -2,6 +2,5 @@
 ibis/ibisclient.c
 ibis/ibisformatting.c
 ibis/ibismessage.c
-ibis/ibisparser.c
 ibis/ibissource.c
 ibis/ibisversion.c
