Sat, 09 Aug 2025 00:19:03 +0800
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; }