diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 2c04141ad..d8de4a7d7 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -3327,6 +3327,9 @@ createNewSecretChat user_id:int32 = Chat; upgradeBasicGroupChatToSupergroupChat chat_id:int53 = Chat; +//@description Moves a chat to a different chat list @chat_id Chat identifier @chat_list New chat list of the chat +setChatChatList chat_id:int53 chat_list:ChatList = Ok; + //@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 c42a21ab7..44ebcbdd3 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 ab2992182..2763ca9a7 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -3340,6 +3340,51 @@ class ReportPeerQuery : public Td::ResultHandler { } }; +class EditPeerFoldersQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit EditPeerFoldersQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, FolderId folder_id) { + dialog_id_ = dialog_id; + + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + CHECK(input_peer != nullptr); + + vector> input_folder_peers; + input_folder_peers.push_back( + telegram_api::make_object(std::move(input_peer), folder_id.get())); + send_query(G()->net_query_creator().create( + create_storer(telegram_api::folders_editPeerFolders(std::move(input_folder_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 ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for EditPeerFoldersQuery: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + if (!td->messages_manager_->on_get_dialog_error(dialog_id_, status, "EditPeerFoldersQuery")) { + LOG(INFO) << "Receive error for EditPeerFoldersQuery: " << status; + } + + // trying to repair folder ID for this dialog + td->messages_manager_->get_dialog_info_full(dialog_id_, Auto()); + + promise_.set_error(std::move(status)); + } +}; + class GetStatsUrlQuery : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; @@ -22414,6 +22459,95 @@ SearchMessagesFilter MessagesManager::get_search_messages_filter( } } +void MessagesManager::set_dialog_folder_id(DialogId dialog_id, FolderId folder_id, Promise &&promise) { + LOG(INFO) << "Receive setChatChatList request to change folder of " << dialog_id << " to " << folder_id; + + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr) { + return promise.set_error(Status::Error(3, "Chat not found")); + } + + if (d->order == DEFAULT_ORDER) { + return promise.set_error(Status::Error(400, "Chat is not in a chat list")); + } + + if (d->folder_id == folder_id) { + return promise.set_value(Unit()); + } + + if (!have_input_peer(dialog_id, AccessRights::Read)) { + return promise.set_error(Status::Error(6, "Can't access the chat")); + } + + set_dialog_folder_id(d, folder_id); + + set_dialog_folder_id_on_server(dialog_id, false); + promise.set_value(Unit()); +} + +class MessagesManager::SetDialogFolderIdOnServerLogEvent { + public: + DialogId dialog_id_; + FolderId folder_id_; + + template + void store(StorerT &storer) const { + td::store(dialog_id_, storer); + td::store(folder_id_, storer); + } + + template + void parse(ParserT &parser) { + td::parse(dialog_id_, parser); + td::parse(folder_id_, parser); + } +}; + +void MessagesManager::set_dialog_folder_id_on_server(DialogId dialog_id, bool from_binlog) { + auto d = get_dialog(dialog_id); + CHECK(d != nullptr); + + if (!from_binlog && G()->parameters().use_message_db) { + SetDialogFolderIdOnServerLogEvent logevent; + logevent.dialog_id_ = dialog_id; + logevent.folder_id_ = d->folder_id; + auto storer = LogEventStorerImpl(logevent); + if (d->set_folder_id_logevent_id == 0) { + d->set_folder_id_logevent_id = + binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::SetDialogFolderIdOnServer, storer); + } else { + binlog_rewrite(G()->td_db()->get_binlog(), d->set_folder_id_logevent_id, + LogEvent::HandlerType::SetDialogFolderIdOnServer, storer); + } + d->set_folder_id_logevent_id_generation++; + } + + Promise<> promise; + if (d->set_folder_id_logevent_id != 0) { + promise = PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, + generation = d->set_folder_id_logevent_id_generation](Result result) { + if (!G()->close_flag()) { + send_closure(actor_id, &MessagesManager::on_updated_dialog_folder_id, dialog_id, generation); + } + }); + } + + // TODO do not send two queries simultaneously or use SequenceDispatcher + td_->create_handler(std::move(promise))->send(dialog_id, d->folder_id); +} + +void MessagesManager::on_updated_dialog_folder_id(DialogId dialog_id, uint64 generation) { + auto d = get_dialog(dialog_id); + CHECK(d != nullptr); + LOG(INFO) << "Saved folder_id of " << dialog_id << " with logevent " << d->set_folder_id_logevent_id; + if (d->set_folder_id_logevent_id_generation == generation) { + CHECK(d->set_folder_id_logevent_id != 0); + LOG(INFO) << "Delete set folder_id logevent " << d->set_folder_id_logevent_id; + binlog_erase(G()->td_db()->get_binlog(), d->set_folder_id_logevent_id); + d->set_folder_id_logevent_id = 0; + } +} + void MessagesManager::set_dialog_photo(DialogId dialog_id, const tl_object_ptr &photo, Promise &&promise) { LOG(INFO) << "Receive setChatPhoto request to change photo of " << dialog_id; @@ -22583,7 +22717,7 @@ void MessagesManager::set_dialog_permissions(DialogId dialog_id, auto new_permissions = get_restricted_rights(permissions); - // TODO this can be wrong if there was previous change title requests + // TODO this can be wrong if there was previous change permissions requests if (get_dialog_permissions(dialog_id) == new_permissions) { return promise.set_value(Unit()); } @@ -26414,10 +26548,6 @@ void MessagesManager::on_get_channel_difference( timeout = difference->timeout_; } - // TODO - // pinned:flags.2?true unread_mark:flags.3?true notify_settings:PeerNotifySettings - // draft:flags.1?DraftMessage folder_id:flags.4?int - auto new_pts = dialog->pts_; if (request_pts + request_limit > new_pts) { LOG(ERROR) << "Receive channelDifferenceTooLong as result of getChannelDifference with pts = " << request_pts @@ -26431,6 +26561,24 @@ void MessagesManager::on_get_channel_difference( td_->contacts_manager_->on_get_users(std::move(difference->users_), "updates.channelDifferenceTooLong"); td_->contacts_manager_->on_get_chats(std::move(difference->chats_), "updates.channelDifferenceTooLong"); + set_dialog_folder_id(d, FolderId((dialog->flags_ & DIALOG_FLAG_HAS_FOLDER_ID) != 0 ? dialog->folder_id_ : 0)); + + on_update_dialog_notify_settings(dialog_id, std::move(dialog->notify_settings_), "on_get_dialogs"); + + bool is_pinned = (dialog->flags_ & DIALOG_FLAG_IS_PINNED) != 0; + bool was_pinned = d->pinned_order != DEFAULT_ORDER; + if (is_pinned != was_pinned) { + set_dialog_is_pinned(d, is_pinned); + } + + bool is_marked_as_unread = (dialog->flags_ & telegram_api::dialog::UNREAD_MARK_MASK) != 0; + if (is_marked_as_unread != d->is_marked_as_unread) { + set_dialog_is_marked_as_unread(d, is_marked_as_unread); + } + + update_dialog_draft_message(d, get_draft_message(td_->contacts_manager_.get(), std::move(dialog->draft_)), true, + false); + on_get_channel_dialog(dialog_id, MessageId(ServerMessageId(dialog->top_message_)), MessageId(ServerMessageId(dialog->read_inbox_max_id_)), dialog->unread_count_, dialog->unread_mentions_count_, MessageId(ServerMessageId(dialog->read_outbox_max_id_)), @@ -27145,6 +27293,29 @@ void MessagesManager::on_binlog_events(vector &&events) { change_dialog_report_spam_state_on_server(dialog_id, log_event.is_spam_dialog_, event.id_, Promise()); break; } + case LogEvent::HandlerType::SetDialogFolderIdOnServer: { + if (!G()->parameters().use_message_db) { + binlog_erase(G()->td_db()->get_binlog(), event.id_); + break; + } + + SetDialogFolderIdOnServerLogEvent log_event; + log_event_parse(log_event, event.data_).ensure(); + + auto dialog_id = log_event.dialog_id_; + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr || !have_input_peer(dialog_id, AccessRights::Read)) { + binlog_erase(G()->td_db()->get_binlog(), event.id_); + break; + } + d->set_folder_id_logevent_id = event.id_; + d->set_folder_id_logevent_id_generation++; + + set_dialog_folder_id(d, log_event.folder_id_); + + set_dialog_folder_id_on_server(dialog_id, true); + break; + } case LogEvent::HandlerType::GetDialogFromServer: { if (!G()->parameters().use_message_db) { binlog_erase(G()->td_db()->get_binlog(), event.id_); diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 11923ade1..9b3155cad 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -415,6 +415,8 @@ class MessagesManager : public Actor { void send_dialog_action(DialogId dialog_id, const tl_object_ptr &action, Promise &&promise); + void set_dialog_folder_id(DialogId dialog_id, FolderId folder_id, Promise &&promise); + void set_dialog_photo(DialogId dialog_id, const tl_object_ptr &photo, Promise &&promise); void set_dialog_title(DialogId dialog_id, const string &title, Promise &&promise); @@ -449,6 +451,8 @@ class MessagesManager : public Actor { string get_dialog_invite_link(DialogId dialog_id); + void get_dialog_info_full(DialogId dialog_id, Promise &&promise); + int64 get_dialog_event_log(DialogId dialog_id, const string &query, int64 from_event_id, int32 limit, const tl_object_ptr &filters, const vector &user_ids, Promise &&promise); @@ -988,6 +992,8 @@ class MessagesManager : public Actor { uint64 save_notification_settings_logevent_id_generation = 0; uint64 read_history_logevent_id = 0; uint64 read_history_logevent_id_generation = 0; + uint64 set_folder_id_logevent_id = 0; + uint64 set_folder_id_logevent_id_generation = 0; FolderId folder_id; MessageId @@ -1312,6 +1318,7 @@ class MessagesManager : public Actor { class UpdateScopeNotificationSettingsOnServerLogEvent; class ResetAllNotificationSettingsOnServerLogEvent; class ChangeDialogReportSpamStateOnServerLogEvent; + class SetDialogFolderIdOnServerLogEvent; class SendBotStartMessageLogEvent; class SendInlineQueryResultMessageLogEvent; class SendMessageLogEvent; @@ -1953,8 +1960,6 @@ class MessagesManager : public Actor { Message *on_get_message_from_database(DialogId dialog_id, Dialog *d, const BufferSlice &value, const char *source); - void get_dialog_info_full(DialogId dialog_id, Promise &&promise); - void get_dialog_message_by_date_from_server(const Dialog *d, int32 date, int64 random_id, bool after_database_search, Promise &&promise); @@ -2082,6 +2087,10 @@ class MessagesManager : public Actor { void change_dialog_report_spam_state_on_server(DialogId dialog_id, bool is_spam_dialog, uint64 logevent_id, Promise &&promise); + void set_dialog_folder_id_on_server(DialogId dialog_id, bool from_binlog); + + void on_updated_dialog_folder_id(DialogId dialog_id, uint64 generation); + int64 get_next_pinned_dialog_order(); void update_dialog_pos(Dialog *d, bool remove_from_dialog_list, const char *source, diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index be7993166..4e90e2ae5 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5975,6 +5975,12 @@ void Td::on_request(uint64 id, const td_api::upgradeBasicGroupChatToSupergroupCh CREATE_REQUEST(UpgradeGroupChatToSupergroupChatRequest, request.chat_id_); } +void Td::on_request(uint64 id, const td_api::setChatChatList &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + messages_manager_->set_dialog_folder_id(DialogId(request.chat_id_), FolderId(request.chat_list_), std::move(promise)); +} + 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 4ac6f050f..55ca9f895 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -644,6 +644,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::upgradeBasicGroupChatToSupergroupChat &request); + void on_request(uint64 id, const td_api::setChatChatList &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/TdDb.cpp b/td/telegram/TdDb.cpp index afa2c7829..42b594748 100644 --- a/td/telegram/TdDb.cpp +++ b/td/telegram/TdDb.cpp @@ -107,6 +107,7 @@ Status init_binlog(Binlog &binlog, string path, BinlogKeyValue &binlog_p case LogEvent::HandlerType::GetChannelDifference: case LogEvent::HandlerType::ReadHistoryInSecretChat: case LogEvent::HandlerType::ToggleDialogIsMarkedAsUnreadOnServer: + case LogEvent::HandlerType::SetDialogFolderIdOnServer: events.to_messages_manager.push_back(event.clone()); break; case LogEvent::HandlerType::AddMessagePushNotification: diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 99da366df..ed7500c70 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -452,6 +452,13 @@ class CliClient final : public Actor { return to_integer(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(); + } + return td_api::make_object(); + } + vector as_chat_ids(Slice chat_ids, char delimiter = ' ') const { return transform(full_split(trim(chat_ids), delimiter), [this](Slice str) { return as_chat_id(str); }); } @@ -1610,10 +1617,6 @@ class CliClient final : public Actor { std::tie(limit, args) = split(args); std::tie(offset_order_string, offset_chat_id) = split(args); - td_api::object_ptr chat_list; - if (op == "gca") { - chat_list = td_api::make_object(); - } if (limit.empty()) { limit = "10000"; } @@ -1623,7 +1626,7 @@ class CliClient final : public Actor { } else { offset_order = to_integer(offset_order_string); } - send_request(td_api::make_object(std::move(chat_list), offset_order, as_chat_id(offset_chat_id), + send_request(td_api::make_object(as_chat_list(op), offset_order, as_chat_id(offset_chat_id), to_integer(limit))); } else if (op == "gctest") { send_request(td_api::make_object(nullptr, std::numeric_limits::max(), 0, 1)); @@ -2658,11 +2661,7 @@ class CliClient final : public Actor { for (auto &chat_id_str : chat_ids_str) { chat_ids.push_back(as_chat_id(chat_id_str)); } - td_api::object_ptr chat_list = td_api::make_object(); - if (op == "spchatsa") { - chat_list = td_api::make_object(); - } - send_request(td_api::make_object(std::move(chat_list), std::move(chat_ids))); + send_request(td_api::make_object(as_chat_list(op), std::move(chat_ids))); } else if (op == "sca") { string chat_id; string action; @@ -3263,6 +3262,9 @@ class CliClient final : public Actor { std::tie(supergroup_id, force) = split(args); send_request(td_api::make_object(as_supergroup_id(supergroup_id), as_bool(force))); + } 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 == "sct") { string chat_id; string title; diff --git a/td/telegram/logevent/LogEvent.h b/td/telegram/logevent/LogEvent.h index 0883fb498..1c08b2929 100644 --- a/td/telegram/logevent/LogEvent.h +++ b/td/telegram/logevent/LogEvent.h @@ -94,6 +94,7 @@ class LogEvent { GetDialogFromServer = 0x113, ReadHistoryInSecretChat = 0x114, ToggleDialogIsMarkedAsUnreadOnServer = 0x115, + SetDialogFolderIdOnServer = 0x116, GetChannelDifference = 0x140, AddMessagePushNotification = 0x200, EditMessagePushNotification = 0x201,