diff --git a/libpurple/purplemessage.h b/libpurple/purplemessage.h
--- a/libpurple/purplemessage.h
+++ b/libpurple/purplemessage.h
@@ -30,6 +30,7 @@
 #include <glib-object.h>
 
 #include "purpleattachment.h"
+#include "purplecontactinfo.h"
 #include "purpleversion.h"
 
 G_BEGIN_DECLS
@@ -90,6 +91,20 @@
 G_DECLARE_FINAL_TYPE(PurpleMessage, purple_message, PURPLE, MESSAGE, GObject)
 
 /**
+ * purple_message_new:
+ * @author: The author.
+ * @contents: The contents of the message.
+ *
+ * Creates a new message from @author with @contents.
+ *
+ * Returns: (transfer full): The new message.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+PurpleMessage *purple_message_new(PurpleContactInfo *author, const char *contents);
+
+/**
  * purple_message_new_outgoing:
  * @author: The author.
  * @contents: The contents.
@@ -104,7 +119,7 @@
  *
  * Since: 3.0
  */
-PURPLE_AVAILABLE_IN_3_0
+PURPLE_DEPRECATED_FOR(purple_message_new)
 PurpleMessage *purple_message_new_outgoing(const char *author, const char *contents, PurpleMessageFlags flags);
 
 /**
@@ -122,7 +137,7 @@
  *
  * Since: 3.0
  */
-PURPLE_AVAILABLE_IN_3_0
+PURPLE_DEPRECATED_FOR(purple_message_new)
 PurpleMessage *purple_message_new_incoming(const char *who, const char *contents, PurpleMessageFlags flags, guint64 timestamp);
 
 /**
@@ -138,303 +153,10 @@
  *
  * Since: 3.0
  */
