diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 00da7b99e..7caf19b7b 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -488,7 +488,7 @@ emojiStatuses emoji_statuses:vector = EmojiStatuses; //@description Describes usernames assigned to a user, a supergroup, or a channel //@active_usernames List of active usernames; the first one must be shown as the primary username. The order of active usernames can be changed with reorderActiveUsernames or reorderSupergroupActiveUsernames -//@disabled_usernames List of currently disabled usernames; the username can be activated or disabled with toggleUsernameIsActive/toggleSupergroupUsernameIsActive +//@disabled_usernames List of currently disabled usernames; the username can be activated with toggleUsernameIsActive/toggleSupergroupUsernameIsActive //@editable_username The active username, which can be changed with setUsername/setSupergroupUsername usernames active_usernames:vector disabled_usernames:vector editable_username:string = Usernames; @@ -6202,6 +6202,9 @@ setBio bio:string = Ok; //@description Changes the editable username of the current user @username The new value of the username. Use an empty string to remove the username. The username can't be completely removed if there is another active or disabled username setUsername username:string = Ok; +//@description Changes active state for a username of the current user. The editable username can't be disabled. May return an error with a message "USERNAMES_ACTIVE_TOO_MUCH" if the maximum number of active usernames has been reached @username The username to change @is_active True, if the username must be activated, or false, if it must be disabled +toggleUsernameIsActive username:string is_active:Bool = Ok; + //@description Changes order of active usernames of the current user @usernames The new order of active usernames. All currently active usernames must be specified reorderActiveUsernames usernames:vector = Ok; @@ -6287,6 +6290,9 @@ disconnectAllWebsites = Ok; //@description Changes the editable username of a supergroup or channel, requires owner 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. The username can't be completely removed if there is another active or disabled username setSupergroupUsername supergroup_id:int53 username:string = Ok; +//@description Changes active state for a username of a supergroup or channel, requires owner privileges in the supergroup or channel. The editable username can't be disabled. May return an error with a message "USERNAMES_ACTIVE_TOO_MUCH" if the maximum number of active usernames has been reached @supergroup_id Identifier of the supergroup or channel @username The username to change @is_active True, if the username must be activated, or false, if it must be disabled +toggleSupergroupUsernameIsActive supergroup_id:int53 username:string is_active:Bool = Ok; + //@description Changes order of active usernames of a supergroup or channel, requires owner privileges in the supergroup or channel @supergroup_id Identifier of the supergroup or channel @usernames The new order of active usernames. All currently active usernames must be specified reorderSupergroupActiveUsernames supergroup_id:int53 usernames:vector = Ok; diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 6eb6fbd95..159c54918 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -673,6 +673,41 @@ class UpdateUsernameQuery final : public Td::ResultHandler { } }; +class ToggleUsernameQuery final : public Td::ResultHandler { + Promise promise_; + string username_; + bool is_active_; + + public: + explicit ToggleUsernameQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(string &&username, bool is_active) { + username_ = std::move(username); + is_active_ = is_active; + send_query(G()->net_query_creator().create(telegram_api::account_toggleUsername(username_, is_active_))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + bool result = result_ptr.ok(); + LOG(DEBUG) << "Receive result for ToggleUsernameQuery: " << result; + td_->contacts_manager_->on_update_username_is_active(std::move(username_), is_active_, std::move(promise_)); + } + + void on_error(Status status) final { + if (status.message() == "USERNAME_NOT_MODIFIED") { + td_->contacts_manager_->on_update_username_is_active(std::move(username_), is_active_, std::move(promise_)); + return; + } + promise_.set_error(std::move(status)); + } +}; + class ReorderUsernamesQuery final : public Td::ResultHandler { Promise promise_; vector usernames_; @@ -829,6 +864,50 @@ class UpdateChannelUsernameQuery final : public Td::ResultHandler { } }; +class ToggleChannelUsernameQuery final : public Td::ResultHandler { + Promise promise_; + ChannelId channel_id_; + string username_; + bool is_active_; + + public: + explicit ToggleChannelUsernameQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id, string &&username, bool is_active) { + channel_id_ = channel_id; + username_ = std::move(username); + is_active_ = is_active; + auto input_channel = td_->contacts_manager_->get_input_channel(channel_id); + CHECK(input_channel != nullptr); + send_query(G()->net_query_creator().create( + telegram_api::channels_toggleUsername(std::move(input_channel), username_, is_active_))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + bool result = result_ptr.ok(); + LOG(DEBUG) << "Receive result for ToggleChannelUsernameQuery: " << result; + td_->contacts_manager_->on_update_channel_username_is_active(channel_id_, std::move(username_), is_active_, + std::move(promise_)); + } + + void on_error(Status status) final { + if (status.message() == "USERNAME_NOT_MODIFIED" || status.message() == "CHAT_NOT_MODIFIED") { + td_->contacts_manager_->on_update_channel_username_is_active(channel_id_, std::move(username_), is_active_, + std::move(promise_)); + return; + } else { + td_->contacts_manager_->on_get_channel_error(channel_id_, status, "ToggleChannelUsernameQuery"); + } + promise_.set_error(std::move(status)); + } +}; + class ReorderChannelUsernamesQuery final : public Td::ResultHandler { Promise promise_; ChannelId channel_id_; @@ -6742,6 +6821,28 @@ void ContactsManager::set_username(const string &username, Promise &&promi td_->create_handler(std::move(promise))->send(username); } +void ContactsManager::toggle_username_is_active(string &&username, bool is_active, Promise &&promise) { + get_me(PromiseCreator::lambda([actor_id = actor_id(this), username = std::move(username), is_active, + promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &ContactsManager::toggle_username_is_active_impl, std::move(username), is_active, + std::move(promise)); + } + })); +} + +void ContactsManager::toggle_username_is_active_impl(string &&username, bool is_active, Promise &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + const User *u = get_user(get_my_id()); + CHECK(u != nullptr); + if (!u->usernames.can_toggle(username)) { + return promise.set_error(Status::Error(400, "Unknown username specified")); + } + td_->create_handler(std::move(promise))->send(std::move(username), is_active); +} + void ContactsManager::reorder_usernames(vector &&usernames, Promise &&promise) { get_me(PromiseCreator::lambda([actor_id = actor_id(this), usernames = std::move(usernames), promise = std::move(promise)](Result &&result) mutable { @@ -6763,6 +6864,18 @@ void ContactsManager::reorder_usernames_impl(vector &&usernames, Promise td_->create_handler(std::move(promise))->send(std::move(usernames)); } +void ContactsManager::on_update_username_is_active(string &&username, bool is_active, Promise &&promise) { + auto user_id = get_my_id(); + User *u = get_user(user_id); + CHECK(u != nullptr); + if (!u->usernames.can_toggle(username)) { + return reload_user(user_id, std::move(promise)); + } + on_update_user_usernames(u, user_id, u->usernames.toggle(username, is_active)); + update_user(u, user_id); + promise.set_value(Unit()); +} + void ContactsManager::on_update_active_usernames_order(vector &&usernames, Promise &&promise) { auto user_id = get_my_id(); User *u = get_user(user_id); @@ -6837,8 +6950,8 @@ void ContactsManager::set_channel_username(ChannelId channel_id, const string &u td_->create_handler(std::move(promise))->send(channel_id, username); } -void ContactsManager::reorder_channel_usernames(ChannelId channel_id, vector &&usernames, - Promise &&promise) { +void ContactsManager::toggle_channel_username_is_active(ChannelId channel_id, string &&username, bool is_active, + Promise &&promise) { const auto *c = get_channel(channel_id); if (c == nullptr) { return promise.set_error(Status::Error(400, "Supergroup not found")); @@ -6846,12 +6959,39 @@ void ContactsManager::reorder_channel_usernames(ChannelId channel_id, vectorusernames.can_toggle(username)) { + return promise.set_error(Status::Error(400, "Unknown username specified")); + } + td_->create_handler(std::move(promise))->send(channel_id, std::move(username), is_active); +} + +void ContactsManager::reorder_channel_usernames(ChannelId channel_id, vector &&usernames, + Promise &&promise) { + const auto *c = get_channel(channel_id); + if (c == nullptr) { + return promise.set_error(Status::Error(400, "Supergroup not found")); + } + if (!get_channel_status(c).is_creator()) { + return promise.set_error(Status::Error(400, "Not enough rights to reorder usernames")); + } if (!c->usernames.can_reorder_to(usernames)) { return promise.set_error(Status::Error(400, "Invalid username order specified")); } td_->create_handler(std::move(promise))->send(channel_id, std::move(usernames)); } +void ContactsManager::on_update_channel_username_is_active(ChannelId channel_id, string &&username, bool is_active, + Promise &&promise) { + auto *c = get_channel(channel_id); + CHECK(c != nullptr); + if (!c->usernames.can_toggle(username)) { + return reload_channel(channel_id, std::move(promise)); + } + on_update_channel_usernames(c, channel_id, c->usernames.toggle(username, is_active)); + update_channel(c, channel_id); + promise.set_value(Unit()); +} + void ContactsManager::on_update_channel_active_usernames_order(ChannelId channel_id, vector &&usernames, Promise &&promise) { auto *c = get_channel(channel_id); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 2d349d8fe..5ddda1c18 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -285,8 +285,13 @@ class ContactsManager final : public Actor { UserId add_channel_bot_user(); + void on_update_username_is_active(string &&username, bool is_active, Promise &&promise); + void on_update_active_usernames_order(vector &&usernames, Promise &&promise); + void on_update_channel_username_is_active(ChannelId channel_id, string &&username, bool is_active, + Promise &&promise); + void on_update_channel_active_usernames_order(ChannelId channel_id, vector &&usernames, Promise &&promise); @@ -352,6 +357,8 @@ class ContactsManager final : public Actor { void set_username(const string &username, Promise &&promise); + void toggle_username_is_active(string &&username, bool is_active, Promise &&promise); + void reorder_usernames(vector &&usernames, Promise &&promise); void set_emoji_status(EmojiStatus emoji_status, Promise &&promise); @@ -360,6 +367,9 @@ class ContactsManager final : public Actor { void set_channel_username(ChannelId channel_id, const string &username, Promise &&promise); + void toggle_channel_username_is_active(ChannelId channel_id, string &&username, bool is_active, + Promise &&promise); + void reorder_channel_usernames(ChannelId channel_id, vector &&usernames, Promise &&promise); void set_channel_sticker_set(ChannelId channel_id, StickerSetId sticker_set_id, Promise &&promise); @@ -1358,6 +1368,8 @@ class ContactsManager final : public Actor { static void on_update_channel_full_bot_user_ids(ChannelFull *channel_full, ChannelId channel_id, vector &&bot_user_ids); + void toggle_username_is_active_impl(string &&username, bool is_active, Promise &&promise); + void reorder_usernames_impl(vector &&usernames, Promise &&promise); void on_channel_status_changed(Channel *c, ChannelId channel_id, const DialogParticipantStatus &old_status, diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index eb009d657..1f3fc3d9d 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6737,6 +6737,13 @@ void Td::on_request(uint64 id, td_api::setUsername &request) { contacts_manager_->set_username(request.username_, std::move(promise)); } +void Td::on_request(uint64 id, td_api::toggleUsernameIsActive &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.username_); + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->toggle_username_is_active(std::move(request.username_), request.is_active_, std::move(promise)); +} + void Td::on_request(uint64 id, td_api::reorderActiveUsernames &request) { CHECK_IS_USER(); for (auto &username : request.usernames_) { @@ -6852,6 +6859,14 @@ void Td::on_request(uint64 id, td_api::setSupergroupUsername &request) { contacts_manager_->set_channel_username(ChannelId(request.supergroup_id_), request.username_, std::move(promise)); } +void Td::on_request(uint64 id, td_api::toggleSupergroupUsernameIsActive &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.username_); + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->toggle_channel_username_is_active(ChannelId(request.supergroup_id_), std::move(request.username_), + request.is_active_, std::move(promise)); +} + void Td::on_request(uint64 id, td_api::reorderSupergroupActiveUsernames &request) { CHECK_IS_USER(); for (auto &username : request.usernames_) { diff --git a/td/telegram/Td.h b/td/telegram/Td.h index f358cef5c..4fded664c 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -1028,6 +1028,8 @@ class Td final : public Actor { void on_request(uint64 id, td_api::setUsername &request); + void on_request(uint64 id, td_api::toggleUsernameIsActive &request); + void on_request(uint64 id, td_api::reorderActiveUsernames &request); void on_request(uint64 id, const td_api::setEmojiStatus &request); @@ -1064,6 +1066,8 @@ class Td final : public Actor { void on_request(uint64 id, td_api::setSupergroupUsername &request); + void on_request(uint64 id, td_api::toggleSupergroupUsernameIsActive &request); + void on_request(uint64 id, td_api::reorderSupergroupActiveUsernames &request); void on_request(uint64 id, const td_api::setSupergroupStickerSet &request); diff --git a/td/telegram/Usernames.cpp b/td/telegram/Usernames.cpp index 27b5bc141..c38e1b4bf 100644 --- a/td/telegram/Usernames.cpp +++ b/td/telegram/Usernames.cpp @@ -9,6 +9,7 @@ #include "td/telegram/misc.h" #include "td/telegram/secret_api.h" +#include "td/utils/algorithm.h" #include "td/utils/misc.h" namespace td { @@ -55,7 +56,7 @@ Usernames::Usernames(string &&first_username, vectorusername_)); } } - CHECK((editable_username_pos_ != -1) == was_editable); + CHECK(has_editable_username() == was_editable); } tl_object_ptr Usernames::get_usernames_object() const { @@ -64,12 +65,12 @@ tl_object_ptr Usernames::get_usernames_object() const { } return make_tl_object( vector(active_usernames_), vector(disabled_usernames_), - editable_username_pos_ == -1 ? string() : active_usernames_[editable_username_pos_]); + has_editable_username() ? active_usernames_[editable_username_pos_] : string()); } Usernames Usernames::change_editable_username(string &&new_username) const { Usernames result = *this; - if (editable_username_pos_ != -1) { + if (has_editable_username()) { // keep position result.active_usernames_[editable_username_pos_] = std::move(new_username); } else { @@ -80,6 +81,48 @@ Usernames Usernames::change_editable_username(string &&new_username) const { return result; } +bool Usernames::can_toggle(const string &username) const { + if (td::contains(active_usernames_, username)) { + return !has_editable_username() || active_usernames_[editable_username_pos_] != username; + } + if (td::contains(disabled_usernames_, username)) { + return true; + } + return false; +} + +Usernames Usernames::toggle(const string &username, bool is_active) const { + Usernames result = *this; + for (size_t i = 0; i < disabled_usernames_.size(); i++) { + if (disabled_usernames_[i] == username) { + if (is_active) { + // activate the username + result.disabled_usernames_.erase(result.disabled_usernames_.begin() + i); + result.active_usernames_.push_back(username); + // editable username position wasn't changed + } + return result; + } + } + for (size_t i = 0; i < active_usernames_.size(); i++) { + if (active_usernames_[i] == username) { + if (!is_active) { + // disable the username + result.active_usernames_.erase(result.active_usernames_.begin() + i); + result.disabled_usernames_.insert(result.disabled_usernames_.begin(), username); + if (result.has_editable_username() && i <= static_cast(result.editable_username_pos_)) { + CHECK(i != static_cast(result.editable_username_pos_)); + CHECK(result.editable_username_pos_ > 0); + result.editable_username_pos_--; + } + } + return result; + } + } + UNREACHABLE(); + return result; +} + bool Usernames::can_reorder_to(const vector &new_username_order) const { if (new_username_order.size() != active_usernames_.size()) { return false; @@ -103,7 +146,7 @@ Usernames Usernames::reorder_to(vector &&new_username_order) const { Usernames result; result.active_usernames_ = std::move(new_username_order); result.disabled_usernames_ = disabled_usernames_; - if (editable_username_pos_ != -1) { + if (has_editable_username()) { const string &editable_username = active_usernames_[editable_username_pos_]; for (size_t i = 0; i < result.active_usernames_.size(); i++) { if (result.active_usernames_[i] == editable_username) { @@ -111,7 +154,7 @@ Usernames Usernames::reorder_to(vector &&new_username_order) const { break; } } - CHECK(result.editable_username_pos_ != -1); + CHECK(result.has_editable_username()); } return result; } @@ -144,7 +187,7 @@ bool operator!=(const Usernames &lhs, const Usernames &rhs) { StringBuilder &operator<<(StringBuilder &string_builder, const Usernames &usernames) { string_builder << "Usernames["; - if (usernames.editable_username_pos_ != -1) { + if (usernames.has_editable_username()) { string_builder << usernames.active_usernames_[usernames.editable_username_pos_]; } if (!usernames.active_usernames_.empty()) { diff --git a/td/telegram/Usernames.h b/td/telegram/Usernames.h index 114e6fb2a..3cf3e3f10 100644 --- a/td/telegram/Usernames.h +++ b/td/telegram/Usernames.h @@ -65,6 +65,10 @@ class Usernames { Usernames change_editable_username(string &&new_username) const; + bool can_toggle(const string &username) const; + + Usernames toggle(const string &username, bool is_active) const; + bool can_reorder_to(const vector &new_username_order) const; Usernames reorder_to(vector &&new_username_order) const; diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 523a77ab3..a106073c8 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -4514,6 +4514,11 @@ class CliClient final : public Actor { send_request(td_api::make_object("\n" + args + "\n" + args + "\n")); } else if (op == "sun") { send_request(td_api::make_object(args)); + } else if (op == "tunia") { + string username; + bool is_active; + get_args(args, username, is_active); + send_request(td_api::make_object(username, is_active)); } else if (op == "raun") { send_request(td_api::make_object(autosplit_str(args))); } else if (op == "sese") { @@ -4542,6 +4547,13 @@ class CliClient final : public Actor { string username; get_args(args, supergroup_id, username); send_request(td_api::make_object(as_supergroup_id(supergroup_id), username)); + } else if (op == "tsgunia" || op == "tchunia") { + string supergroup_id; + string username; + bool is_active; + get_args(args, supergroup_id, username, is_active); + send_request(td_api::make_object(as_supergroup_id(supergroup_id), + username, is_active)); } else if (op == "rsgaun" || op == "rchaun") { string supergroup_id; get_args(args, supergroup_id);