diff --git a/protocols/ircv3/purpleircv3connection.h b/protocols/ircv3/purpleircv3connection.h
--- a/protocols/ircv3/purpleircv3connection.h
+++ b/protocols/ircv3/purpleircv3connection.h
@@ -144,12 +144,12 @@
 /**
  * purple_ircv3_connection_find_or_create_contact:
  * @connection: The instance.
- * @nick: The nickname of the user.
+ * @message: The message to get the contact for.
  *
- * Looks for an existing contact belonging to @connection and returns it if
- * found. If not a new contact will be created.
+ * Looks for an existing contact from @message belonging to @connection and
+ * returns it if found. If not a new contact will be created.
  *
- * This will only ever return %NULL if @connection is invalid or @nick is
+ * This will only ever return %NULL if @connection is invalid or @message is
  * %NULL.
  *
  * Note that the ownership of the contact remains with the default
@@ -160,7 +160,7 @@
  * Since: 3.0
  */
 PURPLE_IRCV3_AVAILABLE_IN_ALL
-PurpleContact *purple_ircv3_connection_find_or_create_contact(PurpleIRCv3Connection *connection, const char *nick);
+PurpleContact *purple_ircv3_connection_find_or_create_contact(PurpleIRCv3Connection *connection, IbisMessage *message);
 
 G_END_DECLS
 
# HG changeset patch
# Node ID a089c8619257130ea1f11227ecd01de32a516a20
# Parent  59e6529a1e788166a4c6f31ad31885fd841e13ae
diff --git a/protocols/ircv3/purpleircv3connection.c b/protocols/ircv3/purpleircv3connection.c
--- a/protocols/ircv3/purpleircv3connection.c
+++ b/protocols/ircv3/purpleircv3connection.c
@@ -778,16 +778,38 @@
 
 PurpleContact *
 purple_ircv3_connection_find_or_create_contact(PurpleIRCv3Connection *connection,
-                                               const char *nick)
+                                               IbisMessage *message)
 {
 	PurpleAccount *account = NULL;
 	PurpleContact *contact = NULL;
 	PurpleContactManager *manager = NULL;
+	IbisTags *tags = NULL;
+	const char *source = NULL;
+	char *nick = NULL;
 
 	account = purple_connection_get_account(PURPLE_CONNECTION(connection));
 	manager = purple_contact_manager_get_default();
-	contact = purple_contact_manager_find_with_id(manager, account, nick);
+
+	tags = ibis_message_get_tags(message);
+	if(IBIS_IS_TAGS(tags)) {
+		const char *account_tag = NULL;
 
+		account_tag = ibis_tags_lookup(tags, IBIS_TAG_ACCOUNT);
+		if(!purple_strempty(account_tag)) {
+			contact = purple_contact_manager_find_with_id(manager, account,
+			                                              account_tag);
+		}
+	}
+
+	source = ibis_message_get_source(message);
+	ibis_source_parse(source, &nick, NULL, NULL);
+
+	/* If we don't have a contact yet, use the source (Luke) to search next. */
+	if(!PURPLE_IS_CONTACT(contact)) {
+		contact = purple_contact_manager_find_with_id(manager, account, nick);
+	}
+
+	/* If we _still_ don't have a contact, create it. */
 	if(!PURPLE_IS_CONTACT(contact)) {
 		contact = purple_contact_new(account, nick);
 		purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact), nick);
@@ -795,5 +817,9 @@
 		purple_contact_manager_add(manager, contact);
 	}
 
+	purple_contact_info_set_sid(PURPLE_CONTACT_INFO(contact), source);
+
+	g_free(nick);
+
 	return contact;
 }
diff --git a/protocols/ircv3/purpleircv3messagehandlers.c b/protocols/ircv3/purpleircv3messagehandlers.c
--- a/protocols/ircv3/purpleircv3messagehandlers.c
+++ b/protocols/ircv3/purpleircv3messagehandlers.c
@@ -60,27 +60,10 @@
 	PurpleConversation *conversation = NULL;
 	GStrv params = NULL;
 	guint n_params = 0;
-	char *nick = NULL;
-	const char *source = NULL;
 	const char *conversation_name = NULL;
 
-	source = ibis_message_get_source(message);
-	ibis_source_parse(source, &nick, NULL, NULL);
-	if(purple_strempty(nick)) {
-		/* We _shouldn't_ be able to get an empty nick, but just in case... */
-		g_clear_pointer(&nick, g_free);
-
-		/* TODO: write message about being unable to parse source to status
-		 * window.
-		 */
-
-		return TRUE;
-	}
-
-	contact = purple_ircv3_connection_find_or_create_contact(connection, nick);
-	purple_contact_info_set_sid(PURPLE_CONTACT_INFO(contact), source);
-
-	g_free(nick);
+	contact = purple_ircv3_connection_find_or_create_contact(connection,
+	                                                         message);
 
 	params = ibis_message_get_params(message);
 	n_params = g_strv_length(params);