-PURPLE_AVAILABLE_IN_3_0
+PURPLE_DEPRECATED_FOR(purple_message_new)
 PurpleMessage *purple_message_new_system(const char *contents, PurpleMessageFlags flags);
 
 /**
- * purple_message_get_id:
- * @message: The message.
- *
- * Returns the unique identifier of the message. These identifiers are not
- * serialized - it's a per-session id.
- *
- * Returns: the global identifier of @message.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-const char *purple_message_get_id(PurpleMessage *message);
-
-/**
- * purple_message_set_id:
- * @message: The instance.
- * @id: (nullable): The new id to set.
- *
- * Sets the id of @message to @id.
- *
- * > Note: This should really only be used by protocol plugins to update an id
- * of a sent message when the server has assigned the final id to the message.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-void purple_message_set_id(PurpleMessage *message, const char *id);
-
-/**
- * purple_message_get_author:
- * @message: The message.
- *
- * Returns the author of the message, not a local alias.
- *
- * Returns: the author of @message.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-const char *purple_message_get_author(PurpleMessage *message);
-
-/**
- * purple_message_set_author_name_color:
- * @message: The #PurpleMessage instance.
- * @color: The hex color code for the author of @message.
- *
- * Sets the author's name color of @message to @color. This is the color that
- * will be used to display the author's name in a user interface. The user
- * interface might not use this exact color, as it might need to adapt for
- * contrast or limits on the number of colors.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-void purple_message_set_author_name_color(PurpleMessage *message, const char *color);
-
-/**
- * purple_message_get_author_name_color:
- * @message: The #PurpleMessage instance.
- *
- * Gets the author's name color for @message.
- *
- * Returns: (transfer none): The hex color for the author of @message's name.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-const char *purple_message_get_author_name_color(PurpleMessage *message);
-
-/**
- * purple_message_set_author_alias:
- * @message: The message.
- * @alias: The alias.
- *
- * Sets the alias of @message's author. You don't normally need to call this.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-void purple_message_set_author_alias(PurpleMessage *message, const char *alias);
-
-/**
- * purple_message_get_author_alias:
- * @message: The message.
- *
- * Returns the alias of @message author.
- *
- * Returns: the @message author's alias.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-const char *purple_message_get_author_alias(PurpleMessage *message);
-
-/**
- * purple_message_set_contents:
- * @message: The message.
- * @cont: The contents.
- *
- * Sets the contents of the @message. It might be HTML.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-void purple_message_set_contents(PurpleMessage *message, const char *cont);
-
-/**
- * purple_message_get_contents:
- * @message: The message.
- *
- * Returns the contents of the message.
- *
- * Returns: the contents of @message.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-const char *purple_message_get_contents(PurpleMessage *message);
-
-/**
- * purple_message_is_empty:
- * @message: The message.
- *
- * Checks, if the message's body is empty.
- *
- * Returns: %TRUE, if @message is empty.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-gboolean purple_message_is_empty(PurpleMessage *message);
-
-/**
- * purple_message_set_timestamp:
- * @message: The #PurpleMessage instance.
- * @timestamp: (nullable): The #GDateTime of the message.
- *
- * Sets the timestamp of @message.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-void purple_message_set_timestamp(PurpleMessage *message, GDateTime *timestamp);
-
-/**
- * purple_message_get_timestamp:
- * @message: The message.
- *
- * Returns a @message's timestamp.  If @message does not currently have a
- * timestamp, the current time will be set as the time stamp and returned.
- *
- * Returns: (transfer none): The #GDateTime timestamp from @message.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-GDateTime *purple_message_get_timestamp(PurpleMessage *message);
-
-/**
- * purple_message_format_timestamp:
- * @message: The #PurpleMessage instance.
- * @format: The format to output the time stamp as.
- *
- * Formats the timestamp of @message and returns it.
- *
- * Returns: The formatted timestamp.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-char *purple_message_format_timestamp(PurpleMessage *message, const char *format);
-
-/**
- * purple_message_set_flags:
- * @message: The message.
- * @flags: The message flags.
- *
- * Sets flags for @message. It shouldn't be in a conflict with a message type,
- * so use it carefully.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-void purple_message_set_flags(PurpleMessage *message, PurpleMessageFlags flags);
-
-/**
- * purple_message_get_flags:
- * @message: The message.
- *
- * Returns the flags of a @message.
- *
- * Returns: the flags of a @message.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-PurpleMessageFlags purple_message_get_flags(PurpleMessage *message);
-
-/**
- * purple_message_set_error:
- * @message: The instance.
- * @error: (nullable) (transfer none): The error to set.
- *
- * Sets the error of @message to @error. Primarily this will be used for
- * delivery failure.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-void purple_message_set_error(PurpleMessage *message, GError *error);
-
-/**
- * purple_message_get_error:
- * @message: The instance.
- *
- * Gets the error from @message.
- *
- * Returns: (nullable) (transfer none): The error from @message or %NULL.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-GError *purple_message_get_error(PurpleMessage *message);
-
-/**
- * purple_message_add_attachment:
- * @message: The #PurpleMessage instance.
- * @attachment: The #PurpleAttachment instance.
- *
- * Adds @attachment to @message.
- *
- * Returns: %TRUE if an attachment with the same ID did not already exist.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-gboolean purple_message_add_attachment(PurpleMessage *message, PurpleAttachment *attachment);
-
-/**
- * purple_message_remove_attachment:
- * @message: The #PurpleMessage instance.
- * @id: The id of the #PurpleAttachment
- *
- * Removes the #PurpleAttachment identified by @id if it exists.
- *
- * Returns: %TRUE if the #PurpleAttachment was found and removed, %FALSE
- *          otherwise.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-gboolean purple_message_remove_attachment(PurpleMessage *message, guint64 id);
-
-/**
- * purple_message_get_attachment:
- * @message: The #PurpleMessage instance.
- * @id: The id of the #PurpleAttachment to get.
- *
- * Retrieves the #PurpleAttachment identified by @id from @message.
- *
- * Returns: (transfer full): The #PurpleAttachment if it was found, otherwise
- *                           %NULL.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-PurpleAttachment *purple_message_get_attachment(PurpleMessage *message, guint64 id);
-
-/**
- * purple_message_foreach_attachment:
- * @message: The #PurpleMessage instance.
- * @func: (scope call): The #PurpleAttachmentForeachFunc to call.
- * @data: User data to pass to @func.
- *
- * Calls @func for each #PurpleAttachment that's attached to @message.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-void purple_message_foreach_attachment(PurpleMessage *message, PurpleAttachmentForeachFunc func, gpointer data);
-
-/**
- * purple_message_clear_attachments:
- * @message: The #PurpleMessage instance.
- *
- * Removes all attachments from @message.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-void purple_message_clear_attachments(PurpleMessage *message);
-
-/**
  * purple_message_get_action:
  * @message: The instance.
  *
@@ -464,6 +186,110 @@
 void purple_message_set_action(PurpleMessage *message, gboolean action);
 
 /**
+ * purple_message_get_author:
+ * @message: The message.
+ *
+ * Returns the author of the message, not a local alias.
+ *
+ * Returns: the author of @message.
+ *
+ * Deprecated: 3.0: this is being repurposed in the future.
+ */
+PURPLE_DEPRECATED_FOR(purple_message_get_author_name)
+const char *purple_message_get_author(PurpleMessage *message);
+
+/**
+ * purple_message_get_author_name:
+ * @message: The message.
+ *
+ * Returns the author of the message, not a local alias.
+ *
+ * Returns: the author of @message.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+const char *purple_message_get_author_name(PurpleMessage *message);
+
+/**
+ * purple_message_get_author_name_color:
+ * @message: The #PurpleMessage instance.
+ *
+ * Gets the author's name color for @message.
+ *
+ * Returns: (transfer none): The hex color for the author of @message's name.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+const char *purple_message_get_author_name_color(PurpleMessage *message);
+
+/**
+ * purple_message_set_author_name_color:
+ * @message: The #PurpleMessage instance.
+ * @color: The hex color code for the author of @message.
+ *
+ * Sets the author's name color of @message to @color. This is the color that
+ * will be used to display the author's name in a user interface. The user
+ * interface might not use this exact color, as it might need to adapt for
+ * contrast or limits on the number of colors.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_message_set_author_name_color(PurpleMessage *message, const char *color);
+
+/**
+ * purple_message_get_author_alias:
+ * @message: The message.
+ *
+ * Returns the alias of @message author.
+ *
+ * Returns: the @message author's alias.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+const char *purple_message_get_author_alias(PurpleMessage *message);
+
+/**
+ * purple_message_set_author_alias:
+ * @message: The message.
+ * @alias: The alias.
+ *
+ * Sets the alias of @message's author. You don't normally need to call this.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_message_set_author_alias(PurpleMessage *message, const char *alias);
+
+/**
+ * purple_message_get_contents:
+ * @message: The message.
+ *
+ * Returns the contents of the message.
+ *
+ * Returns: the contents of @message.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+const char *purple_message_get_contents(PurpleMessage *message);
+
+/**
+ * purple_message_set_contents:
+ * @message: The message.
+ * @cont: The contents.
+ *
+ * Sets the contents of the @message. It might be HTML.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_message_set_contents(PurpleMessage *message, const char *cont);
+
+/**
  * purple_message_get_delivered:
  * @message: The instance.
  *
@@ -558,6 +384,31 @@
 void purple_message_set_edited(PurpleMessage *message, gboolean edited);
 
 /**
+ * purple_message_get_event:
+ * @message: The instance.
+ *
+ * Gets whether or not @message is for an event.
+ *
+ * Returns: %TRUE if the message is for an event, otherwise %FALSE.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_message_get_event(PurpleMessage *message);
+
+/**
+ * purple_message_set_event:
+ * @message: The instance.
+ * @event: The new event state.
+ *
+ * Sets whether or not @message is for an event.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_message_set_event(PurpleMessage *message, gboolean event);
+
+/**
  * purple_message_get_edited_at:
  * @message: The instance.
  *
@@ -586,6 +437,244 @@
 PURPLE_AVAILABLE_IN_3_0
 void purple_message_set_edited_at(PurpleMessage *message, GDateTime *datetime);
 
+/**
+ * purple_message_get_error:
+ * @message: The instance.
+ *
+ * Gets the error from @message.
+ *
+ * Returns: (nullable) (transfer none): The error from @message or %NULL.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+GError *purple_message_get_error(PurpleMessage *message);
+
+/**
+ * purple_message_set_error:
+ * @message: The instance.
+ * @error: (nullable) (transfer none): The error to set.
+ *
+ * Sets the error of @message to @error. Primarily this will be used for
+ * delivery failure.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_message_set_error(PurpleMessage *message, GError *error);
+
+/**
+ * purple_message_get_flags:
+ * @message: The message.
+ *
+ * Returns the flags of a @message.
+ *
+ * Returns: the flags of a @message.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+PurpleMessageFlags purple_message_get_flags(PurpleMessage *message);
+
+/**
+ * purple_message_set_flags:
+ * @message: The message.
+ * @flags: The message flags.
+ *
+ * Sets flags for @message. It shouldn't be in a conflict with a message type,
+ * so use it carefully.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_message_set_flags(PurpleMessage *message, PurpleMessageFlags flags);
+
+/**
+ * purple_message_get_id:
+ * @message: The message.
+ *
+ * Returns the unique identifier of the message. These identifiers are not
+ * serialized - it's a per-session id.
+ *
+ * Returns: the global identifier of @message.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+const char *purple_message_get_id(PurpleMessage *message);
+
+/**
+ * purple_message_set_id:
+ * @message: The instance.
+ * @id: (nullable): The new id to set.
+ *
+ * Sets the id of @message to @id.
+ *
+ * > Note: This should really only be used by protocol plugins to update an id
+ * of a sent message when the server has assigned the final id to the message.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_message_set_id(PurpleMessage *message, const char *id);
+
+/**
+ * purple_message_is_empty:
+ * @message: The message.
+ *
+ * Checks, if the message's body is empty.
+ *
+ * Returns: %TRUE, if @message is empty.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_message_is_empty(PurpleMessage *message);
+
+/**
+ * purple_message_get_notice:
+ * @message: The instance.
+ *
+ * Gets whether or not @message is a notice.
+ *
+ * Returns: %TRUE if @message is a notice, otherwise %FALSE.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_message_get_notice(PurpleMessage *message);
+
+/**
+ * purple_message_set_notice:
+ * @message: The instance.
+ * @notice: %TRUE if this message is a NOTICE.
+ *
+ * Sets whether or not @message is a notice.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_message_set_notice(PurpleMessage *message, gboolean notice);
+
+/**
+ * purple_message_get_timestamp:
+ * @message: The message.
+ *
+ * Returns a @message's timestamp.  If @message does not currently have a
+ * timestamp, the current time will be set as the time stamp and returned.
+ *
+ * Returns: (transfer none): The #GDateTime timestamp from @message.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+GDateTime *purple_message_get_timestamp(PurpleMessage *message);
+
+/**
+ * purple_message_set_timestamp:
+ * @message: The #PurpleMessage instance.
+ * @timestamp: (nullable): The #GDateTime of the message.
+ *
+ * Sets the timestamp of @message.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_message_set_timestamp(PurpleMessage *message, GDateTime *timestamp);
+
+/**
+ * purple_message_set_timestamp_now:
+ * @message: The instance.
+ *
+ * Calls [method@Message.set_timestamp] with the current UTC time.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_message_set_timestamp_now(PurpleMessage *message);
+
+/**
+ * purple_message_format_timestamp:
+ * @message: The #PurpleMessage instance.
+ * @format: The format to output the time stamp as.
+ *
+ * Formats the timestamp of @message and returns it.
+ *
+ * Returns: The formatted timestamp.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+char *purple_message_format_timestamp(PurpleMessage *message, const char *format);
+
+/**
+ * purple_message_add_attachment:
+ * @message: The #PurpleMessage instance.
+ * @attachment: The #PurpleAttachment instance.
+ *
+ * Adds @attachment to @message.
+ *
+ * Returns: %TRUE if an attachment with the same ID did not already exist.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_message_add_attachment(PurpleMessage *message, PurpleAttachment *attachment);
+
+/**
+ * purple_message_remove_attachment:
+ * @message: The #PurpleMessage instance.
+ * @id: The id of the #PurpleAttachment
+ *
+ * Removes the #PurpleAttachment identified by @id if it exists.
+ *
+ * Returns: %TRUE if the #PurpleAttachment was found and removed, %FALSE
+ *          otherwise.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_message_remove_attachment(PurpleMessage *message, guint64 id);
+
+/**
+ * purple_message_get_attachment:
+ * @message: The #PurpleMessage instance.
+ * @id: The id of the #PurpleAttachment to get.
+ *
+ * Retrieves the #PurpleAttachment identified by @id from @message.
+ *
+ * Returns: (transfer full): The #PurpleAttachment if it was found, otherwise
+ *                           %NULL.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+PurpleAttachment *purple_message_get_attachment(PurpleMessage *message, guint64 id);
+
+/**
+ * purple_message_foreach_attachment:
+ * @message: The #PurpleMessage instance.
+ * @func: (scope call): The #PurpleAttachmentForeachFunc to call.
+ * @data: User data to pass to @func.
+ *
+ * Calls @func for each #PurpleAttachment that's attached to @message.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_message_foreach_attachment(PurpleMessage *message, PurpleAttachmentForeachFunc func, gpointer data);
+
+/**
+ * purple_message_clear_attachments:
+ * @message: The #PurpleMessage instance.
+ *
+ * Removes all attachments from @message.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_message_clear_attachments(PurpleMessage *message);
+
 G_END_DECLS
 
 #endif /* PURPLE_MESSAGE_H */
