--- a/satoriapi.c Sun Aug 10 23:03:27 2025 +0800 +++ b/satoriapi.c Sun Aug 10 23:53:22 2025 +0800 @@ -25,6 +25,7 @@ #include "purplesatoriconnection.h" #include "purplesatoriplugin.h" +#include "purplesatoriprotocolconversation.h" #include "satorimessage.h" #include "satoritypes.h" #include "satoriapi.h" @@ -153,9 +154,12 @@ found = FALSE; } - purple_contact_info_set_display_name( - PURPLE_CONTACT_INFO(contact), - user->nick ? user->nick : user->name); + if (user->nick) + purple_contact_info_set_display_name( + PURPLE_CONTACT_INFO(contact), user->nick); + else if (user->name) + purple_contact_info_set_display_name( + PURPLE_CONTACT_INFO(contact), user->name); PurplePresence *presence = purple_contact_info_get_presence( PURPLE_CONTACT_INFO(contact)); @@ -252,8 +256,7 @@ JB_END_OBJ(data, b); } - SoupMessage *msg = satori_message_new( - "POST", SATORI_ENDPOINT("/v1/friend.list")); + SoupMessage *msg = SATORI_ENDPOINT(con, "/friend.list"); soup_message_set_request_body_from_bytes(msg, "application/json", data); purple_satori_connection_send_and_read_async( @@ -337,8 +340,7 @@ JB_END_OBJ(data, b); } - SoupMessage *msg = satori_message_new( - "POST", SATORI_ENDPOINT("/v1/channel.list")); + SoupMessage *msg = SATORI_ENDPOINT(con, "/channel.list"); soup_message_set_request_body_from_bytes(msg, "application/json", data); purple_satori_connection_send_and_read_async( @@ -412,8 +414,7 @@ JB_END_OBJ(data, b); } - SoupMessage *msg = satori_message_new( - "POST", SATORI_ENDPOINT("/v1/guild.list")); + SoupMessage *msg = SATORI_ENDPOINT(con, "/guild.list"); soup_message_set_request_body_from_bytes(msg, "application/json", data); purple_satori_connection_send_and_read_async( @@ -425,6 +426,10 @@ g_bytes_unref(data); } +/****************************************************************************** + * DM Creation + *****************************************************************************/ + typedef struct { PurpleSatoriConnection *con; SatoriUser user; @@ -497,8 +502,7 @@ JB_END_OBJ(data, b); } - SoupMessage *msg = satori_message_new( - "POST", SATORI_ENDPOINT("/v1/user.channel.create")); + SoupMessage *msg = SATORI_ENDPOINT(con, "/user.channel.create"); soup_message_set_request_body_from_bytes(msg, "application/json", data); SatoriOnDmChannelData *dptr = g_new0(SatoriOnDmChannelData, 1); @@ -515,3 +519,110 @@ g_object_unref(msg); g_bytes_unref(data); } + +/****************************************************************************** + * Message Routines + *****************************************************************************/ + +typedef struct { + PurpleConversation *conversation; + PurpleMessage *message; + GTask *task; +} SatoriSendMessageData; + +static void +satori_on_message_sent(SoupSession *session, + GAsyncResult *res, + SatoriSendMessageData *dptr) +{ + GError *error = NULL; + GBytes *resp = soup_session_send_and_read_finish(session, res, &error); + + if (error) { + purple_debug_error("satori", + "create_dm_channel failed: %s", + error->message); + if (resp) + g_bytes_unref(resp); + + g_task_return_error(dptr->task, error); + goto cleanup; + } + + gsize sz; + const gchar *ptr = g_bytes_get_data(resp, &sz); + + JsonParser *parser = json_parser_new(); + if (!json_parser_load_from_data(parser, ptr, sz, NULL)) { + purple_debug_warning("satori", "bad json received from api"); + g_task_return_new_error_literal(dptr->task, PURPLE_SATORI_DOMAIN, 0, + "bad json received from api"); + goto cleanup; + } + + /* Initialize ID */ + + JsonArray *root = json_node_get_array(json_parser_get_root(parser)); + JsonObject *msg_obj = json_array_get_object_element(root, 0); + const gchar *id = json_object_get_string_member_with_default( + msg_obj, "id", NULL); + + if (!id) { + g_task_return_new_error_literal(dptr->task, PURPLE_SATORI_DOMAIN, 0, + "message not found"); + goto cleanup; + } + + purple_message_set_id(dptr->message, id); + purple_message_set_delivered(dptr->message, TRUE); + + /* Initialize created_time */ + + time_t created_at = json_object_get_int_member_with_default( + msg_obj, "created_at", 0) / 1000; /* timestamp in mS */ + + if (created_at) { + GDateTime *ts = g_date_time_new_from_unix_local(created_at); + purple_message_set_delivered_at(dptr->message, ts); + g_date_time_unref(ts); + } + + g_task_return_boolean(dptr->task, TRUE); + g_object_unref(parser); +cleanup: + g_clear_object(&dptr->task); + g_free(dptr); +} + +void +satori_send_message(PurpleSatoriConnection *con, + PurpleConversation *conversation, + PurpleMessage *message, + const gchar *content, + GTask *task) +{ + GBytes *data = NULL; + + { + JB_BEGIN_OBJ(b); + JBA(b, "channel_id", purple_conversation_get_id(conversation)); + JBA(b, "content", content); + JB_END_OBJ(data, b); + } + + SoupMessage *msg = SATORI_ENDPOINT(con, "/message.create"); + soup_message_set_request_body_from_bytes(msg, "application/json", data); + + SatoriSendMessageData *dptr = g_new0(SatoriSendMessageData, 1); + dptr->conversation = conversation; + dptr->message = message; + dptr->task = task; + + purple_satori_connection_send_and_read_async( + con, msg, 0, NULL, + (GAsyncReadyCallback) satori_on_message_sent, + dptr); + + g_object_unref(msg); + g_bytes_unref(data); +}