# HG changeset patch
# Node ID ee939170d2147a748c859f9c839cdb432e2b3b5f
# Parent  757575456cc20cfb510fb14008ce99a03ea4d3b1
diff --git a/libpurple/purplecommands.c b/libpurple/purplecommands.c
--- a/libpurple/purplecommands.c
+++ b/libpurple/purplecommands.c
@@ -34,6 +34,8 @@
 purple_message_from_command(PurpleConversation *conversation, GStrv params) {
 	PurpleAccount *account = NULL;
 	PurpleContactInfo *info = NULL;
+	PurpleConversationMember *member = NULL;
+	PurpleConversationMembers *members = NULL;
 	PurpleMessage *message = NULL;
 	char *body = NULL;
 
@@ -42,10 +44,13 @@
 		return NULL;
 	}
 
+	members = purple_conversation_get_members(conversation);
+
 	info = purple_account_get_contact_info(account);
+	member = purple_conversation_members_find_member(members, info);
 
 	body = g_strjoinv(" ", params);
-	message = purple_message_new(info, body);
+	message = purple_message_new(member, body);
 	g_free(body);
 
 	return message;
diff --git a/libpurple/purpleconversation.c b/libpurple/purpleconversation.c
--- a/libpurple/purpleconversation.c
+++ b/libpurple/purpleconversation.c
@@ -1482,7 +1482,7 @@
 purple_conversation_write_message(PurpleConversation *conversation,
                                   PurpleMessage *message)
 {
-	PurpleContactInfo *info = NULL;
+	PurpleConversationMember *member = NULL;
 
 	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
 	g_return_if_fail(message != NULL);
@@ -1507,19 +1507,10 @@
 		}
 	}
 
-	info = purple_message_get_author(message);
-	if(PURPLE_IS_CONTACT_INFO(info)) {
-		PurpleConversationMember *member = NULL;
-		PurpleConversationMembers *members = NULL;
-
-		members = purple_conversation_get_members(conversation);
-		member = purple_conversation_members_find_member(members, info);
-		if(PURPLE_IS_CONVERSATION_MEMBER(member)) {
-			purple_conversation_member_set_typing_state(member,
-			                                            PURPLE_TYPING_STATE_NONE,
-			                                            0);
-		}
-	}
+	member = purple_message_get_author(message);
+	purple_conversation_member_set_typing_state(member,
+	                                            PURPLE_TYPING_STATE_NONE,
+	                                            0);
 
 	g_list_store_append(conversation->messages, message);
 }
@@ -2091,3 +2082,25 @@
 
 	g_object_notify_by_pspec(G_OBJECT(conversation), properties[PROP_ERROR]);
 }
+
+PurpleConversationMember *
+purple_conversation_find_or_add_member(PurpleConversation *conversation,
+                                       PurpleContactInfo *info,
+                                       gboolean announce,
+                                       const char *message)
+{
+	PurpleConversationMember *member = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), NULL);
+	g_return_val_if_fail(PURPLE_IS_CONTACT_INFO(info), NULL);
+
+	member = purple_conversation_members_find_member(conversation->members,
+	                                                 info);
+	if(!PURPLE_IS_CONVERSATION_MEMBER(member)) {
+		member = purple_conversation_members_add_member(conversation->members,
+		                                                info, announce,
+		                                                message);
+	}
+
+	return member;
+}
diff --git a/libpurple/purpleconversation.h b/libpurple/purpleconversation.h
--- a/libpurple/purpleconversation.h
+++ b/libpurple/purpleconversation.h
@@ -944,6 +944,26 @@
 PURPLE_AVAILABLE_IN_3_0
 void purple_conversation_set_error(PurpleConversation *conversation, GError *error);
 
+/**
+ * purple_conversation_find_or_add_member:
+ * @info: the contact info to find or add
+ * @announce: if true the user interface should tell the user about this member
+ *            being added
+ * @message: the message to use if announcing
+ *
+ * Finds or adds a member to a conversation.
+ *
+ * If @info is not a member of the conversation,
+ * [method@ConversationMembers.add_member] will be called with the given
+ * parameters.
+ *
+ * Returns: (transfer none): The conversation member.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+PurpleConversationMember *purple_conversation_find_or_add_member(PurpleConversation *conversation, PurpleContactInfo *info, gboolean announce, const char *message);
+
 G_END_DECLS
 
 #endif /* PURPLE_CONVERSATION_H */
diff --git a/libpurple/purplemessage.c b/libpurple/purplemessage.c
--- a/libpurple/purplemessage.c
+++ b/libpurple/purplemessage.c
@@ -30,7 +30,7 @@
 	GObject parent;
 
 	char *id;
