From 62a543cb35b8af6ef493382e849474c2b6a49e21 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 19 Jun 2021 04:00:23 +0300 Subject: [PATCH] Add BotCommandScope to setCommands. --- CMakeLists.txt | 2 + td/generate/scheme/td_api.tl | 30 ++++++- td/telegram/BotCommandScope.cpp | 136 ++++++++++++++++++++++++++++++++ td/telegram/BotCommandScope.h | 44 +++++++++++ td/telegram/ContactsManager.cpp | 42 ++++------ td/telegram/ContactsManager.h | 4 +- td/telegram/Td.cpp | 3 +- 7 files changed, 231 insertions(+), 30 deletions(-) create mode 100644 td/telegram/BotCommandScope.cpp create mode 100644 td/telegram/BotCommandScope.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5734308c3..bf9b75001 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,6 +278,7 @@ set(TDLIB_SOURCE td/telegram/AutoDownloadSettings.cpp td/telegram/BackgroundManager.cpp td/telegram/BackgroundType.cpp + td/telegram/BotCommandScope.cpp td/telegram/CallActor.cpp td/telegram/CallDiscardReason.cpp td/telegram/CallManager.cpp @@ -443,6 +444,7 @@ set(TDLIB_SOURCE td/telegram/BackgroundId.h td/telegram/BackgroundManager.h td/telegram/BackgroundType.h + td/telegram/BotCommandScope.h td/telegram/CallActor.h td/telegram/CallDiscardReason.h td/telegram/CallId.h diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index bde78fc0c..d30b4a23b 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -3469,6 +3469,30 @@ vectorPathCommandLine end_point:point = VectorPathCommand; vectorPathCommandCubicBezierCurve start_control_point:point end_control_point:point end_point:point = VectorPathCommand; +//@class BotCommandScope @description Represents the scope to which bot commands are applied + +//@description A scope covering all users +botCommandScopeDefault = BotCommandScope; + +//@description A scope covering all private chats +botCommandScopeAllPrivateChats = BotCommandScope; + +//@description A scope covering all group and supergroup chats +botCommandScopeAllGroupChats = BotCommandScope; + +//@description A scope covering all group and supergroup chat administrators +botCommandScopeAllChatAdministrators = BotCommandScope; + +//@description A scope covering all members of a chat @chat_id Chat identifier +botCommandScopeChat chat_id:int53 = BotCommandScope; + +//@description A scope covering all administrators of a chat @chat_id Chat identifier +botCommandScopeChatAdministrators chat_id:int53 = BotCommandScope; + +//@description A scope covering a member of a chat @chat_id Chat identifier @user_id User identifier +botCommandScopeChatMember chat_id:int53 user_id:int32 = BotCommandScope; + + //@class Update @description Contains notifications about data changes //@description The user authorization state has changed @authorization_state New authorization state @@ -4990,8 +5014,10 @@ resendChangePhoneNumberCode = AuthenticationCodeInfo; //@description Checks the authentication code sent to confirm a new phone number of the user @code Verification code received by SMS, phone call or flash call checkChangePhoneNumberCode code:string = Ok; -//@description Sets the list of commands supported by the bot; for bots only @commands List of the bot's commands -setCommands commands:vector = Ok; +//@description Sets the list of commands supported by the bot for the given user scope and language; for bots only @scope The scope to which the commands are applied +//@language_code A two-letter ISO 639-1 country code. If empty, the commands will be applied to all users from the given scope, for which language there are no dedicated commands +//@commands List of the bot's commands +setCommands scope:BotCommandScope language_code:string commands:vector = Ok; //@description Returns all active sessions of the current user diff --git a/td/telegram/BotCommandScope.cpp b/td/telegram/BotCommandScope.cpp new file mode 100644 index 000000000..7a50c2008 --- /dev/null +++ b/td/telegram/BotCommandScope.cpp @@ -0,0 +1,136 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/BotCommandScope.h" + +#include "td/telegram/AuthManager.h" +#include "td/telegram/ContactsManager.h" +#include "td/telegram/MessagesManager.h" +#include "td/telegram/Td.h" + +namespace td { + +BotCommandScope::BotCommandScope(Type type, DialogId dialog_id, UserId user_id) + : type_(type), dialog_id_(dialog_id), user_id_(user_id) { +} + +Result BotCommandScope::get_bot_command_scope(Td *td, + td_api::object_ptr scope_ptr) { + if (scope_ptr == nullptr) { + return BotCommandScope(Type::Default); + } + + CHECK(td->auth_manager_->is_bot()); + Type type; + DialogId dialog_id; + UserId user_id; + switch (scope_ptr->get_id()) { + case td_api::botCommandScopeDefault::ID: + return BotCommandScope(Type::Default); + case td_api::botCommandScopeAllPrivateChats::ID: + return BotCommandScope(Type::AllUsers); + case td_api::botCommandScopeAllGroupChats::ID: + return BotCommandScope(Type::AllChats); + case td_api::botCommandScopeAllChatAdministrators::ID: + return BotCommandScope(Type::AllChatAdministrators); + case td_api::botCommandScopeChat::ID: { + auto scope = td_api::move_object_as(scope_ptr); + type = Type::Dialog; + dialog_id = DialogId(scope->chat_id_); + break; + } + case td_api::botCommandScopeChatAdministrators::ID: { + auto scope = td_api::move_object_as(scope_ptr); + type = Type::DialogAdministrators; + dialog_id = DialogId(scope->chat_id_); + break; + } + case td_api::botCommandScopeChatMember::ID: { + auto scope = td_api::move_object_as(scope_ptr); + type = Type::DialogParticipant; + dialog_id = DialogId(scope->chat_id_); + user_id = UserId(scope->user_id_); + if (!user_id.is_valid()) { + return Status::Error(400, "User not found"); + } + break; + } + default: + UNREACHABLE(); + return BotCommandScope(Type::Default); + } + + if (!td->messages_manager_->have_dialog_force(dialog_id, "get_bot_command_scope")) { + return Status::Error(400, "Chat not found"); + } + if (!td->messages_manager_->have_input_peer(dialog_id, AccessRights::Read)) { + return Status::Error(400, "Can't access the chat"); + } + switch (dialog_id.get_type()) { + case DialogType::User: + if (type != Type::Dialog) { + return Status::Error(400, "Can't use specified scope in private chats"); + } + break; + case DialogType::Chat: + // ok + break; + case DialogType::Channel: + if (td->contacts_manager_->get_channel_type(dialog_id.get_channel_id()) != + ContactsManager::ChannelType::Megagroup) { + return Status::Error(400, "Can't change commands in channel chats"); + } + break; + case DialogType::SecretChat: + default: + return Status::Error(400, "Can't access the chat"); + } + + return BotCommandScope(type, dialog_id, user_id); +} + +bool BotCommandScope::have_input_bot_command_scope(const Td *td) const { + if (dialog_id_.is_valid() && !td->messages_manager_->have_input_peer(dialog_id_, AccessRights::Read)) { + return false; + } + if (user_id_.is_valid() && !td->contacts_manager_->have_input_user(user_id_)) { + return false; + } + return true; +} + +telegram_api::object_ptr BotCommandScope::get_input_bot_command_scope( + const Td *td) const { + auto input_peer = + dialog_id_.is_valid() ? td->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read) : nullptr; + auto input_user = user_id_.is_valid() ? td->contacts_manager_->get_input_user(user_id_) : nullptr; + switch (type_) { + case Type::Default: + return telegram_api::make_object(); + case Type::AllUsers: + return telegram_api::make_object(); + case Type::AllChats: + return telegram_api::make_object(); + case Type::AllChatAdministrators: + return telegram_api::make_object(); + case Type::Dialog: + CHECK(input_peer != nullptr); + return telegram_api::make_object(std::move(input_peer)); + case Type::DialogAdministrators: + CHECK(input_peer != nullptr); + return telegram_api::make_object(std::move(input_peer)); + case Type::DialogParticipant: + CHECK(input_peer != nullptr); + CHECK(input_user != nullptr); + return telegram_api::make_object(std::move(input_peer), + std::move(input_user)); + default: + UNREACHABLE(); + return nullptr; + } +} + +} // namespace td diff --git a/td/telegram/BotCommandScope.h b/td/telegram/BotCommandScope.h new file mode 100644 index 000000000..28ef6f688 --- /dev/null +++ b/td/telegram/BotCommandScope.h @@ -0,0 +1,44 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/DialogId.h" +#include "td/telegram/td_api.h" +#include "td/telegram/UserId.h" + +#include "td/utils/common.h" +#include "td/utils/Status.h" + +namespace td { + +class Td; + +class BotCommandScope { + enum class Type : int32 { + Default, + AllUsers, + AllChats, + AllChatAdministrators, + Dialog, + DialogAdministrators, + DialogParticipant + }; + Type type_ = Type::Default; + DialogId dialog_id_; + UserId user_id_; + + explicit BotCommandScope(Type type, DialogId dialog_id = DialogId(), UserId user_id = UserId()); + + public: + static Result get_bot_command_scope(Td *td, td_api::object_ptr scope_ptr); + + bool have_input_bot_command_scope(const Td *td) const; + + telegram_api::object_ptr get_input_bot_command_scope(const Td *td) const; +}; + +} // namespace td diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index a1bdc923e..73bc86e65 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -11,6 +11,7 @@ #include "td/telegram/telegram_api.hpp" #include "td/telegram/AuthManager.h" +#include "td/telegram/BotCommandScope.h" #include "td/telegram/ConfigManager.h" #include "td/telegram/ConfigShared.h" #include "td/telegram/Dependencies.h" @@ -981,16 +982,14 @@ class UpdateUsernameQuery : public Td::ResultHandler { class SetBotCommandsQuery : public Td::ResultHandler { Promise promise_; - vector> commands_; public: explicit SetBotCommandsQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(vector> &&commands) { - commands_ = std::move(commands); + void send(BotCommandScope scope, const string &language_code, vector> &&commands) { send_query(G()->net_query_creator().create(telegram_api::bots_setBotCommands( - make_tl_object(), string(), transform(commands_, [](const auto &command) { + scope.get_input_bot_command_scope(td), language_code, transform(commands, [](const auto &command) { return make_tl_object(command.first, command.second); })))); } @@ -1001,10 +1000,7 @@ class SetBotCommandsQuery : public Td::ResultHandler { return on_error(id, result_ptr.move_as_error()); } - bool result = result_ptr.ok(); - if (result) { - td->contacts_manager_->on_set_bot_commands_success(std::move(commands_)); - } else { + if (!result_ptr.ok()) { LOG(ERROR) << "Set bot commands request failed"; } promise_.set_value(Unit()); @@ -6255,7 +6251,18 @@ void ContactsManager::set_username(const string &username, Promise &&promi td_->create_handler(std::move(promise))->send(username); } -void ContactsManager::set_commands(vector> &&commands, Promise &&promise) { +void ContactsManager::set_commands(td_api::object_ptr &&scope_ptr, string &&language_code, + vector> &&commands, Promise &&promise) { + TRY_RESULT_PROMISE(promise, scope, BotCommandScope::get_bot_command_scope(td_, std::move(scope_ptr))); + + if (!language_code.empty() && (language_code.size() != 2 || language_code[0] < 'a' || language_code[0] > 'z' || + language_code[1] < 'a' || language_code[1] > 'z')) { + return promise.set_error(Status::Error(400, "Invalid language code specified")); + } + if (!scope.have_input_bot_command_scope(td_)) { + return promise.set_error(Status::Error(400, "Invalid scope specified")); + } + vector> new_commands; for (auto &command : commands) { if (command == nullptr) { @@ -6297,22 +6304,7 @@ void ContactsManager::set_commands(vector new_commands.emplace_back(std::move(command->command_), std::move(command->description_)); } - td_->create_handler(std::move(promise))->send(std::move(new_commands)); -} - -void ContactsManager::on_set_bot_commands_success(vector> &&commands) { - auto user_id = get_my_id(); - BotInfo *bot_info = get_bot_info_force(user_id); - if (bot_info == nullptr) { - return; - } - if (bot_info->commands == commands) { - return; - } - bot_info->commands = std::move(commands); - bot_info->is_changed = true; - - update_bot_info(bot_info, user_id, true, false); + td_->create_handler(std::move(promise))->send(scope, language_code, std::move(new_commands)); } void ContactsManager::set_chat_description(ChatId chat_id, const string &description, Promise &&promise) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index d2e77a738..48fcc18d0 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -160,7 +160,6 @@ class ContactsManager : public Actor { void on_get_channel_full_failed(ChannelId channel_id); void on_update_profile_success(int32 flags, const string &first_name, const string &last_name, const string &about); - void on_set_bot_commands_success(vector> &&commands); void on_update_user_name(UserId user_id, string &&first_name, string &&last_name, string &&username); void on_update_user_phone_number(UserId user_id, string &&phone_number); @@ -329,7 +328,8 @@ class ContactsManager : public Actor { void set_username(const string &username, Promise &&promise); - void set_commands(vector> &&commands, Promise &&promise); + void set_commands(td_api::object_ptr &&scope_ptr, string &&language_code, + vector> &&commands, Promise &&promise); void set_chat_description(ChatId chat_id, const string &description, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 919fb5087..c3ddd3410 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6874,7 +6874,8 @@ void Td::on_request(uint64 id, td_api::setUsername &request) { void Td::on_request(uint64 id, td_api::setCommands &request) { CHECK_IS_BOT(); CREATE_OK_REQUEST_PROMISE(); - contacts_manager_->set_commands(std::move(request.commands_), std::move(promise)); + contacts_manager_->set_commands(std::move(request.scope_), std::move(request.language_code_), + std::move(request.commands_), std::move(promise)); } void Td::on_request(uint64 id, const td_api::setLocation &request) {