diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 7acac3e6..f90d17e2 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1464,6 +1464,24 @@ wallpapers wallpapers:vector = Wallpapers; hashtags hashtags:vector = Hashtags; +//@class CheckChatUsernameResult @description Represents result of checking whether a username can be set for a chat + +//@description The username can be set +checkChatUsernameResultOk = CheckChatUsernameResult; + +//@description The username is invalid +checkChatUsernameResultUsernameInvalid = CheckChatUsernameResult; + +//@description The username is occupied +checkChatUsernameResultUsernameOccupied = CheckChatUsernameResult; + +//@description The user has too much public chats, one of them should be made private first +checkChatUsernameResultPublicChatsTooMuch = CheckChatUsernameResult; + +//@description The user can't be a member of a public supergroup +checkChatUsernameResultPublicGroupsUnavailable = CheckChatUsernameResult; + + //@class OptionValue @description Represents the value of an option //@description Boolean option @value The value of the option @@ -2100,13 +2118,17 @@ removeRecentlyFoundChat chat_id:int53 = Ok; //@description Clears the list of recently found chats clearRecentlyFoundChats = Ok; -//@description Returns a list of common chats with a given user. Chats are sorted by their type and creation date @user_id User identifier @offset_chat_id Chat identifier starting from which to return chats; use 0 for the first request @limit Maximum number of chats to be returned; up to 100 -getGroupsInCommon user_id:int32 offset_chat_id:int53 limit:int32 = Chats; +//@description Checks whether a username can be set for a chat @chat_id Chat identifier; should be identifier of a supergroup chat, or a channel chat, or a private chat with self, or zero if chat is being created @username Username to be checked +checkChatUsername chat_id:int64 username:string = CheckChatUsernameResult; //@description Returns a list of public chats created by the user getCreatedPublicChats = Chats; +//@description Returns a list of common chats with a given user. Chats are sorted by their type and creation date @user_id User identifier @offset_chat_id Chat identifier starting from which to return chats; use 0 for the first request @limit Maximum number of chats to be returned; up to 100 +getGroupsInCommon user_id:int32 offset_chat_id:int53 limit:int32 = Chats; + + //@description Returns messages in a chat. The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id). //-For optimal performance the number of returned messages is chosen by the library. This is an offline request if only_local is true //@chat_id Chat identifier @@ -2621,7 +2643,7 @@ disconnectAllWebsites = Ok; toggleBasicGroupAdministrators basic_group_id:int32 everyone_is_administrator:Bool = Ok; -//@description Changes the username of the supergroup or channel, requires creator privileges in the supergroup or channel @supergroup_id Identifier of the supergroup or channel @username New value of the username. Use an empty string to remove the username +//@description Changes the username of a supergroup or channel, requires creator privileges in the supergroup or channel @supergroup_id Identifier of the supergroup or channel @username New value of the username. Use an empty string to remove the username setSupergroupUsername supergroup_id:int32 username:string = Ok; //@description Changes the sticker set of a supergroup; requires appropriate rights in the supergroup @supergroup_id Identifier of the supergroup @sticker_set_id New value of the supergroup sticker set identifier. Use 0 to remove the supergroup sticker set diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index b6008490..0f30a701 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 0e5ac429..242d97dd 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -768,6 +768,31 @@ class UpdateProfileQuery : public Td::ResultHandler { } }; +class CheckUsernameQuery : public Td::ResultHandler { + Promise promise_; + + public: + explicit CheckUsernameQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(const string &username) { + send_query(G()->net_query_creator().create(create_storer(telegram_api::account_checkUsername(username)))); + } + + 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()); + } + + promise_.set_value(result_ptr.move_as_ok()); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + class UpdateUsernameQuery : public Td::ResultHandler { Promise promise_; @@ -833,6 +858,45 @@ class ToggleChatAdminsQuery : public Td::ResultHandler { } }; +class CheckChannelUsernameQuery : public Td::ResultHandler { + Promise promise_; + ChannelId channel_id_; + string username_; + + public: + explicit CheckChannelUsernameQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id, const string &username) { + channel_id_ = channel_id; + tl_object_ptr input_channel; + if (channel_id.is_valid()) { + input_channel = td->contacts_manager_->get_input_channel(channel_id); + } else { + input_channel = make_tl_object(); + } + CHECK(input_channel != nullptr); + send_query(G()->net_query_creator().create( + create_storer(telegram_api::channels_checkUsername(std::move(input_channel), username)))); + } + + 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()); + } + + promise_.set_value(result_ptr.move_as_ok()); + } + + void on_error(uint64 id, Status status) override { + if (channel_id_.is_valid()) { + td->contacts_manager_->on_get_channel_error(channel_id_, status, "CheckChannelUsernameQuery"); + } + promise_.set_error(std::move(status)); + } +}; + class UpdateChannelUsernameQuery : public Td::ResultHandler { Promise promise_; ChannelId channel_id_; @@ -3026,6 +3090,105 @@ void ContactsManager::set_my_online_status(bool is_online, bool send_update) { } } +void ContactsManager::check_dialog_username(DialogId dialog_id, const string &username, + Promise &&promise) { + if (dialog_id != DialogId() && !td_->messages_manager_->have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(3, "Chat not found")); + } + + switch (dialog_id.get_type()) { + case DialogType::User: { + if (dialog_id.get_user_id() != get_my_id("check_dialog_username")) { + return promise.set_error(Status::Error(3, "Can't check username for private chat with other user")); + } + break; + } + case DialogType::Channel: { + auto c = get_channel(dialog_id.get_channel_id()); + if (c == nullptr) { + return promise.set_error(Status::Error(6, "Chat not found")); + } + if (!get_channel_status(c).is_creator()) { + return promise.set_error(Status::Error(6, "Not enough rights to change username")); + } + + if (username == c->username) { + return promise.set_value(CheckDialogUsernameResult::Ok); + } + break; + } + case DialogType::None: + break; + case DialogType::Chat: + case DialogType::SecretChat: + if (username.empty()) { + return promise.set_value(CheckDialogUsernameResult::Ok); + } + return promise.set_error(Status::Error(3, "Chat can't have username")); + default: + UNREACHABLE(); + return; + } + + if (username.empty()) { + return promise.set_value(CheckDialogUsernameResult::Ok); + } + if (!is_valid_username(username)) { + return promise.set_value(CheckDialogUsernameResult::Invalid); + } + + auto request_promise = PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + auto error = result.move_as_error(); + if (error.message() == "CHANNEL_PUBLIC_GROUP_NA") { + return promise.set_value(CheckDialogUsernameResult::PublicGroupsUnavailable); + } + if (error.message() == "CHANNELS_ADMIN_PUBLIC_TOO_MUCH") { + return promise.set_value(CheckDialogUsernameResult::PublicDialogsTooMuch); + } + if (error.message() == "USERNAME_INVALID") { + return promise.set_value(CheckDialogUsernameResult::Invalid); + } + promise.set_error(std::move(error)); + } + + promise.set_value(result.ok() ? CheckDialogUsernameResult::Ok : CheckDialogUsernameResult::Occupied); + }); + + switch (dialog_id.get_type()) { + case DialogType::User: + return td_->create_handler(std::move(request_promise))->send(username); + case DialogType::Channel: + return td_->create_handler(std::move(request_promise)) + ->send(dialog_id.get_channel_id(), username); + case DialogType::None: + return td_->create_handler(std::move(request_promise))->send(ChannelId(), username); + case DialogType::Chat: + case DialogType::SecretChat: + default: + UNREACHABLE(); + } +} + +td_api::object_ptr ContactsManager::get_check_chat_username_result_object( + CheckDialogUsernameResult result) { + switch (result) { + case CheckDialogUsernameResult::Ok: + return td_api::make_object(); + case CheckDialogUsernameResult::Invalid: + return td_api::make_object(); + case CheckDialogUsernameResult::Occupied: + return td_api::make_object(); + case CheckDialogUsernameResult::PublicDialogsTooMuch: + return td_api::make_object(); + case CheckDialogUsernameResult::PublicGroupsUnavailable: + return td_api::make_object(); + default: + UNREACHABLE(); + return nullptr; + } +} + void ContactsManager::set_account_ttl(int32 account_ttl, Promise &&promise) const { td_->create_handler(std::move(promise))->send(account_ttl); } diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 49045729..ab05b479 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -53,6 +53,8 @@ struct BotData { enum class ChannelType { Broadcast, Megagroup, Unknown }; +enum class CheckDialogUsernameResult { Ok, Invalid, Occupied, PublicDialogsTooMuch, PublicGroupsUnavailable }; + class ContactsManager : public Actor { public: ContactsManager(Td *td, ActorShared<> parent); @@ -212,6 +214,11 @@ class ContactsManager : public Actor { void on_channel_unban_timeout(ChannelId channel_id); + void check_dialog_username(DialogId dialog_id, const string &username, Promise &&promise); + + static td_api::object_ptr get_check_chat_username_result_object( + CheckDialogUsernameResult result); + void set_account_ttl(int32 account_ttl, Promise &&promise) const; void get_account_ttl(Promise &&promise) const; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index e574c414..6a1f1cc4 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -1001,6 +1001,35 @@ class GetGroupsInCommonRequest : public RequestActor<> { } }; +class CheckChatUsernameRequest : public RequestActor { + DialogId dialog_id_; + string username_; + + CheckDialogUsernameResult result_ = CheckDialogUsernameResult::Ok; + + void do_run(Promise &&promise) override { + if (get_tries() < 2) { + promise.set_value(std::move(result_)); + return; + } + + td->contacts_manager_->check_dialog_username(dialog_id_, username_, std::move(promise)); + } + + void do_set_result(CheckDialogUsernameResult &&result) override { + result_ = std::move(result); + } + + void do_send_result() override { + send_result(ContactsManager::get_check_chat_username_result_object(result_)); + } + + public: + CheckChatUsernameRequest(ActorShared td, uint64 request_id, int64 dialog_id, string username) + : RequestActor(std::move(td), request_id), dialog_id_(dialog_id), username_(std::move(username)) { + } +}; + class GetCreatedPublicChatsRequest : public RequestActor<> { vector dialog_ids_; @@ -5252,6 +5281,13 @@ void Td::on_request(uint64 id, const td_api::getGroupsInCommon &request) { CREATE_REQUEST(GetGroupsInCommonRequest, request.user_id_, request.offset_chat_id_, request.limit_); } +void Td::on_request(uint64 id, td_api::checkChatUsername &request) { + CHECK_AUTH(); + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.username_); + CREATE_REQUEST(CheckChatUsernameRequest, request.chat_id_, std::move(request.username_)); +} + void Td::on_request(uint64 id, const td_api::getCreatedPublicChats &request) { CHECK_AUTH(); CHECK_IS_USER(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index c56974e9..9cdcfe83 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -429,6 +429,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::getGroupsInCommon &request); + void on_request(uint64 id, td_api::checkChatUsername &request); + void on_request(uint64 id, const td_api::getCreatedPublicChats &request); void on_request(uint64 id, const td_api::openChat &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 7062f145..fb7a66e4 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2623,7 +2623,13 @@ class CliClient final : public Actor { std::tie(group_id, everyone_is_administrator) = split(args); send_request(make_tl_object(to_integer(group_id), as_bool(everyone_is_administrator))); - } else if (op == "csgun" || op == "cchun") { + } else if (op == "ccun") { + string chat_id; + string username; + + std::tie(chat_id, username) = split(args); + send_request(make_tl_object(as_chat_id(chat_id), username)); + } else if (op == "ssgun" || op == "schun") { string supergroup_id; string username;