-	PurpleContactInfo *author;
+	PurpleConversationMember *author;
 
 	char *author_name_color;
 
@@ -77,7 +77,9 @@
  * Helpers
  *****************************************************************************/
 static void
-purple_message_set_author(PurpleMessage *message, PurpleContactInfo *author) {
+purple_message_set_author(PurpleMessage *message,
+                          PurpleConversationMember *author)
+{
 	if(g_set_object(&message->author, author)) {
 		g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_AUTHOR]);
 	}
@@ -258,7 +260,7 @@
 	 */
 	properties[PROP_AUTHOR] = g_param_spec_object(
 		"author", NULL, NULL,
-		PURPLE_TYPE_CONTACT_INFO,
+		PURPLE_TYPE_CONVERSATION_MEMBER,
 		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 
 	/**
@@ -431,8 +433,8 @@
  * Public API
  *****************************************************************************/
 PurpleMessage *
-purple_message_new(PurpleContactInfo *author, const char *contents) {
-	g_return_val_if_fail(PURPLE_IS_CONTACT_INFO(author), NULL);
+purple_message_new(PurpleConversationMember *author, const char *contents) {
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION_MEMBER(author), NULL);
 	g_return_val_if_fail(contents != NULL, NULL);
 
 	return g_object_new(
@@ -460,7 +462,7 @@
 	}
 }
 
-PurpleContactInfo *
+PurpleConversationMember *
 purple_message_get_author(PurpleMessage *message) {
 	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), NULL);
 
diff --git a/libpurple/purplemessage.h b/libpurple/purplemessage.h
--- a/libpurple/purplemessage.h
+++ b/libpurple/purplemessage.h
@@ -30,7 +30,7 @@
 #include <glib-object.h>
 
 #include "purpleattachment.h"
-#include "purplecontactinfo.h"
+#include "purpleconversationmember.h"
 #include "purpleversion.h"
 
 G_BEGIN_DECLS
@@ -59,7 +59,7 @@
  * Since: 3.0
  */
 PURPLE_AVAILABLE_IN_3_0
-PurpleMessage *purple_message_new(PurpleContactInfo *author, const char *contents);
+PurpleMessage *purple_message_new(PurpleConversationMember *author, const char *contents);
 
 /**
  * purple_message_get_action:
@@ -99,7 +99,7 @@
  * Returns: (transfer none): The author of @message.
  */
 PURPLE_AVAILABLE_IN_3_0
-PurpleContactInfo *purple_message_get_author(PurpleMessage *message);
+PurpleConversationMember *purple_message_get_author(PurpleMessage *message);
 
 /**
  * purple_message_get_author_name_color:
diff --git a/libpurple/purplesqlitehistoryadapter.c b/libpurple/purplesqlitehistoryadapter.c
--- a/libpurple/purplesqlitehistoryadapter.c
+++ b/libpurple/purplesqlitehistoryadapter.c
@@ -27,6 +27,10 @@
 #include "purplesqlitehistoryadapter.h"
 
 #include "purpleaccount.h"
+#include "purpleaccountmanager.h"
+#include "purpleconversationmanager.h"
+#include "purpleconversationmember.h"
+#include "purpleconversationmembers.h"
 #include "purplesqlite3.h"
 
 struct _PurpleSqliteHistoryAdapter {
@@ -128,8 +132,9 @@
 		}
 	} else {
 		query = g_string_new("SELECT "
-		                     "message_id, author, author_name_color, "
-		                     "author_alias, content, client_timestamp "
+		                     "account, conversation_id, message_id, author, "
+		                     "author_name_color, author_alias, content, "
+		                     "client_timestamp "
 		                     "FROM message_log WHERE TRUE\n");
 	}
 
@@ -269,8 +274,10 @@
 
 static GList*
 purple_sqlite_history_adapter_query(PurpleHistoryAdapter *adapter,
-                                    const gchar *query, GError **error)
+                                    const char *query, GError **error)
 {
+	PurpleAccountManager *account_manager = NULL;
+	PurpleConversationManager *conversation_manager = NULL;
 	PurpleSqliteHistoryAdapter *sqlite_adapter = NULL;
 	sqlite3_stmt *prepared_statement = NULL;
 	GList *results = NULL;
@@ -293,32 +300,72 @@
 		return NULL;
 	}
 
+	account_manager = purple_account_manager_get_default();
+	conversation_manager = purple_conversation_manager_get_default();
+
 	while(sqlite3_step(prepared_statement) == SQLITE_ROW) {
+		PurpleAccount *account = NULL;
 		PurpleContactInfo *info = NULL;
+		PurpleConversation *conversation = NULL;
+		PurpleConversationMember *member = NULL;
 		PurpleMessage *message = NULL;
 		GDateTime *g_date_time = NULL;
-		const gchar *message_id = NULL;
-		const gchar *author = NULL;
-		const gchar *author_name_color = NULL;
-		const gchar *author_alias = NULL;
-		const gchar *content = NULL;
-		const gchar *timestamp = NULL;
+		const char *account_id = NULL;
+		const char *author = NULL;
+		const char *author_name_color = NULL;
+		const char *author_alias = NULL;
+		const char *content = NULL;
+		const char *conversation_id = NULL;
+		const char *message_id = NULL;
+		const char *timestamp = NULL;
 
-		message_id = (const gchar *)sqlite3_column_text(prepared_statement, 0);
-		author = (const gchar *)sqlite3_column_text(prepared_statement, 1);
-		author_name_color = (const gchar *)sqlite3_column_text(prepared_statement, 2);
-		author_alias = (const gchar *)sqlite3_column_text(prepared_statement, 3);
-		content = (const gchar *)sqlite3_column_text(prepared_statement, 4);
-		timestamp = (const gchar *)sqlite3_column_text(prepared_statement, 5);
+		account_id = (const char *)sqlite3_column_text(prepared_statement, 0);
+		conversation_id = (const char *)sqlite3_column_text(prepared_statement, 1);
+		message_id = (const char *)sqlite3_column_text(prepared_statement, 2);
+		author = (const char *)sqlite3_column_text(prepared_statement, 3);
+		author_name_color = (const char *)sqlite3_column_text(prepared_statement, 4);
+		author_alias = (const char *)sqlite3_column_text(prepared_statement, 5);
+		content = (const char *)sqlite3_column_text(prepared_statement, 6);
+		timestamp = (const char *)sqlite3_column_text(prepared_statement, 7);
 		g_date_time = g_date_time_new_from_iso8601(timestamp, NULL);
 
 		/* This is gross but necessary until we finish rethinking history.
 		 * -- gk 2024-08-21
+		 * -- gk 2024-12-02
 		 */
