purplesatoriprotocolconversation.c

Sat, 09 Aug 2025 00:19:03 +0800

author
William Goodspeed <goodspeed@mailo.cat>
date
Sat, 09 Aug 2025 00:19:03 +0800
changeset 1
98bcf06036b8
parent 0
cc7c1f9d20f7
child 3
33a7b189a2c6
permissions
-rw-r--r--

Another Minor Milestone Reached, Conversation Creation & Recv works now

/*
 * Purple Satori Plugin - Satori Protocol Plugin for Purple3
 * Copyright (C) 2025 Gong Zhile
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib/gi18n-lib.h>

#include "purplesatoriprotocolconversation.h"

#include "purplesatoriconnection.h"
#include "purplesatoriplugin.h"
#include "purplesatoriprotocol.h"
#include "satoriapi.h"
#include "satoritypes.h"

typedef struct {
	PurpleConversation *conversation;
	PurpleMessage *message;
} PurpleSatoriProtocolIMInfo;

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
purple_satori_protocol_im_info_free(PurpleSatoriProtocolIMInfo *info) {
	g_clear_object(&info->conversation);
	g_clear_object(&info->message);
	g_free(info);
}

/******************************************************************************
 * Callbacks
 *****************************************************************************/
static gboolean
purple_satori_protocol_echo_im_cb(gpointer data) {
	PurpleSatoriProtocolIMInfo *info = data;

	purple_conversation_write_message(info->conversation, info->message);

	return G_SOURCE_REMOVE;
}

/******************************************************************************
 * PurpleProtocolConversation Implementation
 *****************************************************************************/
static PurpleCreateConversationDetails *
purple_satori_protocol_get_create_conversation_details(G_GNUC_UNUSED PurpleProtocolConversation *protocol,
						       G_GNUC_UNUSED PurpleAccount *account)
{
	return purple_create_conversation_details_new(0);
}

static void
purple_satori_protocol_create_conversation_async(PurpleProtocolConversation *protocol,
						 PurpleAccount *account,
						 PurpleCreateConversationDetails *details,
						 GCancellable *cancellable,
						 GAsyncReadyCallback callback,
						 gpointer data)
{
	GListModel		*participants	= NULL;
	GTask			*task		= NULL;
	guint			 n_participants = 0;

	task = g_task_new(protocol, cancellable, callback, data);
	g_task_set_source_tag(task,
	                      purple_satori_protocol_create_conversation_async);

	participants = purple_create_conversation_details_get_participants(details);
	n_participants = g_list_model_get_n_items(participants);
	if (n_participants == 0) {
		g_task_return_new_error_literal(task, PURPLE_SATORI_DOMAIN, 0,
		                                _("no participants were provided"));
		g_clear_object(&task);
		return;
	}

	if (n_participants > 1) {
		g_task_return_new_error_literal(task, PURPLE_SATORI_DOMAIN, 0,
		                                _("only dm conversation supported"));
		g_clear_object(&task);
		return;		/* not implimented */
	}

	for(guint i = 0; i < g_list_model_get_n_items(participants); i++) {
		PurpleContactInfo *info = NULL;
		SatoriUser user = { 0 };

		info = g_list_model_get_item(participants, i);
		satori_user_from_contactinfo(info, &user);

		satori_create_dm_channel(
			PURPLE_SATORI_CONNECTION(
				purple_account_get_connection(account)),
			&user, task);

		g_clear_object(&info);
		g_clear_object(&details);
		return;
	}
}

static PurpleConversation *
purple_satori_protocol_create_conversation_finish(G_GNUC_UNUSED PurpleProtocolConversation *protocol,
                                                GAsyncResult *result,
                                                GError **error)
{
	GTask *task = G_TASK(result);

	g_return_val_if_fail(g_task_get_source_tag(task) ==
	                     purple_satori_protocol_create_conversation_async,
	                     NULL);

	return g_task_propagate_pointer(task, error);
}

static void
purple_satori_protocol_conversation_leave_conversation_async(PurpleProtocolConversation *protocol,
                                                           G_GNUC_UNUSED PurpleConversation *conversation,
                                                           GCancellable *cancellable,
                                                           GAsyncReadyCallback callback,
                                                           gpointer data)
{
	GTask *task = NULL;

	task = g_task_new(protocol, cancellable, callback, data);
	g_task_set_source_tag(task,
	                      purple_satori_protocol_conversation_leave_conversation_async);

	g_task_return_boolean(task, TRUE);
	g_clear_object(&task);
}

