Implement opening of t.me/bot/app-s.

This commit is contained in:
John Preston 2023-03-03 12:35:49 +04:00 committed by 23rd
parent ae5f2add0e
commit af51307aa6
21 changed files with 358 additions and 44 deletions

View File

@ -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

View File

@ -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";

View File

@ -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()) {

View File

@ -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.

View File

@ -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);
}

View 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) {
}

View 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;
};

View File

@ -21,5 +21,4 @@ struct GameData {
QString description;
PhotoData *photo = nullptr;
DocumentData *document = nullptr;
};

View File

@ -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()) {

View File

@ -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;

View File

@ -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 {

View File

@ -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);
}

View File

@ -431,6 +431,8 @@ struct ActionCustomAction {
};
struct ActionBotAllowed {
uint64 appId = 0;
Utf8String app;
Utf8String domain;
bool attachMenu = false;
};

View File

@ -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) {

View File

@ -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);

View File

@ -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(

View File

@ -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();
};

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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;