diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index b78e051bf..0cd454ff5 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -649,6 +649,22 @@ chatTypeSupergroup supergroup_id:int32 is_channel:Bool = ChatType; chatTypeSecret secret_chat_id:int32 user_id:int32 = ChatType; +//@description Represents a filter of user chats +//@title The title of the filter +//@emoji The emoji for short filter representation +//@pinned_chat_ids The chat identifiers of pinned chats in the filtered chat list +//@included_chat_ids The chat identifiers of always included chats in the filtered chat list +//@excluded_chat_ids The chat identifiers of always excluded chats in the filtered chat list +//@exclude_muted True, if the muted chats need to be excluded +//@exclude_read True, if read chats need to be excluded +//@exclude_archived True, if archived chats need to be excluded +//@include_contacts True, if contacts need to be included +//@include_non_contacts True, if non-contact users need to be included +//@include_bots True, if bots need to be included +//@include_groups True, if basic groups and supergroups need to be included +//@include_channels True, if channels need to be included +chatFilter title:string emoji:string pinned_chat_ids:vector included_chat_ids:vector excluded_chat_ids:vector exclude_muted:Bool exclude_read:Bool exclude_archived:Bool include_contacts:Bool include_non_contacts:Bool include_bots:Bool include_groups:Bool include_channels:Bool = ChatFilter; + //@description Contains basic information about a chat filter //@chat_filter_id Unique chat filter identifier //@title The title of the filter @@ -2943,7 +2959,7 @@ updateChatReplyMarkup chat_id:int53 reply_markup_message_id:int53 = Update; //@description A chat draft has changed. Be aware that the update may come in the currently opened chat but with old content of the draft. If the user has changed the content of the draft, this update shouldn't be applied @chat_id Chat identifier @draft_message The new draft message; may be null @positions The new chat positions in the chat lists updateChatDraftMessage chat_id:int53 draft_message:draftMessage positions:vector = Update; -//@description The list of chat filters has changed @chat_filters The new list of chat filters +//@description The list of chat filters or a chat filter has changed @chat_filters The new list of chat filters updateChatFilters chat_filters:vector = Update; //@description The number of online group members has changed. This update with non-zero count is sent only for currently opened chats. There is no guarantee that it will be sent just after the count has changed @chat_id Identifier of the chat @online_member_count New number of online members in the chat, or 0 if unknown @@ -3687,9 +3703,13 @@ createNewSecretChat user_id:int32 = Chat; upgradeBasicGroupChatToSupergroupChat chat_id:int53 = Chat; -//@description Moves a chat to a different chat list. Current chat list of the chat must ne non-null @chat_id Chat identifier @chat_list New chat list of the chat. The chat with the current user (Saved Messages) and the chat 777000 (Telegram) can't be moved to the Archive chat list +//@description Moves a chat to a different chat list @chat_id Chat identifier @chat_list New chat list of the chat, must be one of chatListMain or chatListArchived. The chat with the current user (Saved Messages) and the chat 777000 (Telegram) can't be moved to the Archive chat list setChatChatList chat_id:int53 chat_list:ChatList = Ok; +//@description Returns information about a chat filter by its identifier @chat_filter_id Chat filter identifier +getChatFilter chat_filter_id:int32 = ChatFilter; + + //@description Changes the chat title. Supported only for basic groups, supergroups and channels. Requires can_change_info rights. The title will not be changed until the request to the server has been completed //@chat_id Chat identifier @title New title of the chat; 1-128 characters setChatTitle chat_id:int53 title:string = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 5ea050c56..7998b8d48 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index b91e15336..525d6ef8a 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -209,6 +209,43 @@ class GetDialogQuery : public Td::ResultHandler { } }; +class GetDialogsQuery : public Td::ResultHandler { + Promise promise_; + + public: + explicit GetDialogsQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(vector input_dialog_ids) { + LOG(INFO) << "Send GetDialogsQuery to get " << input_dialog_ids; + CHECK(input_dialog_ids.size() <= 100); + auto input_dialog_peers = transform( + input_dialog_ids, [](InputDialogId input_dialog_id) -> telegram_api::object_ptr { + return telegram_api::make_object(input_dialog_id.get_input_peer()); + }); + send_query(G()->net_query_creator().create(telegram_api::messages_getPeerDialogs(std::move(input_dialog_peers)))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive chats: " << to_string(result); + + td->contacts_manager_->on_get_users(std::move(result->users_), "GetDialogsQuery"); + td->contacts_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(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + class GetPinnedDialogsActor : public NetActorOnce { FolderId folder_id_; Promise promise_; @@ -13538,6 +13575,54 @@ bool MessagesManager::load_dialog(DialogId dialog_id, int left_tries, Promise &&promise) { + auto filter = get_dialog_filter(dialog_filter_id); + if (filter == nullptr) { + return promise.set_value(Unit()); + } + + vector needed_dialog_ids; + for (auto input_dialog_ids : + {&filter->pinned_dialog_ids, &filter->excluded_dialog_ids, &filter->included_dialog_ids}) { + for (auto input_dialog_id : *input_dialog_ids) { + if (!have_dialog(input_dialog_id.get_dialog_id())) { + needed_dialog_ids.push_back(input_dialog_id); + } + } + } + + vector input_dialog_ids; + for (auto &input_dialog_id : needed_dialog_ids) { + // TODO load dialogs asynchronously + if (!have_dialog_force(input_dialog_id.get_dialog_id())) { + input_dialog_ids.push_back(input_dialog_id); + } + } + + if (!input_dialog_ids.empty() && !force) { + const size_t MAX_SLICE_SIZE = 100; + if (input_dialog_ids.size() <= MAX_SLICE_SIZE) { + td_->create_handler(std::move(promise))->send(std::move(input_dialog_ids)); + return; + } + + MultiPromiseActorSafe mpas{"GetFilterDialogsFromServerMultiPromiseActor"}; + 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(); + td_->create_handler(mpas.get_promise())->send({input_dialog_ids.begin() + i, end}); + } + + lock.set_value(Unit()); + return; + } + + promise.set_value(Unit()); +} + vector MessagesManager::get_dialogs(FolderId folder_id, DialogDate offset, int32 limit, bool force, Promise &&promise) { CHECK(!td_->auth_manager_->is_bot()); @@ -16103,6 +16188,32 @@ tl_object_ptr MessagesManager::get_chats_object(const vector(transform(dialogs, [](DialogId dialog_id) { return dialog_id.get(); })); } +td_api::object_ptr MessagesManager::get_chat_filter_object(DialogFilterId dialog_filter_id) const { + auto filter = get_dialog_filter(dialog_filter_id); + if (filter == nullptr) { + return nullptr; + } + + auto get_chat_ids = [this, dialog_filter_id](const vector &input_dialog_ids) { + vector chat_ids; + chat_ids.reserve(input_dialog_ids.size()); + for (auto &input_dialog_id : input_dialog_ids) { + auto dialog_id = input_dialog_id.get_dialog_id(); + if (have_dialog(dialog_id)) { + chat_ids.push_back(dialog_id.get()); + } else { + LOG(ERROR) << "Can't find " << dialog_id << " from " << dialog_filter_id; + } + } + return chat_ids; + }; + return td_api::make_object( + filter->title, filter->emoji, get_chat_ids(filter->pinned_dialog_ids), get_chat_ids(filter->included_dialog_ids), + get_chat_ids(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); +} + td_api::object_ptr MessagesManager::get_update_scope_notification_settings_object(NotificationSettingsScope scope) const { auto notification_settings = get_scope_notification_settings(scope); @@ -29431,6 +29542,24 @@ MessagesManager::Dialog *MessagesManager::on_load_dialog_from_database(DialogId return add_new_dialog(parse_dialog(dialog_id, value), true); } +MessagesManager::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(); + } + } + return nullptr; +} + +const MessagesManager::DialogFilter *MessagesManager::get_dialog_filter(DialogFilterId dialog_filter_id) const { + for (auto &filter : dialog_filters_) { + if (filter->dialog_filter_id == dialog_filter_id) { + return filter.get(); + } + } + return nullptr; +} + bool MessagesManager::need_dialog_in_list(const DialogList &list, const Dialog *d) const { return d->folder_id == list.folder_id; } diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 3e79a874e..38a3ae9fa 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -499,6 +499,8 @@ class MessagesManager : public Actor { void load_dialogs(vector dialog_ids, Promise &&promise); + void load_dialog_filter(DialogFilterId dialog_id, bool force, Promise &&promise); + vector get_dialogs(FolderId folder_id, DialogDate offset, int32 limit, bool force, Promise &&promise); vector search_public_dialogs(const string &query, Promise &&promise); @@ -611,6 +613,8 @@ class MessagesManager : public Actor { static tl_object_ptr get_chats_object(const vector &dialogs); + tl_object_ptr get_chat_filter_object(DialogFilterId dialog_filter_id) const; + tl_object_ptr get_dialog_history(DialogId dialog_id, MessageId from_message_id, int32 offset, int32 limit, int left_tries, bool only_local, Promise &&promise); @@ -2196,6 +2200,9 @@ class MessagesManager : public Actor { void update_dialogs_hints(const Dialog *d); void update_dialogs_hints_rating(const Dialog *d); + DialogFilter *get_dialog_filter(DialogFilterId dialog_filter_id); + const DialogFilter *get_dialog_filter(DialogFilterId dialog_filter_id) const; + bool need_dialog_in_list(const DialogList &list, const Dialog *d) const; DialogOrderInList get_dialog_order_in_list(const DialogList *list, const Dialog *d, bool actual = false) const; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 297860dc4..0f5fc0636 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -830,6 +830,24 @@ class GetChatRequest : public RequestActor<> { } }; +class GetChatFilterRequest : public RequestActor<> { + DialogFilterId dialog_filter_id_; + + void do_run(Promise &&promise) override { + td->messages_manager_->load_dialog_filter(dialog_filter_id_, get_tries() < 2, std::move(promise)); + } + + void do_send_result() override { + send_result(td->messages_manager_->get_chat_filter_object(dialog_filter_id_)); + } + + public: + GetChatFilterRequest(ActorShared td, uint64 request_id, int32 dialog_filter_id) + : RequestActor(std::move(td), request_id), dialog_filter_id_(dialog_filter_id) { + set_tries(3); + } +}; + class GetChatsRequest : public RequestActor<> { FolderId folder_id_; DialogDate offset_; @@ -5854,6 +5872,11 @@ void Td::on_request(uint64 id, const td_api::setChatChatList &request) { messages_manager_->set_dialog_folder_id(DialogId(request.chat_id_), FolderId(request.chat_list_), std::move(promise)); } +void Td::on_request(uint64 id, const td_api::getChatFilter &request) { + CHECK_IS_USER(); + CREATE_REQUEST(GetChatFilterRequest, request.chat_filter_id_); +} + void Td::on_request(uint64 id, td_api::setChatTitle &request) { CLEAN_INPUT_STRING(request.title_); CREATE_OK_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 3ff12e887..12b27b602 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -675,6 +675,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::setChatChatList &request); + void on_request(uint64 id, const td_api::getChatFilter &request); + void on_request(uint64 id, td_api::setChatTitle &request); void on_request(uint64 id, const td_api::setChatPhoto &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index d68dcc655..67921613f 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -467,6 +467,10 @@ class CliClient final : public Actor { return to_integer(str); } + int32 as_chat_filter_id(Slice str) const { + return to_integer(trim(str)); + } + static td_api::object_ptr as_chat_list(string chat_list) { if (!chat_list.empty() && chat_list.back() == 'a') { return td_api::make_object(); @@ -3481,6 +3485,8 @@ class CliClient final : public Actor { } else if (op == "sccl" || op == "sccla") { string chat_id = args; send_request(td_api::make_object(as_chat_id(chat_id), as_chat_list(op))); + } else if (op == "gcf") { + send_request(td_api::make_object(as_chat_filter_id(args))); } else if (op == "sct") { string chat_id; string title;