+
+		account = purple_account_manager_find_by_id(account_manager,
+		                                            account_id);
+		if(!PURPLE_IS_ACCOUNT(account)) {
+			account = g_object_new(
+				PURPLE_TYPE_ACCOUNT,
+				"id", account_id,
+				NULL);
+			purple_account_manager_add(account_manager, account);
+			g_object_unref(account);
+		}
+
+		conversation = purple_conversation_manager_find_with_id(conversation_manager,
+		                                                        account,
+		                                                        conversation_id);
+		if(!PURPLE_IS_CONVERSATION(conversation)) {
+			conversation = g_object_new(
+				PURPLE_TYPE_CONVERSATION,
+				"id", conversation_id,
+				NULL);
+			purple_conversation_manager_add(conversation_manager, conversation);
+
+			/* The manager holds a reference which we will use for now. */
+			g_object_unref(conversation);
+		}
+
 		info = purple_contact_info_new(author);
 		purple_contact_info_set_alias(info, author_alias);
 
-		message = purple_message_new(info, content);
+		member = purple_conversation_find_or_add_member(conversation, info,
+		                                                FALSE, NULL);
+
+		message = purple_message_new(member, content);
 		purple_message_set_id(message, message_id);
 		purple_message_set_author_name_color(message, author_name_color);
 		purple_message_set_timestamp(message, g_date_time);