@@ -117,9 +100,7 @@
 	GStrv params = NULL;
 	guint n_params = 0;
 	char *reason = NULL;
-	char *nick = NULL;
 	const char *conversation_name = NULL;
-	const char *source = NULL;
 
 	params = ibis_message_get_params(message);
 	if(g_strv_length(params) == 0) {
@@ -143,24 +124,11 @@
 		return TRUE;
 	}
 
-	source = ibis_message_get_source(message);
-	ibis_source_parse(source, &nick, NULL, NULL);
-	if(purple_strempty(nick)) {
-		/* We _shouldn't_ be able to get an empty nick, but just in case... */
-		g_clear_pointer(&nick, g_free);
-
-		/* TODO: write message about being unable to parse source to status
-		 * window.
-		 */
-
-		return TRUE;
-	}
-
 	/* We do want to find or create the contact, even on a part, because we
 	 * could have connected to a BNC and we weren't told about the contact yet.
 	 */
-	contact = purple_ircv3_connection_find_or_create_contact(connection, nick);
-	g_free(nick);
+	contact = purple_ircv3_connection_find_or_create_contact(connection,
+	                                                         message);
 
 	/* If a part message was given, join the remaining parameters with a space.
 	 */
@@ -186,19 +154,13 @@
 	PurpleContact *contact = NULL;
 	PurpleConversation *conversation = NULL;
 	PurpleMessage *message = NULL;
-	PurpleMessageFlags flags = PURPLE_MESSAGE_RECV;
 	GDateTime *dt = NULL;
 	IbisTags *tags = NULL;
 	GStrv params = NULL;
-	char *nick = NULL;
 	char *stripped = NULL;
-	const char *id = NULL;
-	const char *raw_tag = NULL;
-	const char *source = NULL;
 	const char *target = NULL;
 
 	params = ibis_message_get_params(ibis_message);
-	source = ibis_message_get_source(ibis_message);
 	tags = ibis_message_get_tags(ibis_message);
 
 	if(params == NULL) {
@@ -215,30 +177,36 @@
 		return FALSE;
 	}
 
-	ibis_source_parse(source, &nick, NULL, NULL);
+	/* Find or create the contact. */
+	contact = purple_ircv3_connection_find_or_create_contact(connection,
+	                                                         ibis_message);
 
 	/* Find or create the conversation. */
 	target = params[0];
 	if(!purple_ircv3_connection_is_channel(connection, target)) {
-		target = nick;
+		target = purple_contact_info_get_id(PURPLE_CONTACT_INFO(contact));
 	}
+
 	conversation = purple_ircv3_connection_find_or_create_conversation(connection,
 	                                                                   target);
-	/* Find or create the contact. */
-	contact = purple_ircv3_connection_find_or_create_contact(connection, nick);
-	purple_contact_info_set_sid(PURPLE_CONTACT_INFO(contact), source);
 
 	purple_ircv3_add_contact_to_conversation(contact, conversation);
 
+	stripped = ibis_formatting_strip(params[1]);
+	message = purple_message_new(PURPLE_CONTACT_INFO(contact), stripped);
+	g_clear_pointer(&stripped, g_free);
+
 	if(purple_strequal(command, IBIS_MSG_NOTICE)) {
-		flags |= PURPLE_MESSAGE_NOTIFY;
+		purple_message_set_notice(message, TRUE);
 	}
 
 	if(IBIS_IS_TAGS(tags)) {
+		const char *raw_tag = NULL;
+
 		/* Grab the msgid if one was provided. */
 		raw_tag = ibis_tags_lookup(tags, "msgid");
 		if(!purple_strempty(raw_tag)) {
-			id = raw_tag;
+			purple_message_set_id(message, raw_tag);
 		}
 
 		/* Determine the timestamp of the message. */
@@ -247,29 +215,18 @@
 			GTimeZone *tz = g_time_zone_new_utc();
 
 			dt = g_date_time_new_from_iso8601(raw_tag, tz);
+			g_time_zone_unref(tz);
 
-			g_time_zone_unref(tz);
+			purple_message_set_timestamp(message, dt);
+			g_date_time_unref(dt);
 		}
 	}
 
 	/* If the server didn't provide a time, use the current local time. */
 	if(dt == NULL) {
-		dt = g_date_time_new_now_local();
+		purple_message_set_timestamp_now(message);
 	}
 
-	stripped = ibis_formatting_strip(params[1]);
-	message = g_object_new(
-		PURPLE_TYPE_MESSAGE,
-		"author", source,
-		"contents", stripped,
-		"flags", flags,
-		"id", id,
-		"timestamp", dt,
-		NULL);
-	g_free(stripped);
-
-	g_date_time_unref(dt);
-
 	/* Check if this is a CTCP message. */
 	if(ibis_message_get_ctcp(ibis_message)) {
 		/* TODO: later... */
@@ -277,7 +234,6 @@
 
 	purple_conversation_write_message(conversation, message);
 
-	g_clear_pointer(&nick, g_free);
 	g_clear_object(&message);
 
 	return TRUE;