# HG changeset patch
# Node ID 3fa91c2b790aae0a3a2ffbbca709eff9422efa8e
# Parent  3fdd5c82e57cb3d6409f94fcdf452be3d8afd644
diff --git a/libpurple/purplemessage.c b/libpurple/purplemessage.c
--- a/libpurple/purplemessage.c
+++ b/libpurple/purplemessage.c
@@ -31,12 +31,14 @@
 	GObject parent;
 
 	char *id;
-	char *author;
+	char *author_name;
 	char *author_name_color;
 	char *author_alias;
 
 	char *contents;
 	gboolean action;
+	gboolean event;
+	gboolean notice;
 
 	GDateTime *timestamp;
 	PurpleMessageFlags flags;
@@ -51,19 +53,22 @@
 
 enum {
 	PROP_0,
-	PROP_ID,
+	PROP_ACTION,
 	PROP_AUTHOR,
 	PROP_AUTHOR_ALIAS,
+	PROP_AUTHOR_NAME,
 	PROP_AUTHOR_NAME_COLOR,
 	PROP_CONTENTS,
-	PROP_ACTION,
-	PROP_TIMESTAMP,
-	PROP_FLAGS,
-	PROP_ERROR,
 	PROP_DELIVERED,
 	PROP_DELIVERED_AT,
 	PROP_EDITED,
 	PROP_EDITED_AT,
+	PROP_ERROR,
+	PROP_EVENT,
+	PROP_FLAGS,
+	PROP_ID,
+	PROP_NOTICE,
+	PROP_TIMESTAMP,
 	N_PROPERTIES,
 };
 static GParamSpec *properties[N_PROPERTIES] = {NULL, };
