aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/Matrix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/Matrix.cpp')
-rw-r--r--src/plugins/Matrix.cpp172
1 files changed, 141 insertions, 31 deletions
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<BodyItem>(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<Message> *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<BodyItem>("");
+ 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<Message> new_messages;
+ std::vector<std::shared_ptr<Message>> 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>();
+ 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>();
+ 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>();
+ 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 <mx-reply><blockquote... and also support greentext with that
+ PluginResult Matrix::post_reply(const std::string &room_id, const std::string &body, void *relates_to) {
+ auto room_it = room_data_by_id.find(room_id);
+ if(room_it == room_data_by_id.end()) {
+ fprintf(stderr, "Error: no such room: %s\n", room_id.c_str());
+ return PluginResult::ERR;
+ }
+
+ Message *relates_to_message = (Message*)relates_to;
+
+ char random_characters[18];
+ if(!generate_random_characters(random_characters, sizeof(random_characters)))
+ return PluginResult::ERR;
+
+ std::string random_readable_chars = random_characters_to_readable_string(random_characters, sizeof(random_characters));
+
+ Json::Value in_reply_to_json(Json::objectValue);
+ in_reply_to_json["event_id"] = relates_to_message->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<CommandArg> 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::CharReader> 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('/');