@@ -384,14 +431,15 @@
                                     PurpleMessage *message, GError **error)
 {
 	PurpleAccount *account = NULL;
-	PurpleContactInfo *author = NULL;
+	PurpleContactInfo *info = NULL;
+	PurpleConversationMember *author = NULL;
 	PurpleProtocol *protocol = NULL;
 	PurpleSqliteHistoryAdapter *sqlite_adapter = NULL;
 	sqlite3_stmt *prepared_statement = NULL;
 	gchar *timestamp = NULL;
-	const gchar * message_id = NULL;
-	const gchar *script = NULL;
-	gint result = 0;
+	const char * message_id = NULL;
+	const char *script = NULL;
+	int result = 0;
 
 	script = "INSERT INTO message_log(protocol, account, conversation_id, "
 			 "message_id, author, author_name_color, author_alias, "
@@ -419,6 +467,7 @@
 	account = purple_conversation_get_account(conversation);
 	protocol = purple_account_get_protocol(account);
 	author = purple_message_get_author(message);
+	info = purple_conversation_member_get_contact_info(author);
 
 	sqlite3_bind_text(prepared_statement,
 	                  1, purple_protocol_get_name(protocol), -1,
@@ -438,13 +487,13 @@
 		                  g_free);
 	}
 	sqlite3_bind_text(prepared_statement,
-	                  5, purple_contact_info_get_id(author), -1,
+	                  5, purple_contact_info_get_id(info), -1,
 	                  SQLITE_STATIC);
 	sqlite3_bind_text(prepared_statement,
 	                  6, purple_message_get_author_name_color(message), -1,
 	                  SQLITE_STATIC);
 	sqlite3_bind_text(prepared_statement,
-	                  7, purple_contact_info_get_alias(author), -1,
+	                  7, purple_conversation_member_get_name_for_display(author), -1,
 	                  SQLITE_STATIC);
 	sqlite3_bind_text(prepared_statement,
 	                  8, purple_message_get_contents(message), -1,
diff --git a/libpurple/tests/test_conversation.c b/libpurple/tests/test_conversation.c
--- a/libpurple/tests/test_conversation.c
+++ b/libpurple/tests/test_conversation.c
@@ -433,6 +433,7 @@
 	PurpleAccount *account = NULL;
 	PurpleContactInfo *info = NULL;
 	PurpleConversation *conversation = NULL;
+	PurpleConversationMember *author = NULL;
 	PurpleMessage *message = NULL;
 	GListModel *messages = NULL;
 
@@ -442,12 +443,15 @@
 		"account", account,
 		NULL);
 
+	info = purple_account_get_contact_info(account);
+	author = purple_conversation_find_or_add_member(conversation, info, FALSE,
+	                                                NULL);
+
 	messages = purple_conversation_get_messages(conversation);
 	g_assert_nonnull(messages);
 	g_assert_cmpuint(g_list_model_get_n_items(messages), ==, 0);
 
-	info = purple_account_get_contact_info(account);
-	message = purple_message_new(info, "hello world!");
+	message = purple_message_new(author, "hello world!");
 	purple_conversation_write_message(conversation, message);
 	g_assert_cmpuint(g_list_model_get_n_items(messages), ==, 1);
 