@@ -74,9 +79,14 @@
  * Helpers
  *****************************************************************************/
 static void
-purple_message_set_author(PurpleMessage *message, const char *author) {
-	if(g_set_str(&message->author, author)) {
-		g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_AUTHOR]);
+purple_message_set_author_name(PurpleMessage *message, const char *author) {
+	if(g_set_str(&message->author_name, author)) {
+		GObject *obj = G_OBJECT(message);
+
+		g_object_freeze_notify(obj);
+		g_object_notify_by_pspec(obj, properties[PROP_AUTHOR]);
+		g_object_notify_by_pspec(obj, properties[PROP_AUTHOR_NAME]);
+		g_object_thaw_notify(obj);
 	}
 }
 
@@ -90,49 +100,56 @@
 	PurpleMessage *message = PURPLE_MESSAGE(object);
 
 	switch(param_id) {
-		case PROP_ID:
-			g_value_set_string(value, purple_message_get_id(message));
-			break;
-		case PROP_AUTHOR:
-			g_value_set_string(value, purple_message_get_author(message));
-			break;
-		case PROP_AUTHOR_ALIAS:
-			g_value_set_string(value, purple_message_get_author_alias(message));
-			break;
-		case PROP_AUTHOR_NAME_COLOR:
-			g_value_set_string(value,
-			                   purple_message_get_author_name_color(message));
-			break;
-		case PROP_CONTENTS:
-			g_value_set_string(value, purple_message_get_contents(message));
-			break;
-		case PROP_ACTION:
-			g_value_set_boolean(value, purple_message_get_action(message));
-			break;
-		case PROP_TIMESTAMP:
-			g_value_set_boxed(value, purple_message_get_timestamp(message));
-			break;
-		case PROP_FLAGS:
-			g_value_set_flags(value, purple_message_get_flags(message));
-			break;
-		case PROP_ERROR:
-			g_value_set_boxed(value, purple_message_get_error(message));
-			break;
-		case PROP_DELIVERED:
-			g_value_set_boolean(value, purple_message_get_delivered(message));
-			break;
-		case PROP_DELIVERED_AT:
-			g_value_set_boxed(value, purple_message_get_delivered_at(message));
-			break;
-		case PROP_EDITED:
-			g_value_set_boolean(value, purple_message_get_edited(message));
-			break;
-		case PROP_EDITED_AT:
-			g_value_set_boxed(value, purple_message_get_edited_at(message));
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
-			break;
+	case PROP_ACTION:
+		g_value_set_boolean(value, purple_message_get_action(message));
+		break;
+	case PROP_AUTHOR:
+	case PROP_AUTHOR_NAME:
+		g_value_set_string(value, purple_message_get_author_name(message));
+		break;
+	case PROP_AUTHOR_ALIAS:
+		g_value_set_string(value, purple_message_get_author_alias(message));
+		break;
+	case PROP_AUTHOR_NAME_COLOR:
+		g_value_set_string(value,
+		                   purple_message_get_author_name_color(message));
+		break;
+	case PROP_CONTENTS:
+		g_value_set_string(value, purple_message_get_contents(message));
+		break;
+	case PROP_DELIVERED:
+		g_value_set_boolean(value, purple_message_get_delivered(message));
+		break;
+	case PROP_DELIVERED_AT:
+		g_value_set_boxed(value, purple_message_get_delivered_at(message));
+		break;
+	case PROP_EDITED:
+		g_value_set_boolean(value, purple_message_get_edited(message));
+		break;
+	case PROP_EDITED_AT:
+		g_value_set_boxed(value, purple_message_get_edited_at(message));
+		break;
+	case PROP_ERROR:
+		g_value_set_boxed(value, purple_message_get_error(message));
+		break;
+	case PROP_EVENT:
+		g_value_set_boolean(value, purple_message_get_event(message));
+		break;
+	case PROP_FLAGS:
+		g_value_set_flags(value, purple_message_get_flags(message));
+		break;
+	case PROP_ID:
+		g_value_set_string(value, purple_message_get_id(message));
+		break;
+	case PROP_NOTICE:
+		g_value_set_boolean(value, purple_message_get_notice(message));
+		break;
+	case PROP_TIMESTAMP:
+		g_value_set_boxed(value, purple_message_get_timestamp(message));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
+		break;
 	}
 }
 
