From 34828885b99a808a09bb05820faa3f10a5025a47 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 12 Mar 2021 16:13:33 +0100 Subject: Matrix: add support for sending (code)blocks and greentext in replies --- src/plugins/Matrix.cpp | 129 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 85 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index c725fc8..1a70a54 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -2577,6 +2577,84 @@ namespace QuickMedia { return "m.file"; } + static size_t find_backquote_index_with_escape(const std::string &str, size_t start_index = 0) { + bool escape = false; + for(size_t i = start_index; i < str.size(); ++i) { + char c = str[i]; + if(c == '\\') { + escape = !escape; + } else if(c == '`' && !escape) { + return i; + } else { + escape = false; + } + } + return std::string::npos; + } + + static void formatted_body_add_line(std::string &formatted_body, const std::string &line_str) { + size_t index = 0; + while(true) { + size_t backquote_start_index = find_backquote_index_with_escape(line_str, index); + if(backquote_start_index != std::string::npos) { + size_t backquote_end_index = find_backquote_index_with_escape(line_str, backquote_start_index + 1); + if(backquote_end_index != std::string::npos) { + formatted_body += line_str.substr(0, backquote_start_index); + formatted_body += ""; + formatted_body += line_str.substr(backquote_start_index + 1, backquote_end_index - (backquote_start_index + 1)); + formatted_body += ""; + index = backquote_end_index + 1; + continue; + } + } + + formatted_body += line_str.substr(index); + break; + } + } + + static std::string body_to_formatted_body(const std::string &body) { + std::string formatted_body; + bool is_inside_code_block = false; + bool is_first_line = true; + string_split(body, '\n', [&formatted_body, &is_inside_code_block, &is_first_line](const char *str, size_t size){ + if(!is_first_line) + formatted_body += "
"; + + std::string line_str(str, size); + html_escape_sequences(line_str); + if(size >= 3 && strncmp(str, "```", 3) == 0) { + if(is_inside_code_block) { + formatted_body += ""; + is_inside_code_block = false; + } else { + if(size > 3) { + formatted_body += "
";
+                    } else {
+                        formatted_body += "
";
+                    }
+                    is_inside_code_block = true;
+                }
+                is_first_line = true;
+            } else {
+                if(!is_inside_code_block && size > 0 && str[0] == '>') {
+                    formatted_body += "";
+                    formatted_body_add_line(formatted_body, line_str);
+                    formatted_body += "";
+                } else {
+                    if(is_inside_code_block)
+                        formatted_body += line_str;
+                    else
+                        formatted_body_add_line(formatted_body, line_str);
+                }
+                is_first_line = false;
+            }
+
+            return true;
+        });
+        return formatted_body;
+    }
+
     PluginResult Matrix::post_message(RoomData *room, const std::string &body, std::string &event_id_response, const std::optional &file_info, const std::optional &thumbnail_info, const std::string &msgtype) {
         std::string transaction_id = create_transaction_id();
         if(transaction_id.empty())
@@ -2585,26 +2663,8 @@ namespace QuickMedia {
             my_events_transaction_ids.insert(transaction_id);
 
         std::string formatted_body;
-        bool contains_formatted_text = false;
-        if(!file_info) {
-            int line = 0;
-            string_split(body, '\n', [&formatted_body, &contains_formatted_text, &line](const char *str, size_t size){
-                if(line > 0)
-                    formatted_body += "
"; - std::string line_str(str, size); - html_escape_sequences(line_str); - if(size > 0 && str[0] == '>') { - formatted_body += ""; - formatted_body += line_str; - formatted_body += ""; - contains_formatted_text = true; - } else { - formatted_body += line_str; - } - ++line; - return true; - }); - } + if(!file_info) + formatted_body = body_to_formatted_body(body); rapidjson::Document request_data(rapidjson::kObjectType); if(msgtype.empty()) @@ -2612,7 +2672,7 @@ namespace QuickMedia { else request_data.AddMember("msgtype", rapidjson::StringRef(msgtype.c_str()), request_data.GetAllocator()); request_data.AddMember("body", rapidjson::StringRef(body.c_str()), request_data.GetAllocator()); - if(contains_formatted_text) { + if(!formatted_body.empty()) { request_data.AddMember("format", "org.matrix.custom.html", request_data.GetAllocator()); request_data.AddMember("formatted_body", rapidjson::StringRef(formatted_body.c_str()), request_data.GetAllocator()); } @@ -2752,9 +2812,8 @@ namespace QuickMedia { } static std::string create_formatted_body_for_message_reply(RoomData *room, const Message *message, const std::string &body) { - std::string formatted_body = body; + std::string formatted_body = body_to_formatted_body(body); std::string related_to_body = get_reply_message(message); - html_escape_sequences(formatted_body); html_escape_sequences(related_to_body); // TODO: Add keybind to navigate to the reply message, which would also depend on this formatting. // Note: user id and event id is not url escaped here on purpose, because that messes up riot.im replies for certain user ids... @@ -2834,30 +2893,12 @@ namespace QuickMedia { return PluginResult::ERR; my_events_transaction_ids.insert(transaction_id); - std::string formatted_body; - bool contains_formatted_text = false; - int line = 0; - string_split(body, '\n', [&formatted_body, &contains_formatted_text, &line](const char *str, size_t size){ - if(line > 0) - formatted_body += "
"; - if(size > 0 && str[0] == '>') { - std::string line(str, size); - html_escape_sequences(line); - formatted_body += ""; - formatted_body += line; - formatted_body += ""; - contains_formatted_text = true; - } else { - formatted_body.append(str, size); - } - ++line; - return true; - }); + std::string formatted_body = body_to_formatted_body(body); rapidjson::Document new_content_json(rapidjson::kObjectType); new_content_json.AddMember("msgtype", "m.text", new_content_json.GetAllocator()); new_content_json.AddMember("body", rapidjson::StringRef(body.c_str()), new_content_json.GetAllocator()); - if(contains_formatted_text) { + if(!formatted_body.empty()) { new_content_json.AddMember("format", "org.matrix.custom.html", new_content_json.GetAllocator()); new_content_json.AddMember("formatted_body", rapidjson::StringRef(formatted_body.c_str()), new_content_json.GetAllocator()); } @@ -2872,7 +2913,7 @@ namespace QuickMedia { rapidjson::Document request_data(rapidjson::kObjectType); request_data.AddMember("msgtype", "m.text", request_data.GetAllocator()); // TODO: Allow other types of edits request_data.AddMember("body", rapidjson::StringRef(body_edit_str.c_str()), request_data.GetAllocator()); - if(contains_formatted_text) { + if(!formatted_body.empty()) { formatted_body_edit_str = " * " + formatted_body; request_data.AddMember("format", "org.matrix.custom.html", request_data.GetAllocator()); request_data.AddMember("formatted_body", rapidjson::StringRef(formatted_body_edit_str.c_str()), request_data.GetAllocator()); -- cgit v1.2.3