From b1e66279d5904069811850a1adf2c40a4aa32e13 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 31 Mar 2022 13:24:13 +0400 Subject: [PATCH] Update API scheme on layer 140. --- Telegram/Resources/tl/api.tl | 9 +- .../SourceFiles/core/local_url_handlers.cpp | 7 +- .../inline_bots/bot_attach_web_view.cpp | 124 ++++++++----- .../inline_bots/bot_attach_web_view.h | 21 ++- .../ui/chat/attach/attach_bot_webview.cpp | 4 +- .../window/window_session_controller.cpp | 164 ++++++++++-------- .../window/window_session_controller.h | 5 +- 7 files changed, 207 insertions(+), 127 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index acb55dec0..3e6ca9960 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -1336,7 +1336,11 @@ phone.groupCallStreamChannels#d0e482b2 channels:Vector = phone.groupCallStreamRtmpUrl#2dbf3432 url:string key:string = phone.GroupCallStreamRtmpUrl; -attachMenuBot#4fdf05a8 flags:# inactive:flags.0?true bot_id:long attach_menu_name:string attach_menu_icon:Document = AttachMenuBot; +attachMenuBotIconColor#4576f3f0 name:string color:int = AttachMenuBotIconColor; + +attachMenuBotIcon#b2a7386b flags:# name:string icon:Document colors:flags.0?Vector = AttachMenuBotIcon; + +attachMenuBot#e93cb772 flags:# inactive:flags.0?true bot_id:long short_name:string icons:Vector = AttachMenuBot; attachMenuBotsNotModified#f1d88a5c = AttachMenuBots; attachMenuBots#3c4301c0 hash:long bots:Vector users:Vector = AttachMenuBots; @@ -1344,7 +1348,6 @@ attachMenuBots#3c4301c0 hash:long bots:Vector users:Vector attachMenuBotsBot#93bf667f bot:AttachMenuBot users:Vector = AttachMenuBotsBot; webViewResultUrl#c14557c query_id:long url:string = WebViewResult; -webViewResultConfirmationRequired#b1cad385 bot:AttachMenuBot users:Vector = WebViewResult; simpleWebViewResultUrl#882f76bb url:string = SimpleWebViewResult; @@ -1653,7 +1656,7 @@ messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = mes messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots; messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot; messages.toggleBotInAttachMenu#1aee33af bot:InputUser enabled:Bool = Bool; -messages.requestWebView#2221fe98 flags:# silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string theme_params:flags.2?DataJSON reply_to_msg_id:flags.0?int = WebViewResult; +messages.requestWebView#fa04dff flags:# silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON reply_to_msg_id:flags.0?int = WebViewResult; messages.prolongWebView#d22ad148 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int = Bool; messages.requestSimpleWebView#6abb2f73 flags:# bot:InputUser url:string theme_params:flags.0?DataJSON = SimpleWebViewResult; messages.sendWebViewResultMessage#ddcf50eb query_id:long result:InputBotInlineResult = WebViewMessageSent; diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index 957dfd863..a75c5e39e 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -362,7 +362,9 @@ bool ResolveUsernameOrPhone( .startToken = startToken, .startAdminRights = adminRights, .attachBotUsername = params.value(u"attach"_q), - .attachBotToggle = params.contains(u"setattach"_q), + .attachBotToggleCommand = (params.contains(u"startattach"_q) + ? params.value(u"startattach"_q) + : std::optional()), .voicechatHash = (params.contains(u"livestream"_q) ? std::make_optional(params.value(u"livestream"_q)) : params.contains(u"videochat"_q) @@ -782,7 +784,8 @@ QString TryConvertUrlToLocal(QString url) { if (telegramMeMatch) { auto query = telegramMeMatch->capturedView(5); if (auto phoneMatch = regex_match(qsl("^\\+([0-9]+)(\\?|$)"), query, matchOptions)) { - return qsl("tg://resolve?phone=") + phoneMatch->captured(1); + auto params = query.mid(phoneMatch->captured(0).size()).toString(); + return qsl("tg://resolve?phone=") + phoneMatch->captured(1) + (params.isEmpty() ? QString() : '&' + params); } else if (auto joinChatMatch = regex_match(qsl("^(joinchat/|\\+|\\%20)([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), query, matchOptions)) { return qsl("tg://join?invite=") + url_encode(joinChatMatch->captured(2)); } else if (auto stickerSetMatch = regex_match(qsl("^addstickers/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), query, matchOptions)) { diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index aba0a7739..d28be32fd 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "core/application.h" #include "history/history.h" +#include "history/history_item.h" #include "lang/lang_keys.h" #include "base/random.h" #include "base/timer_rpl.h" @@ -46,6 +47,25 @@ struct ParsedBot { bool inactive = false; }; +[[nodiscard]] DocumentData *ResolveIcon( + not_null session, + const MTPDattachMenuBot &data) { + for (const auto &icon : data.vicons().v) { + const auto document = icon.match([&]( + const MTPDattachMenuBotIcon &data + ) -> DocumentData* { + if (data.vname().v == "default_static") { + return session->data().processDocument(data.vicon()).get(); + } + return nullptr; + }); + if (document) { + return document; + } + } + return nullptr; +} + [[nodiscard]] std::optional ParseAttachBot( not_null session, const MTPAttachMenuBot &bot) { @@ -57,13 +77,12 @@ struct ParsedBot { return good ? AttachWebViewBot{ .user = user, - .icon = session->data().processDocument( - data.vattach_menu_icon()), - .name = qs(data.vattach_menu_name()), + .icon = ResolveIcon(session, data), + .name = qs(data.vshort_name()), .inactive = data.is_inactive(), } : std::optional(); }); - if (result) { + if (result && result->icon) { result->icon->forceToCache(true); } return result; @@ -150,7 +169,7 @@ BotAction::BotAction( void BotAction::validateIcon() { if (_mask.isNull()) { - if (!_bot.media->loaded()) { + if (!_bot.media || !_bot.media->loaded()) { return; } auto icon = QSvgRenderer(_bot.media->bytes()); @@ -281,12 +300,15 @@ AttachWebView::~AttachWebView() { void AttachWebView::request( not_null peer, - const QString &botUsername) { + const QString &botUsername, + const QString &startCommand) { if (botUsername.isEmpty()) { return; } const auto username = _bot ? _bot->username : _botUsername; - if (_peer == peer && username.toLower() == botUsername.toLower()) { + if (_peer == peer + && username.toLower() == botUsername.toLower() + && _startCommand == startCommand) { if (_panel) { _panel->requestActivate(); } @@ -296,6 +318,7 @@ void AttachWebView::request( _peer = peer; _botUsername = botUsername; + _startCommand = startCommand; resolve(); } @@ -320,38 +343,30 @@ void AttachWebView::request( void AttachWebView::request(const WebViewButton &button) { Expects(_peer != nullptr && _bot != nullptr); + _startCommand = button.startCommand; + using Flag = MTPmessages_RequestWebView::Flag; const auto flags = Flag::f_theme_params - | (button.url.isEmpty() ? Flag(0) : Flag::f_url); + | (button.url.isEmpty() ? Flag(0) : Flag::f_url) + | (_startCommand.isEmpty() ? Flag(0) : Flag::f_start_param); _requestId = _session->api().request(MTPmessages_RequestWebView( MTP_flags(flags), _peer->input, _bot->inputUser, MTP_bytes(button.url), + MTP_string(_startCommand), MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams())), MTPint() // reply_to_msg_id )).done([=](const MTPWebViewResult &result) { _requestId = 0; result.match([&](const MTPDwebViewResultUrl &data) { show(data.vquery_id().v, qs(data.vurl()), button.text); - }, [&](const MTPDwebViewResultConfirmationRequired &data) { - _session->data().processUsers(data.vusers()); - const auto &received = data.vbot(); - if (const auto bot = ParseAttachBot(_session, received)) { - if (_bot != bot->user) { - cancel(); - return; - } - confirmAddToMenu(*bot, [=] { - request(button); - }); - } else { - cancel(); - } }); }).fail([=](const MTP::Error &error) { _requestId = 0; - int a = error.code(); + if (error.type() == u"BOT_INVALID"_q) { + requestBots(); + } }).send(); } @@ -362,6 +377,7 @@ void AttachWebView::cancel() { _panel = nullptr; _peer = _bot = nullptr; _botUsername = QString(); + _startCommand = QString(); } void AttachWebView::requestBots() { @@ -381,8 +397,10 @@ void AttachWebView::requestBots() { for (const auto &bot : data.vbots().v) { if (auto parsed = ParseAttachBot(_session, bot)) { if (!parsed->inactive) { - parsed->media = parsed->icon->createMediaView(); - parsed->icon->save(Data::FileOrigin(), {}); + if (const auto icon = parsed->icon) { + parsed->media = icon->createMediaView(); + icon->save(Data::FileOrigin(), {}); + } _attachBots.push_back(std::move(*parsed)); } } @@ -394,10 +412,19 @@ void AttachWebView::requestBots() { }).send(); } -void AttachWebView::requestAddToMenu(not_null bot) { +void AttachWebView::requestAddToMenu( + PeerData *peer, + not_null bot, + const QString &startCommand) { if (!bot->isBot() || !bot->botInfo->supportsAttachMenu) { + Ui::ShowMultilineToast({ + .text = { tr::lng_bot_menu_not_supported(tr::now) }, + }); return; - } else if (_addToMenuId) { + } + _addToMenuStartCommand = startCommand; + _addToMenuPeer = peer; + if (_addToMenuId) { if (_addToMenuBot == bot) { return; } @@ -408,19 +435,30 @@ void AttachWebView::requestAddToMenu(not_null bot) { bot->inputUser )).done([=](const MTPAttachMenuBotsBot &result) { _addToMenuId = 0; - const auto requested = base::take(_addToMenuBot); + const auto bot = base::take(_addToMenuBot); + const auto contextPeer = base::take(_addToMenuPeer); + const auto startCommand = base::take(_addToMenuStartCommand); + const auto open = [=] { + if (!contextPeer) { + return false; + } + request(contextPeer, bot, { .startCommand = startCommand }); + return true; + }; result.match([&](const MTPDattachMenuBotsBot &data) { _session->data().processUsers(data.vusers()); - if (const auto bot = ParseAttachBot(_session, data.vbot())) { - if (requested == bot->user) { - if (bot->inactive) { - confirmAddToMenu(*bot); + if (const auto parsed = ParseAttachBot(_session, data.vbot())) { + if (bot == parsed->user) { + if (parsed->inactive) { + confirmAddToMenu(*parsed, open); } else { requestBots(); - Ui::ShowMultilineToast({ - .text = { - tr::lng_bot_menu_already_added(tr::now) }, - }); + if (!open()) { + Ui::ShowMultilineToast({ + .text = { + tr::lng_bot_menu_already_added(tr::now) }, + }); + } } } } @@ -428,6 +466,8 @@ void AttachWebView::requestAddToMenu(not_null bot) { }).fail([=] { _addToMenuId = 0; _addToMenuBot = nullptr; + _addToMenuPeer = nullptr; + _addToMenuStartCommand = QString(); Ui::ShowMultilineToast({ .text = { tr::lng_bot_menu_not_supported(tr::now) }, }); @@ -443,21 +483,15 @@ void AttachWebView::removeFromMenu(not_null bot) { } void AttachWebView::resolve() { - if (!_bot) { - requestByUsername(); - } -} - -void AttachWebView::requestByUsername() { resolveUsername(_botUsername, [=](not_null bot) { _bot = bot->asUser(); - if (!_bot || !_bot->isBot() || !_bot->botInfo->supportsAttachMenu) { + if (!_bot) { Ui::ShowMultilineToast({ .text = { tr::lng_bot_menu_not_supported(tr::now) } }); return; } - request(); + requestAddToMenu(_peer, _bot, _startCommand); }); } @@ -652,7 +686,7 @@ std::unique_ptr MakeAttachBotsMenu( const auto callback = [=] { const auto active = controller->activeChatCurrent(); if (const auto history = active.history()) { - bots->request(history->peer, bot.user); + bots->request(history->peer, bot.user, {}); } }; auto action = base::make_unique_q( diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h index 59456b1b1..7d859279b 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h @@ -35,7 +35,7 @@ namespace InlineBots { struct AttachWebViewBot { not_null user; - not_null icon; + DocumentData *icon = nullptr; std::shared_ptr media; QString name; bool inactive = false; @@ -48,13 +48,17 @@ public: struct WebViewButton { QString text; + QString startCommand; QByteArray url; }; - void request(not_null peer, const QString &botUsername); + void request( + not_null peer, + const QString &botUsername, + const QString &startCommand); void request( not_null peer, not_null bot, - const WebViewButton &button = WebViewButton()); + const WebViewButton &button); void requestSimple( not_null bot, const WebViewButton &button); @@ -69,15 +73,17 @@ public: return _attachBotsUpdates.events(); } - void requestAddToMenu(not_null bot); + void requestAddToMenu( + PeerData *peer, + not_null bot, + const QString &startCommand); void removeFromMenu(not_null bot); static void ClearAll(); private: void resolve(); - void request(const WebViewButton &button = WebViewButton()); - void requestByUsername(); + void request(const WebViewButton &button); void resolveUsername( const QString &username, Fn)> done); @@ -101,6 +107,7 @@ private: PeerData *_peer = nullptr; UserData *_bot = nullptr; QString _botUsername; + QString _startCommand; QPointer _confirmAddBox; MsgId _replyToMsgId; @@ -110,8 +117,10 @@ private: uint64 _botsHash = 0; mtpRequestId _botsRequestId = 0; + PeerData *_addToMenuPeer = nullptr; UserData *_addToMenuBot = nullptr; mtpRequestId _addToMenuId = 0; + QString _addToMenuStartCommand; std::vector _attachBots; rpl::event_stream<> _attachBotsUpdates; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp index daee5a84c..59cdd7c4d 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp @@ -311,9 +311,9 @@ bool Panel::createWebview() { } const auto list = message.array(); const auto command = list.at(0).toString(); - if (command == "webview_close") { + if (command == "web_app_close") { _close(); - } else if (command == "webview_data_send") { + } else if (command == "web_app_data_send") { auto error = QJsonParseError(); auto json = list.at(1).toString(); const auto dictionary = QJsonDocument::fromJson( diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 411850cf4..830ac0fbc 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -295,50 +295,12 @@ void SessionNavigation::showPeerByLinkResolved( }); // Then try to join the voice chat. - const auto bad = [=] { - Ui::ShowMultilineToast({ - .text = { tr::lng_group_invite_bad_link(tr::now) } - }); - }; - const auto hash = *info.voicechatHash; - _api.request(base::take(_resolveRequestId)).cancel(); - _resolveRequestId = _api.request( - MTPchannels_GetFullChannel(peer->asChannel()->inputChannel) - ).done([=](const MTPmessages_ChatFull &result) { - _session->api().processFullPeer(peer, result); - const auto call = peer->groupCall(); - if (!call) { - bad(); - return; - } - const auto join = [=] { - parentController()->startOrJoinGroupCall( - peer, - { hash, Calls::StartGroupCallArgs::JoinConfirm::Always }); - }; - if (call->loaded()) { - join(); - return; - } - const auto id = call->id(); - const auto limit = 5; - _resolveRequestId = _api.request( - MTPphone_GetGroupCall(call->input(), MTP_int(limit)) - ).done([=](const MTPphone_GroupCall &result) { - if (const auto now = peer->groupCall() - ; now && now->id() == id) { - if (!now->loaded()) { - now->processFullCall(result); - } - join(); - } else { - bad(); - } - }).fail(bad).send(); - }).send(); + joinVoiceChatFromLink(peer, info); return; } using Scope = AddBotToGroupBoxController::Scope; + const auto user = peer->asUser(); + const auto bot = (user && user->isBot()) ? user : nullptr; const auto &replies = info.repliesInfo; if (const auto threadId = std::get_if(&replies)) { showRepliesForMessage( @@ -352,8 +314,10 @@ void SessionNavigation::showPeerByLinkResolved( info.messageId, commentId->id, params); - } else if (info.messageId == ShowAtProfileMsgId && !peer->isChannel()) { - const auto user = peer->asUser(); + } else if (bot + && (info.startType == BotStartType::Group + || info.startType == BotStartType::Channel + || info.startType == BotStartType::ShareGame)) { const auto scope = (info.startType == BotStartType::ShareGame) ? Scope::ShareGame : (info.startType == BotStartType::Group) @@ -361,47 +325,111 @@ void SessionNavigation::showPeerByLinkResolved( : (info.startType == BotStartType::Channel) ? Scope::ChannelAdmin : Scope::None; - if (!user || !user->isBot()) { - showPeerInfo(peer, params); - } else if (scope != Scope::None) { - AddBotToGroupBoxController::Start( - user, - scope, - info.startToken, - info.startAdminRights); - } else { + Assert(scope != Scope::None); + + AddBotToGroupBoxController::Start( + bot, + scope, + info.startToken, + info.startAdminRights); + } else if (info.messageId == ShowAtProfileMsgId) { + if (bot) { // Always open bot chats, even from mention links. crl::on_main(this, [=] { - showPeerHistory(peer->id, params); + showPeerHistory(bot->id, params); }); + } else { + showPeerInfo(peer, params); } } else { - const auto user = peer->asUser(); - auto msgId = info.messageId; - if (msgId == ShowAtProfileMsgId || !peer->isChannel()) { - // Show specific posts only in channels / supergroups. - msgId = ShowAtUnreadMsgId; - } + // Show specific posts only in channels / supergroups. + const auto msgId = peer->isChannel() + ? info.messageId + : ShowAtUnreadMsgId; const auto attachBotUsername = info.attachBotUsername; - if (user - && user->isBot() - && user->botInfo->startToken != info.startToken) { - user->botInfo->startToken = info.startToken; - user->session().changes().peerUpdated( - user, + if (bot && bot->botInfo->startToken != info.startToken) { + bot->botInfo->startToken = info.startToken; + bot->session().changes().peerUpdated( + bot, Data::PeerUpdate::Flag::BotStartToken); } - if (user && info.attachBotToggle) { - user->session().attachWebView().requestAddToMenu(user); + if (!attachBotUsername.isEmpty()) { + crl::on_main(this, [=] { + showPeerHistory(peer->id, params, msgId); + peer->session().attachWebView().request( + peer, + attachBotUsername, + info.attachBotToggleCommand.value_or(QString())); + }); + } else if (bot && info.attachBotToggleCommand) { + const auto itemId = info.clickFromMessageId; + const auto item = _session->data().message(itemId); + const auto contextPeer = item + ? item->history()->peer.get() + : nullptr; + const auto contextUser = contextPeer + ? contextPeer->asUser() + : nullptr; + bot->session().attachWebView().requestAddToMenu( + contextUser, + bot, + *info.attachBotToggleCommand); } else { crl::on_main(this, [=] { showPeerHistory(peer->id, params, msgId); - peer->session().attachWebView().request(peer, attachBotUsername); }); } } } +void SessionNavigation::joinVoiceChatFromLink( + not_null peer, + const PeerByLinkInfo &info) { + Expects(info.voicechatHash.has_value()); + + const auto bad = [=] { + Ui::ShowMultilineToast({ + .text = { tr::lng_group_invite_bad_link(tr::now) } + }); + }; + const auto hash = *info.voicechatHash; + _api.request(base::take(_resolveRequestId)).cancel(); + _resolveRequestId = _api.request( + MTPchannels_GetFullChannel(peer->asChannel()->inputChannel) + ).done([=](const MTPmessages_ChatFull &result) { + _session->api().processFullPeer(peer, result); + const auto call = peer->groupCall(); + if (!call) { + bad(); + return; + } + const auto join = [=] { + parentController()->startOrJoinGroupCall( + peer, + { hash, Calls::StartGroupCallArgs::JoinConfirm::Always }); + }; + if (call->loaded()) { + join(); + return; + } + const auto id = call->id(); + const auto limit = 5; + _resolveRequestId = _api.request( + MTPphone_GetGroupCall(call->input(), MTP_int(limit)) + ).done([=](const MTPphone_GroupCall &result) { + if (const auto now = peer->groupCall() + ; now && now->id() == id) { + if (!now->loaded()) { + now->processFullCall(result); + } + join(); + } else { + bad(); + } + }).fail(bad).send(); + }).send(); +} + void SessionNavigation::showRepliesForMessage( not_null history, MsgId rootId, diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index e417f48ed..71c57a713 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -196,7 +196,7 @@ public: QString startToken; ChatAdminRights startAdminRights; QString attachBotUsername; - bool attachBotToggle = false; + std::optional attachBotToggleCommand; std::optional voicechatHash; FullMsgId clickFromMessageId; }; @@ -267,6 +267,9 @@ private: void showPeerByLinkResolved( not_null peer, const PeerByLinkInfo &info); + void joinVoiceChatFromLink( + not_null peer, + const PeerByLinkInfo &info); const not_null _session;