From e1a8d10b61c5f8ca092ba3aa458b661da29ba447 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 30 Sep 2020 19:07:05 +0200 Subject: Matrix: add message replying with ctrl+r, also use shared_ptr for BodyItem --- src/plugins/Matrix.cpp | 172 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 141 insertions(+), 31 deletions(-) (limited to 'src/plugins/Matrix.cpp') diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 513a9fb..50be2de 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -150,7 +150,7 @@ namespace QuickMedia { avatar_url = room_it->second->avatar_url; } - auto body_item = std::make_unique(std::move(room_name)); + auto body_item = BodyItem::create(std::move(room_name)); body_item->url = room_id_str; body_item->thumbnail_url = std::move(avatar_url); result_items.push_back(std::move(body_item)); @@ -159,21 +159,22 @@ namespace QuickMedia { return PluginResult::OK; } - static void room_messages_to_body_items(RoomData *room_data, Message *messages, size_t num_messages, BodyItems &result_items) { + static void room_messages_to_body_items(RoomData *room_data, std::shared_ptr *messages, size_t num_messages, BodyItems &result_items) { for(size_t i = 0; i < num_messages; ++i) { - const UserInfo &user_info = room_data->user_info[messages[i].user_id]; - auto body_item = std::make_unique(""); + const UserInfo &user_info = room_data->user_info[messages[i]->user_id]; + auto body_item = BodyItem::create(""); body_item->set_author(user_info.display_name); - body_item->set_description(messages[i].body); - if(!messages[i].thumbnail_url.empty()) - body_item->thumbnail_url = messages[i].thumbnail_url; - else if(!messages[i].url.empty() && messages->type == MessageType::IMAGE) - body_item->thumbnail_url = messages[i].url; + body_item->set_description(messages[i]->body); + if(!messages[i]->thumbnail_url.empty()) + body_item->thumbnail_url = messages[i]->thumbnail_url; + else if(!messages[i]->url.empty() && messages[i]->type == MessageType::IMAGE) + body_item->thumbnail_url = messages[i]->url; else body_item->thumbnail_url = user_info.avatar_url; // TODO: Show image thumbnail inline instead of url to image and showing it as the thumbnail of the body item - body_item->url = messages[i].url; + body_item->url = messages[i]->url; body_item->author_color = user_info.display_name_color; + body_item->userdata = (void*)messages[i].get(); // Note: messages[i] has to be valid as long as body_item is used! result_items.push_back(std::move(body_item)); } } @@ -368,6 +369,7 @@ namespace QuickMedia { continue; UserInfo user_info; + user_info.user_id = sender_json_str; user_info.avatar_url = avatar_url_json.asString(); 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); @@ -375,7 +377,7 @@ namespace QuickMedia { user_info.avatar_url = homeserver + "/_matrix/media/r0/thumbnail/" + user_info.avatar_url + "?width=32&height=32&method=crop"; user_info.display_name = display_name_json.asString(); user_info.display_name_color = user_id_to_color(sender_json_str); - room_data->user_info.push_back(std::move(user_info)); + room_data->user_info.push_back(user_info); room_data->user_info_by_user_id.insert(std::make_pair(sender_json_str, room_data->user_info.size() - 1)); } } @@ -399,7 +401,7 @@ namespace QuickMedia { if(!events_json.isArray()) return; - std::vector new_messages; + std::vector> new_messages; for(const Json::Value &event_item_json : events_json) { if(!event_item_json.isObject()) @@ -414,6 +416,12 @@ namespace QuickMedia { continue; std::string sender_json_str = sender_json.asString(); + + const Json::Value &event_id_json = event_item_json["event_id"]; + if(!event_id_json.isString()) + continue; + + std::string event_id_str = event_id_json.asString(); const Json::Value &content_json = event_item_json["content"]; if(!content_json.isObject()) @@ -425,6 +433,7 @@ namespace QuickMedia { auto user_it = room_data->user_info_by_user_id.find(sender_json_str); if(user_it == room_data->user_info_by_user_id.end()) { + // Note: this is important because otherwise replying and such is broken fprintf(stderr, "Warning: skipping unknown user: %s\n", sender_json_str.c_str()); continue; } @@ -433,36 +442,52 @@ namespace QuickMedia { if(!body_json.isString()) continue; + bool is_reply = false; + const Json::Value &relates_to_json = content_json["m.relates_to"]; + if(relates_to_json.isObject()) { + const Json::Value &in_reply_to_json = relates_to_json["m.in_reply_to"]; + is_reply = in_reply_to_json.isObject(); + } + if(strcmp(content_type.asCString(), "m.text") == 0) { - Message message; - message.user_id = user_it->second; - message.body = body_json.asString(); - message.type = MessageType::TEXT; - new_messages.push_back(std::move(message)); + auto message = std::make_shared(); + message->user_id = user_it->second; + message->event_id = event_id_str; + message->body = body_json.asString(); + message->type = MessageType::TEXT; + message->is_reply = is_reply; + new_messages.push_back(message); + room_data->message_by_event_id[event_id_str] = message; } else if(strcmp(content_type.asCString(), "m.image") == 0) { const Json::Value &url_json = content_json["url"]; if(!url_json.isString() || strncmp(url_json.asCString(), "mxc://", 6) != 0) continue; - Message message; - message.user_id = user_it->second; - message.body = body_json.asString(); - message.url = homeserver + "/_matrix/media/r0/download/" + url_json.asString().substr(6); - message.thumbnail_url = message_content_extract_thumbnail_url(content_json, homeserver); - message.type = MessageType::IMAGE; - new_messages.push_back(std::move(message)); + auto message = std::make_shared(); + message->user_id = user_it->second; + message->event_id = event_id_str; + message->body = body_json.asString(); + message->url = homeserver + "/_matrix/media/r0/download/" + url_json.asString().substr(6); + message->thumbnail_url = message_content_extract_thumbnail_url(content_json, homeserver); + message->type = MessageType::IMAGE; + message->is_reply = is_reply; + new_messages.push_back(message); + room_data->message_by_event_id[event_id_str] = message; } else if(strcmp(content_type.asCString(), "m.video") == 0) { const Json::Value &url_json = content_json["url"]; if(!url_json.isString() || strncmp(url_json.asCString(), "mxc://", 6) != 0) continue; - Message message; - message.user_id = user_it->second; - message.body = body_json.asString(); - message.url = homeserver + "/_matrix/media/r0/download/" + url_json.asString().substr(6); - message.thumbnail_url = message_content_extract_thumbnail_url(content_json, homeserver); - message.type = MessageType::VIDEO; - new_messages.push_back(std::move(message)); + auto message = std::make_shared(); + message->event_id = event_id_str; + message->user_id = user_it->second; + message->body = body_json.asString(); + message->url = homeserver + "/_matrix/media/r0/download/" + url_json.asString().substr(6); + message->thumbnail_url = message_content_extract_thumbnail_url(content_json, homeserver); + message->type = MessageType::VIDEO; + message->is_reply = is_reply; + new_messages.push_back(message); + room_data->message_by_event_id[event_id_str] = message; } } @@ -746,6 +771,91 @@ namespace QuickMedia { return PluginResult::OK; } + static std::string create_body_for_message_reply(const RoomData *room_data, const Message *message, const std::string &body) { + std::string related_to_body; + switch(message->type) { + case MessageType::TEXT: + related_to_body = message->body; + break; + case MessageType::IMAGE: + related_to_body = "sent an image"; + break; + case MessageType::VIDEO: + related_to_body = "sent a video"; + break; + } + return "> <" + room_data->user_info[message->user_id].user_id + "> " + std::move(related_to_body) + "\n\n" + body; + } + + // TODO: Add formatted_body just like element does with event_id; + + Json::Value relates_to_json(Json::objectValue); + relates_to_json["m.in_reply_to"] = std::move(in_reply_to_json); + + Json::Value request_data(Json::objectValue); + request_data["msgtype"] = message_type_to_request_msg_type_str(MessageType::TEXT); + request_data["body"] = create_body_for_message_reply(room_it->second.get(), relates_to_message, body); + request_data["m.relates_to"] = std::move(relates_to_json); + + Json::StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = ""; + + std::vector additional_args = { + { "-X", "PUT" }, + { "-H", "content-type: application/json" }, + { "-H", "Authorization: Bearer " + access_token }, + { "--data-binary", Json::writeString(builder, std::move(request_data)) } + }; + + char request_url[512]; + snprintf(request_url, sizeof(request_url), "%s/_matrix/client/r0/rooms/%s/send/m.room.message/m%ld.%.*s", homeserver.c_str(), room_id.c_str(), time(NULL), (int)random_readable_chars.size(), random_readable_chars.c_str()); + fprintf(stderr, "Post message to |%s|\n", request_url); + + std::string server_response; + if(download_to_string(request_url, server_response, std::move(additional_args), use_tor, true) != DownloadResult::OK) + return PluginResult::NET_ERR; + + if(server_response.empty()) + return PluginResult::ERR; + + Json::Value json_root; + Json::CharReaderBuilder json_builder; + std::unique_ptr json_reader(json_builder.newCharReader()); + std::string json_errors; + if(!json_reader->parse(&server_response[0], &server_response[server_response.size()], &json_root, &json_errors)) { + fprintf(stderr, "Matrix post message response parse error: %s\n", json_errors.c_str()); + return PluginResult::ERR; + } + + if(!json_root.isObject()) + return PluginResult::ERR; + + const Json::Value &event_id_json = json_root["event_id"]; + if(!event_id_json.isString()) + return PluginResult::ERR; + + fprintf(stderr, "Matrix post reply, response event id: %s\n", event_id_json.asCString()); + return PluginResult::OK; + } + // Returns empty string on error static const char* file_get_filename(const std::string &filepath) { size_t index = filepath.rfind('/'); -- cgit v1.2.3