Move DialogFilter implementation to separate files.
GitOrigin-RevId: c58b7f42811242882f00d48152417826e465b5bc
This commit is contained in:
parent
e665edc4fc
commit
25f77585c9
@ -395,6 +395,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/DhCache.cpp
|
||||
td/telegram/DialogAdministrator.cpp
|
||||
td/telegram/DialogDb.cpp
|
||||
td/telegram/DialogFilter.cpp
|
||||
td/telegram/DialogId.cpp
|
||||
td/telegram/DialogLocation.cpp
|
||||
td/telegram/DialogParticipant.cpp
|
||||
@ -548,6 +549,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/DialogAdministrator.h
|
||||
td/telegram/DialogDate.h
|
||||
td/telegram/DialogDb.h
|
||||
td/telegram/DialogFilter.h
|
||||
td/telegram/DialogFilterId.h
|
||||
td/telegram/DialogId.h
|
||||
td/telegram/DialogListId.h
|
||||
@ -691,6 +693,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/AudiosManager.hpp
|
||||
td/telegram/AuthManager.hpp
|
||||
td/telegram/BackgroundType.hpp
|
||||
td/telegram/DialogFilter.hpp
|
||||
td/telegram/Document.hpp
|
||||
td/telegram/DocumentsManager.hpp
|
||||
td/telegram/DraftMessage.hpp
|
||||
|
409
td/telegram/DialogFilter.cpp
Normal file
409
td/telegram/DialogFilter.cpp
Normal file
@ -0,0 +1,409 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/telegram/DialogFilter.h"
|
||||
|
||||
#include "td/telegram/misc.h"
|
||||
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace td {
|
||||
|
||||
unique_ptr<DialogFilter> DialogFilter::get_dialog_filter(telegram_api::object_ptr<telegram_api::dialogFilter> filter,
|
||||
bool with_id) {
|
||||
DialogFilterId dialog_filter_id(filter->id_);
|
||||
if (with_id && !dialog_filter_id.is_valid()) {
|
||||
LOG(ERROR) << "Receive invalid " << to_string(filter);
|
||||
return nullptr;
|
||||
}
|
||||
auto dialog_filter = make_unique<DialogFilter>();
|
||||
dialog_filter->dialog_filter_id = dialog_filter_id;
|
||||
dialog_filter->title = std::move(filter->title_);
|
||||
dialog_filter->emoji = std::move(filter->emoticon_);
|
||||
std::unordered_set<DialogId, DialogIdHash> added_dialog_ids;
|
||||
dialog_filter->pinned_dialog_ids = InputDialogId::get_input_dialog_ids(filter->pinned_peers_, &added_dialog_ids);
|
||||
dialog_filter->included_dialog_ids = InputDialogId::get_input_dialog_ids(filter->include_peers_, &added_dialog_ids);
|
||||
dialog_filter->excluded_dialog_ids = InputDialogId::get_input_dialog_ids(filter->exclude_peers_, &added_dialog_ids);
|
||||
auto flags = filter->flags_;
|
||||
dialog_filter->exclude_muted = (flags & telegram_api::dialogFilter::EXCLUDE_MUTED_MASK) != 0;
|
||||
dialog_filter->exclude_read = (flags & telegram_api::dialogFilter::EXCLUDE_READ_MASK) != 0;
|
||||
dialog_filter->exclude_archived = (flags & telegram_api::dialogFilter::EXCLUDE_ARCHIVED_MASK) != 0;
|
||||
dialog_filter->include_contacts = (flags & telegram_api::dialogFilter::CONTACTS_MASK) != 0;
|
||||
dialog_filter->include_non_contacts = (flags & telegram_api::dialogFilter::NON_CONTACTS_MASK) != 0;
|
||||
dialog_filter->include_bots = (flags & telegram_api::dialogFilter::BOTS_MASK) != 0;
|
||||
dialog_filter->include_groups = (flags & telegram_api::dialogFilter::GROUPS_MASK) != 0;
|
||||
dialog_filter->include_channels = (flags & telegram_api::dialogFilter::BROADCASTS_MASK) != 0;
|
||||
return dialog_filter;
|
||||
}
|
||||
|
||||
void DialogFilter::remove_secret_chat_dialog_ids() {
|
||||
auto remove_secret_chats = [](vector<InputDialogId> &input_dialog_ids) {
|
||||
td::remove_if(input_dialog_ids, [](InputDialogId input_dialog_id) {
|
||||
return input_dialog_id.get_dialog_id().get_type() == DialogType::SecretChat;
|
||||
});
|
||||
};
|
||||
remove_secret_chats(pinned_dialog_ids);
|
||||
remove_secret_chats(included_dialog_ids);
|
||||
remove_secret_chats(excluded_dialog_ids);
|
||||
}
|
||||
|
||||
bool DialogFilter::is_empty(bool for_server) const {
|
||||
if (include_contacts || include_non_contacts || include_bots || include_groups || include_channels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (for_server) {
|
||||
vector<InputDialogId> empty_input_dialog_ids;
|
||||
return InputDialogId::are_equivalent(pinned_dialog_ids, empty_input_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(included_dialog_ids, empty_input_dialog_ids);
|
||||
} else {
|
||||
return pinned_dialog_ids.empty() && included_dialog_ids.empty();
|
||||
}
|
||||
}
|
||||
|
||||
Status DialogFilter::check_limits() const {
|
||||
auto get_server_dialog_count = [](const vector<InputDialogId> &input_dialog_ids) {
|
||||
int32 result = 0;
|
||||
for (auto &input_dialog_id : input_dialog_ids) {
|
||||
if (input_dialog_id.get_dialog_id().get_type() != DialogType::SecretChat) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
auto excluded_server_dialog_count = get_server_dialog_count(excluded_dialog_ids);
|
||||
auto included_server_dialog_count = get_server_dialog_count(included_dialog_ids);
|
||||
auto pinned_server_dialog_count = get_server_dialog_count(pinned_dialog_ids);
|
||||
|
||||
auto excluded_secret_dialog_count = static_cast<int32>(excluded_dialog_ids.size()) - excluded_server_dialog_count;
|
||||
auto included_secret_dialog_count = static_cast<int32>(included_dialog_ids.size()) - included_server_dialog_count;
|
||||
auto pinned_secret_dialog_count = static_cast<int32>(pinned_dialog_ids.size()) - pinned_server_dialog_count;
|
||||
|
||||
if (excluded_server_dialog_count > MAX_INCLUDED_FILTER_DIALOGS ||
|
||||
excluded_secret_dialog_count > MAX_INCLUDED_FILTER_DIALOGS) {
|
||||
return Status::Error(400, "Maximum number of excluded chats exceeded");
|
||||
}
|
||||
if (included_server_dialog_count > MAX_INCLUDED_FILTER_DIALOGS ||
|
||||
included_secret_dialog_count > MAX_INCLUDED_FILTER_DIALOGS) {
|
||||
return Status::Error(400, "Maximum number of included chats exceeded");
|
||||
}
|
||||
if (included_server_dialog_count + pinned_server_dialog_count > MAX_INCLUDED_FILTER_DIALOGS ||
|
||||
included_secret_dialog_count + pinned_secret_dialog_count > MAX_INCLUDED_FILTER_DIALOGS) {
|
||||
return Status::Error(400, "Maximum number of pinned chats exceeded");
|
||||
}
|
||||
|
||||
if (is_empty(false)) {
|
||||
return Status::Error(400, "Folder must contain at least 1 chat");
|
||||
}
|
||||
|
||||
if (include_contacts && include_non_contacts && include_bots && include_groups && include_channels &&
|
||||
exclude_archived && !exclude_read && !exclude_muted) {
|
||||
return Status::Error(400, "Folder must be different from the main chat list");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
string DialogFilter::get_emoji_by_icon_name(const string &icon_name) {
|
||||
init_icon_names();
|
||||
auto it = icon_name_to_emoji_.find(icon_name);
|
||||
if (it != icon_name_to_emoji_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return string();
|
||||
}
|
||||
|
||||
string DialogFilter::get_icon_name() const {
|
||||
init_icon_names();
|
||||
auto it = emoji_to_icon_name_.find(emoji);
|
||||
if (it != emoji_to_icon_name_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
if (!pinned_dialog_ids.empty() || !included_dialog_ids.empty() || !excluded_dialog_ids.empty()) {
|
||||
return "Custom";
|
||||
}
|
||||
|
||||
if (include_contacts || include_non_contacts) {
|
||||
if (!include_bots && !include_groups && !include_channels) {
|
||||
return "Private";
|
||||
}
|
||||
} else {
|
||||
if (!include_bots && !include_channels) {
|
||||
if (!include_groups) {
|
||||
// just in case
|
||||
return "Custom";
|
||||
}
|
||||
return "Groups";
|
||||
}
|
||||
if (!include_bots && !include_groups) {
|
||||
return "Channels";
|
||||
}
|
||||
if (!include_groups && !include_channels) {
|
||||
return "Bots";
|
||||
}
|
||||
}
|
||||
if (exclude_read && !exclude_muted) {
|
||||
return "Unread";
|
||||
}
|
||||
if (exclude_muted && !exclude_read) {
|
||||
return "Unmuted";
|
||||
}
|
||||
return "Custom";
|
||||
}
|
||||
|
||||
telegram_api::object_ptr<telegram_api::dialogFilter> DialogFilter::get_input_dialog_filter() const {
|
||||
int32 flags = 0;
|
||||
if (!emoji.empty()) {
|
||||
flags |= telegram_api::dialogFilter::EMOTICON_MASK;
|
||||
}
|
||||
if (exclude_muted) {
|
||||
flags |= telegram_api::dialogFilter::EXCLUDE_MUTED_MASK;
|
||||
}
|
||||
if (exclude_read) {
|
||||
flags |= telegram_api::dialogFilter::EXCLUDE_READ_MASK;
|
||||
}
|
||||
if (exclude_archived) {
|
||||
flags |= telegram_api::dialogFilter::EXCLUDE_ARCHIVED_MASK;
|
||||
}
|
||||
if (include_contacts) {
|
||||
flags |= telegram_api::dialogFilter::CONTACTS_MASK;
|
||||
}
|
||||
if (include_non_contacts) {
|
||||
flags |= telegram_api::dialogFilter::NON_CONTACTS_MASK;
|
||||
}
|
||||
if (include_bots) {
|
||||
flags |= telegram_api::dialogFilter::BOTS_MASK;
|
||||
}
|
||||
if (include_groups) {
|
||||
flags |= telegram_api::dialogFilter::GROUPS_MASK;
|
||||
}
|
||||
if (include_channels) {
|
||||
flags |= telegram_api::dialogFilter::BROADCASTS_MASK;
|
||||
}
|
||||
|
||||
return telegram_api::make_object<telegram_api::dialogFilter>(
|
||||
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
|
||||
false /*ignored*/, false /*ignored*/, false /*ignored*/, dialog_filter_id.get(), title, emoji,
|
||||
InputDialogId::get_input_peers(pinned_dialog_ids), InputDialogId::get_input_peers(included_dialog_ids),
|
||||
InputDialogId::get_input_peers(excluded_dialog_ids));
|
||||
}
|
||||
|
||||
// merges changes from old_server_filter to new_server_filter in old_filter
|
||||
unique_ptr<DialogFilter> DialogFilter::merge_dialog_filter_changes(const DialogFilter *old_filter,
|
||||
const DialogFilter *old_server_filter,
|
||||
const DialogFilter *new_server_filter) {
|
||||
CHECK(old_filter != nullptr);
|
||||
CHECK(old_server_filter != nullptr);
|
||||
CHECK(new_server_filter != nullptr);
|
||||
CHECK(old_filter->dialog_filter_id == old_server_filter->dialog_filter_id);
|
||||
CHECK(old_filter->dialog_filter_id == new_server_filter->dialog_filter_id);
|
||||
auto dialog_filter_id = old_filter->dialog_filter_id;
|
||||
auto new_filter = make_unique<DialogFilter>(*old_filter);
|
||||
new_filter->dialog_filter_id = dialog_filter_id;
|
||||
|
||||
auto merge_ordered_changes = [dialog_filter_id](auto &new_dialog_ids, auto old_server_dialog_ids,
|
||||
auto new_server_dialog_ids) {
|
||||
if (old_server_dialog_ids == new_server_dialog_ids) {
|
||||
LOG(INFO) << "Pinned chats was not changed remotely in " << dialog_filter_id << ", keep local changes";
|
||||
return;
|
||||
}
|
||||
|
||||
if (InputDialogId::are_equivalent(new_dialog_ids, old_server_dialog_ids)) {
|
||||
LOG(INFO) << "Pinned chats was not changed locally in " << dialog_filter_id << ", keep remote changes";
|
||||
|
||||
size_t kept_server_dialogs = 0;
|
||||
std::unordered_set<DialogId, DialogIdHash> removed_dialog_ids;
|
||||
auto old_it = old_server_dialog_ids.rbegin();
|
||||
for (auto &input_dialog_id : reversed(new_server_dialog_ids)) {
|
||||
auto dialog_id = input_dialog_id.get_dialog_id();
|
||||
while (old_it < old_server_dialog_ids.rend()) {
|
||||
if (old_it->get_dialog_id() == dialog_id) {
|
||||
kept_server_dialogs++;
|
||||
++old_it;
|
||||
break;
|
||||
}
|
||||
|
||||
// remove the dialog, it could be added back later
|
||||
removed_dialog_ids.insert(old_it->get_dialog_id());
|
||||
++old_it;
|
||||
}
|
||||
}
|
||||
while (old_it < old_server_dialog_ids.rend()) {
|
||||
// remove the dialog, it could be added back later
|
||||
removed_dialog_ids.insert(old_it->get_dialog_id());
|
||||
++old_it;
|
||||
}
|
||||
td::remove_if(new_dialog_ids, [&removed_dialog_ids](auto input_dialog_id) {
|
||||
return removed_dialog_ids.count(input_dialog_id.get_dialog_id()) > 0;
|
||||
});
|
||||
new_dialog_ids.insert(new_dialog_ids.begin(), new_server_dialog_ids.begin(),
|
||||
new_server_dialog_ids.end() - kept_server_dialogs);
|
||||
} else {
|
||||
LOG(WARNING) << "Ignore remote changes of pinned chats in " << dialog_filter_id;
|
||||
// there are both local and remote changes; ignore remote changes for now
|
||||
}
|
||||
};
|
||||
|
||||
auto merge_changes = [](auto &new_dialog_ids, const auto &old_server_dialog_ids, const auto &new_server_dialog_ids) {
|
||||
if (old_server_dialog_ids == new_server_dialog_ids) {
|
||||
// fast path
|
||||
return;
|
||||
}
|
||||
|
||||
// merge additions and deletions from other clients to the local changes
|
||||
std::unordered_set<DialogId, DialogIdHash> deleted_dialog_ids;
|
||||
for (auto old_dialog_id : old_server_dialog_ids) {
|
||||
deleted_dialog_ids.insert(old_dialog_id.get_dialog_id());
|
||||
}
|
||||
std::unordered_set<DialogId, DialogIdHash> added_dialog_ids;
|
||||
for (auto new_dialog_id : new_server_dialog_ids) {
|
||||
auto dialog_id = new_dialog_id.get_dialog_id();
|
||||
if (deleted_dialog_ids.erase(dialog_id) == 0) {
|
||||
added_dialog_ids.insert(dialog_id);
|
||||
}
|
||||
}
|
||||
vector<InputDialogId> result;
|
||||
for (auto input_dialog_id : new_dialog_ids) {
|
||||
// do not add dialog twice
|
||||
added_dialog_ids.erase(input_dialog_id.get_dialog_id());
|
||||
}
|
||||
for (auto new_dialog_id : new_server_dialog_ids) {
|
||||
if (added_dialog_ids.count(new_dialog_id.get_dialog_id()) == 1) {
|
||||
result.push_back(new_dialog_id);
|
||||
}
|
||||
}
|
||||
for (auto input_dialog_id : new_dialog_ids) {
|
||||
if (deleted_dialog_ids.count(input_dialog_id.get_dialog_id()) == 0) {
|
||||
result.push_back(input_dialog_id);
|
||||
}
|
||||
}
|
||||
new_dialog_ids = std::move(result);
|
||||
};
|
||||
|
||||
merge_ordered_changes(new_filter->pinned_dialog_ids, old_server_filter->pinned_dialog_ids,
|
||||
new_server_filter->pinned_dialog_ids);
|
||||
merge_changes(new_filter->included_dialog_ids, old_server_filter->included_dialog_ids,
|
||||
new_server_filter->included_dialog_ids);
|
||||
merge_changes(new_filter->excluded_dialog_ids, old_server_filter->excluded_dialog_ids,
|
||||
new_server_filter->excluded_dialog_ids);
|
||||
|
||||
{
|
||||
std::unordered_set<DialogId, DialogIdHash> added_dialog_ids;
|
||||
auto remove_duplicates = [&added_dialog_ids](auto &input_dialog_ids) {
|
||||
td::remove_if(input_dialog_ids, [&added_dialog_ids](auto input_dialog_id) {
|
||||
return !added_dialog_ids.insert(input_dialog_id.get_dialog_id()).second;
|
||||
});
|
||||
};
|
||||
remove_duplicates(new_filter->pinned_dialog_ids);
|
||||
remove_duplicates(new_filter->included_dialog_ids);
|
||||
remove_duplicates(new_filter->excluded_dialog_ids);
|
||||
}
|
||||
|
||||
auto update_value = [](auto &new_value, const auto &old_server_value, const auto &new_server_value) {
|
||||
// if the value was changed from other client and wasn't changed from the current client, update it
|
||||
if (new_server_value != old_server_value && old_server_value == new_value) {
|
||||
new_value = new_server_value;
|
||||
}
|
||||
};
|
||||
|
||||
update_value(new_filter->exclude_muted, old_server_filter->exclude_muted, new_server_filter->exclude_muted);
|
||||
update_value(new_filter->exclude_read, old_server_filter->exclude_read, new_server_filter->exclude_read);
|
||||
update_value(new_filter->exclude_archived, old_server_filter->exclude_archived, new_server_filter->exclude_archived);
|
||||
update_value(new_filter->include_contacts, old_server_filter->include_contacts, new_server_filter->include_contacts);
|
||||
update_value(new_filter->include_non_contacts, old_server_filter->include_non_contacts,
|
||||
new_server_filter->include_non_contacts);
|
||||
update_value(new_filter->include_bots, old_server_filter->include_bots, new_server_filter->include_bots);
|
||||
update_value(new_filter->include_groups, old_server_filter->include_groups, new_server_filter->include_groups);
|
||||
update_value(new_filter->include_channels, old_server_filter->include_channels, new_server_filter->include_channels);
|
||||
|
||||
if (new_filter->check_limits().is_error()) {
|
||||
LOG(WARNING) << "Failed to merge local and remote changes in " << new_filter->dialog_filter_id
|
||||
<< ", keep only local changes";
|
||||
*new_filter = *old_filter;
|
||||
}
|
||||
|
||||
update_value(new_filter->title, old_server_filter->title, new_server_filter->title);
|
||||
update_value(new_filter->emoji, old_server_filter->emoji, new_server_filter->emoji);
|
||||
return new_filter;
|
||||
}
|
||||
|
||||
bool DialogFilter::are_similar(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
if (lhs.title == rhs.title) {
|
||||
return true;
|
||||
}
|
||||
if (!are_flags_equal(lhs, rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<InputDialogId> empty_input_dialog_ids;
|
||||
if (InputDialogId::are_equivalent(lhs.excluded_dialog_ids, empty_input_dialog_ids) !=
|
||||
InputDialogId::are_equivalent(rhs.excluded_dialog_ids, empty_input_dialog_ids)) {
|
||||
return false;
|
||||
}
|
||||
if ((InputDialogId::are_equivalent(lhs.pinned_dialog_ids, empty_input_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(lhs.included_dialog_ids, empty_input_dialog_ids)) !=
|
||||
(InputDialogId::are_equivalent(rhs.pinned_dialog_ids, empty_input_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(rhs.included_dialog_ids, empty_input_dialog_ids))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DialogFilter::are_equivalent(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
return lhs.title == rhs.title && lhs.emoji == rhs.emoji &&
|
||||
InputDialogId::are_equivalent(lhs.pinned_dialog_ids, rhs.pinned_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(lhs.included_dialog_ids, rhs.included_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(lhs.excluded_dialog_ids, rhs.excluded_dialog_ids) && are_flags_equal(lhs, rhs);
|
||||
}
|
||||
|
||||
StringBuilder &operator<<(StringBuilder &string_builder, const DialogFilter &filter) {
|
||||
return string_builder << filter.dialog_filter_id << " (pinned " << filter.pinned_dialog_ids << ", included "
|
||||
<< filter.included_dialog_ids << ", excluded " << filter.excluded_dialog_ids << ", "
|
||||
<< filter.exclude_muted << ' ' << filter.exclude_read << ' ' << filter.exclude_archived << '/'
|
||||
<< filter.include_contacts << ' ' << filter.include_non_contacts << ' ' << filter.include_bots
|
||||
<< ' ' << filter.include_groups << ' ' << filter.include_channels << ')';
|
||||
}
|
||||
|
||||
void DialogFilter::init_icon_names() {
|
||||
static bool is_inited = [&] {
|
||||
vector<string> emojis{"\xF0\x9F\x92\xAC", "\xE2\x9C\x85", "\xF0\x9F\x94\x94",
|
||||
"\xF0\x9F\xA4\x96", "\xF0\x9F\x93\xA2", "\xF0\x9F\x91\xA5",
|
||||
"\xF0\x9F\x91\xA4", "\xF0\x9F\x93\x81", "\xF0\x9F\x93\x8B",
|
||||
"\xF0\x9F\x90\xB1", "\xF0\x9F\x91\x91", "\xE2\xAD\x90\xEF\xB8\x8F",
|
||||
"\xF0\x9F\x8C\xB9", "\xF0\x9F\x8E\xAE", "\xF0\x9F\x8F\xA0",
|
||||
"\xE2\x9D\xA4\xEF\xB8\x8F", "\xF0\x9F\x8E\xAD", "\xF0\x9F\x8D\xB8",
|
||||
"\xE2\x9A\xBD\xEF\xB8\x8F", "\xF0\x9F\x8E\x93", "\xF0\x9F\x93\x88",
|
||||
"\xE2\x9C\x88\xEF\xB8\x8F", "\xF0\x9F\x92\xBC"};
|
||||
vector<string> icon_names{"All", "Unread", "Unmuted", "Bots", "Channels", "Groups", "Private", "Custom",
|
||||
"Setup", "Cat", "Crown", "Favorite", "Flower", "Game", "Home", "Love",
|
||||
"Mask", "Party", "Sport", "Study", "Trade", "Travel", "Work"};
|
||||
CHECK(emojis.size() == icon_names.size());
|
||||
for (size_t i = 0; i < emojis.size(); i++) {
|
||||
emoji_to_icon_name_[remove_emoji_modifiers(emojis[i])] = icon_names[i];
|
||||
icon_name_to_emoji_[icon_names[i]] = remove_emoji_modifiers(emojis[i]);
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
CHECK(is_inited);
|
||||
}
|
||||
|
||||
bool DialogFilter::are_flags_equal(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
return lhs.exclude_muted == rhs.exclude_muted && lhs.exclude_read == rhs.exclude_read &&
|
||||
lhs.exclude_archived == rhs.exclude_archived && lhs.include_contacts == rhs.include_contacts &&
|
||||
lhs.include_non_contacts == rhs.include_non_contacts && lhs.include_bots == rhs.include_bots &&
|
||||
lhs.include_groups == rhs.include_groups && lhs.include_channels == rhs.include_channels;
|
||||
}
|
||||
|
||||
std::unordered_map<string, string> DialogFilter::emoji_to_icon_name_;
|
||||
std::unordered_map<string, string> DialogFilter::icon_name_to_emoji_;
|
||||
|
||||
} // namespace td
|
98
td/telegram/DialogFilter.h
Normal file
98
td/telegram/DialogFilter.h
Normal file
@ -0,0 +1,98 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "td/telegram/DialogFilterId.h"
|
||||
#include "td/telegram/InputDialogId.h"
|
||||
#include "td/telegram/telegram_api.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace td {
|
||||
|
||||
class DialogFilter {
|
||||
public:
|
||||
static constexpr int32 MAX_INCLUDED_FILTER_DIALOGS = 100; // server side limit
|
||||
|
||||
DialogFilterId dialog_filter_id;
|
||||
string title;
|
||||
string emoji;
|
||||
vector<InputDialogId> pinned_dialog_ids;
|
||||
vector<InputDialogId> included_dialog_ids;
|
||||
vector<InputDialogId> excluded_dialog_ids;
|
||||
bool exclude_muted = false;
|
||||
bool exclude_read = false;
|
||||
bool exclude_archived = false;
|
||||
bool include_contacts = false;
|
||||
bool include_non_contacts = false;
|
||||
bool include_bots = false;
|
||||
bool include_groups = false;
|
||||
bool include_channels = false;
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const;
|
||||
|
||||
template <class ParserT>
|
||||
void parse(ParserT &parser);
|
||||
|
||||
static unique_ptr<DialogFilter> get_dialog_filter(telegram_api::object_ptr<telegram_api::dialogFilter> filter,
|
||||
bool with_id);
|
||||
|
||||
void remove_secret_chat_dialog_ids();
|
||||
|
||||
bool is_empty(bool for_server) const;
|
||||
|
||||
Status check_limits() const;
|
||||
|
||||
static string get_emoji_by_icon_name(const string &icon_name);
|
||||
|
||||
string get_icon_name() const;
|
||||
|
||||
telegram_api::object_ptr<telegram_api::dialogFilter> get_input_dialog_filter() const;
|
||||
|
||||
// merges changes from old_server_filter to new_server_filter in old_filter
|
||||
static unique_ptr<DialogFilter> merge_dialog_filter_changes(const DialogFilter *old_filter,
|
||||
const DialogFilter *old_server_filter,
|
||||
const DialogFilter *new_server_filter);
|
||||
|
||||
static bool are_similar(const DialogFilter &lhs, const DialogFilter &rhs);
|
||||
|
||||
static bool are_equivalent(const DialogFilter &lhs, const DialogFilter &rhs);
|
||||
|
||||
static bool are_flags_equal(const DialogFilter &lhs, const DialogFilter &rhs);
|
||||
|
||||
private:
|
||||
static std::unordered_map<string, string> emoji_to_icon_name_;
|
||||
static std::unordered_map<string, string> icon_name_to_emoji_;
|
||||
|
||||
static void init_icon_names();
|
||||
};
|
||||
|
||||
inline bool operator==(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
return lhs.dialog_filter_id == rhs.dialog_filter_id && lhs.title == rhs.title && lhs.emoji == rhs.emoji &&
|
||||
lhs.pinned_dialog_ids == rhs.pinned_dialog_ids && lhs.included_dialog_ids == rhs.included_dialog_ids &&
|
||||
lhs.excluded_dialog_ids == rhs.excluded_dialog_ids && DialogFilter::are_flags_equal(lhs, rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
inline bool operator==(const unique_ptr<DialogFilter> &lhs, const unique_ptr<DialogFilter> &rhs) {
|
||||
return *lhs == *rhs;
|
||||
}
|
||||
|
||||
inline bool operator!=(const unique_ptr<DialogFilter> &lhs, const unique_ptr<DialogFilter> &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
StringBuilder &operator<<(StringBuilder &string_builder, const DialogFilter &filter);
|
||||
|
||||
} // namespace td
|
84
td/telegram/DialogFilter.hpp
Normal file
84
td/telegram/DialogFilter.hpp
Normal file
@ -0,0 +1,84 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "td/telegram/DialogFilter.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/tl_helpers.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
template <class StorerT>
|
||||
void DialogFilter::store(StorerT &storer) const {
|
||||
using td::store;
|
||||
bool has_pinned_dialog_ids = !pinned_dialog_ids.empty();
|
||||
bool has_included_dialog_ids = !included_dialog_ids.empty();
|
||||
bool has_excluded_dialog_ids = !excluded_dialog_ids.empty();
|
||||
BEGIN_STORE_FLAGS();
|
||||
STORE_FLAG(exclude_muted);
|
||||
STORE_FLAG(exclude_read);
|
||||
STORE_FLAG(exclude_archived);
|
||||
STORE_FLAG(include_contacts);
|
||||
STORE_FLAG(include_non_contacts);
|
||||
STORE_FLAG(include_bots);
|
||||
STORE_FLAG(include_groups);
|
||||
STORE_FLAG(include_channels);
|
||||
STORE_FLAG(has_pinned_dialog_ids);
|
||||
STORE_FLAG(has_included_dialog_ids);
|
||||
STORE_FLAG(has_excluded_dialog_ids);
|
||||
END_STORE_FLAGS();
|
||||
|
||||
store(dialog_filter_id, storer);
|
||||
store(title, storer);
|
||||
store(emoji, storer);
|
||||
if (has_pinned_dialog_ids) {
|
||||
store(pinned_dialog_ids, storer);
|
||||
}
|
||||
if (has_included_dialog_ids) {
|
||||
store(included_dialog_ids, storer);
|
||||
}
|
||||
if (has_excluded_dialog_ids) {
|
||||
store(excluded_dialog_ids, storer);
|
||||
}
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
void DialogFilter::parse(ParserT &parser) {
|
||||
using td::parse;
|
||||
bool has_pinned_dialog_ids = !pinned_dialog_ids.empty();
|
||||
bool has_included_dialog_ids = !included_dialog_ids.empty();
|
||||
bool has_excluded_dialog_ids = !excluded_dialog_ids.empty();
|
||||
BEGIN_PARSE_FLAGS();
|
||||
PARSE_FLAG(exclude_muted);
|
||||
PARSE_FLAG(exclude_read);
|
||||
PARSE_FLAG(exclude_archived);
|
||||
PARSE_FLAG(include_contacts);
|
||||
PARSE_FLAG(include_non_contacts);
|
||||
PARSE_FLAG(include_bots);
|
||||
PARSE_FLAG(include_groups);
|
||||
PARSE_FLAG(include_channels);
|
||||
PARSE_FLAG(has_pinned_dialog_ids);
|
||||
PARSE_FLAG(has_included_dialog_ids);
|
||||
PARSE_FLAG(has_excluded_dialog_ids);
|
||||
END_PARSE_FLAGS();
|
||||
|
||||
parse(dialog_filter_id, parser);
|
||||
parse(title, parser);
|
||||
parse(emoji, parser);
|
||||
if (has_pinned_dialog_ids) {
|
||||
parse(pinned_dialog_ids, parser);
|
||||
}
|
||||
if (has_included_dialog_ids) {
|
||||
parse(included_dialog_ids, parser);
|
||||
}
|
||||
if (has_excluded_dialog_ids) {
|
||||
parse(excluded_dialog_ids, parser);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace td
|
@ -11,6 +11,8 @@
|
||||
#include "td/telegram/ConfigShared.h"
|
||||
#include "td/telegram/ContactsManager.h"
|
||||
#include "td/telegram/DialogDb.h"
|
||||
#include "td/telegram/DialogFilter.h"
|
||||
#include "td/telegram/DialogFilter.hpp"
|
||||
#include "td/telegram/DialogLocation.h"
|
||||
#include "td/telegram/DraftMessage.h"
|
||||
#include "td/telegram/DraftMessage.hpp"
|
||||
@ -4835,539 +4837,6 @@ void MessagesManager::CallsDbState::parse(ParserT &parser) {
|
||||
}
|
||||
}
|
||||
|
||||
struct MessagesManager::DialogFilter {
|
||||
static constexpr int32 MAX_INCLUDED_FILTER_DIALOGS = 100; // server side limit
|
||||
|
||||
DialogFilterId dialog_filter_id;
|
||||
string title;
|
||||
string emoji;
|
||||
vector<InputDialogId> pinned_dialog_ids;
|
||||
vector<InputDialogId> included_dialog_ids;
|
||||
vector<InputDialogId> excluded_dialog_ids;
|
||||
bool exclude_muted = false;
|
||||
bool exclude_read = false;
|
||||
bool exclude_archived = false;
|
||||
bool include_contacts = false;
|
||||
bool include_non_contacts = false;
|
||||
bool include_bots = false;
|
||||
bool include_groups = false;
|
||||
bool include_channels = false;
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const;
|
||||
|
||||
template <class ParserT>
|
||||
void parse(ParserT &parser);
|
||||
|
||||
static unique_ptr<DialogFilter> get_dialog_filter(telegram_api::object_ptr<telegram_api::dialogFilter> filter,
|
||||
bool with_id) {
|
||||
DialogFilterId dialog_filter_id(filter->id_);
|
||||
if (with_id && !dialog_filter_id.is_valid()) {
|
||||
LOG(ERROR) << "Receive invalid " << to_string(filter);
|
||||
return nullptr;
|
||||
}
|
||||
auto dialog_filter = make_unique<DialogFilter>();
|
||||
dialog_filter->dialog_filter_id = dialog_filter_id;
|
||||
dialog_filter->title = std::move(filter->title_);
|
||||
dialog_filter->emoji = std::move(filter->emoticon_);
|
||||
std::unordered_set<DialogId, DialogIdHash> added_dialog_ids;
|
||||
dialog_filter->pinned_dialog_ids = InputDialogId::get_input_dialog_ids(filter->pinned_peers_, &added_dialog_ids);
|
||||
dialog_filter->included_dialog_ids = InputDialogId::get_input_dialog_ids(filter->include_peers_, &added_dialog_ids);
|
||||
dialog_filter->excluded_dialog_ids = InputDialogId::get_input_dialog_ids(filter->exclude_peers_, &added_dialog_ids);
|
||||
auto flags = filter->flags_;
|
||||
dialog_filter->exclude_muted = (flags & telegram_api::dialogFilter::EXCLUDE_MUTED_MASK) != 0;
|
||||
dialog_filter->exclude_read = (flags & telegram_api::dialogFilter::EXCLUDE_READ_MASK) != 0;
|
||||
dialog_filter->exclude_archived = (flags & telegram_api::dialogFilter::EXCLUDE_ARCHIVED_MASK) != 0;
|
||||
dialog_filter->include_contacts = (flags & telegram_api::dialogFilter::CONTACTS_MASK) != 0;
|
||||
dialog_filter->include_non_contacts = (flags & telegram_api::dialogFilter::NON_CONTACTS_MASK) != 0;
|
||||
dialog_filter->include_bots = (flags & telegram_api::dialogFilter::BOTS_MASK) != 0;
|
||||
dialog_filter->include_groups = (flags & telegram_api::dialogFilter::GROUPS_MASK) != 0;
|
||||
dialog_filter->include_channels = (flags & telegram_api::dialogFilter::BROADCASTS_MASK) != 0;
|
||||
return dialog_filter;
|
||||
}
|
||||
|
||||
void remove_secret_chat_dialog_ids() {
|
||||
auto remove_secret_chats = [](vector<InputDialogId> &input_dialog_ids) {
|
||||
td::remove_if(input_dialog_ids, [](InputDialogId input_dialog_id) {
|
||||
return input_dialog_id.get_dialog_id().get_type() == DialogType::SecretChat;
|
||||
});
|
||||
};
|
||||
remove_secret_chats(pinned_dialog_ids);
|
||||
remove_secret_chats(included_dialog_ids);
|
||||
remove_secret_chats(excluded_dialog_ids);
|
||||
}
|
||||
|
||||
bool is_empty(bool for_server) const {
|
||||
if (include_contacts || include_non_contacts || include_bots || include_groups || include_channels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (for_server) {
|
||||
vector<InputDialogId> empty_input_dialog_ids;
|
||||
return InputDialogId::are_equivalent(pinned_dialog_ids, empty_input_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(included_dialog_ids, empty_input_dialog_ids);
|
||||
} else {
|
||||
return pinned_dialog_ids.empty() && included_dialog_ids.empty();
|
||||
}
|
||||
}
|
||||
|
||||
Status check_limits() const {
|
||||
auto get_server_dialog_count = [](const vector<InputDialogId> &input_dialog_ids) {
|
||||
int32 result = 0;
|
||||
for (auto &input_dialog_id : input_dialog_ids) {
|
||||
if (input_dialog_id.get_dialog_id().get_type() != DialogType::SecretChat) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
auto excluded_server_dialog_count = get_server_dialog_count(excluded_dialog_ids);
|
||||
auto included_server_dialog_count = get_server_dialog_count(included_dialog_ids);
|
||||
auto pinned_server_dialog_count = get_server_dialog_count(pinned_dialog_ids);
|
||||
|
||||
auto excluded_secret_dialog_count = static_cast<int32>(excluded_dialog_ids.size()) - excluded_server_dialog_count;
|
||||
auto included_secret_dialog_count = static_cast<int32>(included_dialog_ids.size()) - included_server_dialog_count;
|
||||
auto pinned_secret_dialog_count = static_cast<int32>(pinned_dialog_ids.size()) - pinned_server_dialog_count;
|
||||
|
||||
if (excluded_server_dialog_count > MAX_INCLUDED_FILTER_DIALOGS ||
|
||||
excluded_secret_dialog_count > MAX_INCLUDED_FILTER_DIALOGS) {
|
||||
return Status::Error(400, "Maximum number of excluded chats exceeded");
|
||||
}
|
||||
if (included_server_dialog_count > MAX_INCLUDED_FILTER_DIALOGS ||
|
||||
included_secret_dialog_count > MAX_INCLUDED_FILTER_DIALOGS) {
|
||||
return Status::Error(400, "Maximum number of included chats exceeded");
|
||||
}
|
||||
if (included_server_dialog_count + pinned_server_dialog_count > MAX_INCLUDED_FILTER_DIALOGS ||
|
||||
included_secret_dialog_count + pinned_secret_dialog_count > MAX_INCLUDED_FILTER_DIALOGS) {
|
||||
return Status::Error(400, "Maximum number of pinned chats exceeded");
|
||||
}
|
||||
|
||||
if (is_empty(false)) {
|
||||
return Status::Error(400, "Folder must contain at least 1 chat");
|
||||
}
|
||||
|
||||
if (include_contacts && include_non_contacts && include_bots && include_groups && include_channels &&
|
||||
exclude_archived && !exclude_read && !exclude_muted) {
|
||||
return Status::Error(400, "Folder must be different from the main chat list");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static string get_emoji_by_icon_name(const string &icon_name) {
|
||||
init_icon_names();
|
||||
auto it = icon_name_to_emoji_.find(icon_name);
|
||||
if (it != icon_name_to_emoji_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return string();
|
||||
}
|
||||
|
||||
string get_icon_name() const {
|
||||
init_icon_names();
|
||||
auto it = emoji_to_icon_name_.find(emoji);
|
||||
if (it != emoji_to_icon_name_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
if (!pinned_dialog_ids.empty() || !included_dialog_ids.empty() || !excluded_dialog_ids.empty()) {
|
||||
return "Custom";
|
||||
}
|
||||
|
||||
if (include_contacts || include_non_contacts) {
|
||||
if (!include_bots && !include_groups && !include_channels) {
|
||||
return "Private";
|
||||
}
|
||||
} else {
|
||||
if (!include_bots && !include_channels) {
|
||||
if (!include_groups) {
|
||||
// just in case
|
||||
return "Custom";
|
||||
}
|
||||
return "Groups";
|
||||
}
|
||||
if (!include_bots && !include_groups) {
|
||||
return "Channels";
|
||||
}
|
||||
if (!include_groups && !include_channels) {
|
||||
return "Bots";
|
||||
}
|
||||
}
|
||||
if (exclude_read && !exclude_muted) {
|
||||
return "Unread";
|
||||
}
|
||||
if (exclude_muted && !exclude_read) {
|
||||
return "Unmuted";
|
||||
}
|
||||
return "Custom";
|
||||
}
|
||||
|
||||
telegram_api::object_ptr<telegram_api::dialogFilter> get_input_dialog_filter() const {
|
||||
int32 flags = 0;
|
||||
if (!emoji.empty()) {
|
||||
flags |= telegram_api::dialogFilter::EMOTICON_MASK;
|
||||
}
|
||||
if (exclude_muted) {
|
||||
flags |= telegram_api::dialogFilter::EXCLUDE_MUTED_MASK;
|
||||
}
|
||||
if (exclude_read) {
|
||||
flags |= telegram_api::dialogFilter::EXCLUDE_READ_MASK;
|
||||
}
|
||||
if (exclude_archived) {
|
||||
flags |= telegram_api::dialogFilter::EXCLUDE_ARCHIVED_MASK;
|
||||
}
|
||||
if (include_contacts) {
|
||||
flags |= telegram_api::dialogFilter::CONTACTS_MASK;
|
||||
}
|
||||
if (include_non_contacts) {
|
||||
flags |= telegram_api::dialogFilter::NON_CONTACTS_MASK;
|
||||
}
|
||||
if (include_bots) {
|
||||
flags |= telegram_api::dialogFilter::BOTS_MASK;
|
||||
}
|
||||
if (include_groups) {
|
||||
flags |= telegram_api::dialogFilter::GROUPS_MASK;
|
||||
}
|
||||
if (include_channels) {
|
||||
flags |= telegram_api::dialogFilter::BROADCASTS_MASK;
|
||||
}
|
||||
|
||||
return telegram_api::make_object<telegram_api::dialogFilter>(
|
||||
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
|
||||
false /*ignored*/, false /*ignored*/, false /*ignored*/, dialog_filter_id.get(), title, emoji,
|
||||
InputDialogId::get_input_peers(pinned_dialog_ids), InputDialogId::get_input_peers(included_dialog_ids),
|
||||
InputDialogId::get_input_peers(excluded_dialog_ids));
|
||||
}
|
||||
|
||||
// merges changes from old_server_filter to new_server_filter in old_filter
|
||||
static unique_ptr<DialogFilter> merge_dialog_filter_changes(const DialogFilter *old_filter,
|
||||
const DialogFilter *old_server_filter,
|
||||
const DialogFilter *new_server_filter) {
|
||||
CHECK(old_filter != nullptr);
|
||||
CHECK(old_server_filter != nullptr);
|
||||
CHECK(new_server_filter != nullptr);
|
||||
CHECK(old_filter->dialog_filter_id == old_server_filter->dialog_filter_id);
|
||||
CHECK(old_filter->dialog_filter_id == new_server_filter->dialog_filter_id);
|
||||
auto dialog_filter_id = old_filter->dialog_filter_id;
|
||||
auto new_filter = make_unique<DialogFilter>(*old_filter);
|
||||
new_filter->dialog_filter_id = dialog_filter_id;
|
||||
|
||||
auto merge_ordered_changes = [dialog_filter_id](auto &new_dialog_ids, auto old_server_dialog_ids,
|
||||
auto new_server_dialog_ids) {
|
||||
if (old_server_dialog_ids == new_server_dialog_ids) {
|
||||
LOG(INFO) << "Pinned chats was not changed remotely in " << dialog_filter_id << ", keep local changes";
|
||||
return;
|
||||
}
|
||||
|
||||
if (InputDialogId::are_equivalent(new_dialog_ids, old_server_dialog_ids)) {
|
||||
LOG(INFO) << "Pinned chats was not changed locally in " << dialog_filter_id << ", keep remote changes";
|
||||
|
||||
size_t kept_server_dialogs = 0;
|
||||
std::unordered_set<DialogId, DialogIdHash> removed_dialog_ids;
|
||||
auto old_it = old_server_dialog_ids.rbegin();
|
||||
for (auto &input_dialog_id : reversed(new_server_dialog_ids)) {
|
||||
auto dialog_id = input_dialog_id.get_dialog_id();
|
||||
while (old_it < old_server_dialog_ids.rend()) {
|
||||
if (old_it->get_dialog_id() == dialog_id) {
|
||||
kept_server_dialogs++;
|
||||
++old_it;
|
||||
break;
|
||||
}
|
||||
|
||||
// remove the dialog, it could be added back later
|
||||
removed_dialog_ids.insert(old_it->get_dialog_id());
|
||||
++old_it;
|
||||
}
|
||||
}
|
||||
while (old_it < old_server_dialog_ids.rend()) {
|
||||
// remove the dialog, it could be added back later
|
||||
removed_dialog_ids.insert(old_it->get_dialog_id());
|
||||
++old_it;
|
||||
}
|
||||
td::remove_if(new_dialog_ids, [&removed_dialog_ids](auto input_dialog_id) {
|
||||
return removed_dialog_ids.count(input_dialog_id.get_dialog_id()) > 0;
|
||||
});
|
||||
new_dialog_ids.insert(new_dialog_ids.begin(), new_server_dialog_ids.begin(),
|
||||
new_server_dialog_ids.end() - kept_server_dialogs);
|
||||
} else {
|
||||
LOG(WARNING) << "Ignore remote changes of pinned chats in " << dialog_filter_id;
|
||||
// there are both local and remote changes; ignore remote changes for now
|
||||
}
|
||||
};
|
||||
|
||||
auto merge_changes = [](auto &new_dialog_ids, const auto &old_server_dialog_ids,
|
||||
const auto &new_server_dialog_ids) {
|
||||
if (old_server_dialog_ids == new_server_dialog_ids) {
|
||||
// fast path
|
||||
return;
|
||||
}
|
||||
|
||||
// merge additions and deletions from other clients to the local changes
|
||||
std::unordered_set<DialogId, DialogIdHash> deleted_dialog_ids;
|
||||
for (auto old_dialog_id : old_server_dialog_ids) {
|
||||
deleted_dialog_ids.insert(old_dialog_id.get_dialog_id());
|
||||
}
|
||||
std::unordered_set<DialogId, DialogIdHash> added_dialog_ids;
|
||||
for (auto new_dialog_id : new_server_dialog_ids) {
|
||||
auto dialog_id = new_dialog_id.get_dialog_id();
|
||||
if (deleted_dialog_ids.erase(dialog_id) == 0) {
|
||||
added_dialog_ids.insert(dialog_id);
|
||||
}
|
||||
}
|
||||
vector<InputDialogId> result;
|
||||
for (auto input_dialog_id : new_dialog_ids) {
|
||||
// do not add dialog twice
|
||||
added_dialog_ids.erase(input_dialog_id.get_dialog_id());
|
||||
}
|
||||
for (auto new_dialog_id : new_server_dialog_ids) {
|
||||
if (added_dialog_ids.count(new_dialog_id.get_dialog_id()) == 1) {
|
||||
result.push_back(new_dialog_id);
|
||||
}
|
||||
}
|
||||
for (auto input_dialog_id : new_dialog_ids) {
|
||||
if (deleted_dialog_ids.count(input_dialog_id.get_dialog_id()) == 0) {
|
||||
result.push_back(input_dialog_id);
|
||||
}
|
||||
}
|
||||
new_dialog_ids = std::move(result);
|
||||
};
|
||||
|
||||
merge_ordered_changes(new_filter->pinned_dialog_ids, old_server_filter->pinned_dialog_ids,
|
||||
new_server_filter->pinned_dialog_ids);
|
||||
merge_changes(new_filter->included_dialog_ids, old_server_filter->included_dialog_ids,
|
||||
new_server_filter->included_dialog_ids);
|
||||
merge_changes(new_filter->excluded_dialog_ids, old_server_filter->excluded_dialog_ids,
|
||||
new_server_filter->excluded_dialog_ids);
|
||||
|
||||
{
|
||||
std::unordered_set<DialogId, DialogIdHash> added_dialog_ids;
|
||||
auto remove_duplicates = [&added_dialog_ids](auto &input_dialog_ids) {
|
||||
td::remove_if(input_dialog_ids, [&added_dialog_ids](auto input_dialog_id) {
|
||||
return !added_dialog_ids.insert(input_dialog_id.get_dialog_id()).second;
|
||||
});
|
||||
};
|
||||
remove_duplicates(new_filter->pinned_dialog_ids);
|
||||
remove_duplicates(new_filter->included_dialog_ids);
|
||||
remove_duplicates(new_filter->excluded_dialog_ids);
|
||||
}
|
||||
|
||||
auto update_value = [](auto &new_value, const auto &old_server_value, const auto &new_server_value) {
|
||||
// if the value was changed from other client and wasn't changed from the current client, update it
|
||||
if (new_server_value != old_server_value && old_server_value == new_value) {
|
||||
new_value = new_server_value;
|
||||
}
|
||||
};
|
||||
|
||||
update_value(new_filter->exclude_muted, old_server_filter->exclude_muted, new_server_filter->exclude_muted);
|
||||
update_value(new_filter->exclude_read, old_server_filter->exclude_read, new_server_filter->exclude_read);
|
||||
update_value(new_filter->exclude_archived, old_server_filter->exclude_archived,
|
||||
new_server_filter->exclude_archived);
|
||||
update_value(new_filter->include_contacts, old_server_filter->include_contacts,
|
||||
new_server_filter->include_contacts);
|
||||
update_value(new_filter->include_non_contacts, old_server_filter->include_non_contacts,
|
||||
new_server_filter->include_non_contacts);
|
||||
update_value(new_filter->include_bots, old_server_filter->include_bots, new_server_filter->include_bots);
|
||||
update_value(new_filter->include_groups, old_server_filter->include_groups, new_server_filter->include_groups);
|
||||
update_value(new_filter->include_channels, old_server_filter->include_channels,
|
||||
new_server_filter->include_channels);
|
||||
|
||||
if (new_filter->check_limits().is_error()) {
|
||||
LOG(WARNING) << "Failed to merge local and remote changes in " << new_filter->dialog_filter_id
|
||||
<< ", keep only local changes";
|
||||
*new_filter = *old_filter;
|
||||
}
|
||||
|
||||
update_value(new_filter->title, old_server_filter->title, new_server_filter->title);
|
||||
update_value(new_filter->emoji, old_server_filter->emoji, new_server_filter->emoji);
|
||||
return new_filter;
|
||||
}
|
||||
|
||||
static bool are_similar(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
if (lhs.title == rhs.title) {
|
||||
return true;
|
||||
}
|
||||
if (!are_flags_equal(lhs, rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<InputDialogId> empty_input_dialog_ids;
|
||||
if (InputDialogId::are_equivalent(lhs.excluded_dialog_ids, empty_input_dialog_ids) !=
|
||||
InputDialogId::are_equivalent(rhs.excluded_dialog_ids, empty_input_dialog_ids)) {
|
||||
return false;
|
||||
}
|
||||
if ((InputDialogId::are_equivalent(lhs.pinned_dialog_ids, empty_input_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(lhs.included_dialog_ids, empty_input_dialog_ids)) !=
|
||||
(InputDialogId::are_equivalent(rhs.pinned_dialog_ids, empty_input_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(rhs.included_dialog_ids, empty_input_dialog_ids))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool are_equivalent(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
return lhs.title == rhs.title && lhs.emoji == rhs.emoji &&
|
||||
InputDialogId::are_equivalent(lhs.pinned_dialog_ids, rhs.pinned_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(lhs.included_dialog_ids, rhs.included_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(lhs.excluded_dialog_ids, rhs.excluded_dialog_ids) && are_flags_equal(lhs, rhs);
|
||||
}
|
||||
|
||||
friend bool operator==(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
return lhs.dialog_filter_id == rhs.dialog_filter_id && lhs.title == rhs.title && lhs.emoji == rhs.emoji &&
|
||||
lhs.pinned_dialog_ids == rhs.pinned_dialog_ids && lhs.included_dialog_ids == rhs.included_dialog_ids &&
|
||||
lhs.excluded_dialog_ids == rhs.excluded_dialog_ids && are_flags_equal(lhs, rhs);
|
||||
}
|
||||
|
||||
friend bool operator!=(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
friend bool operator==(const unique_ptr<DialogFilter> &lhs, const unique_ptr<DialogFilter> &rhs) {
|
||||
return *lhs == *rhs;
|
||||
}
|
||||
|
||||
friend bool operator!=(const unique_ptr<DialogFilter> &lhs, const unique_ptr<DialogFilter> &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
friend StringBuilder &operator<<(StringBuilder &string_builder, const DialogFilter &filter) {
|
||||
return string_builder << filter.dialog_filter_id << " (pinned " << filter.pinned_dialog_ids << ", included "
|
||||
<< filter.included_dialog_ids << ", excluded " << filter.excluded_dialog_ids << ", "
|
||||
<< filter.exclude_muted << ' ' << filter.exclude_read << ' ' << filter.exclude_archived << '/'
|
||||
<< filter.include_contacts << ' ' << filter.include_non_contacts << ' ' << filter.include_bots
|
||||
<< ' ' << filter.include_groups << ' ' << filter.include_channels << ')';
|
||||
}
|
||||
|
||||
private:
|
||||
static std::unordered_map<string, string> emoji_to_icon_name_;
|
||||
static std::unordered_map<string, string> icon_name_to_emoji_;
|
||||
|
||||
static void init_icon_names() {
|
||||
static bool is_inited = [&] {
|
||||
vector<string> emojis{"\xF0\x9F\x92\xAC", "\xE2\x9C\x85", "\xF0\x9F\x94\x94",
|
||||
"\xF0\x9F\xA4\x96", "\xF0\x9F\x93\xA2", "\xF0\x9F\x91\xA5",
|
||||
"\xF0\x9F\x91\xA4", "\xF0\x9F\x93\x81", "\xF0\x9F\x93\x8B",
|
||||
"\xF0\x9F\x90\xB1", "\xF0\x9F\x91\x91", "\xE2\xAD\x90\xEF\xB8\x8F",
|
||||
"\xF0\x9F\x8C\xB9", "\xF0\x9F\x8E\xAE", "\xF0\x9F\x8F\xA0",
|
||||
"\xE2\x9D\xA4\xEF\xB8\x8F", "\xF0\x9F\x8E\xAD", "\xF0\x9F\x8D\xB8",
|
||||
"\xE2\x9A\xBD\xEF\xB8\x8F", "\xF0\x9F\x8E\x93", "\xF0\x9F\x93\x88",
|
||||
"\xE2\x9C\x88\xEF\xB8\x8F", "\xF0\x9F\x92\xBC"};
|
||||
vector<string> icon_names{"All", "Unread", "Unmuted", "Bots", "Channels", "Groups", "Private", "Custom",
|
||||
"Setup", "Cat", "Crown", "Favorite", "Flower", "Game", "Home", "Love",
|
||||
"Mask", "Party", "Sport", "Study", "Trade", "Travel", "Work"};
|
||||
CHECK(emojis.size() == icon_names.size());
|
||||
for (size_t i = 0; i < emojis.size(); i++) {
|
||||
emoji_to_icon_name_[remove_emoji_modifiers(emojis[i])] = icon_names[i];
|
||||
icon_name_to_emoji_[icon_names[i]] = remove_emoji_modifiers(emojis[i]);
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
CHECK(is_inited);
|
||||
}
|
||||
|
||||
static bool are_flags_equal(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
return lhs.exclude_muted == rhs.exclude_muted && lhs.exclude_read == rhs.exclude_read &&
|
||||
lhs.exclude_archived == rhs.exclude_archived && lhs.include_contacts == rhs.include_contacts &&
|
||||
lhs.include_non_contacts == rhs.include_non_contacts && lhs.include_bots == rhs.include_bots &&
|
||||
lhs.include_groups == rhs.include_groups && lhs.include_channels == rhs.include_channels;
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_map<string, string> MessagesManager::DialogFilter::emoji_to_icon_name_;
|
||||
std::unordered_map<string, string> MessagesManager::DialogFilter::icon_name_to_emoji_;
|
||||
|
||||
template <class StorerT>
|
||||
void MessagesManager::DialogFilter::store(StorerT &storer) const {
|
||||
using td::store;
|
||||
bool has_pinned_dialog_ids = !pinned_dialog_ids.empty();
|
||||
bool has_included_dialog_ids = !included_dialog_ids.empty();
|
||||
bool has_excluded_dialog_ids = !excluded_dialog_ids.empty();
|
||||
BEGIN_STORE_FLAGS();
|
||||
STORE_FLAG(exclude_muted);
|
||||
STORE_FLAG(exclude_read);
|
||||
STORE_FLAG(exclude_archived);
|
||||
STORE_FLAG(include_contacts);
|
||||
STORE_FLAG(include_non_contacts);
|
||||
STORE_FLAG(include_bots);
|
||||
STORE_FLAG(include_groups);
|
||||
STORE_FLAG(include_channels);
|
||||
STORE_FLAG(has_pinned_dialog_ids);
|
||||
STORE_FLAG(has_included_dialog_ids);
|
||||
STORE_FLAG(has_excluded_dialog_ids);
|
||||
END_STORE_FLAGS();
|
||||
|
||||
store(dialog_filter_id, storer);
|
||||
store(title, storer);
|
||||
store(emoji, storer);
|
||||
if (has_pinned_dialog_ids) {
|
||||
store(pinned_dialog_ids, storer);
|
||||
}
|
||||
if (has_included_dialog_ids) {
|
||||
store(included_dialog_ids, storer);
|
||||
}
|
||||
if (has_excluded_dialog_ids) {
|
||||
store(excluded_dialog_ids, storer);
|
||||
}
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
void MessagesManager::DialogFilter::parse(ParserT &parser) {
|
||||
using td::parse;
|
||||
bool has_pinned_dialog_ids = !pinned_dialog_ids.empty();
|
||||
bool has_included_dialog_ids = !included_dialog_ids.empty();
|
||||
bool has_excluded_dialog_ids = !excluded_dialog_ids.empty();
|
||||
BEGIN_PARSE_FLAGS();
|
||||
PARSE_FLAG(exclude_muted);
|
||||
PARSE_FLAG(exclude_read);
|
||||
PARSE_FLAG(exclude_archived);
|
||||
PARSE_FLAG(include_contacts);
|
||||
PARSE_FLAG(include_non_contacts);
|
||||
PARSE_FLAG(include_bots);
|
||||
PARSE_FLAG(include_groups);
|
||||
PARSE_FLAG(include_channels);
|
||||
PARSE_FLAG(has_pinned_dialog_ids);
|
||||
PARSE_FLAG(has_included_dialog_ids);
|
||||
PARSE_FLAG(has_excluded_dialog_ids);
|
||||
END_PARSE_FLAGS();
|
||||
|
||||
parse(dialog_filter_id, parser);
|
||||
parse(title, parser);
|
||||
parse(emoji, parser);
|
||||
if (has_pinned_dialog_ids) {
|
||||
parse(pinned_dialog_ids, parser);
|
||||
}
|
||||
if (has_included_dialog_ids) {
|
||||
parse(included_dialog_ids, parser);
|
||||
}
|
||||
if (has_excluded_dialog_ids) {
|
||||
parse(excluded_dialog_ids, parser);
|
||||
}
|
||||
}
|
||||
|
||||
class MessagesManager::DialogFiltersLogEvent {
|
||||
public:
|
||||
int32 updated_date = 0;
|
||||
const vector<unique_ptr<DialogFilter>> *server_dialog_filters_in;
|
||||
const vector<unique_ptr<DialogFilter>> *dialog_filters_in;
|
||||
vector<unique_ptr<DialogFilter>> server_dialog_filters_out;
|
||||
vector<unique_ptr<DialogFilter>> dialog_filters_out;
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const {
|
||||
td::store(updated_date, storer);
|
||||
td::store(*server_dialog_filters_in, storer);
|
||||
td::store(*dialog_filters_in, storer);
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
void parse(ParserT &parser) {
|
||||
td::parse(updated_date, parser);
|
||||
td::parse(server_dialog_filters_out, parser);
|
||||
td::parse(dialog_filters_out, parser);
|
||||
}
|
||||
};
|
||||
|
||||
void MessagesManager::load_calls_db_state() {
|
||||
if (!G()->parameters().use_message_db) {
|
||||
return;
|
||||
@ -11213,6 +10682,29 @@ void MessagesManager::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
class MessagesManager::DialogFiltersLogEvent {
|
||||
public:
|
||||
int32 updated_date = 0;
|
||||
const vector<unique_ptr<DialogFilter>> *server_dialog_filters_in;
|
||||
const vector<unique_ptr<DialogFilter>> *dialog_filters_in;
|
||||
vector<unique_ptr<DialogFilter>> server_dialog_filters_out;
|
||||
vector<unique_ptr<DialogFilter>> dialog_filters_out;
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const {
|
||||
td::store(updated_date, storer);
|
||||
td::store(*server_dialog_filters_in, storer);
|
||||
td::store(*dialog_filters_in, storer);
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
void parse(ParserT &parser) {
|
||||
td::parse(updated_date, parser);
|
||||
td::parse(server_dialog_filters_out, parser);
|
||||
td::parse(dialog_filters_out, parser);
|
||||
}
|
||||
};
|
||||
|
||||
void MessagesManager::tear_down() {
|
||||
parent_.reset();
|
||||
}
|
||||
@ -15810,8 +15302,8 @@ void MessagesManager::sort_dialog_filter_input_dialog_ids(DialogFilter *dialog_f
|
||||
}
|
||||
}
|
||||
|
||||
Result<unique_ptr<MessagesManager::DialogFilter>> MessagesManager::create_dialog_filter(
|
||||
DialogFilterId dialog_filter_id, td_api::object_ptr<td_api::chatFilter> filter) {
|
||||
Result<unique_ptr<DialogFilter>> MessagesManager::create_dialog_filter(DialogFilterId dialog_filter_id,
|
||||
td_api::object_ptr<td_api::chatFilter> filter) {
|
||||
CHECK(filter != nullptr);
|
||||
for (auto chat_ids : {&filter->pinned_chat_ids_, &filter->excluded_chat_ids_, &filter->included_chat_ids_}) {
|
||||
for (auto chat_id : *chat_ids) {
|
||||
@ -31352,7 +30844,7 @@ MessagesManager::Dialog *MessagesManager::on_load_dialog_from_database(DialogId
|
||||
return add_new_dialog(parse_dialog(dialog_id, value), true);
|
||||
}
|
||||
|
||||
const MessagesManager::DialogFilter *MessagesManager::get_server_dialog_filter(DialogFilterId dialog_filter_id) const {
|
||||
const DialogFilter *MessagesManager::get_server_dialog_filter(DialogFilterId dialog_filter_id) const {
|
||||
for (const auto &filter : server_dialog_filters_) {
|
||||
if (filter->dialog_filter_id == dialog_filter_id) {
|
||||
return filter.get();
|
||||
@ -31361,7 +30853,7 @@ const MessagesManager::DialogFilter *MessagesManager::get_server_dialog_filter(D
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MessagesManager::DialogFilter *MessagesManager::get_dialog_filter(DialogFilterId dialog_filter_id) {
|
||||
DialogFilter *MessagesManager::get_dialog_filter(DialogFilterId dialog_filter_id) {
|
||||
for (auto &filter : dialog_filters_) {
|
||||
if (filter->dialog_filter_id == dialog_filter_id) {
|
||||
return filter.get();
|
||||
@ -31370,7 +30862,7 @@ MessagesManager::DialogFilter *MessagesManager::get_dialog_filter(DialogFilterId
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const MessagesManager::DialogFilter *MessagesManager::get_dialog_filter(DialogFilterId dialog_filter_id) const {
|
||||
const DialogFilter *MessagesManager::get_dialog_filter(DialogFilterId dialog_filter_id) const {
|
||||
for (const auto &filter : dialog_filters_) {
|
||||
if (filter->dialog_filter_id == dialog_filter_id) {
|
||||
return filter.get();
|
||||
|
@ -76,6 +76,8 @@ namespace td {
|
||||
|
||||
struct BinlogEvent;
|
||||
|
||||
class DialogFilter;
|
||||
|
||||
class DraftMessage;
|
||||
|
||||
struct InputMessageContent;
|
||||
@ -1237,8 +1239,6 @@ class MessagesManager : public Actor {
|
||||
void parse(ParserT &parser);
|
||||
};
|
||||
|
||||
struct DialogFilter;
|
||||
|
||||
struct RecommendedDialogFilter {
|
||||
unique_ptr<DialogFilter> dialog_filter;
|
||||
string description;
|
||||
|
Loading…
Reference in New Issue
Block a user