@@ -143,49 +160,56 @@
 	PurpleMessage *message = PURPLE_MESSAGE(object);
 
 	switch(param_id) {
-		case PROP_ID:
-			purple_message_set_id(message, g_value_get_string(value));
-			break;
-		case PROP_AUTHOR:
-			purple_message_set_author(message, g_value_get_string(value));
-			break;
-		case PROP_AUTHOR_ALIAS:
-			purple_message_set_author_alias(message, g_value_get_string(value));
-			break;
-		case PROP_AUTHOR_NAME_COLOR:
-			purple_message_set_author_name_color(message,
-			                                     g_value_get_string(value));
-			break;
-		case PROP_CONTENTS:
-			purple_message_set_contents(message, g_value_get_string(value));
-			break;
-		case PROP_ACTION:
-			purple_message_set_action(message, g_value_get_boolean(value));
-			break;
-		case PROP_TIMESTAMP:
-			purple_message_set_timestamp(message, g_value_get_boxed(value));
-			break;
-		case PROP_FLAGS:
-			purple_message_set_flags(message, g_value_get_flags(value));
-			break;
-		case PROP_ERROR:
-			purple_message_set_error(message, g_value_get_boxed(value));
-			break;
-		case PROP_DELIVERED:
-			purple_message_set_delivered(message, g_value_get_boolean(value));
-			break;
-		case PROP_DELIVERED_AT:
-			purple_message_set_delivered_at(message, g_value_get_boxed(value));
-			break;
-		case PROP_EDITED:
-			purple_message_set_edited(message, g_value_get_boolean(value));
-			break;
-		case PROP_EDITED_AT:
-			purple_message_set_edited_at(message, g_value_get_boxed(value));
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
-			break;
+	case PROP_ACTION:
+		purple_message_set_action(message, g_value_get_boolean(value));
+		break;
+	case PROP_AUTHOR:
+	case PROP_AUTHOR_NAME:
+		purple_message_set_author_name(message, g_value_get_string(value));
+		break;
+	case PROP_AUTHOR_ALIAS:
+		purple_message_set_author_alias(message, g_value_get_string(value));
+		break;
+	case PROP_AUTHOR_NAME_COLOR:
+		purple_message_set_author_name_color(message,
+		                                     g_value_get_string(value));
+		break;
+	case PROP_CONTENTS:
+		purple_message_set_contents(message, g_value_get_string(value));
+		break;
+	case PROP_DELIVERED:
+		purple_message_set_delivered(message, g_value_get_boolean(value));
+		break;
+	case PROP_DELIVERED_AT:
+		purple_message_set_delivered_at(message, g_value_get_boxed(value));
+		break;
+	case PROP_EDITED:
+		purple_message_set_edited(message, g_value_get_boolean(value));
+		break;
+	case PROP_EDITED_AT:
+		purple_message_set_edited_at(message, g_value_get_boxed(value));
+		break;
+	case PROP_ERROR:
+		purple_message_set_error(message, g_value_get_boxed(value));
+		break;
+	case PROP_EVENT:
+		purple_message_set_event(message, g_value_get_boolean(value));
+		break;
+	case PROP_FLAGS:
+		purple_message_set_flags(message, g_value_get_flags(value));
+		break;
+	case PROP_ID:
+		purple_message_set_id(message, g_value_get_string(value));
+		break;
+	case PROP_NOTICE:
+		purple_message_set_notice(message, g_value_get_boolean(value));
+		break;
+	case PROP_TIMESTAMP:
+		purple_message_set_timestamp(message, g_value_get_boxed(value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
+		break;
 	}
 }
 
@@ -194,7 +218,7 @@
 	PurpleMessage *message = PURPLE_MESSAGE(obj);
 
 	g_free(message->id);
-	g_free(message->author);
+	g_free(message->author_name);
 	g_free(message->author_name_color);
 	g_free(message->author_alias);
 	g_free(message->contents);
@@ -225,71 +249,6 @@
 	obj_class->finalize = purple_message_finalize;
 
 	/**
-	 * PurpleMessage:id:
-	 *
-	 * The protocol-specific identifier of the message.
-	 *
-	 * Since: 3.0
-	 */
-	properties[PROP_ID] = g_param_spec_string(
-		"id", "ID",
-		"The protocol specific message id",
-		NULL,
-		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-
-	/**
-	 * PurpleMessage:author:
-	 *
-	 * The author of the message.
-	 *
-	 * Since: 3.0
-	 */
-	properties[PROP_AUTHOR] = g_param_spec_string(
-		"author", "Author",
-		"The username of the person, who sent the message.",
-		NULL,
-		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-
-	/**
-	 * PurpleMessage:author-name-color:
-	 *
-	 * The hex color for the author's name.
-	 *
-	 * Since: 3.0
-	 */
-	properties[PROP_AUTHOR_NAME_COLOR] = g_param_spec_string(
-		"author-name-color", "author-name-color",
-		"The hex color to display the author's name with",
-		NULL,
-		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-
-	/**
-	 * PurpleMessage:author-alias:
-	 *
-	 * The alias of the author.
-	 *
-	 * Since: 3.0
-	 */
-	properties[PROP_AUTHOR_ALIAS] = g_param_spec_string(
-		"author-alias", "Author's alias",
-		"The alias of the sender",
-		NULL,
-		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-
-	/**
-	 * PurpleMessage:contents:
-	 *
-	 * The contents of the message.
-	 *
-	 * Since: 3.0
-	 */
-	properties[PROP_CONTENTS] = g_param_spec_string(
-		"contents", "Contents",
-		"The message text",
-		NULL,
-		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-
-	/**
 	 * PurpleMessage:action:
 	 *
 	 * Whether or not the message is an action.
@@ -302,49 +261,71 @@
 	 * Since: 3.0
 	 */
 	properties[PROP_ACTION] = g_param_spec_boolean(
-		"action", "action",
-		"Whether or not the message is an action.",
+		"action", NULL, NULL,
 		FALSE,
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
-	 * PurpleMessage:timestamp:
+	 * PurpleMessage:author:
 	 *
-	 * The timestamp of the message.
+	 * The author of the message.
 	 *
-	 * Since: 3.0
+	 * Deprecated: 3.0: This going to be repurposed once all uses are removed.
 	 */
-	properties[PROP_TIMESTAMP] = g_param_spec_boxed(
-		"timestamp", "timestamp",
-		"The timestamp of the message",
-		G_TYPE_DATE_TIME,
-		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+	properties[PROP_AUTHOR] = g_param_spec_string(
+		"author", NULL, NULL,
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED);
 
 	/**
-	 * PurpleMessage:flags:
+	 * PurpleMessage:author-alias:
 	 *
-	 * The #PurpleMessageFlags for the message.
+	 * The alias of the author.
 	 *
 	 * Since: 3.0
 	 */
-	properties[PROP_FLAGS] = g_param_spec_flags(
-		"flags", "Flags",
-		"Bitwise set of #PurpleMessageFlags flags",
-		PURPLE_TYPE_MESSAGE_FLAGS, 0,
+	properties[PROP_AUTHOR_ALIAS] = g_param_spec_string(
+		"author-alias", NULL, NULL,
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleMessage:author-name:
+	 *
+	 * The name of author of the message.
+	 *
+	 * This is a temporary property that will be used to migrate to so that
+	 * [property@Message:author]'s type can be changed in the near future.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_AUTHOR_NAME] = g_param_spec_string(
+		"author-name", NULL, NULL,
+		NULL,
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
-	 * PurpleMessage:error:
+	 * PurpleMessage:author-name-color:
 	 *
-	 * An error that this message encountered. This could be something like a
-	 * failed delivery, or failed redaction, or rate limited, etc.
+	 * The hex color for the author's name.
 	 *
 	 * Since: 3.0
 	 */
-	properties[PROP_ERROR] = g_param_spec_boxed(
-		"error", "error",
-		"An error that the message encountered",
-		G_TYPE_ERROR,
+	properties[PROP_AUTHOR_NAME_COLOR] = g_param_spec_string(
+		"author-name-color", NULL, NULL,
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleMessage:contents:
+	 *
+	 * The contents of the message.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_CONTENTS] = g_param_spec_string(
+		"contents", NULL, NULL,
+		NULL,
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
@@ -357,8 +338,7 @@
 	 * Since: 3.0
 	 */
 	properties[PROP_DELIVERED] = g_param_spec_boolean(
-		"delivered", "delivered",
-		"Whether or not the message was delivered.",
+		"delivered", NULL, NULL,
 		FALSE,
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
@@ -372,8 +352,7 @@
 	 * Since: 3.0
 	 */
 	properties[PROP_DELIVERED_AT] = g_param_spec_boxed(
-		"delivered-at", "delivered-at",
-		"The time that the message was delivered.",
+		"delivered-at", NULL, NULL,
 		G_TYPE_DATE_TIME,
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
@@ -387,8 +366,7 @@
 	 * Since: 3.0
 	 */
 	properties[PROP_EDITED] = g_param_spec_boolean(
-		"edited", "edited",
-		"Whether or not this message has been edited.",
+		"edited", NULL, NULL,
 		FALSE,
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
@@ -402,8 +380,88 @@
 	 * Since: 3.0
 	 */
 	properties[PROP_EDITED_AT] = g_param_spec_boxed(
-		"edited-at", "edited-at",
-		"The time that the message was last edited.",
+		"edited-at", NULL, NULL,
+		G_TYPE_DATE_TIME,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleMessage:error:
+	 *
+	 * An error that this message encountered. This could be something like a
+	 * failed delivery, or failed redaction, or rate limited, etc.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_ERROR] = g_param_spec_boxed(
+		"error", NULL, NULL,
+		G_TYPE_ERROR,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleMessage:event:
+	 *
+	 * Whether not this message is for an event.
+	 *
+	 * Event messages can include things like a topic changing, a contact
+	 * changing their display name, someone join or leaving a channel, and so
+	 * on.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_EVENT] = g_param_spec_boolean(
+		"event", NULL, NULL,
+		FALSE,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleMessage:flags:
+	 *
+	 * The #PurpleMessageFlags for the message.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_FLAGS] = g_param_spec_flags(
+		"flags", NULL, NULL,
+		PURPLE_TYPE_MESSAGE_FLAGS, 0,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleMessage:id:
+	 *
+	 * The protocol-specific identifier of the message.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_ID] = g_param_spec_string(
+		"id", NULL, NULL,
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleMessage:notice:
+	 *
+	 * Whether or not this message is a notice.
+	 *
+	 * Notice typically means that a message should not be auto replied to, but
+	 * this can vary across protocols. However, user interfaces may just want
+	 * to notice that the message was a notice instead of a normal message.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_NOTICE] = g_param_spec_boolean(
+		"notice", NULL, NULL,
+		FALSE,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleMessage:timestamp:
+	 *
+	 * The timestamp of the message.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_TIMESTAMP] = g_param_spec_boxed(
+		"timestamp", NULL, NULL,
 		G_TYPE_DATE_TIME,
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
@@ -414,6 +472,25 @@
  * Public API
  *****************************************************************************/
 PurpleMessage *
+purple_message_new(PurpleContactInfo *author, const char *contents) {
+	const char *author_name = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_CONTACT_INFO(author), NULL);
+	g_return_val_if_fail(contents != NULL, NULL);
+
+	author_name = purple_contact_info_get_username(author);
+	if(author_name == NULL) {
+		author_name = purple_contact_info_get_id(author);
+	}
+
+	return g_object_new(
+		PURPLE_TYPE_MESSAGE,
+		"author-name", author_name,
+		"contents", contents,
+		NULL);
+}
+
+PurpleMessage *
 purple_message_new_outgoing(const char *author, const char *contents,
                             PurpleMessageFlags flags)
 {
@@ -495,19 +572,21 @@
 	return message;
 }
 
-const char *
-purple_message_get_id(PurpleMessage *message) {
-	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), 0);
+gboolean
+purple_message_get_action(PurpleMessage *message) {
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE);
 
-	return message->id;
+	return message->action;
 }
 
 void
-purple_message_set_id(PurpleMessage *message, const char *id) {
+purple_message_set_action(PurpleMessage *message, gboolean action) {
 	g_return_if_fail(PURPLE_IS_MESSAGE(message));
 
-	if(g_set_str(&message->id, id)) {
-		g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_ID]);
+	if(action != message->action) {
+		message->action = action;
+
+		g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_ACTION]);
 	}
 }
 
@@ -515,7 +594,14 @@
 purple_message_get_author(PurpleMessage *message) {
 	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), NULL);
 
-	return message->author;
+	return purple_message_get_author_name(message);
+}
+
+const char *
+purple_message_get_author_name(PurpleMessage *message) {
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), NULL);
+
+	return message->author_name;
 }
 
 void
@@ -553,10 +639,11 @@
 purple_message_get_author_alias(PurpleMessage *message) {
 	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), NULL);
 
-	if (message->author_alias == NULL)
-		return purple_message_get_author(message);
+	if(message->author_alias != NULL) {
+		return message->author_alias;
+	}
 
-	return message->author_alias;
+	return message->author_name;
 }
 
 void
@@ -576,158 +663,6 @@
 }
 
 gboolean
-purple_message_is_empty(PurpleMessage *message) {
-	return (message->contents == NULL || message->contents[0] == '\0');
-}
-
-void
-purple_message_set_timestamp(PurpleMessage *message, GDateTime *timestamp) {
-	g_return_if_fail(PURPLE_IS_MESSAGE(message));
-
-	g_clear_pointer(&message->timestamp, g_date_time_unref);
-	if(timestamp != NULL) {
-		message->timestamp = g_date_time_ref(timestamp);
-	}
-
-	g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_TIMESTAMP]);
-}
-
-GDateTime *
-purple_message_get_timestamp(PurpleMessage *message) {
-	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), 0);
-
-	if(message->timestamp == NULL) {
-		GDateTime *dt = g_date_time_new_now_local();
-		purple_message_set_timestamp(message, dt);
-		g_date_time_unref(dt);
-	}
-
-	return message->timestamp;
-}
-
-char *
-purple_message_format_timestamp(PurpleMessage *message, const char *format) {
-	GDateTime *dt = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), NULL);
-	g_return_val_if_fail(format != NULL, NULL);
-
-	dt = purple_message_get_timestamp(message);
-
-	return g_date_time_format(dt, format);
-}
-
-void
-purple_message_set_flags(PurpleMessage *message, PurpleMessageFlags flags) {
-	g_return_if_fail(PURPLE_IS_MESSAGE(message));
-
-	message->flags = flags;
-
-	g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_FLAGS]);
-}
-
-PurpleMessageFlags
-purple_message_get_flags(PurpleMessage *message) {
-	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), 0);
-
-	return message->flags;
-}
-
-void
-purple_message_set_error(PurpleMessage *message, GError *error) {
-	g_return_if_fail(PURPLE_IS_MESSAGE(message));
-
-	g_clear_error(&message->error);
-	if(error != NULL) {
-		message->error = g_error_copy(error);
-	}
-
-	g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_ERROR]);
-}
-
-GError *
-purple_message_get_error(PurpleMessage *message) {
-	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), NULL);
-
-	return message->error;
-}
-
-gboolean
-purple_message_add_attachment(PurpleMessage *message,
-                              PurpleAttachment *attachment)
-{
-	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE);
-	g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), FALSE);
-
-	return g_hash_table_insert(message->attachments,
-	                           purple_attachment_get_hash_key(attachment),
-	                           g_object_ref(attachment));
-}
-
-gboolean
-purple_message_remove_attachment(PurpleMessage *message, guint64 id) {
-	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE);
-
-	return g_hash_table_remove(message->attachments, &id);
-}
-
-PurpleAttachment *
-purple_message_get_attachment(PurpleMessage *message, guint64 id) {
-	PurpleAttachment *attachment = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), NULL);
-
-	attachment = g_hash_table_lookup(message->attachments, &id);
-	if(PURPLE_IS_ATTACHMENT(attachment)) {
-		return g_object_ref(attachment);
-	}
-
-	return NULL;
-}
-
-void
-purple_message_foreach_attachment(PurpleMessage *message,
-                                  PurpleAttachmentForeachFunc func,
-                                  gpointer data)
-{
-	GHashTableIter iter;
-	gpointer value;
-
-	g_return_if_fail(PURPLE_IS_MESSAGE(message));
-	g_return_if_fail(func != NULL);
-
-	g_hash_table_iter_init(&iter, message->attachments);
-	while(g_hash_table_iter_next(&iter, NULL, &value)) {
-		func(PURPLE_ATTACHMENT(value), data);
-	}
-}
-
-void
-purple_message_clear_attachments(PurpleMessage *message) {
-	g_return_if_fail(PURPLE_IS_MESSAGE(message));
-
-	g_hash_table_remove_all(message->attachments);
-}
-
-gboolean
-purple_message_get_action(PurpleMessage *message) {
-	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE);
-
-	return message->action;
-}
-
-void
-purple_message_set_action(PurpleMessage *message, gboolean action) {
-	g_return_if_fail(PURPLE_IS_MESSAGE(message));
-
-	if(action != message->action) {
-		message->action = action;
-
-		g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_ACTION]);
-	}
-}
-
-gboolean
 purple_message_get_delivered(PurpleMessage *message) {
 	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE);
 