static gboolean
purple_satori_protocol_conversation_leave_conversation_finish(G_GNUC_UNUSED PurpleProtocolConversation *protocol,
                                                            GAsyncResult *result,
                                                            GError **error)
{
	gpointer tag = purple_satori_protocol_conversation_leave_conversation_async;

	g_return_val_if_fail(g_async_result_is_tagged(result, tag), FALSE);

	return g_task_propagate_boolean(G_TASK(result), error);
}

static void
purple_satori_protocol_send_message_async(G_GNUC_UNUSED PurpleProtocolConversation *protocol,
                                        PurpleConversation *conversation,
                                        PurpleMessage *message,
                                        GCancellable *cancellable,
                                        GAsyncReadyCallback callback,
                                        gpointer data)
{
	GTask *task = NULL;

	if(purple_conversation_is_dm(conversation)) {
		PurpleAccount *account = NULL;
		PurpleContact *contact = NULL;
		PurpleContactInfo *contact_info = NULL;
		PurpleContactManager *manager = NULL;
		PurpleConversationMember *member = NULL;
		PurpleConversationMembers *members = NULL;

		account = purple_conversation_get_account(conversation);
		members = purple_conversation_get_members(conversation);

		manager = purple_contact_manager_get_default();

		/* Check if this dm is with echo. */
		contact = purple_contact_manager_find_with_id(manager, account,
		                                              "echo");
		contact_info = PURPLE_CONTACT_INFO(contact);
		member = purple_conversation_members_find_member(members, contact_info);
		if(PURPLE_IS_CONVERSATION_MEMBER(member)) {
			PurpleSatoriProtocolIMInfo *info = NULL;
			const char *contents = purple_message_get_contents(message);

			info = g_new(PurpleSatoriProtocolIMInfo, 1);
			info->conversation = g_object_ref(conversation);
			info->message = purple_message_new(member, contents);
			purple_message_set_edited(info->message,
			                          purple_message_get_edited(message));

			g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
			                purple_satori_protocol_echo_im_cb, info,
			                (GDestroyNotify)purple_satori_protocol_im_info_free);
		}

		/* Check if this dm is with aegina. */
		contact = purple_contact_manager_find_with_id(manager, account,
		                                              "aegina");
		contact_info = PURPLE_CONTACT_INFO(contact);
		member = purple_conversation_members_find_member(members, contact_info);
		if(PURPLE_IS_CONVERSATION_MEMBER(member)) {
			PurpleSatoriProtocolIMInfo *info = g_new(PurpleSatoriProtocolIMInfo, 1);
			PurpleConversationMember *author = purple_message_get_author(message);
			PurpleContactInfo *author_info = NULL;
			const char *contents = NULL;
			const char *author_id = NULL;

			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 {
				/* TRANSLATORS: This is a reference to the Cap of Invisibility owned by
				 * various Greek gods, such as Hades, as mentioned. */
				contents = _("Don't tell Hades I have his Cap");
			}

			info->conversation = g_object_ref(conversation);
			info->message = purple_message_new(member, contents);

			g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, purple_satori_protocol_echo_im_cb,
			                info, (GDestroyNotify)purple_satori_protocol_im_info_free);
		}
	}

	purple_conversation_write_message(conversation, message);

	task = g_task_new(protocol, cancellable, callback, data);
	g_task_return_boolean(task, TRUE);

	g_clear_object(&task);
}

static gboolean
purple_satori_protocol_send_message_finish(G_GNUC_UNUSED PurpleProtocolConversation *protocol,
                                         GAsyncResult *result,
                                         GError **error)
{
	g_return_val_if_fail(G_IS_TASK(result), FALSE);

	return g_task_propagate_boolean(G_TASK(result), error);
}

void
purple_satori_protocol_conversation_init(PurpleProtocolConversationInterface *iface) {
	iface->get_create_conversation_details =
		purple_satori_protocol_get_create_conversation_details;
	iface->create_conversation_async =
		purple_satori_protocol_create_conversation_async;
	iface->create_conversation_finish =
		purple_satori_protocol_create_conversation_finish;

	iface->leave_conversation_async =
		purple_satori_protocol_conversation_leave_conversation_async;
	iface->leave_conversation_finish =
		purple_satori_protocol_conversation_leave_conversation_finish;

	iface->send_message_async = purple_satori_protocol_send_message_async;
	iface->send_message_finish = purple_satori_protocol_send_message_finish;
}

mercurial