diff --git a/libpurple/tests/test_message.c b/libpurple/tests/test_message.c
--- a/libpurple/tests/test_message.c
+++ b/libpurple/tests/test_message.c
@@ -38,8 +38,11 @@
  *****************************************************************************/
 static void
 test_purple_message_properties(void) {
-	PurpleContactInfo *author = NULL;
-	PurpleContactInfo *author1 = NULL;
+	PurpleContactInfo *info = NULL;
+	PurpleConversation *conversation = NULL;
+	PurpleConversationMember *author = NULL;
+	PurpleConversationMember *author1 = NULL;
+	PurpleConversationMembers *members = NULL;
 	PurpleMessage *message = NULL;
 	GDateTime *timestamp = NULL;
 	GDateTime *timestamp1 = NULL;
@@ -61,8 +64,13 @@
 	error = g_error_new(g_quark_from_static_string("test-message"), 0,
 	                    "delivery failed");
 
-	author = purple_contact_info_new(NULL);
-	purple_contact_info_set_username(author, "author");
+	conversation = g_object_new(PURPLE_TYPE_CONVERSATION, NULL);
+	members = purple_conversation_get_members(conversation);
+
+	info = purple_contact_info_new(NULL);
+	purple_contact_info_set_username(info, "author");
+	author = purple_conversation_members_add_member(members, info, FALSE,
+	                                                NULL);
 
 	/* We don't set delivered-at here because delivered will set it for us so
 	 * it's impossible to check that here, which is why we have separate tests
@@ -138,8 +146,9 @@
 	g_clear_pointer(&timestamp, g_date_time_unref);
 	g_clear_pointer(&timestamp1, g_date_time_unref);
 
+	g_assert_finalize_object(conversation);
 	g_assert_finalize_object(message);
-	g_assert_finalize_object(author);
+	g_assert_finalize_object(info);
 }
 
 static void
diff --git a/libpurple/tests/test_messages.c b/libpurple/tests/test_messages.c
--- a/libpurple/tests/test_messages.c
+++ b/libpurple/tests/test_messages.c
@@ -108,25 +108,29 @@
 static void
 test_purple_messages_add_single(void) {
 	PurpleAccount *account = NULL;
+	PurpleContactInfo *info = NULL;
 	PurpleConversation *conversation = NULL;
+	PurpleConversationMember *author = NULL;
 	PurpleMessage *message1 = NULL;
 	PurpleMessage *message2 = NULL;
 	PurpleMessages *messages = NULL;
 	guint counter = 0;
 
 	account = purple_account_new("test", "test");
+	info = purple_account_get_contact_info(account);
 	conversation = g_object_new(
 		PURPLE_TYPE_CONVERSATION,
 		"account", account,
 		NULL);
+	author = purple_conversation_find_or_add_member(conversation, info, FALSE,
+	                                                NULL);
 
 	messages = purple_messages_new(conversation);
 	g_signal_connect(messages, "items-changed",
 	                 G_CALLBACK(test_purple_messages_items_changed_counter),
 	                 &counter);
 
-	message1 = purple_message_new(purple_account_get_contact_info(account),
-	                              "test message");
+	message1 = purple_message_new(author, "test message");
 	purple_messages_add(messages, message1);
 	g_assert_cmpuint(counter, ==, 1);
 
@@ -144,7 +148,9 @@
 static void
 test_purple_messages_add_multiple(void) {
 	PurpleAccount *account = NULL;
+	PurpleContactInfo *info = NULL;
 	PurpleConversation *conversation = NULL;
+	PurpleConversationMember *author = NULL;
 	PurpleMessage *message = NULL;
 	PurpleMessage *message1 = NULL;
 	PurpleMessage *message2 = NULL;
@@ -160,10 +166,13 @@
 	 */
 
 	account = purple_account_new("test", "test");
+	info = purple_account_get_contact_info(account);
 	conversation = g_object_new(
 		PURPLE_TYPE_CONVERSATION,
 		"account", account,
 		NULL);
+	author = purple_conversation_find_or_add_member(conversation, info, FALSE,
+	                                                NULL);
 
 	messages = purple_messages_new(conversation);
 	g_signal_connect(messages, "items-changed",
@@ -172,16 +181,14 @@
 
 	zone = g_time_zone_new_utc();
 
-	message1 = purple_message_new(purple_account_get_contact_info(account),
-	                              "second message");
+	message1 = purple_message_new(author, "second message");
 	dt1 = g_date_time_new_from_iso8601("2024-08-07T03:07:33+0000", zone);
 	purple_message_set_timestamp(message1, dt1);
 	g_clear_pointer(&dt1, g_date_time_unref);
 	purple_messages_add(messages, message1);
 	g_assert_cmpuint(counter, ==, 1);
 
-	message2 = purple_message_new(purple_account_get_contact_info(account),
-	                              "first message");
+	message2 = purple_message_new(author, "first message");
 	dt2 = g_date_time_new_from_iso8601("2024-08-07T03:06:33+0000", zone);
 	purple_message_set_timestamp(message2, dt2);
 	g_clear_pointer(&dt2, g_date_time_unref);
diff --git a/pidgin/pidginconversation.c b/pidgin/pidginconversation.c
--- a/pidgin/pidginconversation.c
+++ b/pidgin/pidginconversation.c
@@ -127,9 +127,13 @@
 	}
 
 	if(!command_executed) {
+		PurpleConversationMember *author = NULL;
+
 		/* Create the message. */
 		info = purple_account_get_contact_info(account);
-		message = purple_message_new(info, contents);
+		author = purple_conversation_find_or_add_member(conversation->conversation,
+		                                                info, FALSE, NULL);
+		message = purple_message_new(author, contents);
 
 		/* Send the message and clean up. We don't worry about the callback as we
 		 * don't have anything to do in it right now.
diff --git a/pidgin/pidginmessage.c b/pidgin/pidginmessage.c
--- a/pidgin/pidginmessage.c
+++ b/pidgin/pidginmessage.c
@@ -83,12 +83,12 @@
                           G_GNUC_UNUSED gpointer data)
 {
 	if(PURPLE_IS_MESSAGE(message)) {
-		PurpleContactInfo *author = NULL;
+		PurpleConversationMember *author = NULL;
 
 		author = purple_message_get_author(message);
 
-		if(PURPLE_IS_CONTACT_INFO(author)) {
-			return g_strdup(purple_contact_info_get_name_for_display(author));
+		if(PURPLE_IS_CONVERSATION_MEMBER(author)) {
+			return g_strdup(purple_conversation_member_get_name_for_display(author));
 		}
 	}
 
@@ -115,11 +115,11 @@
 	}
 
 	if(!color_valid) {
-		PurpleContactInfo *author = NULL;
+		PurpleConversationMember *author = NULL;
 		const char *name_for_display = NULL;
 
 		author = purple_message_get_author(message);
-		name_for_display = purple_contact_info_get_name_for_display(author);
+		name_for_display = purple_conversation_member_get_name_for_display(author);
 
 		pidgin_color_calculate_for_text(name_for_display, &rgba);
 		color_valid = TRUE;
diff --git a/protocols/demo/purpledemocommands.c b/protocols/demo/purpledemocommands.c
--- a/protocols/demo/purpledemocommands.c
+++ b/protocols/demo/purpledemocommands.c
@@ -33,15 +33,20 @@
 {
 	PurpleAccount *account = NULL;
 	PurpleContactInfo *info = NULL;
+	PurpleConversationMember *member = NULL;
+	PurpleConversationMembers *members = NULL;
 	PurpleMessage *message = NULL;
 	char *contents = NULL;
 
+	members = purple_conversation_get_members(conversation);
+
 	account = purple_conversation_get_account(conversation);
 	info = purple_account_get_contact_info(account);
+	member = purple_conversation_members_find_member(members, info);
 
 	contents = g_strjoinv(" ", params);
 
-	message = purple_message_new(info, contents);
+	message = purple_message_new(member, contents);
 	purple_message_set_edited(message, TRUE);
 
 	g_clear_pointer(&contents, g_free);
diff --git a/protocols/demo/purpledemoprotocolconversation.c b/protocols/demo/purpledemoprotocolconversation.c
--- a/protocols/demo/purpledemoprotocolconversation.c
+++ b/protocols/demo/purpledemoprotocolconversation.c
@@ -264,6 +264,7 @@
 		PurpleContact *contact = NULL;
 		PurpleContactInfo *contact_info = NULL;
 		PurpleContactManager *manager = NULL;
+		PurpleConversationMember *member = NULL;
 		PurpleConversationMembers *members = NULL;
 
 		account = purple_conversation_get_account(conversation);
@@ -275,14 +276,14 @@
 		contact = purple_contact_manager_find_with_id(manager, account,
 		                                              "echo");
 		contact_info = PURPLE_CONTACT_INFO(contact);
-		if(purple_conversation_members_has_member(members, contact_info, NULL))
-		{
+		member = purple_conversation_members_find_member(members, contact_info);
+		if(PURPLE_IS_CONVERSATION_MEMBER(member)) {
 			PurpleDemoProtocolIMInfo *info = NULL;
 			const char *contents = purple_message_get_contents(message);
 
 			info = g_new(PurpleDemoProtocolIMInfo, 1);
 			info->conversation = g_object_ref(conversation);
-			info->message = purple_message_new(contact_info, contents);
+			info->message = purple_message_new(member, contents);
 			purple_message_set_edited(info->message,
 			                          purple_message_get_edited(message));
 
@@ -295,14 +296,16 @@
 		contact = purple_contact_manager_find_with_id(manager, account,
 		                                              "aegina");
 		contact_info = PURPLE_CONTACT_INFO(contact);
-		if(purple_conversation_members_has_member(members, contact_info, NULL))
-		{
+		member = purple_conversation_members_find_member(members, contact_info);
+		if(PURPLE_IS_CONVERSATION_MEMBER(member)) {
 			PurpleDemoProtocolIMInfo *info = g_new(PurpleDemoProtocolIMInfo, 1);
-			PurpleContactInfo *author = purple_message_get_author(message);
+			PurpleConversationMember *author = purple_message_get_author(message);
+			PurpleContactInfo *author_info = NULL;
 			const char *contents = NULL;
 			const char *author_id = NULL;
 
-			author_id = purple_contact_info_get_id(author);
+			author_info = purple_conversation_member_get_contact_info(author);
+			author_id = purple_contact_info_get_id(author_info);
 			if(purple_strequal(author_id, "hades")) {
 				contents = "🫥️";
 			} else {
@@ -312,7 +315,7 @@
 			}
 
 			info->conversation = g_object_ref(conversation);
-			info->message = purple_message_new(contact_info, contents);
+			info->message = purple_message_new(member, contents);
 
 			g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, purple_demo_protocol_echo_im_cb,
 			                info, (GDestroyNotify)purple_demo_protocol_im_info_free);
diff --git a/protocols/ircv3/purpleircv3commands.c b/protocols/ircv3/purpleircv3commands.c
--- a/protocols/ircv3/purpleircv3commands.c
+++ b/protocols/ircv3/purpleircv3commands.c
@@ -53,13 +53,18 @@
 
     if(n_params > 1) {
         PurpleMessage *message = NULL;
-        PurpleContactInfo *author = NULL;
+        PurpleContactInfo *info = NULL;
+        PurpleConversationMember *member = NULL;
+        PurpleConversationMembers *members = NULL;
         char *contents = NULL;
 
-        author = purple_account_get_contact_info(account);
+        info = purple_account_get_contact_info(account);
+        members = purple_conversation_get_members(conversation);
+        member = purple_conversation_members_find_member(members, info);
+
         contents = g_strjoinv(" ", params + 1);
 
-        message = purple_message_new(author, contents);
+        message = purple_message_new(member, contents);
         g_free(contents);
 
         purple_conversation_send_message_async(new_conversation, message, NULL,
diff --git a/protocols/ircv3/purpleircv3connection.c b/protocols/ircv3/purpleircv3connection.c
--- a/protocols/ircv3/purpleircv3connection.c
+++ b/protocols/ircv3/purpleircv3connection.c
@@ -173,14 +173,15 @@
 {
 	PurpleIRCv3Connection *connection = data;
 	PurpleAccount *account = NULL;
-	PurpleContactInfo *contact = NULL;
+	PurpleContactInfo *info = NULL;
+	PurpleConversationMember *author = NULL;
 	PurpleMessage *purple_message = NULL;
 	GStrv params = NULL;
 	char *body = NULL;
 	const char *command = NULL;
 
 	account = purple_connection_get_account(PURPLE_CONNECTION(connection));
-	contact = purple_account_get_contact_info(account);
+	info = purple_account_get_contact_info(account);
 
 	command = ibis_message_get_command(message);
 	params = ibis_message_get_params(message);
@@ -192,7 +193,9 @@
 		body = g_strdup(command);
 	}
 
-	purple_message = purple_message_new(PURPLE_CONTACT_INFO(contact), body);
+	author = purple_conversation_find_or_add_member(connection->status_conversation,
+	                                                info, FALSE, NULL);
+	purple_message = purple_message_new(author, body);
 	g_clear_pointer(&body, g_free);
 
 	purple_conversation_write_message(connection->status_conversation,
@@ -321,12 +324,16 @@
                                     gpointer data)
 {
 	PurpleIRCv3Connection *connection = data;
-	PurpleContact *author = NULL;
+	PurpleContact *contact = NULL;
+	PurpleConversationMember *author = NULL;
 	PurpleMessage *purple_message = NULL;
 
-	author = purple_ircv3_connection_find_or_create_contact(connection,
-	                                                        ibis_message);
-	purple_message = purple_message_new(PURPLE_CONTACT_INFO(author),
+	contact = purple_ircv3_connection_find_or_create_contact(connection,
+	                                                         ibis_message);
+	author = purple_conversation_find_or_add_member(connection->status_conversation,
+	                                                PURPLE_CONTACT_INFO(contact),
+	                                                FALSE, NULL);
+	purple_message = purple_message_new(author,
 	                                    _("no message of the day found"));
 	purple_conversation_write_message(connection->status_conversation,
 	                                  purple_message);
@@ -469,16 +476,20 @@
                                            gpointer data)
 {
 	PurpleIRCv3Connection *connection = data;
-	PurpleContact *author = NULL;
+	PurpleContact *contact = NULL;
+	PurpleConversationMember *author = NULL;
 	PurpleMessage *purple_message = NULL;
 	char *contents = NULL;
 
-	author = purple_ircv3_connection_find_or_create_contact(connection,
-	                                                        ibis_message);
+	contact = purple_ircv3_connection_find_or_create_contact(connection,
+	                                                         ibis_message);
+	author = purple_conversation_find_or_add_member(connection->status_conversation,
+	                                                PURPLE_CONTACT_INFO(contact),
+	                                                FALSE, NULL);
 	contents = g_strdup_printf(_("unhandled message: '%s'"),
 	                           ibis_message_get_raw_message(ibis_message));
 
-	purple_message = purple_message_new(PURPLE_CONTACT_INFO(author), contents);
+	purple_message = purple_message_new(author, contents);
 	purple_conversation_write_message(connection->status_conversation,
 	                                  purple_message);
 	g_clear_object(&purple_message);
@@ -811,7 +822,8 @@
 purple_ircv3_connection_add_status_message(PurpleIRCv3Connection *connection,
                                            IbisMessage *ibis_message)
 {
-	PurpleContact *author = NULL;
+	PurpleContact *contact = NULL;
+	PurpleConversationMember *author = NULL;
 	PurpleMessage *purple_message = NULL;
 	GString *str = NULL;
 	GStrv params = NULL;
@@ -837,9 +849,12 @@
 	stripped = ibis_formatting_strip(str->str);
 	g_string_free(str, TRUE);
 
-	author = purple_ircv3_connection_find_or_create_contact(connection,
-	                                                        ibis_message);
-	purple_message = purple_message_new(PURPLE_CONTACT_INFO(author), stripped);
+	contact = purple_ircv3_connection_find_or_create_contact(connection,
+	                                                         ibis_message);
+	author = purple_conversation_find_or_add_member(connection->status_conversation,
+	                                                PURPLE_CONTACT_INFO(contact),
+	                                                FALSE, NULL);
+	purple_message = purple_message_new(author, stripped);
 	g_free(stripped);
 
 	purple_conversation_write_message(connection->status_conversation,
@@ -994,6 +1009,7 @@
                                              gboolean show_command)
 {
 	PurpleContact *contact = NULL;
+	PurpleConversationMember *author = NULL;
 	PurpleMessage *purple_message = NULL;
 	GString *str = NULL;
 	GStrv params = NULL;
@@ -1021,9 +1037,11 @@
 
 	contact = purple_ircv3_connection_find_or_create_contact(connection,
 	                                                         ibis_message);
+	author = purple_conversation_find_or_add_member(connection->status_conversation,
+	                                                PURPLE_CONTACT_INFO(contact),
+	                                                FALSE, NULL);
 
-	purple_message = purple_message_new(PURPLE_CONTACT_INFO(contact),
-	                                    str->str);
+	purple_message = purple_message_new(author, str->str);
 	g_string_free(str, TRUE);
 
 	purple_conversation_write_message(connection->status_conversation,
diff --git a/protocols/ircv3/purpleircv3messagehandlers.c b/protocols/ircv3/purpleircv3messagehandlers.c
--- a/protocols/ircv3/purpleircv3messagehandlers.c
+++ b/protocols/ircv3/purpleircv3messagehandlers.c
@@ -31,7 +31,7 @@
 /******************************************************************************
  * Helpers
  *****************************************************************************/
-static void
+static PurpleConversationMember *
 purple_ircv3_add_contact_to_conversation(PurpleContact *contact,
                                          PurpleConversation *conversation,
                                          gboolean announce)
@@ -52,10 +52,12 @@
 			                          purple_conversation_get_title_for_display(conversation));
 		}
 