@@ -848,3 +783,201 @@
 	g_object_notify_by_pspec(obj, properties[PROP_EDITED_AT]);
 	g_object_thaw_notify(obj);
 }
+
+GError *
+purple_message_get_error(PurpleMessage *message) {
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), NULL);
+
+	return message->error;
+}
+
+void
+purple_message_set_error(PurpleMessage *message, GError *error) {
+	g_return_if_fail(PURPLE_IS_MESSAGE(message));
+
+	g_clear_error(&message->error);
+	if(error != NULL) {
+		message->error = g_error_copy(error);
+	}
+
+	g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_ERROR]);
+}
+
+gboolean
+purple_message_get_event(PurpleMessage *message) {
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE);
+
+	return message->event;
+}
+
+void
+purple_message_set_event(PurpleMessage *message, gboolean event) {
+	g_return_if_fail(PURPLE_IS_MESSAGE(message));
+
+	if(message->event != event) {
+		message->event = event;
+
+		g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_EVENT]);
+	}
+}
+
+PurpleMessageFlags
+purple_message_get_flags(PurpleMessage *message) {
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), 0);
+
+	return message->flags;
+}
+
+void
+purple_message_set_flags(PurpleMessage *message, PurpleMessageFlags flags) {
+	g_return_if_fail(PURPLE_IS_MESSAGE(message));
+
+	message->flags = flags;
+
+	g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_FLAGS]);
+}
+
+const char *
+purple_message_get_id(PurpleMessage *message) {
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), 0);
+
+	return message->id;
+}
+
+void
+purple_message_set_id(PurpleMessage *message, const char *id) {
+	g_return_if_fail(PURPLE_IS_MESSAGE(message));
+
+	if(g_set_str(&message->id, id)) {
+		g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_ID]);
+	}
+}
+
+gboolean
+purple_message_is_empty(PurpleMessage *message) {
+	return (message->contents == NULL || message->contents[0] == '\0');
+}
+
+gboolean
+purple_message_get_notice(PurpleMessage *message) {
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE);
+
+	return message->notice;
+}
+
+void
+purple_message_set_notice(PurpleMessage *message, gboolean notice) {
+	g_return_if_fail(PURPLE_IS_MESSAGE(message));
+
+	if(message->notice != notice) {
+		message->notice = notice;
+
+		g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_NOTICE]);
+	}
+}
+
+GDateTime *
+purple_message_get_timestamp(PurpleMessage *message) {
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), 0);
+
+	if(message->timestamp == NULL) {
+		GDateTime *dt = g_date_time_new_now_local();
+		purple_message_set_timestamp(message, dt);
+		g_date_time_unref(dt);
+	}
+
+	return message->timestamp;
+}
+
+void
+purple_message_set_timestamp(PurpleMessage *message, GDateTime *timestamp) {
+	g_return_if_fail(PURPLE_IS_MESSAGE(message));
+
+	g_clear_pointer(&message->timestamp, g_date_time_unref);
+	if(timestamp != NULL) {
+		message->timestamp = g_date_time_ref(timestamp);
+	}
+
+	g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_TIMESTAMP]);
+}
+
+void
+purple_message_set_timestamp_now(PurpleMessage *message) {
+	GDateTime *dt = NULL;
+
+	g_return_if_fail(PURPLE_IS_MESSAGE(message));
+
+	dt = g_date_time_new_now_utc();
+	purple_message_set_timestamp(message, dt);
+	g_date_time_unref(dt);
+}
+
+char *
+purple_message_format_timestamp(PurpleMessage *message, const char *format) {
+	GDateTime *dt = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), NULL);
+	g_return_val_if_fail(format != NULL, NULL);
+
+	dt = purple_message_get_timestamp(message);
+
+	return g_date_time_format(dt, format);
+}
+
+gboolean
+purple_message_add_attachment(PurpleMessage *message,
+                              PurpleAttachment *attachment)
+{
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE);
+	g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), FALSE);
+
+	return g_hash_table_insert(message->attachments,
+	                           purple_attachment_get_hash_key(attachment),
+	                           g_object_ref(attachment));
+}
+
+gboolean
+purple_message_remove_attachment(PurpleMessage *message, guint64 id) {
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE);
+
+	return g_hash_table_remove(message->attachments, &id);
+}
+
+PurpleAttachment *
+purple_message_get_attachment(PurpleMessage *message, guint64 id) {
+	PurpleAttachment *attachment = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_MESSAGE(message), NULL);
+
+	attachment = g_hash_table_lookup(message->attachments, &id);
+	if(PURPLE_IS_ATTACHMENT(attachment)) {
+		return g_object_ref(attachment);
+	}
+
+	return NULL;
+}
+
+void
+purple_message_foreach_attachment(PurpleMessage *message,
+                                  PurpleAttachmentForeachFunc func,
+                                  gpointer data)
+{
+	GHashTableIter iter;
+	gpointer value;
+
+	g_return_if_fail(PURPLE_IS_MESSAGE(message));
+	g_return_if_fail(func != NULL);
+
+	g_hash_table_iter_init(&iter, message->attachments);
+	while(g_hash_table_iter_next(&iter, NULL, &value)) {
+		func(PURPLE_ATTACHMENT(value), data);
+	}
+}
+
+void
+purple_message_clear_attachments(PurpleMessage *message) {
+	g_return_if_fail(PURPLE_IS_MESSAGE(message));
+
+	g_hash_table_remove_all(message->attachments);
+}
+
diff --git a/libpurple/tests/meson.build b/libpurple/tests/meson.build
--- a/libpurple/tests/meson.build
+++ b/libpurple/tests/meson.build
@@ -59,6 +59,7 @@
 )
 
 testenv.set('XDG_CONFIG_HOME', meson.current_build_dir() / 'config')
