diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index c39ff1d39..fcb29b9ea 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1201,6 +1201,10 @@ loginUrlInfoOpen url:string skip_confirm:Bool = LoginUrlInfo; loginUrlInfoRequestConfirmation url:string domain:string bot_user_id:int53 request_write_access:Bool = LoginUrlInfo; +//@description Contains information about URL to be opened in web view @query_id Unique identifier for the web view opening query @url The URL to open in web view +webViewInfo query_id:int64 url:string = WebViewInfo; + + //@description Contains information about a message thread //@chat_id Identifier of the chat to which the message thread belongs //@message_thread_id Message thread identifier, unique within the chat @@ -4865,6 +4869,15 @@ getWebViewUrl bot_user_id:int53 url:string theme:themeParameters = HttpUrl; //@bot_user_id Identifier of the target bot @button_text Text of the keyboardButtonTypeWebView button, which opened the web view @data Received data sendWebViewData bot_user_id:int53 button_text:string data:string = Ok; +//@description Informs TDLib that a web view is being opened from attach menu, bot menu, internalLinkTypeAttachMenuBot links, or an inlineKeyboardButtonTypeWebView button +//@chat_id Identifier of the chat in which web view is opened. Web view can be opened only in private chats for now +//@bot_user_id Identifier of the target bot, providing web view +//@url The URL from the inlineKeyboardButtonTypeWebView button or the internalLinkTypeAttachMenuBot link, or an empty string otherwise +//@from_bot_menu Pass true if the web view is opened from bot menu +//@theme Preferred web view theme; pass null to use the default theme +//@reply_to_message_id Identifier of the message to reply to by web view-generated message or 0 +openWebView chat_id:int53 bot_user_id:int53 url:string from_bot_menu:Bool theme:themeParameters reply_to_message_id:int53 = WebViewInfo; + //@description Sets the result of interaction with web view and sends corresponding message on behalf of the user to the chat from which the query originated; for bots only //@web_view_query_id Identifier of the web view query //@result The result of the query diff --git a/td/telegram/AttachMenuManager.cpp b/td/telegram/AttachMenuManager.cpp index efe03d3ad..bfd80f072 100644 --- a/td/telegram/AttachMenuManager.cpp +++ b/td/telegram/AttachMenuManager.cpp @@ -6,6 +6,7 @@ // #include "td/telegram/AttachMenuManager.h" +#include "td/telegram/AccessRights.h" #include "td/telegram/AuthManager.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/Dependencies.h" @@ -14,8 +15,10 @@ #include "td/telegram/files/FileId.hpp" #include "td/telegram/files/FileManager.h" #include "td/telegram/logevent/LogEvent.h" +#include "td/telegram/MessagesManager.h" #include "td/telegram/Td.h" #include "td/telegram/TdDb.h" +#include "td/telegram/ThemeManager.h" #include "td/utils/algorithm.h" #include "td/utils/buffer.h" @@ -25,6 +28,80 @@ namespace td { +class RequestWebViewQuery final : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + bool from_attach_menu_ = false; + + public: + explicit RequestWebViewQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, tl_object_ptr &&input_user, string &&url, bool from_bot_menu, + td_api::object_ptr &&theme, MessageId reply_to_message_id, bool silent) { + dialog_id_ = dialog_id; + + int32 flags = 0; + + auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); + CHECK(input_peer != nullptr); + + string start_parameter; + if (begins_with(url, "start://")) { + start_parameter = url.substr(8); + url = string(); + + flags |= telegram_api::messages_requestWebView::START_PARAM_MASK; + } else if (!url.empty()) { + flags |= telegram_api::messages_requestWebView::URL_MASK; + } else if (from_bot_menu) { + flags |= telegram_api::messages_requestWebView::FROM_BOT_MENU_MASK; + } else { + from_attach_menu_ = true; + } + + tl_object_ptr theme_parameters; + if (theme != nullptr) { + theme_parameters = make_tl_object(string()); + theme_parameters->data_ = ThemeManager::get_theme_parameters_json_string(theme, false); + + flags |= telegram_api::messages_requestWebView::THEME_PARAMS_MASK; + } + + if (reply_to_message_id.is_valid()) { + flags |= telegram_api::messages_requestWebView::REPLY_TO_MSG_ID_MASK; + } + + if (silent) { + flags |= telegram_api::messages_requestWebView::SILENT_MASK; + } + + send_query(G()->net_query_creator().create(telegram_api::messages_requestWebView( + flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), std::move(input_user), url, start_parameter, + std::move(theme_parameters), reply_to_message_id.get_server_message_id().get()))); + } + + 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()); + } + + auto ptr = result_ptr.move_as_ok(); + promise_.set_value(td_api::make_object(ptr->query_id_, ptr->url_)); + } + + void on_error(Status status) final { + if (!td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "RequestWebViewQuery")) { + if (from_attach_menu_) { + td_->attach_menu_manager_->reload_attach_menu_bots(Promise()); + } + } + promise_.set_error(std::move(status)); + } +}; + class GetAttachMenuBotsQuery final : public Td::ResultHandler { Promise> promise_; @@ -315,6 +392,50 @@ bool AttachMenuManager::is_active() const { return !G()->close_flag() && td_->auth_manager_->is_authorized() && !td_->auth_manager_->is_bot(); } +void AttachMenuManager::request_web_view(DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id, + string &&url, bool from_bot_menu, + td_api::object_ptr &&theme, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, td_->contacts_manager_->get_bot_data(bot_user_id)); + TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(bot_user_id)); + + if (!td_->messages_manager_->have_dialog_force(dialog_id, "request_web_view")) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + + switch (dialog_id.get_type()) { + case DialogType::User: + // ok + break; + case DialogType::Chat: + case DialogType::Channel: + case DialogType::SecretChat: + return promise.set_error(Status::Error(400, "Web apps can be opened only in private chats")); + case DialogType::None: + default: + UNREACHABLE(); + } + + if (from_bot_menu && !url.empty()) { + return promise.set_error(Status::Error(400, "URL can't be specified when web app is opened from bot menu")); + } + + if (!td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Write)) { + return promise.set_error(Status::Error(400, "Have no write access to the chat")); + } + + if (!reply_to_message_id.is_valid() || !reply_to_message_id.is_server() || + !td_->messages_manager_->have_message_force({dialog_id, reply_to_message_id}, "request_web_view")) { + reply_to_message_id = MessageId(); + } + + bool silent = td_->messages_manager_->get_dialog_silent_send_message(dialog_id); + + td_->create_handler(std::move(promise)) + ->send(dialog_id, std::move(input_user), std::move(url), from_bot_menu, std::move(theme), reply_to_message_id, + silent); +} + Result AttachMenuManager::get_attach_menu_bot( tl_object_ptr &&bot) const { UserId user_id(bot->bot_id_); diff --git a/td/telegram/AttachMenuManager.h b/td/telegram/AttachMenuManager.h index 9c599c6d9..aae8d7e5b 100644 --- a/td/telegram/AttachMenuManager.h +++ b/td/telegram/AttachMenuManager.h @@ -6,7 +6,9 @@ // #pragma once +#include "td/telegram/DialogId.h" #include "td/telegram/files/FileId.h" +#include "td/telegram/MessageId.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" @@ -27,6 +29,10 @@ class AttachMenuManager final : public Actor { void init(); + void request_web_view(DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id, string &&url, + bool from_bot_menu, td_api::object_ptr &&theme, + Promise> &&promise); + void reload_attach_menu_bots(Promise &&promise); void get_attach_menu_bot(UserId user_id, Promise> &&promise); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index b726ad1d4..991fd34a4 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -25019,6 +25019,12 @@ bool MessagesManager::is_anonymous_administrator(DialogId dialog_id, string *aut return true; } +bool MessagesManager::get_dialog_silent_send_message(DialogId dialog_id) const { + auto *d = get_dialog(dialog_id); + CHECK(d != nullptr); + return d->notification_settings.silent_send_message; +} + int64 MessagesManager::generate_new_random_id() { int64 random_id; do { @@ -26529,9 +26535,7 @@ void MessagesManager::on_yet_unsent_media_queue_updated(DialogId dialog_id) { Result MessagesManager::send_bot_start_message(UserId bot_user_id, DialogId dialog_id, const string ¶meter) { LOG(INFO) << "Begin to send bot start message to " << dialog_id; - if (td_->auth_manager_->is_bot()) { - return Status::Error(400, "Bot can't send start message to another bot"); - } + CHECK(!td_->auth_manager_->is_bot()); TRY_RESULT(bot_data, td_->contacts_manager_->get_bot_data(bot_user_id)); diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 4cf84643a..9478a6936 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -421,6 +421,8 @@ class MessagesManager final : public Actor { void set_dialog_default_send_message_as_dialog_id(DialogId dialog_id, DialogId message_sender_dialog_id, Promise &&promise); + bool get_dialog_silent_send_message(DialogId dialog_id) const; + Result> send_message( DialogId dialog_id, MessageId top_thread_message_id, MessageId reply_to_message_id, tl_object_ptr &&options, tl_object_ptr &&reply_markup, diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 66aa79a79..f20a59893 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -7360,6 +7360,15 @@ void Td::on_request(uint64 id, td_api::sendWebViewData &request) { std::move(request.data_), std::move(promise)); } +void Td::on_request(uint64 id, td_api::openWebView &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.url_); + CREATE_REQUEST_PROMISE(); + attach_menu_manager_->request_web_view(DialogId(request.chat_id_), UserId(request.bot_user_id_), + MessageId(request.reply_to_message_id_), std::move(request.url_), + request.from_bot_menu_, std::move(request.theme_), std::move(promise)); +} + void Td::on_request(uint64 id, td_api::answerWebViewQuery &request) { CHECK_IS_BOT(); CLEAN_INPUT_STRING(request.web_view_query_id_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 9e8b796a5..96d94d77f 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -1159,6 +1159,8 @@ class Td final : public Actor { void on_request(uint64 id, td_api::sendWebViewData &request); + void on_request(uint64 id, td_api::openWebView &request); + void on_request(uint64 id, td_api::answerWebViewQuery &request); void on_request(uint64 id, td_api::getCallbackQueryAnswer &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 79637dbf5..3891fe8e9 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3439,6 +3439,15 @@ class CliClient final : public Actor { string data; get_args(args, user_id, button_text, data); send_request(td_api::make_object(user_id, button_text, data)); + } else if (op == "owv") { + ChatId chat_id; + UserId bot_user_id; + string url; + bool from_bot_menu; + MessageId reply_to_message_id; + get_args(args, chat_id, bot_user_id, url, from_bot_menu, reply_to_message_id); + send_request(td_api::make_object(chat_id, bot_user_id, url, from_bot_menu, + get_theme_parameters(), reply_to_message_id)); } else if (op == "sca") { ChatId chat_id; string message_thread_id;