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.cpp244
1 files changed, 195 insertions, 49 deletions
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 1d471fc..db31303 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -99,7 +99,7 @@ namespace QuickMedia {
return std::abs(hash);
}
- static sf::Color user_id_to_color(const std::string &user_id) {
+ sf::Color user_id_to_color(const std::string &user_id) {
const int num_colors = 8;
const sf::Color colors[num_colors] = {
sf::Color(54, 139, 214),
@@ -115,13 +115,13 @@ namespace QuickMedia {
}
UserInfo::UserInfo(RoomData *room, std::string user_id) :
- room(room), display_name_color(user_id_to_color(user_id)), user_id(user_id), resolve_state(UserResolveState::NOT_RESOLVED)
+ room(room), display_name_color(user_id_to_color(user_id)), user_id(user_id)
{
display_name = user_id;
}
UserInfo::UserInfo(RoomData *room, std::string user_id, std::string display_name, std::string avatar_url) :
- room(room), display_name_color(user_id_to_color(user_id)), user_id(std::move(user_id)), resolve_state(UserResolveState::RESOLVED), display_name(std::move(display_name)), avatar_url(std::move(avatar_url)) {
+ room(room), display_name_color(user_id_to_color(user_id)), user_id(std::move(user_id)), display_name(std::move(display_name)), avatar_url(std::move(avatar_url)) {
}
@@ -168,13 +168,11 @@ namespace QuickMedia {
user->display_name = std::move(display_name);
if(user->display_name.empty())
user->display_name = user->user_id;
- user->resolve_state = UserResolveState::RESOLVED;
}
void RoomData::set_user_avatar_url(std::shared_ptr<UserInfo> &user, std::string avatar_url) {
std::lock_guard<std::recursive_mutex> lock(user_mutex);
user->avatar_url = std::move(avatar_url);
- user->resolve_state = UserResolveState::RESOLVED;
}
size_t RoomData::prepend_messages_reverse(const std::vector<std::shared_ptr<Message>> &new_messages) {
@@ -219,6 +217,16 @@ namespace QuickMedia {
return message_it->second;
}
+ std::vector<std::shared_ptr<UserInfo>> RoomData::get_users() {
+ std::lock_guard<std::recursive_mutex> lock(user_mutex);
+ std::vector<std::shared_ptr<UserInfo>> users(user_info_by_user_id.size());
+ size_t i = 0;
+ for(auto &[user_id, user] : user_info_by_user_id) {
+ users[i++] = user;
+ }
+ return users;
+ }
+
std::vector<std::shared_ptr<UserInfo>> RoomData::get_users_excluding_me(const std::string &my_user_id) {
std::lock_guard<std::recursive_mutex> lock(user_mutex);
std::vector<std::shared_ptr<UserInfo>> users_excluding_me;
@@ -1697,9 +1705,24 @@ namespace QuickMedia {
//auto user_info = std::make_shared<UserInfo>(room_data, user_id, std::move(display_name), std::move(avatar_url));
// Overwrites user data
//room_data->add_user(user_info);
- auto user_info = get_user_by_id(room_data, user_id);
- room_data->set_user_display_name(user_info, std::move(display_name));
- room_data->set_user_avatar_url(user_info, std::move(avatar_url));
+ bool is_new_user;
+ auto user_info = get_user_by_id(room_data, user_id, &is_new_user);
+ room_data->set_user_display_name(user_info, display_name);
+ room_data->set_user_avatar_url(user_info, avatar_url);
+
+ MatrixEventUserInfo event_user_info;
+ event_user_info.user_id = user_id;
+ event_user_info.display_name = display_name;
+ event_user_info.avatar_url = avatar_url;
+
+ if(is_new_user) {
+ auto event = std::make_unique<MatrixAddUserEvent>(std::move(event_user_info));
+ trigger_event(room_data, std::move(event));
+ } else {
+ auto event = std::make_unique<MatrixUserInfoEvent>(std::move(event_user_info));
+ trigger_event(room_data, std::move(event));
+ }
+
return user_info;
}
@@ -1945,16 +1968,28 @@ namespace QuickMedia {
if(!content_json->IsObject())
return nullptr;
- auto user = get_user_by_id(room_data, sender_json_str);
- if(!user) {
- // Note: this is important because otherwise replying and such is broken
- fprintf(stderr, "Warning: skipping unknown user: %s\n", sender_json_str.c_str());
- return nullptr;
+ bool is_new_user;
+ auto user = get_user_by_id(room_data, sender_json_str, &is_new_user);
+
+ if(is_new_user) {
+ MatrixEventUserInfo user_info;
+ user_info.user_id = user->user_id;
+ auto event = std::make_unique<MatrixAddUserEvent>(std::move(user_info));
+ trigger_event(room_data, std::move(event));
}
auto user_sender = user;
- if(sent_by_somebody_else)
- user_sender = get_user_by_id(room_data, sender_json_orig->GetString());
+ if(sent_by_somebody_else) {
+ bool is_new_user;
+ user_sender = get_user_by_id(room_data, sender_json_orig->GetString(), &is_new_user);
+
+ if(is_new_user) {
+ MatrixEventUserInfo user_info;
+ user_info.user_id = user_sender->user_id;
+ auto event = std::make_unique<MatrixAddUserEvent>(std::move(user_info));
+ trigger_event(room_data, std::move(event));
+ }
+ }
time_t timestamp = 0;
const rapidjson::Value &origin_server_ts = GetMember(event_item_json, "origin_server_ts");
@@ -2053,6 +2088,10 @@ namespace QuickMedia {
const rapidjson::Value &new_displayname_json = GetMember(*content_json, "displayname");
const rapidjson::Value &new_avatar_url_json = GetMember(*content_json, "avatar_url");
const rapidjson::Value &prev_membership_json = GetMember(prev_content_json, "membership");
+
+ std::optional<std::string> new_display_name;
+ std::optional<std::string> new_avatar_url;
+
if(prev_membership_json.IsString() && strcmp(prev_membership_json.GetString(), "leave") == 0) {
body = user_display_name + " joined the room";
} else if(new_displayname_json.IsString() && new_displayname_json.GetStringLength() > 0 && (!prev_displayname_json.IsString() || strcmp(new_displayname_json.GetString(), prev_displayname_json.GetString()) != 0)) {
@@ -2063,22 +2102,36 @@ namespace QuickMedia {
else
prev_displayname_str = sender_json_str;
body = extract_first_line_remove_newline_elipses(prev_displayname_str, AUTHOR_MAX_LENGTH) + " changed their display name to " + extract_first_line_remove_newline_elipses(new_displayname_str, AUTHOR_MAX_LENGTH);
+ new_display_name = new_displayname_str;
room_data->set_user_display_name(user, std::move(new_displayname_str));
} else if((!new_displayname_json.IsString() || new_displayname_json.GetStringLength() == 0) && prev_displayname_json.IsString()) {
body = user_display_name + " removed their display name";
+ new_display_name = "";
room_data->set_user_display_name(user, "");
} else if(new_avatar_url_json.IsString() && new_avatar_url_json.GetStringLength() > 0 && (!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";
std::string new_avatar_url_str = thumbnail_url_extract_media_id(new_avatar_url_json.GetString());
if(!new_avatar_url_str.empty())
new_avatar_url_str = get_thumbnail_url(homeserver, new_avatar_url_str); // TODO: Remove the constant strings around to reduce memory usage (6.3mb)
+ new_avatar_url = new_avatar_url_str;
room_data->set_user_avatar_url(user, std::move(new_avatar_url_str));
} else if((!new_avatar_url_json.IsString() || new_avatar_url_json.GetStringLength() == 0) && prev_avatar_url_json.IsString()) {
body = user_display_name + " removed their profile picture";
+ new_avatar_url = "";
room_data->set_user_avatar_url(user, "");
} else {
body = user_display_name + " joined the room";
}
+
+ if(new_display_name || new_avatar_url) {
+ MatrixEventUserInfo user_info;
+ user_info.user_id = user->user_id;
+ user_info.display_name = std::move(new_display_name);
+ user_info.avatar_url = std::move(new_avatar_url);
+
+ auto event = std::make_unique<MatrixUserInfoEvent>(std::move(user_info));
+ trigger_event(room_data, std::move(event));
+ }
} else {
body = user_display_name + " joined the room";
}
@@ -2483,6 +2536,8 @@ namespace QuickMedia {
const rapidjson::Value &membership_json = GetMember(content_json, "membership");
if(membership_json.IsString() && strcmp(membership_json.GetString(), "invite") == 0) {
+ // TODO: Check this this room should be saved in the rooms list, which might be needed if the server doesn't give a non-invite events
+ // for the same data (user display name update, etc)
Invite invite;
RoomData invite_room;
events_add_user_info(events_json, &invite_room);
@@ -2490,10 +2545,6 @@ namespace QuickMedia {
std::string sender_json_str(sender_json.GetString(), sender_json.GetStringLength());
auto invited_by = get_user_by_id(&invite_room, sender_json_str);
- if(!invited_by) {
- fprintf(stderr, "Invited by unknown user. Bug in homeserver?\n");
- break;
- }
set_room_info_to_users_if_empty(&invite_room, sender_json_str);
@@ -2721,11 +2772,68 @@ namespace QuickMedia {
}
}
- static std::string body_to_formatted_body(const std::string &body) {
+ void Matrix::replace_mentions(RoomData *room, std::string &text) {
+ size_t index = 0;
+ while(index < text.size()) {
+ index = text.find('@', index);
+ if(index == std::string::npos)
+ return;
+
+ bool is_valid_user_id = false;
+ bool user_id_finished = false;
+ size_t user_id_start = index;
+ size_t user_id_end = 0;
+ index += 1;
+ for(size_t i = index; i < text.size() && !user_id_finished; ++i) {
+ char c = text[i];
+ switch(c) {
+ case ':': {
+ if(is_valid_user_id) {
+ user_id_finished = true;
+ user_id_end = i;
+ index = i;
+ }
+ is_valid_user_id = true;
+ break;
+ }
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '@': {
+ user_id_finished = true;
+ user_id_end = i;
+ index = i;
+ break;
+ }
+ }
+ }
+
+ if(user_id_end == 0)
+ user_id_end = text.size();
+
+ if(is_valid_user_id) {
+ std::string user_id = text.substr(user_id_start, user_id_end - user_id_start);
+ auto user = get_user_by_id(room, user_id, nullptr, false);
+ if(user) {
+ std::string user_id_escaped = user_id;
+ html_escape_sequences(user_id_escaped);
+
+ std::string display_name_escaped = room->get_user_display_name(user);
+ html_escape_sequences(display_name_escaped);
+
+ std::string mention_text = "<a href=\"https://matrix.to/#/" + user_id_escaped + "\">" + display_name_escaped + "</a>";
+ text.replace(user_id_start, user_id.size(), mention_text);
+ }
+ }
+ }
+ }
+
+ std::string Matrix::body_to_formatted_body(RoomData *room, 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){
+ string_split(body, '\n', [this, room, &formatted_body, &is_inside_code_block, &is_first_line](const char *str, size_t size){
if(!is_first_line)
formatted_body += "<br/>";
@@ -2747,13 +2855,16 @@ namespace QuickMedia {
} else {
if(!is_inside_code_block && size > 0 && str[0] == '>') {
formatted_body += "<font color=\"#789922\">";
+ replace_mentions(room, line_str);
formatted_body_add_line(formatted_body, line_str);
formatted_body += "</font>";
} else {
- if(is_inside_code_block)
+ if(is_inside_code_block) {
formatted_body += line_str;
- else
+ } else {
+ replace_mentions(room, line_str);
formatted_body_add_line(formatted_body, line_str);
+ }
}
is_first_line = false;
}
@@ -2772,7 +2883,7 @@ namespace QuickMedia {
std::string formatted_body;
if(!file_info)
- formatted_body = body_to_formatted_body(body);
+ formatted_body = body_to_formatted_body(room, body);
rapidjson::Document request_data(rapidjson::kObjectType);
if(msgtype.empty())
@@ -2919,8 +3030,8 @@ namespace QuickMedia {
return "";
}
- static std::string create_formatted_body_for_message_reply(RoomData *room, const Message *message, const std::string &body) {
- std::string formatted_body = body_to_formatted_body(body);
+ std::string Matrix::create_formatted_body_for_message_reply(RoomData *room, const Message *message, const std::string &body) {
+ std::string formatted_body = body_to_formatted_body(room, body);
std::string related_to_body = get_reply_message(message);
html_escape_sequences(related_to_body);
// TODO: Add keybind to navigate to the reply message, which would also depend on this formatting.
@@ -3001,7 +3112,7 @@ namespace QuickMedia {
return PluginResult::ERR;
my_events_transaction_ids.insert(transaction_id);
- std::string formatted_body = body_to_formatted_body(body);
+ std::string formatted_body = body_to_formatted_body(room, body);
rapidjson::Document new_content_json(rapidjson::kObjectType);
new_content_json.AddMember("msgtype", "m.text", new_content_json.GetAllocator());
@@ -3908,34 +4019,25 @@ namespace QuickMedia {
delegate->clear_data();
}
- std::shared_ptr<UserInfo> Matrix::get_user_by_id(RoomData *room, const std::string &user_id) {
+ std::shared_ptr<UserInfo> Matrix::get_user_by_id(RoomData *room, const std::string &user_id, bool *is_new_user, bool create_if_not_found) {
auto user = room->get_user_by_id(user_id);
- if(user)
+ if(user) {
+ if(is_new_user)
+ *is_new_user = false;
return user;
+ }
+
+ if(!create_if_not_found)
+ return nullptr;
//fprintf(stderr, "Unknown user: %s, creating locally... synapse bug?\n", user_id.c_str());
auto user_info = std::make_shared<UserInfo>(room, user_id);
room->add_user(user_info);
+ if(is_new_user)
+ *is_new_user = true;
return user_info;
}
- void Matrix::update_user_with_latest_state(RoomData *room, const std::string &user_id) {
- char url[512];
- snprintf(url, sizeof(url), "%s/_matrix/client/r0/profile/%s", homeserver.c_str(), user_id.c_str());
-
- rapidjson::Document json_root;
- DownloadResult download_result = download_json(json_root, url, {}, true);
- if(download_result != DownloadResult::OK || !json_root.IsObject()) {
- fprintf(stderr, "Fetching profile for user %s failed!\n", user_id.c_str());
- auto user = get_user_by_id(room, user_id);
- assert(user);
- user->resolve_state = UserResolveState::RESOLVED;
- return;
- }
-
- parse_user_info(json_root, user_id, room);
- }
-
void Matrix::update_room_users(RoomData *room) {
#if 1
std::vector<CommandArg> additional_args = {
@@ -3964,7 +4066,8 @@ namespace QuickMedia {
const rapidjson::Value &display_name_json = GetMember(joined_obj.value, "display_name");
const rapidjson::Value &displayname_json = GetMember(joined_obj.value, "displayname"); // Construct bug...
std::string user_id(joined_obj.name.GetString(), joined_obj.name.GetStringLength());
- auto user = get_user_by_id(room, user_id);
+ bool is_new_user;
+ auto user = get_user_by_id(room, user_id, &is_new_user);
assert(user);
std::string display_name;
@@ -3980,8 +4083,21 @@ namespace QuickMedia {
avatar_url = std::string(avatar_url_json.GetString(), avatar_url_json.GetStringLength());
if(!avatar_url.empty())
avatar_url = get_thumbnail_url(homeserver, thumbnail_url_extract_media_id(avatar_url)); // TODO: Remove the constant strings around to reduce memory usage (6.3mb)
- room->set_user_avatar_url(user, std::move(avatar_url));
- room->set_user_display_name(user, std::move(display_name));
+ room->set_user_avatar_url(user, avatar_url);
+ room->set_user_display_name(user, display_name);
+
+ MatrixEventUserInfo user_info;
+ user_info.user_id = user_id;
+ user_info.display_name = display_name;
+ user_info.avatar_url = avatar_url;
+
+ if(is_new_user) {
+ auto event = std::make_unique<MatrixAddUserEvent>(std::move(user_info));
+ trigger_event(room, std::move(event));
+ } else {
+ auto event = std::make_unique<MatrixUserInfoEvent>(std::move(user_info));
+ trigger_event(room, std::move(event));
+ }
}
#else
std::vector<CommandArg> additional_args = {
@@ -4051,4 +4167,34 @@ namespace QuickMedia {
return INITIAL_FILTER;
#endif
}
+
+ void Matrix::enable_event_queue(RoomData *room) {
+ std::lock_guard<std::mutex> lock(event_queue_mutex);
+ assert(!current_event_queue_room);
+ current_event_queue_room = room;
+ }
+
+ void Matrix::disable_event_queue() {
+ std::lock_guard<std::mutex> lock(event_queue_mutex);
+ assert(current_event_queue_room);
+ current_event_queue_room = nullptr;
+ event_queue.clear();
+ }
+
+ std::unique_ptr<MatrixEvent> Matrix::pop_event() {
+ std::lock_guard<std::mutex> lock(event_queue_mutex);
+ if(!current_event_queue_room || event_queue.empty())
+ return nullptr;
+
+ auto event_data = std::move(event_queue.front());
+ event_queue.pop_front();
+ return event_data;
+ }
+
+ void Matrix::trigger_event(RoomData *room, std::unique_ptr<MatrixEvent> event) {
+ std::lock_guard<std::mutex> lock(event_queue_mutex);
+ if(sync_is_cache || !current_event_queue_room || current_event_queue_room != room)
+ return;
+ event_queue.push_back(std::move(event));
+ }
} \ No newline at end of file