tdlight/td/telegram/DialogFilterManager.cpp

2207 lines
88 KiB
C++

//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
//
// 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/DialogFilterManager.h"
#include "td/telegram/AccessRights.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/ChatManager.h"
#include "td/telegram/DialogFilter.h"
#include "td/telegram/DialogFilter.hpp"
#include "td/telegram/DialogFilterInviteLink.h"
#include "td/telegram/DialogManager.h"
#include "td/telegram/DialogParticipantManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/LinkManager.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/OptionManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/TdDb.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UpdatesManager.h"
#include "td/telegram/UserManager.h"
#include "td/telegram/Version.h"
#include "td/actor/MultiPromise.h"
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Random.h"
#include "td/utils/tl_helpers.h"
#include <unordered_set>
namespace td {
class GetDialogFiltersQuery final : public Td::ResultHandler {
Promise<telegram_api::object_ptr<telegram_api::messages_dialogFilters>> promise_;
public:
explicit GetDialogFiltersQuery(Promise<telegram_api::object_ptr<telegram_api::messages_dialogFilters>> &&promise)
: promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::messages_getDialogFilters()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_getDialogFilters>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
promise_.set_value(result_ptr.move_as_ok());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class UpdateDialogFilterQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit UpdateDialogFilterQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(DialogFilterId dialog_filter_id, telegram_api::object_ptr<telegram_api::DialogFilter> filter) {
int32 flags = 0;
if (filter != nullptr) {
flags |= telegram_api::messages_updateDialogFilter::FILTER_MASK;
}
send_query(G()->net_query_creator().create(
telegram_api::messages_updateDialogFilter(flags, dialog_filter_id.get(), std::move(filter))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_updateDialogFilter>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
LOG(INFO) << "Receive result for UpdateDialogFilterQuery: " << result_ptr.ok();
promise_.set_value(Unit());
}
void on_error(Status status) final {
LOG(ERROR) << "Receive error for UpdateDialogFilterQuery: " << status;
promise_.set_error(std::move(status));
}
};
class UpdateDialogFiltersOrderQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit UpdateDialogFiltersOrderQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(const vector<DialogFilterId> &dialog_filter_ids, int32 main_dialog_list_position) {
auto filter_ids = transform(dialog_filter_ids, [](auto dialog_filter_id) { return dialog_filter_id.get(); });
CHECK(0 <= main_dialog_list_position);
CHECK(main_dialog_list_position <= static_cast<int32>(filter_ids.size()));
filter_ids.insert(filter_ids.begin() + main_dialog_list_position, 0);
send_query(G()->net_query_creator().create(telegram_api::messages_updateDialogFiltersOrder(std::move(filter_ids))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_updateDialogFiltersOrder>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
LOG(INFO) << "Receive result for UpdateDialogFiltersOrderQuery: " << result_ptr.ok();
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ToggleDialogFilterTagsQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ToggleDialogFilterTagsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(bool are_tags_enabled) {
send_query(G()->net_query_creator().create(telegram_api::messages_toggleDialogFilterTags(are_tags_enabled)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_toggleDialogFilterTags>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
LOG(INFO) << "Receive result for ToggleDialogFilterTagsQuery: " << result_ptr.ok();
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ExportChatlistInviteQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::chatFolderInviteLink>> promise_;
public:
explicit ExportChatlistInviteQuery(Promise<td_api::object_ptr<td_api::chatFolderInviteLink>> &&promise)
: promise_(std::move(promise)) {
}
void send(DialogFilterId dialog_filter_id, const string &title,
vector<tl_object_ptr<telegram_api::InputPeer>> &&input_peers) {
send_query(G()->net_query_creator().create(telegram_api::chatlists_exportChatlistInvite(
dialog_filter_id.get_input_chatlist(), title, std::move(input_peers))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::chatlists_exportChatlistInvite>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for ExportChatlistInviteQuery: " << to_string(ptr);
td_->dialog_filter_manager_->on_get_dialog_filter(std::move(ptr->filter_));
promise_.set_value(DialogFilterInviteLink(td_, std::move(ptr->invite_)).get_chat_folder_invite_link_object(td_));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetExportedChatlistInvitesQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::chatFolderInviteLinks>> promise_;
DialogFilterId dialog_filter_id_;
public:
explicit GetExportedChatlistInvitesQuery(Promise<td_api::object_ptr<td_api::chatFolderInviteLinks>> &&promise)
: promise_(std::move(promise)) {
}
void send(DialogFilterId dialog_filter_id) {
dialog_filter_id_ = dialog_filter_id;
send_query(G()->net_query_creator().create(
telegram_api::chatlists_getExportedInvites(dialog_filter_id.get_input_chatlist())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::chatlists_getExportedInvites>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetExportedChatlistInvitesQuery: " << to_string(ptr);
td_->user_manager_->on_get_users(std::move(ptr->users_), "GetExportedChatlistInvitesQuery");
td_->chat_manager_->on_get_chats(std::move(ptr->chats_), "GetExportedChatlistInvitesQuery");
auto result = td_api::make_object<td_api::chatFolderInviteLinks>();
for (auto &invite : ptr->invites_) {
result->invite_links_.push_back(
DialogFilterInviteLink(td_, std::move(invite)).get_chat_folder_invite_link_object(td_));
}
td_->dialog_filter_manager_->set_dialog_filter_has_my_invite_links(dialog_filter_id_,
!result->invite_links_.empty());
promise_.set_value(std::move(result));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class EditExportedChatlistInviteQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::chatFolderInviteLink>> promise_;
public:
explicit EditExportedChatlistInviteQuery(Promise<td_api::object_ptr<td_api::chatFolderInviteLink>> &&promise)
: promise_(std::move(promise)) {
}
void send(DialogFilterId dialog_filter_id, const string &slug, const string &title,
vector<tl_object_ptr<telegram_api::InputPeer>> &&input_peers) {
int32 flags =
telegram_api::chatlists_editExportedInvite::TITLE_MASK | telegram_api::chatlists_editExportedInvite::PEERS_MASK;
send_query(G()->net_query_creator().create(telegram_api::chatlists_editExportedInvite(
flags, dialog_filter_id.get_input_chatlist(), slug, title, std::move(input_peers))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::chatlists_editExportedInvite>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for EditExportedChatlistInviteQuery: " << to_string(ptr);
promise_.set_value(DialogFilterInviteLink(td_, std::move(ptr)).get_chat_folder_invite_link_object(td_));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class DeleteExportedChatlistInviteQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit DeleteExportedChatlistInviteQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(DialogFilterId dialog_filter_id, const string &slug) {
send_query(G()->net_query_creator().create(
telegram_api::chatlists_deleteExportedInvite(dialog_filter_id.get_input_chatlist(), slug)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::chatlists_deleteExportedInvite>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for DeleteExportedChatlistInviteQuery: " << ptr;
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class LeaveChatlistQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit LeaveChatlistQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(DialogFilterId dialog_filter_id) {
send_query(G()->net_query_creator().create(telegram_api::chatlists_leaveChatlist(
dialog_filter_id.get_input_chatlist(), vector<telegram_api::object_ptr<telegram_api::InputPeer>>())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::chatlists_leaveChatlist>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for LeaveChatlistQuery: " << to_string(ptr);
// must be set before updates are processed to drop are_dialog_filters_being_synchronized_
promise_.set_value(Unit());
td_->updates_manager_->on_get_updates(std::move(ptr), Promise<Unit>());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetLeaveChatlistSuggestionsQuery final : public Td::ResultHandler {
Promise<vector<telegram_api::object_ptr<telegram_api::Peer>>> promise_;
public:
explicit GetLeaveChatlistSuggestionsQuery(Promise<vector<telegram_api::object_ptr<telegram_api::Peer>>> &&promise)
: promise_(std::move(promise)) {
}
void send(DialogFilterId dialog_filter_id) {
send_query(G()->net_query_creator().create(
telegram_api::chatlists_getLeaveChatlistSuggestions(dialog_filter_id.get_input_chatlist())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::chatlists_getLeaveChatlistSuggestions>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetLeaveChatlistSuggestionsQuery: " << to_string(ptr);
promise_.set_value(std::move(ptr));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class CheckChatlistInviteQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::chatFolderInviteLinkInfo>> promise_;
string invite_link_;
public:
explicit CheckChatlistInviteQuery(Promise<td_api::object_ptr<td_api::chatFolderInviteLinkInfo>> &&promise)
: promise_(std::move(promise)) {
}
void send(const string &invite_link) {
invite_link_ = invite_link;
send_query(G()->net_query_creator().create(
telegram_api::chatlists_checkChatlistInvite(LinkManager::get_dialog_filter_invite_link_slug(invite_link_))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::chatlists_checkChatlistInvite>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
td_->dialog_filter_manager_->on_get_chatlist_invite(invite_link_, result_ptr.move_as_ok(), std::move(promise_));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class JoinChatlistInviteQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit JoinChatlistInviteQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(const string &invite_link, vector<DialogId> dialog_ids) {
send_query(G()->net_query_creator().create(telegram_api::chatlists_joinChatlistInvite(
LinkManager::get_dialog_filter_invite_link_slug(invite_link),
td_->dialog_manager_->get_input_peers(dialog_ids, AccessRights::Know))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::chatlists_joinChatlistInvite>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for JoinChatlistInviteQuery: " << to_string(ptr);
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetChatlistUpdatesQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::chats>> promise_;
public:
explicit GetChatlistUpdatesQuery(Promise<td_api::object_ptr<td_api::chats>> &&promise)
: promise_(std::move(promise)) {
}
void send(DialogFilterId dialog_filter_id) {
send_query(G()->net_query_creator().create(
telegram_api::chatlists_getChatlistUpdates(dialog_filter_id.get_input_chatlist())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::chatlists_getChatlistUpdates>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetChatlistUpdatesQuery: " << to_string(ptr);
td_->user_manager_->on_get_users(std::move(ptr->users_), "GetChatlistUpdatesQuery");
td_->chat_manager_->on_get_chats(std::move(ptr->chats_), "GetChatlistUpdatesQuery");
auto missing_dialog_ids = td_->dialog_manager_->get_peers_dialog_ids(std::move(ptr->missing_peers_), true);
promise_.set_value(td_->dialog_manager_->get_chats_object(-1, missing_dialog_ids, "GetChatlistUpdatesQuery"));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class JoinChatlistUpdatesQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit JoinChatlistUpdatesQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(DialogFilterId dialog_filter_id, vector<DialogId> dialog_ids) {
send_query(G()->net_query_creator().create(telegram_api::chatlists_joinChatlistUpdates(
dialog_filter_id.get_input_chatlist(), td_->dialog_manager_->get_input_peers(dialog_ids, AccessRights::Know))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::chatlists_joinChatlistUpdates>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for JoinChatlistUpdatesQuery: " << to_string(ptr);
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class HideChatlistUpdatesQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit HideChatlistUpdatesQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(DialogFilterId dialog_filter_id) {
send_query(G()->net_query_creator().create(
telegram_api::chatlists_hideChatlistUpdates(dialog_filter_id.get_input_chatlist())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::chatlists_hideChatlistUpdates>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for HideChatlistUpdatesQuery: " << ptr;
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetDialogsQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
bool is_single_ = false;
public:
explicit GetDialogsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(vector<InputDialogId> input_dialog_ids) {
CHECK(!input_dialog_ids.empty());
CHECK(input_dialog_ids.size() <= 100);
is_single_ = input_dialog_ids.size() == 1;
auto input_dialog_peers = InputDialogId::get_input_dialog_peers(input_dialog_ids);
CHECK(input_dialog_peers.size() == input_dialog_ids.size());
send_query(G()->net_query_creator().create(telegram_api::messages_getPeerDialogs(std::move(input_dialog_peers))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_getPeerDialogs>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto result = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetDialogsQuery: " << to_string(result);
td_->user_manager_->on_get_users(std::move(result->users_), "GetDialogsQuery");
td_->chat_manager_->on_get_chats(std::move(result->chats_), "GetDialogsQuery");
td_->messages_manager_->on_get_dialogs(FolderId(), std::move(result->dialogs_), -1, std::move(result->messages_),
std::move(promise_));
}
void on_error(Status status) final {
if (is_single_ && status.code() == 400) {
return promise_.set_value(Unit());
}
promise_.set_error(std::move(status));
}
};
class GetSuggestedDialogFiltersQuery final : public Td::ResultHandler {
Promise<vector<telegram_api::object_ptr<telegram_api::dialogFilterSuggested>>> promise_;
public:
explicit GetSuggestedDialogFiltersQuery(
Promise<vector<telegram_api::object_ptr<telegram_api::dialogFilterSuggested>>> &&promise)
: promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::messages_getSuggestedDialogFilters()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_getSuggestedDialogFilters>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
promise_.set_value(result_ptr.move_as_ok());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
DialogFilterManager::DialogFilterManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
}
DialogFilterManager::~DialogFilterManager() = default;
void DialogFilterManager::hangup() {
fail_promises(dialog_filter_reload_queries_, Global::request_aborted_error());
stop();
}
void DialogFilterManager::tear_down() {
parent_.reset();
}
class DialogFilterManager::DialogFiltersLogEvent {
public:
int32 server_main_dialog_list_position = 0;
int32 main_dialog_list_position = 0;
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;
bool server_are_tags_enabled = false;
bool are_tags_enabled = false;
template <class StorerT>
void store(StorerT &storer) const {
bool has_server_dialog_filters = !server_dialog_filters_in->empty();
bool has_dialog_filters = !dialog_filters_in->empty();
bool has_server_main_dialog_list_position = server_main_dialog_list_position != 0;
bool has_main_dialog_list_position = main_dialog_list_position != 0;
BEGIN_STORE_FLAGS();
STORE_FLAG(has_server_dialog_filters);
STORE_FLAG(has_dialog_filters);
STORE_FLAG(has_server_main_dialog_list_position);
STORE_FLAG(has_main_dialog_list_position);
STORE_FLAG(server_are_tags_enabled);
STORE_FLAG(are_tags_enabled);
END_STORE_FLAGS();
td::store(updated_date, storer);
if (has_server_dialog_filters) {
td::store(*server_dialog_filters_in, storer);
}
if (has_dialog_filters) {
td::store(*dialog_filters_in, storer);
}
if (has_server_main_dialog_list_position) {
td::store(server_main_dialog_list_position, storer);
}
if (has_main_dialog_list_position) {
td::store(main_dialog_list_position, storer);
}
}
template <class ParserT>
void parse(ParserT &parser) {
bool has_server_dialog_filters = true;
bool has_dialog_filters = true;
bool has_server_main_dialog_list_position = false;
bool has_main_dialog_list_position = false;
if (parser.version() >= static_cast<int32>(Version::AddMainDialogListPosition)) {
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_server_dialog_filters);
PARSE_FLAG(has_dialog_filters);
PARSE_FLAG(has_server_main_dialog_list_position);
PARSE_FLAG(has_main_dialog_list_position);
PARSE_FLAG(server_are_tags_enabled);
PARSE_FLAG(are_tags_enabled);
END_PARSE_FLAGS();
}
td::parse(updated_date, parser);
if (has_server_dialog_filters) {
td::parse(server_dialog_filters_out, parser);
}
if (has_dialog_filters) {
td::parse(dialog_filters_out, parser);
}
if (has_server_main_dialog_list_position) {
td::parse(server_main_dialog_list_position, parser);
}
if (has_main_dialog_list_position) {
td::parse(main_dialog_list_position, parser);
}
}
};
void DialogFilterManager::init() {
if (is_inited_) {
return;
}
is_inited_ = true;
bool is_authorized = td_->auth_manager_->is_authorized();
bool was_authorized_user = td_->auth_manager_->was_authorized() && !td_->auth_manager_->is_bot();
if (is_authorized && td_->auth_manager_->is_bot()) {
disable_get_dialog_filter_ = true;
}
if (was_authorized_user) {
auto dialog_filters = G()->td_db()->get_binlog_pmc()->get("dialog_filters");
if (!dialog_filters.empty()) {
DialogFiltersLogEvent log_event;
if (log_event_parse(log_event, dialog_filters).is_ok()) {
server_are_tags_enabled_ = log_event.server_are_tags_enabled;
are_tags_enabled_ = log_event.are_tags_enabled;
server_main_dialog_list_position_ = log_event.server_main_dialog_list_position;
main_dialog_list_position_ = log_event.main_dialog_list_position;
if (!td_->option_manager_->get_option_boolean("is_premium")) {
if (server_main_dialog_list_position_ != 0 || main_dialog_list_position_ != 0) {
LOG(INFO) << "Ignore main chat list position " << server_main_dialog_list_position_ << '/'
<< main_dialog_list_position_;
server_main_dialog_list_position_ = 0;
main_dialog_list_position_ = 0;
}
if (server_are_tags_enabled_ || are_tags_enabled_) {
LOG(INFO) << "Ignore enabled tags " << server_are_tags_enabled_ << '/' << are_tags_enabled_;
server_are_tags_enabled_ = false;
are_tags_enabled_ = false;
}
}
dialog_filters_updated_date_ = td_->ignore_background_updates() ? 0 : log_event.updated_date;
std::unordered_set<DialogFilterId, DialogFilterIdHash> server_dialog_filter_ids;
for (auto &dialog_filter : log_event.server_dialog_filters_out) {
if (dialog_filter->get_dialog_filter_id().is_valid() &&
server_dialog_filter_ids.insert(dialog_filter->get_dialog_filter_id()).second) {
server_dialog_filters_.push_back(std::move(dialog_filter));
}
}
for (auto &dialog_filter : log_event.dialog_filters_out) {
add_dialog_filter(std::move(dialog_filter), false, "binlog");
}
LOG(INFO) << "Loaded server chat folders "
<< DialogFilter::get_dialog_filter_ids(server_dialog_filters_, server_main_dialog_list_position_)
<< " and local chat folders "
<< DialogFilter::get_dialog_filter_ids(dialog_filters_, main_dialog_list_position_);
} else {
LOG(ERROR) << "Failed to parse chat folders from binlog";
}
}
send_update_chat_folders(); // always send updateChatFolders
if (is_authorized) {
if (need_synchronize_dialog_filters()) {
reload_dialog_filters();
} else {
auto cache_time = get_dialog_filters_cache_time();
schedule_dialog_filters_reload(cache_time - max(0, G()->unix_time() - dialog_filters_updated_date_));
}
}
}
}
void DialogFilterManager::on_authorization_success() {
CHECK(td_->auth_manager_->is_authorized());
if (td_->auth_manager_->is_bot()) {
disable_get_dialog_filter_ = true;
return;
}
reload_dialog_filters();
}
void DialogFilterManager::on_update_dialog_filters(Promise<Unit> &&promise) {
if (td_->auth_manager_->is_bot()) {
// just in case
return promise.set_value(Unit());
}
schedule_reload_dialog_filters(std::move(promise));
}
void DialogFilterManager::schedule_reload_dialog_filters(Promise<Unit> &&promise) {
schedule_dialog_filters_reload(0.0);
dialog_filter_reload_queries_.push_back(std::move(promise));
}
bool DialogFilterManager::have_dialog_filters() const {
return !dialog_filters_.empty();
}
vector<FolderId> DialogFilterManager::get_dialog_filter_folder_ids(DialogFilterId dialog_filter_id) const {
const auto *dialog_filter = get_dialog_filter(dialog_filter_id);
CHECK(dialog_filter != nullptr);
return dialog_filter->get_folder_ids();
}
vector<DialogFilterId> DialogFilterManager::get_dialog_filters_to_add_dialog(DialogId dialog_id) const {
vector<DialogFilterId> result;
for (const auto &dialog_filter : dialog_filters_) {
if (dialog_filter->can_include_dialog(dialog_id)) {
result.push_back(dialog_filter->get_dialog_filter_id());
}
}
return result;
}
bool DialogFilterManager::need_dialog_in_filter(DialogFilterId dialog_filter_id,
const DialogFilterDialogInfo &dialog_info) const {
const auto *dialog_filter = get_dialog_filter(dialog_filter_id);
CHECK(dialog_filter != nullptr);
return dialog_filter->need_dialog(td_, dialog_info);
}
bool DialogFilterManager::is_dialog_pinned(DialogFilterId dialog_filter_id, DialogId dialog_id) const {
const auto *dialog_filter = get_dialog_filter(dialog_filter_id);
return dialog_filter != nullptr && dialog_filter->is_dialog_pinned(dialog_id);
}
const vector<InputDialogId> &DialogFilterManager::get_pinned_input_dialog_ids(DialogFilterId dialog_filter_id) const {
auto *dialog_filter = get_dialog_filter(dialog_filter_id);
CHECK(dialog_filter != nullptr);
return dialog_filter->get_pinned_input_dialog_ids();
}
vector<DialogId> DialogFilterManager::get_pinned_dialog_ids(DialogFilterId dialog_filter_id) const {
auto *dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return {};
}
return InputDialogId::get_dialog_ids(dialog_filter->get_pinned_input_dialog_ids());
}
Status DialogFilterManager::set_dialog_is_pinned(DialogFilterId dialog_filter_id, InputDialogId input_dialog_id,
bool is_pinned) {
CHECK(is_update_chat_folders_sent_);
auto old_dialog_filter = get_dialog_filter(dialog_filter_id);
CHECK(old_dialog_filter != nullptr);
auto new_dialog_filter = make_unique<DialogFilter>(*old_dialog_filter);
new_dialog_filter->set_dialog_is_pinned(input_dialog_id, is_pinned);
TRY_STATUS(new_dialog_filter->check_limits());
new_dialog_filter->sort_input_dialog_ids(td_, "set_dialog_is_pinned");
do_edit_dialog_filter(std::move(new_dialog_filter),
input_dialog_id.get_dialog_id().get_type() != DialogType::SecretChat, "set_dialog_is_pinned");
return Status::OK();
}
Status DialogFilterManager::set_pinned_dialog_ids(DialogFilterId dialog_filter_id,
vector<InputDialogId> input_dialog_ids, bool need_synchronize) {
CHECK(is_update_chat_folders_sent_);
auto old_dialog_filter = get_dialog_filter(dialog_filter_id);
CHECK(old_dialog_filter != nullptr);
auto new_dialog_filter = make_unique<DialogFilter>(*old_dialog_filter);
new_dialog_filter->set_pinned_dialog_ids(std::move(input_dialog_ids));
TRY_STATUS(new_dialog_filter->check_limits());
new_dialog_filter->sort_input_dialog_ids(td_, "set_pinned_dialog_ids");
do_edit_dialog_filter(std::move(new_dialog_filter), need_synchronize, "set_pinned_dialog_ids");
return Status::OK();
}
Status DialogFilterManager::add_dialog(DialogFilterId dialog_filter_id, InputDialogId input_dialog_id) {
CHECK(is_update_chat_folders_sent_);
auto old_dialog_filter = get_dialog_filter(dialog_filter_id);
CHECK(old_dialog_filter != nullptr);
if (old_dialog_filter->is_dialog_included(input_dialog_id.get_dialog_id())) {
return Status::OK();
}
auto new_dialog_filter = make_unique<DialogFilter>(*old_dialog_filter);
new_dialog_filter->include_dialog(input_dialog_id);
TRY_STATUS(new_dialog_filter->check_limits());
new_dialog_filter->sort_input_dialog_ids(td_, "add_dialog");
do_edit_dialog_filter(std::move(new_dialog_filter),
input_dialog_id.get_dialog_id().get_type() != DialogType::SecretChat, "add_dialog");
return Status::OK();
}
bool DialogFilterManager::is_recommended_dialog_filter(const DialogFilter *dialog_filter) {
for (const auto &recommended_dialog_filter : recommended_dialog_filters_) {
if (DialogFilter::are_similar(*recommended_dialog_filter.dialog_filter, *dialog_filter)) {
return true;
}
}
return false;
}
td_api::object_ptr<td_api::chatFolder> DialogFilterManager::get_chat_folder_object(DialogFilterId dialog_filter_id) {
CHECK(!td_->auth_manager_->is_bot());
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return nullptr;
}
return get_chat_folder_object(dialog_filter);
}
td_api::object_ptr<td_api::chatFolder> DialogFilterManager::get_chat_folder_object(const DialogFilter *dialog_filter) {
DialogFilterId dialog_filter_id = dialog_filter->get_dialog_filter_id();
vector<DialogId> left_dialog_ids;
vector<DialogId> unknown_dialog_ids;
dialog_filter->for_each_dialog([&](const InputDialogId &input_dialog_id) {
auto dialog_id = input_dialog_id.get_dialog_id();
if (td_->messages_manager_->is_dialog_in_dialog_list(dialog_id)) {
return;
}
if (td_->messages_manager_->have_dialog(dialog_id)) {
LOG(INFO) << "Skip nonjoined " << dialog_id << " from " << dialog_filter_id;
unknown_dialog_ids.push_back(dialog_id);
left_dialog_ids.push_back(dialog_id);
} else {
// possible if the chat folder has just been edited from another device
LOG(ERROR) << "Can't find " << dialog_id << " from " << dialog_filter_id;
unknown_dialog_ids.push_back(dialog_id);
}
});
auto result = dialog_filter->get_chat_folder_object(unknown_dialog_ids);
if (dialog_filter_id.is_valid()) {
delete_dialogs_from_filter(dialog_filter, std::move(left_dialog_ids), "get_chat_folder_object");
}
return result;
}
void DialogFilterManager::get_recommended_dialog_filters(
Promise<td_api::object_ptr<td_api::recommendedChatFolders>> &&promise) {
CHECK(!td_->auth_manager_->is_bot());
auto query_promise = PromiseCreator::lambda(
[actor_id = actor_id(this), promise = std::move(promise)](
Result<vector<telegram_api::object_ptr<telegram_api::dialogFilterSuggested>>> result) mutable {
send_closure(actor_id, &DialogFilterManager::on_get_recommended_dialog_filters, std::move(result),
std::move(promise));
});
td_->create_handler<GetSuggestedDialogFiltersQuery>(std::move(query_promise))->send();
}
void DialogFilterManager::on_get_recommended_dialog_filters(
Result<vector<telegram_api::object_ptr<telegram_api::dialogFilterSuggested>>> result,
Promise<td_api::object_ptr<td_api::recommendedChatFolders>> &&promise) {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
CHECK(!td_->auth_manager_->is_bot());
auto suggested_filters = result.move_as_ok();
MultiPromiseActorSafe mpas{"LoadRecommendedFiltersMultiPromiseActor"};
mpas.add_promise(Promise<Unit>());
auto lock = mpas.get_promise();
vector<RecommendedDialogFilter> filters;
for (auto &suggested_filter : suggested_filters) {
RecommendedDialogFilter recommended_dialog_filter;
recommended_dialog_filter.dialog_filter =
DialogFilter::get_dialog_filter(std::move(suggested_filter->filter_), false);
if (recommended_dialog_filter.dialog_filter == nullptr) {
continue;
}
load_dialog_filter(recommended_dialog_filter.dialog_filter.get(), mpas.get_promise());
recommended_dialog_filter.description = std::move(suggested_filter->description_);
filters.push_back(std::move(recommended_dialog_filter));
}
mpas.add_promise(PromiseCreator::lambda([actor_id = actor_id(this), filters = std::move(filters),
promise = std::move(promise)](Result<Unit> &&result) mutable {
send_closure(actor_id, &DialogFilterManager::on_load_recommended_dialog_filters, std::move(result),
std::move(filters), std::move(promise));
}));
lock.set_value(Unit());
}
void DialogFilterManager::on_load_recommended_dialog_filters(
Result<Unit> &&result, vector<RecommendedDialogFilter> &&filters,
Promise<td_api::object_ptr<td_api::recommendedChatFolders>> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
CHECK(!td_->auth_manager_->is_bot());
auto chat_folders = transform(filters, [this](const RecommendedDialogFilter &recommended_dialog_filter) {
return td_api::make_object<td_api::recommendedChatFolder>(
get_chat_folder_object(recommended_dialog_filter.dialog_filter.get()), recommended_dialog_filter.description);
});
recommended_dialog_filters_ = std::move(filters);
promise.set_value(td_api::make_object<td_api::recommendedChatFolders>(std::move(chat_folders)));
}
void DialogFilterManager::get_dialog_filter(DialogFilterId dialog_filter_id,
Promise<td_api::object_ptr<td_api::chatFolder>> &&promise) {
CHECK(!td_->auth_manager_->is_bot());
if (!dialog_filter_id.is_valid()) {
return promise.set_error(Status::Error(400, "Invalid chat folder identifier specified"));
}
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_value(nullptr);
}
auto load_promise = PromiseCreator::lambda(
[actor_id = actor_id(this), dialog_filter_id, promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
send_closure(actor_id, &DialogFilterManager::on_load_dialog_filter, dialog_filter_id, std::move(promise));
});
load_dialog_filter(dialog_filter, std::move(load_promise));
}
void DialogFilterManager::on_load_dialog_filter(DialogFilterId dialog_filter_id,
Promise<td_api::object_ptr<td_api::chatFolder>> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
promise.set_value(get_chat_folder_object(dialog_filter_id));
}
void DialogFilterManager::load_dialog_filter(const DialogFilter *dialog_filter, Promise<Unit> &&promise) {
CHECK(!td_->auth_manager_->is_bot());
vector<InputDialogId> needed_dialog_ids;
dialog_filter->for_each_dialog([&](const InputDialogId &input_dialog_id) {
if (!td_->messages_manager_->have_dialog(input_dialog_id.get_dialog_id())) {
needed_dialog_ids.push_back(input_dialog_id);
}
});
vector<InputDialogId> input_dialog_ids;
for (const auto &input_dialog_id : needed_dialog_ids) {
auto dialog_id = input_dialog_id.get_dialog_id();
// TODO load dialogs asynchronously
if (!td_->dialog_manager_->have_dialog_force(dialog_id, "load_dialog_filter")) {
if (dialog_id.get_type() == DialogType::SecretChat) {
if (td_->dialog_manager_->have_dialog_info_force(dialog_id, "load_dialog_filter")) {
td_->dialog_manager_->force_create_dialog(dialog_id, "load_dialog_filter");
}
} else {
input_dialog_ids.push_back(input_dialog_id);
}
}
}
if (!input_dialog_ids.empty()) {
return load_dialog_filter_dialogs(dialog_filter->get_dialog_filter_id(), std::move(input_dialog_ids),
std::move(promise));
}
promise.set_value(Unit());
}
void DialogFilterManager::load_dialog_filter_dialogs(DialogFilterId dialog_filter_id,
vector<InputDialogId> &&input_dialog_ids,
Promise<Unit> &&promise) {
const size_t MAX_SLICE_SIZE = 100; // server side limit
MultiPromiseActorSafe mpas{"GetFilterDialogsOnServerMultiPromiseActor"};
mpas.add_promise(std::move(promise));
auto lock = mpas.get_promise();
for (size_t i = 0; i < input_dialog_ids.size(); i += MAX_SLICE_SIZE) {
auto end_i = i + MAX_SLICE_SIZE;
auto end = end_i < input_dialog_ids.size() ? input_dialog_ids.begin() + end_i : input_dialog_ids.end();
vector<InputDialogId> slice_input_dialog_ids = {input_dialog_ids.begin() + i, end};
auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), dialog_filter_id,
dialog_ids = InputDialogId::get_dialog_ids(slice_input_dialog_ids),
promise = mpas.get_promise()](Result<Unit> &&result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
send_closure(actor_id, &DialogFilterManager::on_load_dialog_filter_dialogs, dialog_filter_id,
std::move(dialog_ids), std::move(promise));
});
td_->create_handler<GetDialogsQuery>(std::move(query_promise))->send(std::move(slice_input_dialog_ids));
}
lock.set_value(Unit());
}
void DialogFilterManager::on_load_dialog_filter_dialogs(DialogFilterId dialog_filter_id, vector<DialogId> &&dialog_ids,
Promise<Unit> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
td::remove_if(dialog_ids, [dialog_manager = td_->dialog_manager_.get()](DialogId dialog_id) {
return dialog_manager->have_dialog_force(dialog_id, "on_load_dialog_filter_dialogs");
});
if (dialog_ids.empty()) {
LOG(INFO) << "All chats from " << dialog_filter_id << " were loaded";
return promise.set_value(Unit());
}
LOG(INFO) << "Failed to load chats " << dialog_ids << " from " << dialog_filter_id;
auto old_dialog_filter = get_dialog_filter(dialog_filter_id);
if (old_dialog_filter == nullptr) {
return promise.set_value(Unit());
}
CHECK(is_update_chat_folders_sent_);
delete_dialogs_from_filter(old_dialog_filter, std::move(dialog_ids), "on_load_dialog_filter_dialogs");
promise.set_value(Unit());
}
void DialogFilterManager::load_input_dialog(const InputDialogId &input_dialog_id, Promise<Unit> &&promise) {
td_->create_handler<GetDialogsQuery>(std::move(promise))->send({input_dialog_id});
}
void DialogFilterManager::delete_dialogs_from_filter(const DialogFilter *dialog_filter, vector<DialogId> &&dialog_ids,
const char *source) {
if (dialog_ids.empty()) {
return;
}
bool was_valid = dialog_filter->check_limits().is_ok();
auto new_dialog_filter = td::make_unique<DialogFilter>(*dialog_filter);
for (auto dialog_id : dialog_ids) {
new_dialog_filter->remove_dialog_id(dialog_id);
}
if (new_dialog_filter->is_empty(false)) {
delete_dialog_filter(dialog_filter->get_dialog_filter_id(), vector<DialogId>(), Promise<Unit>());
return;
}
CHECK(!was_valid || new_dialog_filter->check_limits().is_ok());
if (*new_dialog_filter != *dialog_filter) {
LOG(INFO) << "Update " << *dialog_filter << " to " << *new_dialog_filter;
do_edit_dialog_filter(std::move(new_dialog_filter), true, "delete_dialogs_from_filter");
}
}
const DialogFilter *DialogFilterManager::get_server_dialog_filter(DialogFilterId dialog_filter_id) const {
CHECK(!disable_get_dialog_filter_);
for (const auto &dialog_filter : server_dialog_filters_) {
if (dialog_filter->get_dialog_filter_id() == dialog_filter_id) {
return dialog_filter.get();
}
}
return nullptr;
}
DialogFilter *DialogFilterManager::get_dialog_filter(DialogFilterId dialog_filter_id) {
CHECK(!disable_get_dialog_filter_);
for (auto &dialog_filter : dialog_filters_) {
if (dialog_filter->get_dialog_filter_id() == dialog_filter_id) {
return dialog_filter.get();
}
}
return nullptr;
}
const DialogFilter *DialogFilterManager::get_dialog_filter(DialogFilterId dialog_filter_id) const {
CHECK(!disable_get_dialog_filter_);
for (const auto &dialog_filter : dialog_filters_) {
if (dialog_filter->get_dialog_filter_id() == dialog_filter_id) {
return dialog_filter.get();
}
}
return nullptr;
}
int32 DialogFilterManager::get_server_main_dialog_list_position() const {
int32 current_position = 0;
int32 current_server_position = 0;
if (current_position == main_dialog_list_position_) {
return current_server_position;
}
for (const auto &dialog_filter : dialog_filters_) {
current_position++;
if (!dialog_filter->is_empty(true)) {
current_server_position++;
}
if (current_position == main_dialog_list_position_) {
return current_server_position;
}
}
LOG(WARNING) << "Failed to find server position for " << main_dialog_list_position_ << " in chat folders";
return current_server_position;
}
double DialogFilterManager::get_dialog_filters_cache_time() {
return DIALOG_FILTERS_CACHE_TIME * 0.0001 * Random::fast(9000, 11000);
}
void DialogFilterManager::schedule_dialog_filters_reload(double timeout) {
if (td_->auth_manager_->is_bot()) {
// just in case
return;
}
if (timeout <= 0) {
timeout = 0.0;
if (dialog_filters_updated_date_ != 0) {
dialog_filters_updated_date_ = 0;
save_dialog_filters();
}
}
LOG(INFO) << "Schedule reload of chat folders in " << timeout;
reload_dialog_filters_timeout_.set_callback(std::move(DialogFilterManager::on_reload_dialog_filters_timeout));
reload_dialog_filters_timeout_.set_callback_data(static_cast<void *>(this));
reload_dialog_filters_timeout_.set_timeout_in(timeout);
}
void DialogFilterManager::on_reload_dialog_filters_timeout(void *dialog_filter_manager_ptr) {
if (G()->close_flag()) {
return;
}
auto dialog_filter_manager = static_cast<DialogFilterManager *>(dialog_filter_manager_ptr);
send_closure_later(dialog_filter_manager->actor_id(dialog_filter_manager),
&DialogFilterManager::reload_dialog_filters);
}
void DialogFilterManager::reload_dialog_filters() {
if (G()->close_flag()) {
return;
}
CHECK(!td_->auth_manager_->is_bot());
if (are_dialog_filters_being_synchronized_ || are_dialog_filters_being_reloaded_) {
need_dialog_filters_reload_ = true;
return;
}
LOG(INFO) << "Reload chat folders from server";
are_dialog_filters_being_reloaded_ = true;
need_dialog_filters_reload_ = false;
auto promise = PromiseCreator::lambda(
[actor_id = actor_id(this)](Result<telegram_api::object_ptr<telegram_api::messages_dialogFilters>> r_filters) {
send_closure(actor_id, &DialogFilterManager::on_get_dialog_filters, std::move(r_filters), false);
});
td_->create_handler<GetDialogFiltersQuery>(std::move(promise))->send();
}
void DialogFilterManager::on_get_dialog_filter(telegram_api::object_ptr<telegram_api::DialogFilter> filter) {
CHECK(!td_->auth_manager_->is_bot());
auto new_server_filter = DialogFilter::get_dialog_filter(std::move(filter), true);
if (new_server_filter == nullptr) {
return;
}
new_server_filter->sort_input_dialog_ids(td_, "on_get_dialog_filter 1");
auto dialog_filter_id = new_server_filter->get_dialog_filter_id();
auto old_filter = get_dialog_filter(dialog_filter_id);
if (old_filter == nullptr) {
return;
}
bool is_server_changed = false;
bool is_changed = false;
for (auto &old_server_filter : server_dialog_filters_) {
if (old_server_filter->get_dialog_filter_id() == dialog_filter_id && *new_server_filter != *old_server_filter) {
if (!DialogFilter::are_equivalent(*old_filter, *new_server_filter)) {
auto new_filter =
DialogFilter::merge_dialog_filter_changes(old_filter, old_server_filter.get(), new_server_filter.get());
new_filter->sort_input_dialog_ids(td_, "on_get_dialog_filter");
if (*new_filter != *old_filter) {
is_changed = true;
edit_dialog_filter(std::move(new_filter), "on_get_dialog_filter");
}
}
is_server_changed = true;
old_server_filter = std::move(new_server_filter);
break;
}
}
if (!is_server_changed) {
return;
}
if (is_changed || !is_update_chat_folders_sent_) {
send_update_chat_folders();
}
schedule_dialog_filters_reload(get_dialog_filters_cache_time());
save_dialog_filters();
if (need_synchronize_dialog_filters()) {
synchronize_dialog_filters();
}
}
void DialogFilterManager::on_get_dialog_filters(
Result<telegram_api::object_ptr<telegram_api::messages_dialogFilters>> r_filters, bool dummy) {
if (G()->close_flag()) {
return;
}
are_dialog_filters_being_reloaded_ = false;
CHECK(!td_->auth_manager_->is_bot());
auto promises = std::move(dialog_filter_reload_queries_);
dialog_filter_reload_queries_.clear();
if (r_filters.is_error()) {
if (!G()->is_expected_error(r_filters.error())) {
LOG(WARNING) << "Receive error " << r_filters.error() << " for GetDialogFiltersQuery";
}
set_promises(promises); // ignore error
need_dialog_filters_reload_ = false;
schedule_dialog_filters_reload(Random::fast(60, 5 * 60));
return;
}
auto dialog_filters = r_filters.move_as_ok();
auto filters = std::move(dialog_filters->filters_);
vector<unique_ptr<DialogFilter>> new_server_dialog_filters;
LOG(INFO) << "Receive chat folders from server: " << to_string(filters);
std::unordered_set<DialogFilterId, DialogFilterIdHash> new_dialog_filter_ids;
bool server_are_tags_enabled = dialog_filters->tags_enabled_;
int32 server_main_dialog_list_position = -1;
int32 position = 0;
for (auto &filter : filters) {
if (filter->get_id() == telegram_api::dialogFilterDefault::ID) {
if (server_main_dialog_list_position == -1) {
server_main_dialog_list_position = position;
} else {
LOG(ERROR) << "Receive duplicate dialogFilterDefault";
}
continue;
}
auto dialog_filter = DialogFilter::get_dialog_filter(std::move(filter), true);
if (dialog_filter == nullptr) {
continue;
}
if (!new_dialog_filter_ids.insert(dialog_filter->get_dialog_filter_id()).second) {
LOG(ERROR) << "Receive duplicate " << dialog_filter->get_dialog_filter_id();
continue;
}
dialog_filter->sort_input_dialog_ids(td_, "on_get_dialog_filters 1");
new_server_dialog_filters.push_back(std::move(dialog_filter));
position++;
}
if (server_main_dialog_list_position == -1) {
LOG(ERROR) << "Receive no dialogFilterDefault";
server_main_dialog_list_position = 0;
}
if (server_main_dialog_list_position != 0 && !td_->option_manager_->get_option_boolean("is_premium")) {
LOG(INFO) << "Ignore server main chat list position " << server_main_dialog_list_position;
server_main_dialog_list_position = 0;
}
if (server_are_tags_enabled && !td_->option_manager_->get_option_boolean("is_premium")) {
LOG(INFO) << "Ignore server enabled tags";
server_are_tags_enabled = false;
}
bool is_changed = false;
dialog_filters_updated_date_ = G()->unix_time();
if (server_dialog_filters_ != new_server_dialog_filters) {
LOG(INFO) << "Change server chat folders from "
<< DialogFilter::get_dialog_filter_ids(server_dialog_filters_, server_main_dialog_list_position_)
<< " to "
<< DialogFilter::get_dialog_filter_ids(new_server_dialog_filters, server_main_dialog_list_position);
FlatHashMap<DialogFilterId, const DialogFilter *, DialogFilterIdHash> old_server_dialog_filters;
for (const auto &dialog_filter : server_dialog_filters_) {
old_server_dialog_filters.emplace(dialog_filter->get_dialog_filter_id(), dialog_filter.get());
}
for (const auto &new_server_filter : new_server_dialog_filters) {
auto dialog_filter_id = new_server_filter->get_dialog_filter_id();
auto old_filter = get_dialog_filter(dialog_filter_id);
auto it = old_server_dialog_filters.find(dialog_filter_id);
if (it != old_server_dialog_filters.end()) {
auto old_server_filter = it->second;
if (*new_server_filter != *old_server_filter) {
if (old_filter == nullptr) {
// the filter was deleted, don't need to edit it
} else {
if (DialogFilter::are_equivalent(*old_filter, *new_server_filter)) { // fast path
// the filter was edited from this client, nothing to do
} else {
auto new_filter =
DialogFilter::merge_dialog_filter_changes(old_filter, old_server_filter, new_server_filter.get());
new_filter->sort_input_dialog_ids(td_, "on_get_dialog_filters");
if (*new_filter != *old_filter) {
is_changed = true;
edit_dialog_filter(std::move(new_filter), "on_get_dialog_filters");
}
}
}
}
old_server_dialog_filters.erase(it);
} else {
if (old_filter == nullptr) {
// the filter was added from another client
is_changed = true;
add_dialog_filter(make_unique<DialogFilter>(*new_server_filter), false, "on_get_dialog_filters");
} else {
// the filter was added from this client
// after that it could be added from another client, or edited from this client, or edited from another client
// prefer local value, so do nothing
// effectively, ignore edits from other clients, if didn't receive UpdateDialogFilterQuery response
}
}
}
vector<DialogFilterId> left_old_server_dialog_filter_ids;
for (const auto &dialog_filter : server_dialog_filters_) {
if (old_server_dialog_filters.count(dialog_filter->get_dialog_filter_id()) == 0) {
left_old_server_dialog_filter_ids.push_back(dialog_filter->get_dialog_filter_id());
}
}
LOG(INFO) << "Still existing server chat folders: " << left_old_server_dialog_filter_ids;
for (auto &old_server_filter : old_server_dialog_filters) {
auto dialog_filter_id = old_server_filter.first;
// deleted filter
auto old_filter = get_dialog_filter(dialog_filter_id);
if (old_filter == nullptr) {
// the filter was deleted from this client, nothing to do
} else {
// the filter was deleted from another client
// ignore edits done from the current client and just delete the filter
is_changed = true;
do_delete_dialog_filter(dialog_filter_id, "on_get_dialog_filters");
}
}
bool is_order_changed = [&] {
vector<DialogFilterId> new_server_dialog_filter_ids =
DialogFilter::get_dialog_filter_ids(new_server_dialog_filters, -1);
CHECK(new_server_dialog_filter_ids.size() >= left_old_server_dialog_filter_ids.size());
new_server_dialog_filter_ids.resize(left_old_server_dialog_filter_ids.size());
return new_server_dialog_filter_ids != left_old_server_dialog_filter_ids;
}();
if (is_order_changed) { // if order is changed from this and other clients, prefer order from another client
vector<DialogFilterId> new_dialog_filter_order;
for (const auto &new_server_filter : new_server_dialog_filters) {
auto dialog_filter_id = new_server_filter->get_dialog_filter_id();
if (get_dialog_filter(dialog_filter_id) != nullptr) {
new_dialog_filter_order.push_back(dialog_filter_id);
}
}
is_changed = true;
DialogFilter::set_dialog_filters_order(dialog_filters_, new_dialog_filter_order);
}
server_dialog_filters_ = std::move(new_server_dialog_filters);
}
if (server_main_dialog_list_position_ != server_main_dialog_list_position) {
server_main_dialog_list_position_ = server_main_dialog_list_position;
int32 main_dialog_list_position = -1;
if (server_main_dialog_list_position == 0) {
main_dialog_list_position = 0;
} else {
int32 current_position = 0;
int32 current_server_position = 0;
for (const auto &dialog_filter : dialog_filters_) {
current_position++;
if (!dialog_filter->is_empty(true)) {
current_server_position++;
}
if (current_server_position == server_main_dialog_list_position) {
main_dialog_list_position = current_position;
}
}
if (main_dialog_list_position == -1) {
LOG(INFO) << "Failed to find server position " << server_main_dialog_list_position << " in chat folders";
main_dialog_list_position = static_cast<int32>(dialog_filters_.size());
}
}
if (main_dialog_list_position != main_dialog_list_position_) {
LOG(INFO) << "Change main chat list position from " << main_dialog_list_position_ << " to "
<< main_dialog_list_position;
main_dialog_list_position_ = main_dialog_list_position;
is_changed = true;
}
}
if (server_are_tags_enabled_ != server_are_tags_enabled) {
server_are_tags_enabled_ = server_are_tags_enabled;
if (server_are_tags_enabled != are_tags_enabled_) {
LOG(INFO) << "Change are_tags_enabled_ from " << are_tags_enabled_ << " to " << server_are_tags_enabled;
are_tags_enabled_ = server_are_tags_enabled;
is_changed = true;
}
}
if (is_changed || !is_update_chat_folders_sent_) {
send_update_chat_folders();
}
schedule_dialog_filters_reload(get_dialog_filters_cache_time());
save_dialog_filters();
if (need_synchronize_dialog_filters()) {
synchronize_dialog_filters();
}
set_promises(promises);
}
bool DialogFilterManager::need_synchronize_dialog_filters() const {
CHECK(!td_->auth_manager_->is_bot());
size_t server_dialog_filter_count = 0;
vector<DialogFilterId> dialog_filter_ids;
for (const auto &dialog_filter : dialog_filters_) {
if (dialog_filter->is_empty(true)) {
continue;
}
server_dialog_filter_count++;
auto server_dialog_filter = get_server_dialog_filter(dialog_filter->get_dialog_filter_id());
if (server_dialog_filter == nullptr || !DialogFilter::are_equivalent(*server_dialog_filter, *dialog_filter)) {
// need update dialog filter on server
return true;
}
dialog_filter_ids.push_back(dialog_filter->get_dialog_filter_id());
}
if (server_dialog_filter_count != server_dialog_filters_.size()) {
// need delete dialog filter on server
return true;
}
if (dialog_filter_ids != DialogFilter::get_dialog_filter_ids(server_dialog_filters_, -1)) {
// need reorder dialog filters on server
return true;
}
if (get_server_main_dialog_list_position() != server_main_dialog_list_position_) {
// need reorder main chat list on server
return true;
}
if (are_tags_enabled_ != server_are_tags_enabled_) {
// need enable/disable tags
return true;
}
return false;
}
void DialogFilterManager::synchronize_dialog_filters() {
if (G()->close_flag()) {
return;
}
CHECK(!td_->auth_manager_->is_bot());
if (are_dialog_filters_being_synchronized_ || are_dialog_filters_being_reloaded_) {
return;
}
if (need_dialog_filters_reload_) {
return reload_dialog_filters();
}
if (!need_synchronize_dialog_filters()) {
// reload filters to repair their order if the server added new filter to the beginning of the list
return reload_dialog_filters();
}
LOG(INFO) << "Synchronize chat folder changes with server having local "
<< DialogFilter::get_dialog_filter_ids(dialog_filters_, main_dialog_list_position_) << " and server "
<< DialogFilter::get_dialog_filter_ids(server_dialog_filters_, server_main_dialog_list_position_);
for (const auto &server_dialog_filter : server_dialog_filters_) {
if (get_dialog_filter(server_dialog_filter->get_dialog_filter_id()) == nullptr) {
return delete_dialog_filter_on_server(server_dialog_filter->get_dialog_filter_id(),
server_dialog_filter->is_shareable());
}
}
vector<DialogFilterId> dialog_filter_ids;
for (const auto &dialog_filter : dialog_filters_) {
if (dialog_filter->is_empty(true)) {
continue;
}
auto server_dialog_filter = get_server_dialog_filter(dialog_filter->get_dialog_filter_id());
if (server_dialog_filter == nullptr || !DialogFilter::are_equivalent(*server_dialog_filter, *dialog_filter)) {
return update_dialog_filter_on_server(make_unique<DialogFilter>(*dialog_filter));
}
dialog_filter_ids.push_back(dialog_filter->get_dialog_filter_id());
}
auto server_main_dialog_list_position = get_server_main_dialog_list_position();
if (dialog_filter_ids != DialogFilter::get_dialog_filter_ids(server_dialog_filters_, -1) ||
server_main_dialog_list_position != server_main_dialog_list_position_) {
return reorder_dialog_filters_on_server(std::move(dialog_filter_ids), server_main_dialog_list_position);
}
if (are_tags_enabled_ != server_are_tags_enabled_) {
return toggle_are_tags_enabled_on_server(are_tags_enabled_);
}
UNREACHABLE();
}
void DialogFilterManager::send_update_chat_folders() {
if (td_->auth_manager_->is_bot()) {
return;
}
is_update_chat_folders_sent_ = true;
send_closure(G()->td(), &Td::send_update, get_update_chat_folders_object());
}
td_api::object_ptr<td_api::updateChatFolders> DialogFilterManager::get_update_chat_folders_object() const {
CHECK(!td_->auth_manager_->is_bot());
auto update = td_api::make_object<td_api::updateChatFolders>();
for (const auto &dialog_filter : dialog_filters_) {
update->chat_folders_.push_back(dialog_filter->get_chat_folder_info_object());
}
update->main_chat_list_position_ = main_dialog_list_position_;
update->are_tags_enabled_ = are_tags_enabled_;
return update;
}
void DialogFilterManager::create_dialog_filter(td_api::object_ptr<td_api::chatFolder> filter,
Promise<td_api::object_ptr<td_api::chatFolderInfo>> &&promise) {
auto max_dialog_filters = clamp(td_->option_manager_->get_option_integer("chat_folder_count_max"),
static_cast<int64>(0), static_cast<int64>(100));
if (dialog_filters_.size() >= narrow_cast<size_t>(max_dialog_filters)) {
return promise.set_error(Status::Error(400, "The maximum number of chat folders exceeded"));
}
if (!is_update_chat_folders_sent_) {
return promise.set_error(Status::Error(400, "Chat folders are not synchronized yet"));
}
DialogFilterId dialog_filter_id;
do {
auto min_id = static_cast<int>(DialogFilterId::min().get());
auto max_id = static_cast<int>(DialogFilterId::max().get());
dialog_filter_id = DialogFilterId(static_cast<int32>(Random::fast(min_id, max_id)));
} while (get_dialog_filter(dialog_filter_id) != nullptr || get_server_dialog_filter(dialog_filter_id) != nullptr);
TRY_RESULT_PROMISE(promise, dialog_filter,
DialogFilter::create_dialog_filter(td_, dialog_filter_id, std::move(filter)));
if (dialog_filter->is_shareable()) {
return promise.set_error(Status::Error(400, "Can't create shareable folder"));
}
auto chat_folder_info = dialog_filter->get_chat_folder_info_object();
bool at_beginning = is_recommended_dialog_filter(dialog_filter.get());
add_dialog_filter(std::move(dialog_filter), at_beginning, "create_dialog_filter");
if (at_beginning && main_dialog_list_position_ != 0) {
main_dialog_list_position_++;
}
save_dialog_filters();
send_update_chat_folders();
synchronize_dialog_filters();
promise.set_value(std::move(chat_folder_info));
}
void DialogFilterManager::edit_dialog_filter(DialogFilterId dialog_filter_id,
td_api::object_ptr<td_api::chatFolder> filter,
Promise<td_api::object_ptr<td_api::chatFolderInfo>> &&promise) {
CHECK(!td_->auth_manager_->is_bot());
auto old_dialog_filter = get_dialog_filter(dialog_filter_id);
if (old_dialog_filter == nullptr) {
return promise.set_error(Status::Error(400, "Chat folder not found"));
}
CHECK(is_update_chat_folders_sent_);
TRY_RESULT_PROMISE(promise, new_dialog_filter,
DialogFilter::create_dialog_filter(td_, dialog_filter_id, std::move(filter)));
if (new_dialog_filter->is_shareable() != old_dialog_filter->is_shareable()) {
return promise.set_error(Status::Error(400, "Can't convert a shareable folder to a non-shareable"));
}
new_dialog_filter->update_from(*old_dialog_filter);
auto chat_folder_info = new_dialog_filter->get_chat_folder_info_object();
if (*new_dialog_filter == *old_dialog_filter) {
return promise.set_value(std::move(chat_folder_info));
}
do_edit_dialog_filter(std::move(new_dialog_filter), true, "edit_dialog_filter");
promise.set_value(std::move(chat_folder_info));
}
void DialogFilterManager::do_edit_dialog_filter(unique_ptr<DialogFilter> &&filter, bool need_synchronize,
const char *source) {
edit_dialog_filter(std::move(filter), source);
save_dialog_filters();
send_update_chat_folders();
if (need_synchronize) {
synchronize_dialog_filters();
}
}
void DialogFilterManager::update_dialog_filter_on_server(unique_ptr<DialogFilter> &&dialog_filter) {
CHECK(!td_->auth_manager_->is_bot());
CHECK(dialog_filter != nullptr);
are_dialog_filters_being_synchronized_ = true;
dialog_filter->remove_secret_chat_dialog_ids();
auto dialog_filter_id = dialog_filter->get_dialog_filter_id();
auto input_dialog_filter = dialog_filter->get_input_dialog_filter();
auto promise = PromiseCreator::lambda(
[actor_id = actor_id(this), dialog_filter = std::move(dialog_filter)](Result<Unit> result) mutable {
send_closure(actor_id, &DialogFilterManager::on_update_dialog_filter, std::move(dialog_filter),
result.is_error() ? result.move_as_error() : Status::OK());
});
td_->create_handler<UpdateDialogFilterQuery>(std::move(promise))
->send(dialog_filter_id, std::move(input_dialog_filter));
}
void DialogFilterManager::on_update_dialog_filter(unique_ptr<DialogFilter> dialog_filter, Status result) {
CHECK(!td_->auth_manager_->is_bot());
if (result.is_error()) {
// TODO rollback dialog_filters_ changes if error isn't 429
} else {
bool is_edited = false;
for (auto &server_dialog_filter : server_dialog_filters_) {
if (server_dialog_filter->get_dialog_filter_id() == dialog_filter->get_dialog_filter_id()) {
if (*server_dialog_filter != *dialog_filter) {
server_dialog_filter = std::move(dialog_filter);
}
is_edited = true;
break;
}
}
if (!is_edited) {
bool at_beginning = is_recommended_dialog_filter(dialog_filter.get());
if (at_beginning) {
server_dialog_filters_.insert(server_dialog_filters_.begin(), std::move(dialog_filter));
} else {
server_dialog_filters_.push_back(std::move(dialog_filter));
}
if (at_beginning && server_main_dialog_list_position_ != 0) {
server_main_dialog_list_position_++;
}
}
save_dialog_filters();
}
are_dialog_filters_being_synchronized_ = false;
synchronize_dialog_filters();
}
void DialogFilterManager::delete_dialog_filter(DialogFilterId dialog_filter_id, vector<DialogId> leave_dialog_ids,
Promise<Unit> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
CHECK(!td_->auth_manager_->is_bot());
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_value(Unit());
}
for (auto &leave_dialog_id : leave_dialog_ids) {
if (!dialog_filter->is_dialog_included(leave_dialog_id)) {
return promise.set_error(Status::Error(400, "The chat doesn't included in the folder"));
}
}
if (!leave_dialog_ids.empty()) {
MultiPromiseActorSafe mpas{"LeaveDialogsMultiPromiseActor"};
mpas.add_promise(PromiseCreator::lambda(
[actor_id = actor_id(this), dialog_filter_id, promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
send_closure(actor_id, &DialogFilterManager::delete_dialog_filter, dialog_filter_id, vector<DialogId>(),
std::move(promise));
}));
auto lock = mpas.get_promise();
for (auto &leave_dialog_id : leave_dialog_ids) {
td_->dialog_participant_manager_->leave_dialog(leave_dialog_id, mpas.get_promise());
}
lock.set_value(Unit());
return;
}
int32 position = do_delete_dialog_filter(dialog_filter_id, "delete_dialog_filter");
if (main_dialog_list_position_ > position) {
main_dialog_list_position_--;
}
save_dialog_filters();
send_update_chat_folders();
synchronize_dialog_filters();
promise.set_value(Unit());
}
void DialogFilterManager::delete_dialog_filter_on_server(DialogFilterId dialog_filter_id, bool is_shareable) {
CHECK(!td_->auth_manager_->is_bot());
are_dialog_filters_being_synchronized_ = true;
auto promise = PromiseCreator::lambda([actor_id = actor_id(this), dialog_filter_id](Result<Unit> result) {
send_closure(actor_id, &DialogFilterManager::on_delete_dialog_filter, dialog_filter_id,
result.is_error() ? result.move_as_error() : Status::OK());
});
if (is_shareable) {
td_->create_handler<LeaveChatlistQuery>(std::move(promise))->send(dialog_filter_id);
} else {
td_->create_handler<UpdateDialogFilterQuery>(std::move(promise))->send(dialog_filter_id, nullptr);
}
}
void DialogFilterManager::on_delete_dialog_filter(DialogFilterId dialog_filter_id, Status result) {
CHECK(!td_->auth_manager_->is_bot());
if (result.is_error()) {
// TODO rollback dialog_filters_ changes if error isn't 429
} else {
for (auto it = server_dialog_filters_.begin(); it != server_dialog_filters_.end(); ++it) {
if ((*it)->get_dialog_filter_id() == dialog_filter_id) {
server_dialog_filters_.erase(it);
save_dialog_filters();
break;
}
}
}
are_dialog_filters_being_synchronized_ = false;
synchronize_dialog_filters();
}
void DialogFilterManager::get_leave_dialog_filter_suggestions(DialogFilterId dialog_filter_id,
Promise<td_api::object_ptr<td_api::chats>> &&promise) {
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_error(Status::Error(400, "Chat folder not found"));
}
if (!dialog_filter->is_shareable()) {
return promise.set_value(td_api::make_object<td_api::chats>());
}
auto query_promise =
PromiseCreator::lambda([actor_id = actor_id(this), dialog_filter_id, promise = std::move(promise)](
Result<vector<telegram_api::object_ptr<telegram_api::Peer>>> &&result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
send_closure(actor_id, &DialogFilterManager::on_get_leave_dialog_filter_suggestions, dialog_filter_id,
result.move_as_ok(), std::move(promise));
});
td_->create_handler<GetLeaveChatlistSuggestionsQuery>(std::move(query_promise))->send(dialog_filter_id);
}
void DialogFilterManager::on_get_leave_dialog_filter_suggestions(
DialogFilterId dialog_filter_id, vector<telegram_api::object_ptr<telegram_api::Peer>> peers,
Promise<td_api::object_ptr<td_api::chats>> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_error(Status::Error(400, "Chat folder not found"));
}
if (!dialog_filter->is_shareable()) {
return promise.set_value(td_api::make_object<td_api::chats>());
}
auto dialog_ids = td_->dialog_manager_->get_peers_dialog_ids(std::move(peers));
td::remove_if(dialog_ids, [&](DialogId dialog_id) { return !dialog_filter->is_dialog_included(dialog_id); });
promise.set_value(td_->dialog_manager_->get_chats_object(-1, dialog_ids, "on_get_leave_dialog_filter_suggestions"));
}
void DialogFilterManager::reorder_dialog_filters(vector<DialogFilterId> dialog_filter_ids,
int32 main_dialog_list_position, Promise<Unit> &&promise) {
CHECK(!td_->auth_manager_->is_bot());
for (auto dialog_filter_id : dialog_filter_ids) {
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_error(Status::Error(400, "Chat folder not found"));
}
}
std::unordered_set<DialogFilterId, DialogFilterIdHash> new_dialog_filter_ids_set(dialog_filter_ids.begin(),
dialog_filter_ids.end());
if (new_dialog_filter_ids_set.size() != dialog_filter_ids.size()) {
return promise.set_error(Status::Error(400, "Duplicate chat folders in the new list"));
}
if (main_dialog_list_position < 0 || main_dialog_list_position > static_cast<int32>(dialog_filters_.size())) {
return promise.set_error(Status::Error(400, "Invalid main chat list position specified"));
}
if (!td_->option_manager_->get_option_boolean("is_premium")) {
main_dialog_list_position = 0;
}
if (DialogFilter::set_dialog_filters_order(dialog_filters_, dialog_filter_ids) ||
main_dialog_list_position != main_dialog_list_position_) {
main_dialog_list_position_ = main_dialog_list_position;
save_dialog_filters();
send_update_chat_folders();
synchronize_dialog_filters();
}
promise.set_value(Unit());
}
void DialogFilterManager::reorder_dialog_filters_on_server(vector<DialogFilterId> dialog_filter_ids,
int32 main_dialog_list_position) {
CHECK(!td_->auth_manager_->is_bot());
are_dialog_filters_being_synchronized_ = true;
auto promise = PromiseCreator::lambda(
[actor_id = actor_id(this), dialog_filter_ids, main_dialog_list_position](Result<Unit> result) mutable {
send_closure(actor_id, &DialogFilterManager::on_reorder_dialog_filters, std::move(dialog_filter_ids),
main_dialog_list_position, result.is_error() ? result.move_as_error() : Status::OK());
});
td_->create_handler<UpdateDialogFiltersOrderQuery>(std::move(promise))
->send(dialog_filter_ids, main_dialog_list_position);
}
void DialogFilterManager::on_reorder_dialog_filters(vector<DialogFilterId> dialog_filter_ids,
int32 main_dialog_list_position, Status result) {
CHECK(!td_->auth_manager_->is_bot());
if (result.is_error()) {
// TODO rollback dialog_filters_ changes if error isn't 429
} else {
if (DialogFilter::set_dialog_filters_order(server_dialog_filters_, std::move(dialog_filter_ids)) ||
server_main_dialog_list_position_ != main_dialog_list_position) {
server_main_dialog_list_position_ = main_dialog_list_position;
save_dialog_filters();
}
}
are_dialog_filters_being_synchronized_ = false;
synchronize_dialog_filters();
}
void DialogFilterManager::toggle_dialog_filter_tags(bool are_tags_enabled, Promise<Unit> &&promise) {
if (!td_->option_manager_->get_option_boolean("is_premium")) {
if (!are_tags_enabled) {
return promise.set_value(Unit());
}
return promise.set_error(Status::Error(400, "Method not available"));
}
if (are_tags_enabled_ != are_tags_enabled) {
are_tags_enabled_ = are_tags_enabled;
save_dialog_filters();
send_update_chat_folders();
synchronize_dialog_filters();
}
promise.set_value(Unit());
}
void DialogFilterManager::toggle_are_tags_enabled_on_server(bool are_tags_enabled) {
CHECK(!td_->auth_manager_->is_bot());
are_dialog_filters_being_synchronized_ = true;
auto promise = PromiseCreator::lambda([actor_id = actor_id(this), are_tags_enabled](Result<Unit> result) mutable {
send_closure(actor_id, &DialogFilterManager::on_toggle_are_tags_enabled, are_tags_enabled,
result.is_error() ? result.move_as_error() : Status::OK());
});
td_->create_handler<ToggleDialogFilterTagsQuery>(std::move(promise))->send(are_tags_enabled);
}
void DialogFilterManager::on_toggle_are_tags_enabled(bool are_tags_enabled, Status result) {
CHECK(!td_->auth_manager_->is_bot());
if (result.is_error()) {
are_tags_enabled_ = !are_tags_enabled;
} else {
if (server_are_tags_enabled_ != are_tags_enabled) {
server_are_tags_enabled_ = are_tags_enabled;
save_dialog_filters();
}
}
are_dialog_filters_being_synchronized_ = false;
synchronize_dialog_filters();
}
void DialogFilterManager::add_dialog_filter(unique_ptr<DialogFilter> dialog_filter, bool at_beginning,
const char *source) {
if (td_->auth_manager_->is_bot()) {
// just in case
return;
}
CHECK(dialog_filter != nullptr);
auto dialog_filter_id = dialog_filter->get_dialog_filter_id();
LOG(INFO) << "Add " << dialog_filter_id << " from " << source;
CHECK(get_dialog_filter(dialog_filter_id) == nullptr);
if (at_beginning) {
dialog_filters_.insert(dialog_filters_.begin(), std::move(dialog_filter));
} else {
dialog_filters_.push_back(std::move(dialog_filter));
}
td_->messages_manager_->add_dialog_list_for_dialog_filter(dialog_filter_id);
}
void DialogFilterManager::edit_dialog_filter(unique_ptr<DialogFilter> new_dialog_filter, const char *source) {
if (td_->auth_manager_->is_bot()) {
// just in case
return;
}
CHECK(new_dialog_filter != nullptr);
LOG(INFO) << "Edit " << new_dialog_filter->get_dialog_filter_id() << " from " << source;
for (auto &old_dialog_filter : dialog_filters_) {
if (old_dialog_filter->get_dialog_filter_id() == new_dialog_filter->get_dialog_filter_id()) {
CHECK(*old_dialog_filter != *new_dialog_filter);
disable_get_dialog_filter_ = true; // to ensure crash if get_dialog_filter is called
td_->messages_manager_->edit_dialog_list_for_dialog_filter(old_dialog_filter, std::move(new_dialog_filter),
disable_get_dialog_filter_, source);
return;
}
}
UNREACHABLE();
}
int32 DialogFilterManager::do_delete_dialog_filter(DialogFilterId dialog_filter_id, const char *source) {
if (td_->auth_manager_->is_bot()) {
// just in case
return -1;
}
LOG(INFO) << "Delete " << dialog_filter_id << " from " << source;
for (auto it = dialog_filters_.begin(); it != dialog_filters_.end(); ++it) {
if ((*it)->get_dialog_filter_id() == dialog_filter_id) {
auto position = static_cast<int32>(it - dialog_filters_.begin());
td_->messages_manager_->delete_dialog_list_for_dialog_filter(dialog_filter_id, source);
dialog_filters_.erase(it);
return position;
}
}
UNREACHABLE();
return -1;
}
void DialogFilterManager::save_dialog_filters() {
if (td_->auth_manager_->is_bot()) {
return;
}
DialogFiltersLogEvent log_event;
log_event.server_are_tags_enabled = server_are_tags_enabled_;
log_event.are_tags_enabled = are_tags_enabled_;
log_event.server_main_dialog_list_position = server_main_dialog_list_position_;
log_event.main_dialog_list_position = main_dialog_list_position_;
log_event.updated_date = dialog_filters_updated_date_;
log_event.server_dialog_filters_in = &server_dialog_filters_;
log_event.dialog_filters_in = &dialog_filters_;
LOG(INFO) << "Save server chat folders "
<< DialogFilter::get_dialog_filter_ids(server_dialog_filters_, server_main_dialog_list_position_)
<< " and local chat folders "
<< DialogFilter::get_dialog_filter_ids(dialog_filters_, main_dialog_list_position_);
G()->td_db()->get_binlog_pmc()->set("dialog_filters", log_event_store(log_event).as_slice().str());
}
void DialogFilterManager::get_dialogs_for_dialog_filter_invite_link(
DialogFilterId dialog_filter_id, Promise<td_api::object_ptr<td_api::chats>> promise) {
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_error(Status::Error(400, "Chat folder not found"));
}
auto load_promise = PromiseCreator::lambda(
[actor_id = actor_id(this), dialog_filter_id, promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
send_closure(actor_id, &DialogFilterManager::do_get_dialogs_for_dialog_filter_invite_link, dialog_filter_id,
std::move(promise));
});
load_dialog_filter(dialog_filter, std::move(load_promise));
}
void DialogFilterManager::do_get_dialogs_for_dialog_filter_invite_link(
DialogFilterId dialog_filter_id, Promise<td_api::object_ptr<td_api::chats>> promise) {
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_error(Status::Error(400, "Chat folder not found"));
}
promise.set_value(td_->dialog_manager_->get_chats_object(-1, dialog_filter->get_dialogs_for_invite_link(td_),
"do_get_dialogs_for_dialog_filter_invite_link"));
}
void DialogFilterManager::create_dialog_filter_invite_link(
DialogFilterId dialog_filter_id, string invite_link_name, vector<DialogId> dialog_ids,
Promise<td_api::object_ptr<td_api::chatFolderInviteLink>> promise) {
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_error(Status::Error(400, "Chat folder not found"));
}
vector<tl_object_ptr<telegram_api::InputPeer>> input_peers;
input_peers.reserve(dialog_ids.size());
for (auto &dialog_id : dialog_ids) {
if (!td_->dialog_manager_->have_dialog_force(dialog_id, "create_dialog_filter_invite_link")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write);
if (input_peer == nullptr) {
return promise.set_error(Status::Error(400, "Have no access to the chat"));
}
input_peers.push_back(std::move(input_peer));
}
if (input_peers.empty()) {
return promise.set_error(Status::Error(400, "At least one chat must be included"));
}
td_->create_handler<ExportChatlistInviteQuery>(std::move(promise))
->send(dialog_filter_id, invite_link_name, std::move(input_peers));
}
void DialogFilterManager::get_dialog_filter_invite_links(
DialogFilterId dialog_filter_id, Promise<td_api::object_ptr<td_api::chatFolderInviteLinks>> promise) {
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_error(Status::Error(400, "Chat folder not found"));
}
if (!dialog_filter->is_shareable()) {
return promise.set_value(td_api::make_object<td_api::chatFolderInviteLinks>());
}
td_->create_handler<GetExportedChatlistInvitesQuery>(std::move(promise))->send(dialog_filter_id);
}
void DialogFilterManager::edit_dialog_filter_invite_link(
DialogFilterId dialog_filter_id, string invite_link, string invite_link_name, vector<DialogId> dialog_ids,
Promise<td_api::object_ptr<td_api::chatFolderInviteLink>> promise) {
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_error(Status::Error(400, "Chat folder not found"));
}
vector<tl_object_ptr<telegram_api::InputPeer>> input_peers;
input_peers.reserve(dialog_ids.size());
for (auto &dialog_id : dialog_ids) {
if (!td_->dialog_manager_->have_dialog_force(dialog_id, "edit_dialog_filter_invite_link")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write);
if (input_peer == nullptr) {
return promise.set_error(Status::Error(400, "Have no access to the chat"));
}
input_peers.push_back(std::move(input_peer));
}
if (input_peers.empty()) {
return promise.set_error(Status::Error(400, "At least one chat must be included"));
}
td_->create_handler<EditExportedChatlistInviteQuery>(std::move(promise))
->send(dialog_filter_id, invite_link, invite_link_name, std::move(input_peers));
}
void DialogFilterManager::delete_dialog_filter_invite_link(DialogFilterId dialog_filter_id, string invite_link,
Promise<Unit> promise) {
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_error(Status::Error(400, "Chat folder not found"));
}
td_->create_handler<DeleteExportedChatlistInviteQuery>(std::move(promise))->send(dialog_filter_id, invite_link);
}
void DialogFilterManager::check_dialog_filter_invite_link(
const string &invite_link, Promise<td_api::object_ptr<td_api::chatFolderInviteLinkInfo>> &&promise) {
if (!DialogFilterInviteLink::is_valid_invite_link(invite_link)) {
return promise.set_error(Status::Error(400, "Wrong invite link"));
}
CHECK(!invite_link.empty());
td_->create_handler<CheckChatlistInviteQuery>(std::move(promise))->send(invite_link);
}
void DialogFilterManager::on_get_chatlist_invite(
const string &invite_link, telegram_api::object_ptr<telegram_api::chatlists_ChatlistInvite> &&invite_ptr,
Promise<td_api::object_ptr<td_api::chatFolderInviteLinkInfo>> &&promise) {
CHECK(invite_ptr != nullptr);
LOG(INFO) << "Receive information about chat folder invite link " << invite_link << ": " << to_string(invite_ptr);
td_api::object_ptr<td_api::chatFolderInfo> info;
vector<telegram_api::object_ptr<telegram_api::Peer>> missing_peers;
vector<telegram_api::object_ptr<telegram_api::Peer>> already_peers;
vector<telegram_api::object_ptr<telegram_api::Chat>> chats;
vector<telegram_api::object_ptr<telegram_api::User>> users;
switch (invite_ptr->get_id()) {
case telegram_api::chatlists_chatlistInviteAlready::ID: {
auto invite = move_tl_object_as<telegram_api::chatlists_chatlistInviteAlready>(invite_ptr);
DialogFilterId dialog_filter_id = DialogFilterId(invite->filter_id_);
if (!dialog_filter_id.is_valid()) {
return promise.set_error(Status::Error(500, "Receive invalid chat folder identifier"));
}
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
reload_dialog_filters();
return promise.set_error(Status::Error(500, "Receive unknown chat folder"));
}
info = dialog_filter->get_chat_folder_info_object();
missing_peers = std::move(invite->missing_peers_);
already_peers = std::move(invite->already_peers_);
chats = std::move(invite->chats_);
users = std::move(invite->users_);
break;
}
case telegram_api::chatlists_chatlistInvite::ID: {
auto invite = move_tl_object_as<telegram_api::chatlists_chatlistInvite>(invite_ptr);
auto icon_name = DialogFilter::get_icon_name_by_emoji(invite->emoticon_);
if (icon_name.empty()) {
icon_name = "Custom";
}
info = td_api::make_object<td_api::chatFolderInfo>(
0, invite->title_, td_api::make_object<td_api::chatFolderIcon>(icon_name), -1, true, false);
missing_peers = std::move(invite->peers_);
chats = std::move(invite->chats_);
users = std::move(invite->users_);
break;
}
default:
UNREACHABLE();
break;
}
td_->user_manager_->on_get_users(std::move(users), "on_get_chatlist_invite");
td_->chat_manager_->on_get_chats(std::move(chats), "on_get_chatlist_invite");
auto missing_dialog_ids = td_->dialog_manager_->get_peers_dialog_ids(std::move(missing_peers), true);
auto already_dialog_ids = td_->dialog_manager_->get_peers_dialog_ids(std::move(already_peers));
promise.set_value(td_api::make_object<td_api::chatFolderInviteLinkInfo>(
std::move(info), td_->dialog_manager_->get_chat_ids_object(missing_dialog_ids, "chatFolderInviteLinkInfo 1"),
td_->dialog_manager_->get_chat_ids_object(already_dialog_ids, "chatFolderInviteLinkInfo 1")));
}
void DialogFilterManager::add_dialog_filter_by_invite_link(const string &invite_link, vector<DialogId> dialog_ids,
Promise<Unit> &&promise) {
if (!DialogFilterInviteLink::is_valid_invite_link(invite_link)) {
return promise.set_error(Status::Error(400, "Wrong invite link"));
}
for (auto dialog_id : dialog_ids) {
TRY_STATUS_PROMISE(promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Know,
"add_dialog_filter_by_invite_link"));
}
CHECK(!invite_link.empty());
td_->create_handler<JoinChatlistInviteQuery>(std::move(promise))->send(invite_link, std::move(dialog_ids));
}
void DialogFilterManager::get_dialog_filter_new_chats(DialogFilterId dialog_filter_id,
Promise<td_api::object_ptr<td_api::chats>> &&promise) {
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_error(Status::Error(400, "Chat folder not found"));
}
if (!dialog_filter->is_shareable()) {
return promise.set_value(td_api::make_object<td_api::chats>());
}
td_->create_handler<GetChatlistUpdatesQuery>(std::move(promise))->send(dialog_filter_id);
}
void DialogFilterManager::add_dialog_filter_new_chats(DialogFilterId dialog_filter_id, vector<DialogId> dialog_ids,
Promise<Unit> &&promise) {
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return promise.set_error(Status::Error(400, "Chat folder not found"));
}
if (!dialog_filter->is_shareable()) {
return promise.set_error(Status::Error(400, "Chat folder must be shareable"));
}
for (auto dialog_id : dialog_ids) {
TRY_STATUS_PROMISE(promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Know,
"add_dialog_filter_new_chats"));
}
if (dialog_ids.empty()) {
td_->create_handler<HideChatlistUpdatesQuery>(std::move(promise))->send(dialog_filter_id);
} else {
td_->create_handler<JoinChatlistUpdatesQuery>(std::move(promise))->send(dialog_filter_id, std::move(dialog_ids));
}
}
void DialogFilterManager::set_dialog_filter_has_my_invite_links(DialogFilterId dialog_filter_id,
bool has_my_invite_links) {
auto dialog_filter = get_dialog_filter(dialog_filter_id);
if (dialog_filter == nullptr) {
return;
}
dialog_filter->set_has_my_invite_links(has_my_invite_links);
}
void DialogFilterManager::get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const {
if (have_dialog_filters()) {
updates.push_back(get_update_chat_folders_object());
}
}
} // namespace td