From c22aab0620c79a80535c85c0dad87a1f288a884c Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 4 Nov 2020 14:16:25 +0100 Subject: Matrix: show membership events, unimplemented events, do not fetch users remote during cache (slow) --- src/QuickMedia.cpp | 78 +++++++++++++++++++++---------- src/plugins/Matrix.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 159 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index f009cdd..1caf831 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -3073,6 +3073,28 @@ namespace QuickMedia { return result_items; } + static bool is_state_message_type(const Message *message) { + if(!message) + return true; + + switch(message->type) { + case MessageType::TEXT: + return false; + case MessageType::IMAGE: + return false; + case MessageType::VIDEO: + return false; + case MessageType::AUDIO: + return false; + case MessageType::FILE: + return false; + default: + return true; + } + + return true; + } + struct PinnedEventData { std::string event_id; FetchStatus status = FetchStatus::NONE; @@ -3730,10 +3752,12 @@ namespace QuickMedia { frame_skip_text_entry = true; std::shared_ptr selected = tabs[selected_tab].body->get_selected_shared(); if(selected) { - chat_state = ChatState::REPLYING; - currently_operating_on_item = selected; - chat_input.set_editable(true); - replying_to_text.setString("Replying to:"); + if(!is_state_message_type(static_cast(selected->userdata))) { + chat_state = ChatState::REPLYING; + currently_operating_on_item = selected; + chat_input.set_editable(true); + replying_to_text.setString("Replying to:"); + } } else { // TODO: Show inline notification show_notification("QuickMedia", "No message selected for replying"); @@ -3744,19 +3768,21 @@ namespace QuickMedia { frame_skip_text_entry = true; std::shared_ptr selected = tabs[selected_tab].body->get_selected_shared(); if(selected) { - if(!selected->url.empty()) { // cant edit messages that are image/video posts - // TODO: Show inline notification - show_notification("QuickMedia", "You can only edit messages with no file attached to it"); - } else if(!matrix->was_message_posted_by_me(selected->userdata)) { - // TODO: Show inline notification - show_notification("QuickMedia", "You can't edit a message that was posted by somebody else"); - } else { - chat_state = ChatState::EDITING; - currently_operating_on_item = selected; - chat_input.set_editable(true); - chat_input.set_text(selected->get_description()); // TODO: Description? it may change in the future, in which case this should be edited - chat_input.move_caret_to_end(); - replying_to_text.setString("Editing message:"); + if(!is_state_message_type(static_cast(selected->userdata))) { + if(!selected->url.empty()) { // cant edit messages that are image/video posts + // TODO: Show inline notification + show_notification("QuickMedia", "You can only edit messages with no file attached to it"); + } else if(!matrix->was_message_posted_by_me(selected->userdata)) { + // TODO: Show inline notification + show_notification("QuickMedia", "You can't edit a message that was posted by somebody else"); + } else { + chat_state = ChatState::EDITING; + currently_operating_on_item = selected; + chat_input.set_editable(true); + chat_input.set_text(selected->get_description()); // TODO: Description? it may change in the future, in which case this should be edited + chat_input.move_caret_to_end(); + replying_to_text.setString("Editing message:"); + } } } else { // TODO: Show inline notification @@ -3768,14 +3794,16 @@ namespace QuickMedia { frame_skip_text_entry = true; BodyItem *selected = tabs[selected_tab].body->get_selected(); if(selected) { - void *selected_message = selected->userdata; - post_task_queue.push([this, ¤t_room, selected_message]() { - std::string err_msg; - if(matrix->delete_message(current_room, selected_message, err_msg) != PluginResult::OK) { - // TODO: Show inline notification - fprintf(stderr, "Failed to delete message, reason: %s\n", err_msg.c_str()); - } - }); + if(!is_state_message_type(static_cast(selected->userdata))) { + void *selected_message = selected->userdata; + post_task_queue.push([this, ¤t_room, selected_message]() { + std::string err_msg; + if(matrix->delete_message(current_room, selected_message, err_msg) != PluginResult::OK) { + // TODO: Show inline notification + fprintf(stderr, "Failed to delete message, reason: %s\n", err_msg.c_str()); + } + }); + } } else { // TODO: Show inline notification show_notification("QuickMedia", "No message selected for deletion"); diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 198b2fd..11d0746 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -995,6 +995,7 @@ namespace QuickMedia { sync_data.messages.insert(sync_data.messages.end(), room_messages.begin() + room->messages_read_index, room_messages.end()); room->messages_read_index = room_messages.size(); } else { + // TODO: BUG fprintf(stderr, "Unexpected behavior!!!! get_room_sync_data said read index is %zu but we only have %zu messages\n", room->messages_read_index, room_messages.size()); room->messages_read_index = room_messages.size(); } @@ -1032,7 +1033,10 @@ namespace QuickMedia { size_t num_new_messages = num_messages_after - num_messages_before; messages.insert(messages.end(), room->get_messages_thread_unsafe().begin(), room->get_messages_thread_unsafe().begin() + num_new_messages); room->messages_read_index += num_new_messages; - assert(room->messages_read_index <= room->get_messages_thread_unsafe().size()); + //assert(room->messages_read_index <= num_messages_after); + // TODO: BUG + if(room->messages_read_index >= num_messages_after) + room->messages_read_index = num_messages_after; room->release_room_lock(); return PluginResult::OK; } @@ -1293,15 +1297,21 @@ namespace QuickMedia { if(!content_json.IsObject()) continue; - const rapidjson::Value &membership_json = GetMember(content_json, "membership"); - if(!membership_json.IsString() || strcmp(membership_json.GetString(), "join") != 0) - continue; - std::string sender_json_str = sender_json.GetString(); parse_user_info(content_json, sender_json_str, room_data); } } + static std::string thumbnail_url_extract_media_id(const std::string &media_url) { + size_t start = 0; + size_t end = media_url.size(); + if(strncmp(media_url.c_str(), "mxc://", 6) == 0) + start = 6; + if(media_url.size() >= start + 5 && strncmp(media_url.c_str() + media_url.size() - 5, "#auto", 5) == 0) + end = media_url.size() - 5; + return media_url.substr(start, end - start); + } + std::shared_ptr Matrix::parse_user_info(const rapidjson::Value &json, const std::string &user_id, RoomData *room_data) { assert(json.IsObject()); std::string avatar_url_str; @@ -1313,9 +1323,7 @@ namespace QuickMedia { auto user_info = std::make_shared(); user_info->user_id = user_id; - user_info->avatar_url = std::move(avatar_url_str); - if(strncmp(user_info->avatar_url.c_str(), "mxc://", 6) == 0) - user_info->avatar_url.erase(user_info->avatar_url.begin(), user_info->avatar_url.begin() + 6); + user_info->avatar_url = thumbnail_url_extract_media_id(avatar_url_str); if(!user_info->avatar_url.empty()) user_info->avatar_url = homeserver + "/_matrix/media/r0/thumbnail/" + user_info->avatar_url + "?width=32&height=32&method=crop"; // TODO: Remove the constant strings around to reduce memory usage (6.3mb) user_info->display_name = display_name_json.IsString() ? display_name_json.GetString() : user_id; @@ -1554,11 +1562,20 @@ namespace QuickMedia { if(!event_item_json.IsObject()) return nullptr; - const rapidjson::Value &sender_json = GetMember(event_item_json, "sender"); - if(!sender_json.IsString()) + const rapidjson::Value *sender_json = &GetMember(event_item_json, "sender"); + const rapidjson::Value *sender_json_orig = sender_json; + if(!sender_json->IsString()) return nullptr; - std::string sender_json_str = sender_json.GetString(); + bool sent_by_somebody_else = false; + const rapidjson::Value *state_key_json = &GetMember(event_item_json, "state_key"); + if(state_key_json->IsString() && state_key_json->GetStringLength() != 0) { + if(strcmp(sender_json->GetString(), state_key_json->GetString()) != 0) + sent_by_somebody_else = true; + sender_json = state_key_json; + } + + std::string sender_json_str = sender_json->GetString(); const rapidjson::Value &event_id_json = GetMember(event_item_json, "event_id"); if(!event_id_json.IsString()) @@ -1577,6 +1594,10 @@ namespace QuickMedia { return nullptr; } + auto user_sender = user; + if(sent_by_somebody_else) + user_sender = get_user_by_id(room_data, sender_json_orig->GetString()); + time_t timestamp = 0; const rapidjson::Value &origin_server_ts = GetMember(event_item_json, "origin_server_ts"); if(origin_server_ts.IsNumber()) @@ -1634,8 +1655,68 @@ namespace QuickMedia { return message; } - if(strcmp(type_json.GetString(), "m.room.message") != 0) - return nullptr; + if(strcmp(type_json.GetString(), "m.room.message") == 0) { + + } else if(strcmp(type_json.GetString(), "m.room.member") == 0) { + std::string body; + const rapidjson::Value &membership_json = GetMember(*content_json, "membership"); + if(strcmp(membership_json.GetString(), "join") == 0) { + const rapidjson::Value &unsigned_json = GetMember(event_item_json, "unsigned"); + if(unsigned_json.IsObject()) { + const rapidjson::Value &prev_content_json = GetMember(unsigned_json, "prev_content"); + if(prev_content_json.IsObject()) { + const rapidjson::Value &prev_displayname_json = GetMember(prev_content_json, "displayname"); + const rapidjson::Value &prev_avatar_url_json = GetMember(prev_content_json, "avatar_url"); + const rapidjson::Value &new_displayname_json = GetMember(*content_json, "displayname"); + const rapidjson::Value &new_avatar_url_json = GetMember(*content_json, "avatar_url"); + if(new_displayname_json.IsString() && (!prev_displayname_json.IsString() || strcmp(new_displayname_json.GetString(), prev_displayname_json.GetString()) != 0)) + body = user->display_name + " changed their display name to " + std::string(new_displayname_json.GetString()); + else if(!new_displayname_json.IsString() && prev_displayname_json.IsString()) + body = user->display_name + " removed their display name"; + else if(new_avatar_url_json.IsString() && (!prev_avatar_url_json.IsString() || strcmp(new_avatar_url_json.GetString(), prev_avatar_url_json.GetString()) != 0)) + body = user->display_name + " changed their profile picture"; + else if(!new_avatar_url_json.IsString() && prev_avatar_url_json.IsString()) + body = user->display_name + " removed their profile picture."; + else + body = user->display_name + " joined the room"; + } else { + body = user->display_name + " joined the room"; + } + } else { + body = user->display_name + " joined the room"; + } + } else if(strcmp(membership_json.GetString(), "leave") == 0) { + if(sent_by_somebody_else) + body = user->display_name + " was kicked from the room by " + user_sender->display_name; + else + body = user->display_name + " left the room"; + } else if(strcmp(membership_json.GetString(), "invite") == 0) { + body = user->display_name + " was invited to the room by " + user_sender->display_name; + } else if(strcmp(membership_json.GetString(), "ban") == 0) { + body = user->display_name + " was banned from the room by " + user_sender->display_name; + } else { + body = "unimplemented membership: " + std::string(membership_json.GetString()); + } + auto message = std::make_shared(); + message->type = MessageType::MEMBERSHIP; + message->user = user; + message->event_id = event_id_str; + message->body = std::move(body); + message->related_event_id = std::move(related_event_id); + message->related_event_type = related_event_type; + message->timestamp = timestamp; + return message; + } else { + auto message = std::make_shared(); + message->type = MessageType::UNIMPLEMENTED; + message->user = user; + message->event_id = event_id_str; + message->body = "unimplemented event type: " + std::string(type_json.GetString()); + message->related_event_id = std::move(related_event_id); + message->related_event_type = related_event_type; + message->timestamp = timestamp; + return message; + } const rapidjson::Value &body_json = GetMember(*content_json, "body"); if(!body_json.IsString()) @@ -1767,11 +1848,11 @@ namespace QuickMedia { continue; const rapidjson::Value &url_json = GetMember(content_json, "url"); - if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0) + if(!url_json.IsString()) continue; std::string url_json_str = url_json.GetString() + 6; - room_data->set_avatar_url(homeserver + "/_matrix/media/r0/thumbnail/" + std::move(url_json_str) + "?width=32&height=32&method=crop"); + room_data->set_avatar_url(homeserver + "/_matrix/media/r0/thumbnail/" + thumbnail_url_extract_media_id(url_json_str) + "?width=32&height=32&method=crop"); } bool has_room_name = room_data->has_name(); @@ -3081,7 +3162,7 @@ namespace QuickMedia { auto user = room->get_user_by_id(user_id); if(user) return user; - + #if 0 // TODO: Instead of guessing notification limit with 100, accumulate rooms unread_notifications count and use that as the limit // (and take into account that notification response may have notifications after call to sync above). char url[512]; @@ -3097,6 +3178,15 @@ namespace QuickMedia { // Is this a synapse bug? sometimes lazy_fetch_members doesn't contain all related clients fprintf(stderr, "User was not available locally, fetched from server...\n"); return parse_user_info(json_root, user_id, room); + #else + fprintf(stderr, "Unknown user: %s, creating locally... synapse bug?\n", user_id.c_str()); + auto user_info = std::make_shared(); + user_info->user_id = user_id; + user_info->display_name = user_id; + user_info->display_name_color = user_id_to_color(user_id); + room->add_user(user_info); + return user_info; + #endif } DownloadResult Matrix::download_json(rapidjson::Document &result, const std::string &url, std::vector additional_args, bool use_browser_useragent, std::string *err_msg) const { -- cgit v1.2.3