mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Implement opening of t.me/bot/app-s.
This commit is contained in:
parent
ae5f2add0e
commit
af51307aa6
21 changed files with 358 additions and 44 deletions
|
@ -444,6 +444,8 @@ PRIVATE
|
|||
data/data_audio_msg_id.h
|
||||
data/data_auto_download.cpp
|
||||
data/data_auto_download.h
|
||||
data/data_bot_app.cpp
|
||||
data/data_bot_app.h
|
||||
data/data_chat.cpp
|
||||
data/data_chat.h
|
||||
data/data_chat_filters.cpp
|
||||
|
|
|
@ -396,6 +396,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_username_available" = "This username is available.";
|
||||
"lng_username_not_found" = "User @{user} not found.";
|
||||
"lng_username_by_phone_not_found" = "User {phone} not found.";
|
||||
"lng_username_app_not_found" = "Bot application not found.";
|
||||
"lng_username_link" = "This link opens a chat with you:";
|
||||
"lng_username_copied" = "Link copied to clipboard.";
|
||||
|
||||
|
@ -1463,6 +1464,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_action_took_screenshot" = "{from} took a screenshot!";
|
||||
"lng_action_you_took_screenshot" = "You took a screenshot!";
|
||||
"lng_action_bot_allowed_from_domain" = "You allowed this bot to message you when you logged in on {domain}.";
|
||||
"lng_action_bot_allowed_from_app" = "You allowed this bot to message you when you opened {app}.";
|
||||
"lng_action_secure_values_sent" = "{user} received the following documents: {documents}";
|
||||
"lng_action_secure_personal_details" = "personal details";
|
||||
"lng_action_secure_proof_of_identity" = "proof of identity";
|
||||
|
|
|
@ -108,7 +108,11 @@ void HiddenUrlClickHandler::Open(QString url, QVariant context) {
|
|||
};
|
||||
if (url.startsWith(u"tg://"_q, Qt::CaseInsensitive)
|
||||
|| url.startsWith(u"internal:"_q, Qt::CaseInsensitive)) {
|
||||
open();
|
||||
UrlClickHandler::Open(url, QVariant::fromValue([&] {
|
||||
auto result = context.value<ClickHandlerContext>();
|
||||
result.mayShowConfirmation = !base::IsCtrlPressed();
|
||||
return result;
|
||||
}()));
|
||||
} else {
|
||||
const auto parsedUrl = QUrl::fromUserInput(url);
|
||||
if (UrlRequiresConfirmation(parsedUrl) && !base::IsCtrlPressed()) {
|
||||
|
|
|
@ -42,6 +42,7 @@ struct ClickHandlerContext {
|
|||
Fn<HistoryView::ElementDelegate*()> elementDelegate;
|
||||
base::weak_ptr<Window::SessionController> sessionWindow;
|
||||
std::shared_ptr<Ui::Show> show;
|
||||
bool mayShowConfirmation = false;
|
||||
bool skipBotAutoLogin = false;
|
||||
bool botStartAutoSubmit = false;
|
||||
// Is filled from peer info.
|
||||
|
|
|
@ -373,6 +373,8 @@ bool ResolveUsernameOrPhone(
|
|||
if (const auto postId = postParam.toInt()) {
|
||||
post = postId;
|
||||
}
|
||||
const auto appname = params.value(u"appname"_q);
|
||||
const auto appstart = params.value(u"startapp"_q);
|
||||
const auto commentParam = params.value(u"comment"_q);
|
||||
const auto commentId = commentParam.toInt();
|
||||
const auto topicParam = params.value(u"topic"_q);
|
||||
|
@ -384,6 +386,12 @@ bool ResolveUsernameOrPhone(
|
|||
startToken = gameParam;
|
||||
resolveType = ResolveType::ShareGame;
|
||||
}
|
||||
if (startToken.isEmpty() && params.contains(u"startapp"_q)) {
|
||||
startToken = params.value(u"startapp"_q);
|
||||
}
|
||||
if (!appname.isEmpty()) {
|
||||
resolveType = ResolveType::BotApp;
|
||||
}
|
||||
const auto myContext = context.value<ClickHandlerContext>();
|
||||
using Navigation = Window::SessionNavigation;
|
||||
controller->showPeerByLink(Navigation::PeerByLinkInfo{
|
||||
|
@ -403,6 +411,8 @@ bool ResolveUsernameOrPhone(
|
|||
.startToken = startToken,
|
||||
.startAdminRights = adminRights,
|
||||
.startAutoSubmit = myContext.botStartAutoSubmit,
|
||||
.botAppName = appname.isEmpty() ? postParam : appname,
|
||||
.botAppForceConfirmation = myContext.mayShowConfirmation,
|
||||
.attachBotUsername = params.value(u"attach"_q),
|
||||
.attachBotToggleCommand = (params.contains(u"startattach"_q)
|
||||
? params.value(u"startattach"_q)
|
||||
|
@ -1004,6 +1014,7 @@ QString TryConvertUrlToLocal(QString url) {
|
|||
"("
|
||||
"/?\\?|"
|
||||
"/?$|"
|
||||
"/[a-zA-Z0-9\\.\\_]+|"
|
||||
"/\\d+/?(\\?|$)|"
|
||||
"/\\d+/\\d+/?(\\?|$)"
|
||||
")"_q, query, matchOptions)) {
|
||||
|
@ -1014,6 +1025,8 @@ QString TryConvertUrlToLocal(QString url) {
|
|||
added = u"&topic=%1&post=%2"_q.arg(threadPostMatch->captured(1)).arg(threadPostMatch->captured(2));
|
||||
} else if (const auto postMatch = regex_match(u"^/(\\d+)(/?\\?|/?$)"_q, usernameMatch->captured(2))) {
|
||||
added = u"&post="_q + postMatch->captured(1);
|
||||
} else if (const auto appNameMatch = regex_match(u"^/([a-zA-Z0-9\\.\\_]+)(/?\\?|/?$)"_q, usernameMatch->captured(2))) {
|
||||
added = u"&appname="_q + appNameMatch->captured(1);
|
||||
}
|
||||
return base + added + (params.isEmpty() ? QString() : '&' + params);
|
||||
}
|
||||
|
|
13
Telegram/SourceFiles/data/data_bot_app.cpp
Normal file
13
Telegram/SourceFiles/data/data_bot_app.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
#include "data/data_bot_app.h"
|
||||
|
||||
BotAppData::BotAppData(not_null<Data::Session*> owner, const BotAppId &id)
|
||||
: owner(owner)
|
||||
, id(id) {
|
||||
}
|
27
Telegram/SourceFiles/data/data_bot_app.h
Normal file
27
Telegram/SourceFiles/data/data_bot_app.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
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 "data/data_photo.h"
|
||||
#include "data/data_document.h"
|
||||
|
||||
struct BotAppData {
|
||||
BotAppData(not_null<Data::Session*> owner, const BotAppId &id);
|
||||
|
||||
const not_null<Data::Session*> owner;
|
||||
BotAppId id = 0;
|
||||
PeerId botId = 0;
|
||||
QString shortName;
|
||||
QString title;
|
||||
QString description;
|
||||
PhotoData *photo = nullptr;
|
||||
DocumentData *document = nullptr;
|
||||
|
||||
uint64 accessHash = 0;
|
||||
uint64 hash = 0;
|
||||
};
|
|
@ -21,5 +21,4 @@ struct GameData {
|
|||
QString description;
|
||||
PhotoData *photo = nullptr;
|
||||
DocumentData *document = nullptr;
|
||||
|
||||
};
|
||||
|
|
|
@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "data/data_bot_app.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "data/data_media_types.h"
|
||||
|
@ -3450,6 +3451,45 @@ void Session::gameApplyFields(
|
|||
notifyGameUpdateDelayed(game);
|
||||
}
|
||||
|
||||
not_null<BotAppData*> Session::botApp(BotAppId id) {
|
||||
const auto i = _botApps.find(id);
|
||||
return (i != end(_botApps))
|
||||
? i->second.get()
|
||||
: _botApps.emplace(
|
||||
id,
|
||||
std::make_unique<BotAppData>(this, id)).first->second.get();
|
||||
}
|
||||
|
||||
BotAppData *Session::findBotApp(PeerId botId, const QString &appName) const {
|
||||
for (const auto &[id, app] : _botApps) {
|
||||
if (app->botId == botId && app->shortName == appName) {
|
||||
return app.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BotAppData *Session::processBotApp(
|
||||
PeerId botId,
|
||||
const MTPBotApp &data) {
|
||||
return data.match([&](const MTPDbotApp &data) {
|
||||
const auto result = botApp(data.vid().v);
|
||||
result->botId = botId;
|
||||
result->shortName = qs(data.vshort_name());
|
||||
result->title = qs(data.vtitle());
|
||||
result->description = qs(data.vdescription());
|
||||
result->photo = processPhoto(data.vphoto());
|
||||
result->document = data.vdocument()
|
||||
? processDocument(*data.vdocument()).get()
|
||||
: nullptr;
|
||||
result->accessHash = data.vaccess_hash().v;
|
||||
result->hash = data.vhash().v;
|
||||
return result.get();
|
||||
}, [](const MTPDbotAppNotModified &) {
|
||||
return (BotAppData*)nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
not_null<PollData*> Session::poll(PollId id) {
|
||||
auto i = _polls.find(id);
|
||||
if (i == _polls.cend()) {
|
||||
|
|
|
@ -570,6 +570,12 @@ public:
|
|||
not_null<GameData*> original,
|
||||
const MTPGame &data);
|
||||
|
||||
[[nodiscard]] not_null<BotAppData*> botApp(BotAppId id);
|
||||
BotAppData *findBotApp(PeerId botId, const QString &appName) const;
|
||||
BotAppData *processBotApp(
|
||||
PeerId botId,
|
||||
const MTPBotApp &data);
|
||||
|
||||
[[nodiscard]] not_null<PollData*> poll(PollId id);
|
||||
not_null<PollData*> processPoll(const MTPPoll &data);
|
||||
not_null<PollData*> processPoll(const MTPDmessageMediaPoll &data);
|
||||
|
@ -922,6 +928,9 @@ private:
|
|||
std::unordered_map<
|
||||
GameId,
|
||||
std::unique_ptr<GameData>> _games;
|
||||
std::unordered_map<
|
||||
BotAppId,
|
||||
std::unique_ptr<BotAppData>> _botApps;
|
||||
std::unordered_map<
|
||||
not_null<const GameData*>,
|
||||
base::flat_set<not_null<ViewElement*>>> _gameViews;
|
||||
|
|
|
@ -118,6 +118,7 @@ class DocumentData;
|
|||
class PhotoData;
|
||||
struct WebPageData;
|
||||
struct GameData;
|
||||
struct BotAppData;
|
||||
struct PollData;
|
||||
|
||||
using PhotoId = uint64;
|
||||
|
@ -129,6 +130,7 @@ using GameId = uint64;
|
|||
using PollId = uint64;
|
||||
using WallPaperId = uint64;
|
||||
using CallId = uint64;
|
||||
using BotAppId = uint64;
|
||||
constexpr auto CancelledWebPageId = WebPageId(0xFFFFFFFFFFFFFFFFULL);
|
||||
|
||||
struct PreparedPhotoThumb {
|
||||
|
|
|
@ -1059,6 +1059,12 @@ ServiceAction ParseServiceAction(
|
|||
result.content = content;
|
||||
}, [&](const MTPDmessageActionBotAllowed &data) {
|
||||
auto content = ActionBotAllowed();
|
||||
if (const auto app = data.vapp()) {
|
||||
app->match([&](const MTPDbotApp &data) {
|
||||
content.appId = data.vid().v;
|
||||
content.app = ParseString(data.vtitle());
|
||||
}, [](const MTPDbotAppNotModified &) {});
|
||||
}
|
||||
if (const auto domain = data.vdomain()) {
|
||||
content.domain = ParseString(*domain);
|
||||
}
|
||||
|
|
|
@ -431,6 +431,8 @@ struct ActionCustomAction {
|
|||
};
|
||||
|
||||
struct ActionBotAllowed {
|
||||
uint64 appId = 0;
|
||||
Utf8String app;
|
||||
Utf8String domain;
|
||||
bool attachMenu = false;
|
||||
};
|
||||
|
|
|
@ -1033,6 +1033,9 @@ auto HtmlWriter::Wrap::pushMessage(
|
|||
return data.attachMenu
|
||||
? "You allowed this bot to message you "
|
||||
"when you added it in the attachment menu."_q
|
||||
: data.app.isEmpty()
|
||||
? ("You allowed this bot to message you when you opened "
|
||||
+ SerializeString(data.app))
|
||||
: ("You allowed this bot to message you when you logged in on "
|
||||
+ SerializeString(data.domain));
|
||||
}, [&](const ActionSecureValuesSent &data) {
|
||||
|
|
|
@ -477,6 +477,10 @@ QByteArray SerializeMessage(
|
|||
}, [&](const ActionBotAllowed &data) {
|
||||
if (data.attachMenu) {
|
||||
pushAction("attach_menu_bot_allowed");
|
||||
} else if (data.appId) {
|
||||
pushAction("allow_sending_messages");
|
||||
push("reason_app_id", data.appId);
|
||||
push("reason_app_name", data.app);
|
||||
} else {
|
||||
pushAction("allow_sending_messages");
|
||||
push("reason_domain", data.domain);
|
||||
|
|
|
@ -49,6 +49,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_updates.h"
|
||||
#include "dialogs/ui/dialogs_message_view.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "data/data_bot_app.h"
|
||||
#include "data/data_scheduled_messages.h" // kScheduledUntilOnlineTimestamp
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_session.h"
|
||||
|
@ -3689,6 +3690,21 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
|||
result.text = {
|
||||
tr::lng_action_attach_menu_bot_allowed(tr::now)
|
||||
};
|
||||
} else if (const auto app = action.vapp()) {
|
||||
const auto bot = history()->peer->asUser();
|
||||
const auto botId = bot ? bot->id : PeerId();
|
||||
const auto info = history()->owner().processBotApp(botId, *app);
|
||||
const auto url = (bot && info)
|
||||
? history()->session().createInternalLinkFull(
|
||||
bot->username() + '/' + info->shortName)
|
||||
: QString();
|
||||
result.text = tr::lng_action_bot_allowed_from_app(
|
||||
tr::now,
|
||||
lt_app,
|
||||
(url.isEmpty()
|
||||
? TextWithEntities{ u"App"_q }
|
||||
: Ui::Text::Link(info->title, url)),
|
||||
Ui::Text::WithEntities);
|
||||
} else {
|
||||
const auto domain = qs(action.vdomain().value_or_empty());
|
||||
result.text = tr::lng_action_bot_allowed_from_domain(
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "inline_bots/bot_attach_web_view.h"
|
||||
|
||||
#include "api/api_common.h"
|
||||
#include "data/data_bot_app.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_document.h"
|
||||
|
@ -424,6 +425,7 @@ struct AttachWebView::Context {
|
|||
Dialogs::EntryState dialogsEntryState;
|
||||
Api::SendAction action;
|
||||
bool fromSwitch = false;
|
||||
bool fromBotApp = false;
|
||||
};
|
||||
|
||||
AttachWebView::AttachWebView(not_null<Main::Session*> session)
|
||||
|
@ -551,13 +553,12 @@ void AttachWebView::request(const WebViewButton &button) {
|
|||
: MTP_inputPeerEmpty())
|
||||
)).done([=](const MTPWebViewResult &result) {
|
||||
_requestId = 0;
|
||||
result.match([&](const MTPDwebViewResultUrl &data) {
|
||||
show(
|
||||
data.vquery_id().v,
|
||||
qs(data.vurl()),
|
||||
button.text,
|
||||
button.fromMenu || button.url.isEmpty());
|
||||
});
|
||||
const auto &data = result.data();
|
||||
show(
|
||||
data.vquery_id().v,
|
||||
qs(data.vurl()),
|
||||
button.text,
|
||||
button.fromMenu || button.url.isEmpty());
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_requestId = 0;
|
||||
if (error.type() == u"BOT_INVALID"_q) {
|
||||
|
@ -573,7 +574,9 @@ void AttachWebView::cancel() {
|
|||
_panel = nullptr;
|
||||
_context = nullptr;
|
||||
_bot = nullptr;
|
||||
_app = nullptr;
|
||||
_botUsername = QString();
|
||||
_botAppName = QString();
|
||||
_startCommand = QString();
|
||||
}
|
||||
|
||||
|
@ -624,9 +627,7 @@ void AttachWebView::requestAddToMenu(
|
|||
Expects(controller != nullptr || _context != nullptr);
|
||||
|
||||
if (!bot->isBot() || !bot->botInfo->supportsAttachMenu) {
|
||||
Ui::ShowMultilineToast({
|
||||
.text = { tr::lng_bot_menu_not_supported(tr::now) },
|
||||
});
|
||||
showToast(tr::lng_bot_menu_not_supported(tr::now), controller);
|
||||
return;
|
||||
}
|
||||
const auto wasController = (controller != nullptr);
|
||||
|
@ -694,10 +695,8 @@ void AttachWebView::requestAddToMenu(
|
|||
} else {
|
||||
requestBots();
|
||||
if (!open(types)) {
|
||||
Ui::ShowMultilineToast({
|
||||
.text = {
|
||||
tr::lng_bot_menu_already_added(tr::now) },
|
||||
});
|
||||
showToast(
|
||||
tr::lng_bot_menu_already_added(tr::now));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -708,17 +707,13 @@ void AttachWebView::requestAddToMenu(
|
|||
_addToMenuBot = nullptr;
|
||||
_addToMenuContext = nullptr;
|
||||
_addToMenuStartCommand = QString();
|
||||
Ui::ShowMultilineToast({
|
||||
.text = { tr::lng_bot_menu_not_supported(tr::now) },
|
||||
});
|
||||
showToast(tr::lng_bot_menu_not_supported(tr::now));
|
||||
}).send();
|
||||
}
|
||||
|
||||
void AttachWebView::removeFromMenu(not_null<UserData*> bot) {
|
||||
toggleInMenu(bot, ToggledState::Removed, [=] {
|
||||
Ui::ShowMultilineToast({
|
||||
.text = { tr::lng_bot_remove_from_menu_done(tr::now) },
|
||||
});
|
||||
showToast(tr::lng_bot_remove_from_menu_done(tr::now));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -729,9 +724,7 @@ void AttachWebView::resolve() {
|
|||
}
|
||||
_bot = bot->asUser();
|
||||
if (!_bot) {
|
||||
Ui::ShowMultilineToast({
|
||||
.text = { tr::lng_bot_menu_not_supported(tr::now) }
|
||||
});
|
||||
showToast(tr::lng_bot_menu_not_supported(tr::now));
|
||||
return;
|
||||
}
|
||||
requestAddToMenu(_bot, _startCommand);
|
||||
|
@ -760,11 +753,8 @@ void AttachWebView::resolveUsername(
|
|||
}).fail([=](const MTP::Error &error) {
|
||||
_requestId = 0;
|
||||
if (error.code() == 400) {
|
||||
Ui::ShowMultilineToast({
|
||||
.text = {
|
||||
tr::lng_username_not_found(tr::now, lt_user, username),
|
||||
},
|
||||
});
|
||||
showToast(
|
||||
tr::lng_username_not_found(tr::now, lt_user, username));
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
@ -838,9 +828,8 @@ void AttachWebView::requestMenu(
|
|||
: MTP_inputPeerEmpty())
|
||||
)).done([=](const MTPWebViewResult &result) {
|
||||
_requestId = 0;
|
||||
result.match([&](const MTPDwebViewResultUrl &data) {
|
||||
show(data.vquery_id().v, qs(data.vurl()), text);
|
||||
});
|
||||
const auto &data = result.data();
|
||||
show(data.vquery_id().v, qs(data.vurl()), text);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_requestId = 0;
|
||||
if (error.type() == u"BOT_INVALID"_q) {
|
||||
|
@ -850,6 +839,129 @@ void AttachWebView::requestMenu(
|
|||
});
|
||||
}
|
||||
|
||||
void AttachWebView::requestApp(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const Api::SendAction &action,
|
||||
not_null<UserData*> bot,
|
||||
const QString &appName,
|
||||
const QString &startParam,
|
||||
bool forceConfirmation) {
|
||||
const auto context = LookupContext(controller, action);
|
||||
if (_requestId
|
||||
&& _bot == bot
|
||||
&& _startCommand == startParam
|
||||
&& _botAppName == appName
|
||||
&& IsSame(_context, context)) {
|
||||
return;
|
||||
}
|
||||
cancel();
|
||||
_bot = bot;
|
||||
_startCommand = startParam;
|
||||
_botAppName = appName;
|
||||
_context = std::make_unique<Context>(context);
|
||||
_context->fromBotApp = true;
|
||||
const auto already = _session->data().findBotApp(_bot->id, appName);
|
||||
_requestId = _session->api().request(MTPmessages_GetBotApp(
|
||||
MTP_inputBotAppShortName(
|
||||
bot->inputUser,
|
||||
MTP_string(appName)),
|
||||
MTP_long(already ? already->hash : 0)
|
||||
)).done([=](const MTPmessages_BotApp &result) {
|
||||
_requestId = 0;
|
||||
if (!_bot || !_context) {
|
||||
return;
|
||||
}
|
||||
const auto &data = result.data();
|
||||
const auto firstTime = data.is_inactive();
|
||||
const auto received = _session->data().processBotApp(
|
||||
_bot->id,
|
||||
data.vapp());
|
||||
_app = received ? received : already;
|
||||
if (!_app) {
|
||||
cancel();
|
||||
showToast(tr::lng_username_app_not_found(tr::now));
|
||||
return;
|
||||
}
|
||||
const auto confirm = firstTime || forceConfirmation;
|
||||
if (confirm) {
|
||||
confirmAppOpen(result.data().is_request_write_access());
|
||||
} else {
|
||||
requestAppView(false);
|
||||
}
|
||||
}).fail([=] {
|
||||
cancel();
|
||||
showToast(tr::lng_username_app_not_found(tr::now));
|
||||
}).send();
|
||||
}
|
||||
|
||||
void AttachWebView::confirmAppOpen(bool requestWriteAccess) {
|
||||
const auto controller = _context ? _context->controller.get() : nullptr;
|
||||
if (!controller || !_bot) {
|
||||
return;
|
||||
}
|
||||
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
const auto allowed = std::make_shared<Ui::Checkbox*>();
|
||||
const auto done = [=](Fn<void()> close) {
|
||||
requestAppView((*allowed) && (*allowed)->checked());
|
||||
close();
|
||||
};
|
||||
Ui::ConfirmBox(box, {
|
||||
tr::lng_allow_bot_webview(
|
||||
tr::now,
|
||||
lt_bot_name,
|
||||
Ui::Text::Bold(_bot->name()),
|
||||
Ui::Text::RichLangValue),
|
||||
done,
|
||||
});
|
||||
if (requestWriteAccess) {
|
||||
(*allowed) = box->addRow(
|
||||
object_ptr<Ui::Checkbox>(
|
||||
box,
|
||||
tr::lng_url_auth_allow_messages(
|
||||
tr::now,
|
||||
lt_bot,
|
||||
Ui::Text::Bold(_bot->name()),
|
||||
Ui::Text::WithEntities),
|
||||
true,
|
||||
st::urlAuthCheckbox),
|
||||
style::margins(
|
||||
st::boxRowPadding.left(),
|
||||
st::boxPhotoCaptionSkip,
|
||||
st::boxRowPadding.right(),
|
||||
st::boxPhotoCaptionSkip));
|
||||
(*allowed)->setAllowTextLines();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void AttachWebView::requestAppView(bool allowWrite) {
|
||||
if (!_context || !_app) {
|
||||
return;
|
||||
}
|
||||
using Flag = MTPmessages_RequestAppWebView::Flag;
|
||||
const auto flags = Flag::f_theme_params
|
||||
| (_startCommand.isEmpty() ? Flag(0) : Flag::f_start_param)
|
||||
| (allowWrite ? Flag::f_write_allowed : Flag(0));
|
||||
_requestId = _session->api().request(MTPmessages_RequestAppWebView(
|
||||
MTP_flags(flags),
|
||||
_context->action.history->peer->input,
|
||||
MTP_inputBotAppID(MTP_long(_app->id), MTP_long(_app->accessHash)),
|
||||
MTP_string(_startCommand),
|
||||
MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams().json)),
|
||||
MTP_string("tdesktop")
|
||||
)).done([=](const MTPAppWebViewResult &result) {
|
||||
_requestId = 0;
|
||||
const auto &data = result.data();
|
||||
const auto queryId = uint64();
|
||||
show(queryId, qs(data.vurl()));
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_requestId = 0;
|
||||
if (error.type() == u"BOT_INVALID"_q) {
|
||||
requestBots();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void AttachWebView::confirmOpen(
|
||||
not_null<Window::SessionController*> controller,
|
||||
Fn<void()> done) {
|
||||
|
@ -895,6 +1007,7 @@ void AttachWebView::show(
|
|||
const auto sendData = crl::guard(this, [=](QByteArray data) {
|
||||
if (!_context
|
||||
|| _context->fromSwitch
|
||||
|| _context->fromBotApp
|
||||
|| _context->action.history->peer != _bot
|
||||
|| queryId) {
|
||||
return;
|
||||
|
@ -1061,7 +1174,7 @@ void AttachWebView::show(
|
|||
void AttachWebView::started(uint64 queryId) {
|
||||
Expects(_bot != nullptr && _context != nullptr);
|
||||
|
||||
if (_context->fromSwitch) {
|
||||
if (_context->fromSwitch || !queryId) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1098,6 +1211,24 @@ void AttachWebView::started(uint64 queryId) {
|
|||
}, _panel->lifetime());
|
||||
}
|
||||
|
||||
void AttachWebView::showToast(
|
||||
const QString &text,
|
||||
Window::SessionController *controller) {
|
||||
const auto strong = controller
|
||||
? controller
|
||||
: _context
|
||||
? _context->controller.get()
|
||||
: _addToMenuContext
|
||||
? _addToMenuContext->controller.get()
|
||||
: nullptr;
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = (strong
|
||||
? Window::Show(strong).toastParent().get()
|
||||
: nullptr),
|
||||
.text = { text },
|
||||
});
|
||||
}
|
||||
|
||||
void AttachWebView::confirmAddToMenu(
|
||||
AttachWebViewBot bot,
|
||||
Fn<void()> callback) {
|
||||
|
@ -1115,9 +1246,7 @@ void AttachWebView::confirmAddToMenu(
|
|||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
Ui::ShowMultilineToast({
|
||||
.text = { tr::lng_bot_add_to_menu_done(tr::now) },
|
||||
});
|
||||
showToast(tr::lng_bot_add_to_menu_done(tr::now));
|
||||
});
|
||||
close();
|
||||
};
|
||||
|
|
|
@ -93,6 +93,13 @@ public:
|
|||
void requestMenu(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<UserData*> bot);
|
||||
void requestApp(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const Api::SendAction &action,
|
||||
not_null<UserData*> bot,
|
||||
const QString &appName,
|
||||
const QString &startParam,
|
||||
bool forceConfirmation);
|
||||
|
||||
void cancel();
|
||||
|
||||
|
@ -162,14 +169,22 @@ private:
|
|||
void confirmAddToMenu(
|
||||
AttachWebViewBot bot,
|
||||
Fn<void()> callback = nullptr);
|
||||
void confirmAppOpen(bool requestWriteAccess);
|
||||
void requestAppView(bool allowWrite);
|
||||
void started(uint64 queryId);
|
||||
|
||||
void showToast(
|
||||
const QString &text,
|
||||
Window::SessionController *controller = nullptr);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
std::unique_ptr<Context> _context;
|
||||
UserData *_bot = nullptr;
|
||||
QString _botUsername;
|
||||
QString _botAppName;
|
||||
QString _startCommand;
|
||||
BotAppData *_app = nullptr;
|
||||
QPointer<Ui::GenericBox> _confirmAddBox;
|
||||
|
||||
mtpRequestId _requestId = 0;
|
||||
|
|
|
@ -447,7 +447,7 @@ void Inner::refreshMosaicOffset() {
|
|||
const auto top = _switchPmButton
|
||||
? (_switchPmButton->height() + st::inlineResultsSkip)
|
||||
: 0;
|
||||
_mosaic.setPadding(st::gifsPadding + QMargins(0, top, 0, 0));
|
||||
_mosaic.setPadding(st::emojiPanMargins + QMargins(0, top, 0, 0));
|
||||
}
|
||||
|
||||
void Inner::refreshSwitchPmButton(const CacheEntry *entry) {
|
||||
|
|
|
@ -357,6 +357,15 @@ void SessionNavigation::showPeerByLinkResolved(
|
|||
using Scope = AddBotToGroupBoxController::Scope;
|
||||
const auto user = peer->asUser();
|
||||
const auto bot = (user && user->isBot()) ? user : nullptr;
|
||||
|
||||
// t.me/username/012345 - we thought it was a channel post link, but
|
||||
// after resolving the username we found out it is a bot.
|
||||
const auto resolveType = (bot
|
||||
&& !info.botAppName.isEmpty()
|
||||
&& info.resolveType == ResolveType::Default)
|
||||
? ResolveType::BotApp
|
||||
: info.resolveType;
|
||||
|
||||
const auto &replies = info.repliesInfo;
|
||||
if (const auto threadId = std::get_if<ThreadId>(&replies)) {
|
||||
showRepliesForMessage(
|
||||
|
@ -389,14 +398,29 @@ void SessionNavigation::showPeerByLinkResolved(
|
|||
info.messageId,
|
||||
callback);
|
||||
}
|
||||
} else if (bot && info.resolveType == ResolveType::ShareGame) {
|
||||
} else if (bot && resolveType == ResolveType::BotApp) {
|
||||
const auto itemId = info.clickFromMessageId;
|
||||
const auto item = _session->data().message(itemId);
|
||||
const auto contextPeer = item
|
||||
? item->history()->peer
|
||||
: bot;
|
||||
crl::on_main(this, [=] {
|
||||
bot->session().attachWebView().requestApp(
|
||||
parentController(),
|
||||
Api::SendAction(bot->owner().history(contextPeer)),
|
||||
bot,
|
||||
info.botAppName,
|
||||
info.startToken,
|
||||
info.botAppForceConfirmation);
|
||||
});
|
||||
} else if (bot && resolveType == ResolveType::ShareGame) {
|
||||
Window::ShowShareGameBox(parentController(), bot, info.startToken);
|
||||
} else if (bot
|
||||
&& (info.resolveType == ResolveType::AddToGroup
|
||||
|| info.resolveType == ResolveType::AddToChannel)) {
|
||||
const auto scope = (info.resolveType == ResolveType::AddToGroup)
|
||||
&& (resolveType == ResolveType::AddToGroup
|
||||
|| resolveType == ResolveType::AddToChannel)) {
|
||||
const auto scope = (resolveType == ResolveType::AddToGroup)
|
||||
? (info.startAdminRights ? Scope::GroupAdmin : Scope::All)
|
||||
: (info.resolveType == ResolveType::AddToChannel)
|
||||
: (resolveType == ResolveType::AddToChannel)
|
||||
? Scope::ChannelAdmin
|
||||
: Scope::None;
|
||||
Assert(scope != Scope::None);
|
||||
|
@ -407,7 +431,7 @@ void SessionNavigation::showPeerByLinkResolved(
|
|||
scope,
|
||||
info.startToken,
|
||||
info.startAdminRights);
|
||||
} else if (info.resolveType == ResolveType::Mention) {
|
||||
} else if (resolveType == ResolveType::Mention) {
|
||||
if (bot || peer->isChannel()) {
|
||||
crl::on_main(this, [=] {
|
||||
showPeerHistory(peer, params);
|
||||
|
|
|
@ -97,6 +97,7 @@ inline constexpr bool is_flag_type(GifPauseReason) { return true; };
|
|||
|
||||
enum class ResolveType {
|
||||
Default,
|
||||
BotApp,
|
||||
BotStart,
|
||||
AddToGroup,
|
||||
AddToChannel,
|
||||
|
@ -208,6 +209,8 @@ public:
|
|||
QString startToken;
|
||||
ChatAdminRights startAdminRights;
|
||||
bool startAutoSubmit = false;
|
||||
QString botAppName;
|
||||
bool botAppForceConfirmation = false;
|
||||
QString attachBotUsername;
|
||||
std::optional<QString> attachBotToggleCommand;
|
||||
InlineBots::PeerTypes attachBotChooseTypes;
|
||||
|
|
Loading…
Add table
Reference in a new issue