+testenv.set('G_ENABLE_DIAGNOSTIC', '1')
 
 foreach prog : PROGS
     e = executable(f'test_@prog@', f'test_@prog@.c',
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
@@ -54,6 +54,8 @@
 	gboolean action = FALSE;
 	gboolean delivered = FALSE;
 	gboolean edited = FALSE;
+	gboolean event = FALSE;
+	gboolean notice = FALSE;
 
 	timestamp = g_date_time_new_from_unix_utc(911347200);
 	error = g_error_new(g_quark_from_static_string("test-message"), 0,
@@ -65,61 +67,80 @@
 	 */
 	message = g_object_new(
 		PURPLE_TYPE_MESSAGE,
-		"id", "id",
 		"action", TRUE,
-		"author", "author",
 		"author-alias", "alias",
+		"author-name", "author",
 		"author-name-color", "purple",
+		"contents", "Now that is a big door",
 		"delivered", TRUE,
 		"edited", TRUE,
-		"contents", "Now that is a big door",
+		"error", error,
+		"event", TRUE,
+		"flags", PURPLE_MESSAGE_SYSTEM,
+		"id", "id",
+		"notice", TRUE,
 		"timestamp", timestamp,
-		"flags", PURPLE_MESSAGE_SYSTEM,
-		"error", error,
 		NULL);
 
 	g_object_get(
 		message,
-		"id", &id,
 		"action", &action,
-		"author", &author,
 		"author-alias", &author_alias,
+		"author-name", &author,
 		"author-name-color", &author_name_color,
+		"contents", &contents,
 		"delivered", &delivered,
 		"delivered-at", &delivered_at1,
 		"edited", &edited,
 		"edited-at", &edited_at1,
-		"contents", &contents,
+		"error", &error1,
+		"event", &event,
+		"flags", &flags,
+		"id", &id,
+		"notice", &notice,
 		"timestamp", &timestamp1,
-		"flags", &flags,
-		"error", &error1,
 		NULL);
 
-	g_assert_cmpstr(id, ==, "id");
 	g_assert_true(action);
+
 	g_assert_cmpstr(author, ==, "author");
+	g_clear_pointer(&author, g_free);
+
 	g_assert_cmpstr(author_alias, ==, "alias");
+	g_clear_pointer(&author_alias, g_free);
+
 	g_assert_cmpstr(author_name_color, ==, "purple");
+	g_clear_pointer(&author_name_color, g_free);
+
+	g_assert_cmpstr(contents, ==, "Now that is a big door");
+	g_clear_pointer(&contents, g_free);
+
 	g_assert_true(delivered);
 	g_assert_nonnull(delivered_at1);
+	g_clear_pointer(&delivered_at1, g_date_time_unref);
+
 	g_assert_true(edited);
 	g_assert_nonnull(edited_at1);
-	g_assert_cmpstr(contents, ==, "Now that is a big door");
-	g_assert_true(g_date_time_equal(timestamp1, timestamp));
+	g_clear_pointer(&edited_at1, g_date_time_unref);
+
+	g_assert_error(error1, error->domain, error->code);
+	g_clear_error(&error);
+	g_clear_error(&error1);
+
+	g_assert_true(event);
+
 	g_assert_cmpint(flags, ==, PURPLE_MESSAGE_SYSTEM);
-	g_assert_error(error1, error->domain, error->code);
 
+	g_assert_cmpstr(id, ==, "id");
 	g_clear_pointer(&id, g_free);
-	g_clear_pointer(&author, g_free);
-	g_clear_pointer(&author_alias, g_free);
-	g_clear_pointer(&author_name_color, g_free);
-	g_clear_pointer(&delivered_at1, g_date_time_unref);
-	g_clear_pointer(&edited_at1, g_date_time_unref);
-	g_clear_pointer(&contents, g_free);
+
+	g_assert_true(notice);
+
+	g_assert_true(g_date_time_equal(timestamp1, timestamp));
 	g_clear_pointer(&timestamp, g_date_time_unref);
 	g_clear_pointer(&timestamp1, g_date_time_unref);
-	g_clear_error(&error1);
-	g_clear_object(&message);
+
+	g_assert_finalize_object(message);
 }
 
 static void
diff --git a/meson.build b/meson.build
--- a/meson.build
+++ b/meson.build
@@ -77,6 +77,10 @@
 # For running `meson devenv`.
 devenv = environment()
 
+# Set G_ENABLE_DIAGNOSTIC so we get warnings about deprecated signals and
+# properties.
+devenv.set('G_ENABLE_DIAGNOSTIC', '1')
+
 # For man pages.
 man_conf.set('VERSION', meson.project_version())
 man_conf.set('prefix', get_option('prefix'))
