diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index fc821009..39732134 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2096,6 +2096,21 @@ inputBackgroundRemote background_id:int64 = InputBackground; hashtags hashtags:vector = Hashtags; +//@class CanTransferOwnershipResult @description Represents result of checking whether the current session can be used to transfer a chat ownership to another user + +//@description The session can be used +canTransferOwnershipResultOk = CanTransferOwnershipResult; + +//@description The 2-step verification needs to be enabled first +canTransferOwnershipResultPasswordNeeded = CanTransferOwnershipResult; + +//@description The 2-step verification was enabled recently, user needs to wait @retry_after Time left before the session can be used to transfer ownership of a chat, in seconds +canTransferOwnershipResultPasswordTooFresh retry_after:int32 = CanTransferOwnershipResult; + +//@description The session was created recently, user needs to wait @retry_after Time left before the session can be used to transfer ownership of a chat, in seconds +canTransferOwnershipResultSessionTooFresh retry_after:int32 = CanTransferOwnershipResult; + + //@class CheckChatUsernameResult @description Represents result of checking whether a username can be set for a chat //@description The username can be set @@ -3483,6 +3498,9 @@ addChatMembers chat_id:int53 user_ids:vector = Ok; //@chat_id Chat identifier @user_id User identifier @status The new status of the member in the chat setChatMemberStatus chat_id:int53 user_id:int32 status:ChatMemberStatus = Ok; +//@description Checks whether the current session can be used to transfer a chat ownership to another user +canTransferOwnership = CanTransferOwnershipResult; + //@description Changes owner of a chat. The current user must be a current owner of the chat. Use the method canTransferOwnership to check whether the ownership can be transferred from the current session. Available only for supergroups and channel chats //@chat_id Chat identifier @user_id Identifier of the user to which transfer the ownership. The ownership can't be transferred to a bot or to a deleted user @password The password of the current user transferChatOwnership chat_id:int53 user_id:int32 password:string = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 592e67de..aef1b682 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index dd71559d..aa95de79 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1862,6 +1862,37 @@ class LeaveChannelQuery : public Td::ResultHandler { } }; +class CanEditChannelCreatorQuery : public Td::ResultHandler { + Promise promise_; + + public: + explicit CanEditChannelCreatorQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send() { + auto input_user = td->contacts_manager_->get_input_user(td->contacts_manager_->get_my_id()); + CHECK(input_user != nullptr); + send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_editCreator( + telegram_api::make_object(), std::move(input_user), + make_tl_object())))); + } + + 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(ERROR) << "Receive result for CanEditChannelCreator: " << to_string(ptr); + promise_.set_error(Status::Error(500, "Server doesn't returned error")); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + class EditChannelCreatorQuery : public Td::ResultHandler { Promise promise_; ChannelId channel_id_; @@ -5398,6 +5429,58 @@ void ContactsManager::change_chat_participant_status(ChatId chat_id, UserId user ->send(chat_id, std::move(input_user), status.is_administrator()); } +void ContactsManager::can_transfer_ownership(Promise &&promise) { + auto request_promise = PromiseCreator::lambda([promise = std::move(promise)](Result r_result) mutable { + CHECK(r_result.is_error()); + + auto error = r_result.move_as_error(); + CanTransferOwnershipResult result; + if (error.message() == "PASSWORD_HASH_INVALID") { + return promise.set_value(std::move(result)); + } + if (error.message() == "PASSWORD_MISSING") { + result.type = CanTransferOwnershipResult::Type::PasswordNeeded; + return promise.set_value(std::move(result)); + } + if (begins_with(error.message(), "PASSWORD_TOO_FRESH_")) { + result.type = CanTransferOwnershipResult::Type::PasswordTooFresh; + result.retry_after = to_integer(error.message().substr(Slice("PASSWORD_TOO_FRESH_").size())); + if (result.retry_after < 0) { + result.retry_after = 0; + } + return promise.set_value(std::move(result)); + } + if (begins_with(error.message(), "SESSION_TOO_FRESH_")) { + result.type = CanTransferOwnershipResult::Type::SessionTooFresh; + result.retry_after = to_integer(error.message().substr(Slice("SESSION_TOO_FRESH_").size())); + if (result.retry_after < 0) { + result.retry_after = 0; + } + return promise.set_value(std::move(result)); + } + promise.set_error(std::move(error)); + }); + + td_->create_handler(std::move(request_promise))->send(); +} + +td_api::object_ptr ContactsManager::get_can_transfer_ownership_result_object( + CanTransferOwnershipResult result) { + switch (result.type) { + case CanTransferOwnershipResult::Type::Ok: + return td_api::make_object(); + case CanTransferOwnershipResult::Type::PasswordNeeded: + return td_api::make_object(); + case CanTransferOwnershipResult::Type::PasswordTooFresh: + return td_api::make_object(result.retry_after); + case CanTransferOwnershipResult::Type::SessionTooFresh: + return td_api::make_object(result.retry_after); + default: + UNREACHABLE(); + return nullptr; + } +} + void ContactsManager::transfer_dialog_ownership(DialogId dialog_id, UserId user_id, const string &password, Promise &&promise) { if (!td_->messages_manager_->have_dialog_force(dialog_id)) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index c1cd2ca6..4c53e08e 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -61,6 +61,12 @@ enum class ChannelType : uint8 { Broadcast, Megagroup, Unknown }; enum class CheckDialogUsernameResult : uint8 { Ok, Invalid, Occupied, PublicDialogsTooMuch, PublicGroupsUnavailable }; +struct CanTransferOwnershipResult { + enum class Type : uint8 { Ok, PasswordNeeded, PasswordTooFresh, SessionTooFresh }; + Type type = Type::Ok; + int32 retry_after = 0; +}; + class ContactsManager : public Actor { public: ContactsManager(Td *td, ActorShared<> parent); @@ -336,6 +342,11 @@ class ContactsManager : public Actor { void change_channel_participant_status(ChannelId channel_id, UserId user_id, DialogParticipantStatus status, Promise &&promise); + void can_transfer_ownership(Promise &&promise); + + static td_api::object_ptr get_can_transfer_ownership_result_object( + CanTransferOwnershipResult result); + void transfer_dialog_ownership(DialogId dialog_id, UserId user_id, const string &password, Promise &&promise); void export_chat_invite_link(ChatId chat_id, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 871e6311..708fb814 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -3368,7 +3368,7 @@ void Td::on_get_terms_of_service(Result> result pending_terms_of_service_ = std::move(result.ok().second); auto update = get_update_terms_of_service_object(); if (update == nullptr) { - expires_in = min(max(result.ok().first, G()->unix_time() + 60) - G()->unix_time(), 86400); + expires_in = min(max(result.ok().first, G()->unix_time() + 3600) - G()->unix_time(), 86400); } else { send_update(std::move(update)); } @@ -6118,7 +6118,22 @@ void Td::on_request(uint64 id, td_api::setChatMemberStatus &request) { request.status_, std::move(promise)); } +void Td::on_request(uint64 id, const td_api::canTransferOwnership &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + auto query_promise = + PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + promise.set_value(ContactsManager::get_can_transfer_ownership_result_object(result.ok())); + } + }); + contacts_manager_->can_transfer_ownership(std::move(query_promise)); +} + void Td::on_request(uint64 id, td_api::transferChatOwnership &request) { + CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.password_); contacts_manager_->transfer_dialog_ownership(DialogId(request.chat_id_), UserId(request.user_id_), request.password_, diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 34d90de7..fbac1e65 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -688,6 +688,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::setChatMemberStatus &request); + void on_request(uint64 id, const td_api::canTransferOwnership &request); + void on_request(uint64 id, td_api::transferChatOwnership &request); void on_request(uint64 id, const td_api::getChatMember &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 9560441d..a19e0c10 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3420,6 +3420,8 @@ class CliClient final : public Actor { } else { LOG(ERROR) << "Unknown status \"" << status_str << "\""; } + } else if (op == "cto") { + send_request(td_api::make_object()); } else if (op == "transferChatOwnership") { string chat_id; string user_id;