-		purple_conversation_members_add_member(members, info, announce,
-		                                       message);
+		member = purple_conversation_members_add_member(members, info,
+		                                                announce, message);
 		g_clear_pointer(&message, g_free);
 	}
+
+	return member;
 }
 
 /******************************************************************************
@@ -329,6 +331,7 @@
 	PurpleIRCv3Connection *connection = data;
 	PurpleContact *contact = NULL;
 	PurpleConversation *conversation = NULL;
+	PurpleConversationMember *member = NULL;
 	PurpleMessage *message = NULL;
 	GDateTime *dt = NULL;
 	IbisCTCPMessage *ctcp_message = NULL;
@@ -376,7 +379,8 @@
 		                                                                   target);
 	}
 
-	purple_ircv3_add_contact_to_conversation(contact, conversation, announce);
+	member = purple_ircv3_add_contact_to_conversation(contact, conversation,
+	                                                  announce);
 
 	if(IBIS_IS_CTCP_MESSAGE(ctcp_message)) {
 		if(ibis_ctcp_message_is_command(ctcp_message, IBIS_CTCP_ACTION)) {
@@ -389,8 +393,7 @@
 			stripped = ibis_formatting_strip(ctcp_body);
 			g_free(ctcp_body);
 
-			message = purple_message_new(PURPLE_CONTACT_INFO(contact),
-			                             stripped);
+			message = purple_message_new(member, stripped);
 			g_free(stripped);
 
 			purple_message_set_action(message, TRUE);
@@ -402,7 +405,7 @@
 			                       params[0],
 			                       purple_contact_info_get_id(PURPLE_CONTACT_INFO(contact)));
 
-			message = purple_message_new(PURPLE_CONTACT_INFO(contact), body);
+			message = purple_message_new(member, body);
 			g_free(body);
 		}
 	}
@@ -411,7 +414,7 @@
 		char *stripped = NULL;
 
 		stripped = ibis_formatting_strip(params[1]);
-		message = purple_message_new(PURPLE_CONTACT_INFO(contact), stripped);
+		message = purple_message_new(member, stripped);
 		g_clear_pointer(&stripped, g_free);
 	}
 
diff --git a/purple-history/purplehistorycore.c b/purple-history/purplehistorycore.c
--- a/purple-history/purplehistorycore.c
+++ b/purple-history/purplehistorycore.c
@@ -44,9 +44,10 @@
 
 	while(results != NULL) {
 		PurpleMessage *message = PURPLE_MESSAGE(results->data);
-		PurpleContactInfo *author = purple_message_get_author(message);
+		PurpleConversationMember *member = purple_message_get_author(message);
 
-		g_printf("%s: %s\n", purple_contact_info_get_name_for_display(author),
+		g_printf("%s: %s\n",
+		         purple_conversation_member_get_name_for_display(member),
 		         purple_message_get_contents(message));
 
 		g_clear_object(&message);
