/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once #include "base/flags.h" #include "base/timer.h" #include "base/weak_ptr.h" #include "dialogs/dialogs_key.h" #include "api/api_common.h" #include "mtproto/sender.h" #include "ui/chat/attach/attach_bot_webview.h" #include "ui/rp_widget.h" namespace Data { class Thread; } // namespace Data namespace Ui { class Show; class GenericBox; class DropdownMenu; } // namespace Ui namespace Ui::BotWebView { class Panel; } // namespace Ui::BotWebView namespace Main { class Session; class SessionShow; } // namespace Main namespace Window { class SessionController; } // namespace Window namespace Data { class DocumentMedia; } // namespace Data namespace Payments { struct NonPanelPaymentForm; enum class CheckoutResult; } // namespace Payments namespace InlineBots { class WebViewInstance; enum class PeerType : uint8 { SameBot = 0x01, Bot = 0x02, User = 0x04, Group = 0x08, Broadcast = 0x10, }; using PeerTypes = base::flags; [[nodiscard]] bool PeerMatchesTypes( not_null peer, not_null bot, PeerTypes types); [[nodiscard]] PeerTypes ParseChooseTypes(QStringView choose); struct AttachWebViewBot { not_null user; DocumentData *icon = nullptr; std::shared_ptr media; QString name; PeerTypes types = 0; bool inactive : 1 = false; bool inMainMenu : 1 = false; bool inAttachMenu : 1 = false; bool disclaimerRequired : 1 = false; bool requestWriteAccess : 1 = false; }; struct AddToMenuOpenAttach { QString startCommand; PeerTypes chooseTypes; }; struct AddToMenuOpenMenu { QString startCommand; }; struct AddToMenuOpenApp { not_null app; QString startCommand; }; struct AddToMenuOpen : std::variant< AddToMenuOpenAttach, AddToMenuOpenMenu, AddToMenuOpenApp> { using variant::variant; }; struct WebViewSourceButton { bool simple = false; friend inline bool operator==( WebViewSourceButton, WebViewSourceButton) = default; }; struct WebViewSourceSwitch { friend inline bool operator==( const WebViewSourceSwitch &, const WebViewSourceSwitch &) = default; }; struct WebViewSourceLinkApp { // t.me/botusername/appname base::weak_ptr from; QString appname; QString token; friend inline bool operator==( const WebViewSourceLinkApp &, const WebViewSourceLinkApp &) = default; }; struct WebViewSourceLinkAttachMenu { // ?startattach base::weak_ptr from; base::weak_ptr thread; PeerTypes choose; QString token; friend inline bool operator==( const WebViewSourceLinkAttachMenu &, const WebViewSourceLinkAttachMenu &) = default; }; struct WebViewSourceLinkBotProfile { // t.me/botusername?startapp base::weak_ptr from; QString token; bool compact = false; friend inline bool operator==( const WebViewSourceLinkBotProfile &, const WebViewSourceLinkBotProfile &) = default; }; struct WebViewSourceMainMenu { friend inline bool operator==( WebViewSourceMainMenu, WebViewSourceMainMenu) = default; }; struct WebViewSourceAttachMenu { base::weak_ptr thread; friend inline bool operator==( const WebViewSourceAttachMenu &, const WebViewSourceAttachMenu &) = default; }; struct WebViewSourceBotMenu { friend inline bool operator==( WebViewSourceBotMenu, WebViewSourceBotMenu) = default; }; struct WebViewSourceGame { FullMsgId messageId; QString title; friend inline bool operator==( WebViewSourceGame, WebViewSourceGame) = default; }; struct WebViewSourceBotProfile { friend inline bool operator==( WebViewSourceBotProfile, WebViewSourceBotProfile) = default; }; struct WebViewSource : std::variant< WebViewSourceButton, WebViewSourceSwitch, WebViewSourceLinkApp, WebViewSourceLinkAttachMenu, WebViewSourceLinkBotProfile, WebViewSourceMainMenu, WebViewSourceAttachMenu, WebViewSourceBotMenu, WebViewSourceGame, WebViewSourceBotProfile> { using variant::variant; }; struct WebViewButton { QString text; QString startCommand; QByteArray url; bool fromAttachMenu = false; bool fromMainMenu = false; bool fromSwitch = false; }; struct WebViewContext { base::weak_ptr controller; Dialogs::EntryState dialogsEntryState; std::optional action; bool maySkipConfirmation = false; }; struct WebViewDescriptor { not_null bot; std::shared_ptr parentShow; WebViewContext context; WebViewButton button; WebViewSource source; }; class WebViewInstance final : public base::has_weak_ptr , public Ui::BotWebView::Delegate { public: explicit WebViewInstance(WebViewDescriptor &&descriptor); ~WebViewInstance(); [[nodiscard]] Main::Session &session() const; [[nodiscard]] not_null bot() const; [[nodiscard]] WebViewSource source() const; void activate(); void close(); [[nodiscard]] std::shared_ptr uiShow(); private: void resolve(); bool openAppFromBotMenuLink(); void requestButton(); void requestSimple(); void requestMain(); void requestApp(bool allowWrite); void requestWithMainMenuDisclaimer(); void requestWithMenuAdd(); void maybeChooseAndRequestButton(PeerTypes supported); void resolveApp( const QString &appname, const QString &startparam, bool forceConfirmation); void confirmOpen(Fn done); void confirmAppOpen(bool writeAccess, Fn done); void show(const QString &url, uint64 queryId = 0); void showGame(); void started(uint64 queryId); auto nonPanelPaymentFormFactory( Fn reactivate) -> Fn; Webview::ThemeParams botThemeParams() override; bool botHandleLocalUri(QString uri, bool keepOpen) override; void botHandleInvoice(QString slug) override; void botHandleMenuButton(Ui::BotWebView::MenuButton button) override; bool botValidateExternalLink(QString uri) override; void botOpenIvLink(QString uri) override; void botSendData(QByteArray data) override; void botSwitchInlineQuery( std::vector chatTypes, QString query) override; void botCheckWriteAccess(Fn callback) override; void botAllowWriteAccess(Fn callback) override; void botSharePhone(Fn callback) override; void botInvokeCustomMethod( Ui::BotWebView::CustomMethodRequest request) override; void botOpenPrivacyPolicy() override; void botClose() override; const std::shared_ptr _parentShow; const not_null _session; const not_null _bot; const WebViewContext _context; const WebViewButton _button; const WebViewSource _source; BotAppData *_app = nullptr; QString _appStartParam; bool _dataSent = false; mtpRequestId _requestId = 0; mtpRequestId _prolongId = 0; QString _panelUrl; std::unique_ptr _panel; static base::weak_ptr PendingActivation; }; class AttachWebView final : public base::has_weak_ptr { public: explicit AttachWebView(not_null session); ~AttachWebView(); void open(WebViewDescriptor &&descriptor); void openByUsername( not_null controller, const Api::SendAction &action, const QString &botUsername, const QString &startCommand); void cancel(); void requestBots(Fn callback = nullptr); [[nodiscard]] const std::vector &attachBots() const { return _attachBots; } [[nodiscard]] rpl::producer<> attachBotsUpdates() const { return _attachBotsUpdates.events(); } void notifyBotIconLoaded() { _attachBotsUpdates.fire({}); } [[nodiscard]] bool disclaimerAccepted( const AttachWebViewBot &bot) const; [[nodiscard]] bool showMainMenuNewBadge( const AttachWebViewBot &bot) const; void removeFromMenu( std::shared_ptr show, not_null bot); enum class AddToMenuResult { AlreadyInMenu, Added, Unsupported, Cancelled, }; void requestAddToMenu( not_null bot, Fn done); void acceptMainMenuDisclaimer( std::shared_ptr show, not_null bot, Fn done); void close(not_null instance); void closeAll(); void loadPopularAppBots(); [[nodiscard]] auto popularAppBots() const -> const std::vector> &; [[nodiscard]] rpl::producer<> popularAppBotsLoaded() const; private: void resolveUsername( std::shared_ptr show, Fn)> done); enum class ToggledState { Removed, Added, AllowedToWrite, }; void toggleInMenu( not_null bot, ToggledState state, Fn callback = nullptr); void confirmAddToMenu( AttachWebViewBot bot, Fn callback = nullptr); const not_null _session; base::Timer _refreshTimer; QString _botUsername; QString _startCommand; mtpRequestId _requestId = 0; uint64 _botsHash = 0; mtpRequestId _botsRequestId = 0; std::vector> _botsRequestCallbacks; struct AddToMenuProcess { mtpRequestId requestId = 0; std::vector> done; }; base::flat_map, AddToMenuProcess> _addToMenu; std::vector _attachBots; rpl::event_stream<> _attachBotsUpdates; base::flat_set> _disclaimerAccepted; std::vector> _instances; std::vector> _popularAppBots; mtpRequestId _popularAppBotsRequestId = 0; rpl::variable _popularAppBotsLoaded = false; }; [[nodiscard]] std::unique_ptr MakeAttachBotsMenu( not_null parent, not_null controller, not_null peer, Fn actionFactory, Fn attach); class MenuBotIcon final : public Ui::RpWidget { public: MenuBotIcon( QWidget *parent, std::shared_ptr media); private: void paintEvent(QPaintEvent *e) override; void validate(); std::shared_ptr _media; QImage _image; QImage _mask; }; } // namespace InlineBots