Add bot to group / channel by link with rights.

This commit is contained in:
John Preston 2022-03-24 13:46:44 +04:00
parent 649f2908e8
commit 468917a91a
11 changed files with 302 additions and 102 deletions

View file

@ -3526,9 +3526,11 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
finishForwarding(action); finishForwarding(action);
} }
void ApiWrap::sendBotStart(not_null<UserData*> bot, PeerData *chat) { void ApiWrap::sendBotStart(
not_null<UserData*> bot,
PeerData *chat,
const QString &startTokenForChat) {
Expects(bot->isBot()); Expects(bot->isBot());
Expects(chat == nullptr || !bot->botInfo->startGroupToken.isEmpty());
if (chat && chat->isChannel() && !chat->isMegagroup()) { if (chat && chat->isChannel() && !chat->isMegagroup()) {
ShowAddParticipantsError("USER_BOT", chat, { 1, bot }); ShowAddParticipantsError("USER_BOT", chat, { 1, bot });
@ -3536,20 +3538,28 @@ void ApiWrap::sendBotStart(not_null<UserData*> bot, PeerData *chat) {
} }
auto &info = bot->botInfo; auto &info = bot->botInfo;
auto &token = chat ? info->startGroupToken : info->startToken; auto &token = chat ? startTokenForChat : info->startToken;
if (token.isEmpty()) { if (token.isEmpty()) {
auto message = MessageToSend( auto message = MessageToSend(
Api::SendAction(_session->data().history(bot))); Api::SendAction(_session->data().history(chat
message.textWithTags = { qsl("/start"), TextWithTags::Tags() }; ? chat
: bot.get())));
message.textWithTags = { u"/start"_q, TextWithTags::Tags() };
if (chat) {
message.textWithTags.text += '@' + bot->username;
}
sendMessage(std::move(message)); sendMessage(std::move(message));
return; return;
} }
const auto randomId = base::RandomValue<uint64>(); const auto randomId = base::RandomValue<uint64>();
if (!chat) {
info->startToken = QString();
}
request(MTPmessages_StartBot( request(MTPmessages_StartBot(
bot->inputUser, bot->inputUser,
chat ? chat->input : MTP_inputPeerEmpty(), chat ? chat->input : MTP_inputPeerEmpty(),
MTP_long(randomId), MTP_long(randomId),
MTP_string(base::take(token)) MTP_string(token)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
applyUpdates(result); applyUpdates(result);
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {

View file

@ -320,7 +320,10 @@ public:
void cancelLocalItem(not_null<HistoryItem*> item); void cancelLocalItem(not_null<HistoryItem*> item);
void sendMessage(MessageToSend &&message); void sendMessage(MessageToSend &&message);
void sendBotStart(not_null<UserData*> bot, PeerData *chat = nullptr); void sendBotStart(
not_null<UserData*> bot,
PeerData *chat = nullptr,
const QString &startTokenForChat = QString());
void sendInlineResult( void sendInlineResult(
not_null<UserData*> bot, not_null<UserData*> bot,
not_null<InlineBots::Result*> data, not_null<InlineBots::Result*> data,

View file

@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_participants_box.h"
#include "boxes/filters/edit_filter_chats_list.h" #include "boxes/filters/edit_filter_chats_list.h"
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "base/random.h" #include "base/random.h"
#include "base/weak_ptr.h" #include "base/weak_ptr.h"
#include "api/api_chat_participants.h" #include "api/api_chat_participants.h"
@ -54,7 +56,10 @@ private:
}; };
void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) { void ShareBotGame(
not_null<UserData*> bot,
not_null<PeerData*> chat,
const QString &shortName) {
const auto history = chat->owner().history(chat); const auto history = chat->owner().history(chat);
auto &histories = history->owner().histories(); auto &histories = history->owner().histories();
const auto requestType = Data::Histories::RequestType::Send; const auto requestType = Data::Histories::RequestType::Send;
@ -68,7 +73,7 @@ void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) {
MTP_inputMediaGame( MTP_inputMediaGame(
MTP_inputGameShortName( MTP_inputGameShortName(
bot->inputUser, bot->inputUser,
MTP_string(bot->botInfo->shareGameShortName))), MTP_string(shortName))),
MTP_string(), MTP_string(),
MTP_long(randomId), MTP_long(randomId),
MTPReplyMarkup(), MTPReplyMarkup(),
@ -138,23 +143,40 @@ void Controller::addRow(not_null<PeerData*> peer) {
} // namespace } // namespace
void AddBotToGroupBoxController::Start(not_null<UserData*> bot) { void AddBotToGroupBoxController::Start(
not_null<UserData*> bot,
Scope scope,
const QString &token,
ChatAdminRights requestedRights) {
auto initBox = [=](not_null<PeerListBox*> box) { auto initBox = [=](not_null<PeerListBox*> box) {
box->addButton(tr::lng_cancel(), [box] { box->closeBox(); }); box->addButton(tr::lng_cancel(), [box] { box->closeBox(); });
}; };
Ui::show(Box<PeerListBox>( Ui::show(Box<PeerListBox>(
std::make_unique<AddBotToGroupBoxController>(bot), std::make_unique<AddBotToGroupBoxController>(
bot,
scope,
token,
requestedRights),
std::move(initBox))); std::move(initBox)));
} }
AddBotToGroupBoxController::AddBotToGroupBoxController( AddBotToGroupBoxController::AddBotToGroupBoxController(
not_null<UserData*> bot) not_null<UserData*> bot,
: ChatsListBoxController(SharingBotGame(bot) Scope scope,
const QString &token,
ChatAdminRights requestedRights)
: ChatsListBoxController((scope == Scope::ShareGame)
? std::make_unique<PeerListGlobalSearchController>(&bot->session()) ? std::make_unique<PeerListGlobalSearchController>(&bot->session())
: nullptr) : nullptr)
, _bot(bot) , _bot(bot)
, _adminToGroup(_bot->botInfo->groupAdminRights != 0) , _scope(scope)
, _adminToChannel(_bot->botInfo->channelAdminRights != 0) { , _token(token)
, _requestedRights(requestedRights)
, _adminToGroup((scope == Scope::GroupAdmin)
|| (scope == Scope::All && _bot->botInfo->groupAdminRights != 0))
, _adminToChannel((scope == Scope::ChannelAdmin)
|| (scope == Scope::All && _bot->botInfo->channelAdminRights != 0))
, _memberToGroup(scope == Scope::All) {
} }
Main::Session &AddBotToGroupBoxController::session() const { Main::Session &AddBotToGroupBoxController::session() const {
@ -170,8 +192,8 @@ void AddBotToGroupBoxController::rowClicked(not_null<PeerListRow*> row) {
} }
void AddBotToGroupBoxController::shareBotGame(not_null<PeerData*> chat) { void AddBotToGroupBoxController::shareBotGame(not_null<PeerData*> chat) {
auto send = crl::guard(this, [bot = _bot, chat] { auto send = crl::guard(this, [bot = _bot, chat, token = _token] {
ShareBotGame(bot, chat); ShareBotGame(bot, chat, token);
}); });
auto confirmText = [chat] { auto confirmText = [chat] {
if (chat->isUser()) { if (chat->isUser()) {
@ -187,6 +209,34 @@ void AddBotToGroupBoxController::shareBotGame(not_null<PeerData*> chat) {
Ui::LayerOption::KeepOther); Ui::LayerOption::KeepOther);
} }
void AddBotToGroupBoxController::requestExistingRights(
not_null<ChannelData*> channel) {
if (_existingRightsChannel == channel) {
return;
}
_existingRightsChannel = channel;
_bot->session().api().request(_existingRightsRequestId).cancel();
_existingRightsRequestId = _bot->session().api().request(
MTPchannels_GetParticipant(
_existingRightsChannel->inputChannel,
_bot->input)
).done([=](const MTPchannels_ChannelParticipant &result) {
result.match([&](const MTPDchannels_channelParticipant &data) {
channel->owner().processUsers(data.vusers());
const auto participant = Api::ChatParticipant(
data.vparticipant(),
channel);
_existingRights = participant.rights().flags;
_existingRank = participant.rank();
addBotToGroup(_existingRightsChannel);
});
}).fail([=] {
_existingRights = ChatAdminRights();
_existingRank = QString();
addBotToGroup(_existingRightsChannel);
}).send();
}
void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) { void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) {
if (const auto megagroup = chat->asMegagroup()) { if (const auto megagroup = chat->asMegagroup()) {
if (!megagroup->canAddMembers()) { if (!megagroup->canAddMembers()) {
@ -196,38 +246,66 @@ void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) {
return; return;
} }
} }
if (_existingRightsChannel != chat) {
_existingRights = {};
_existingRank = QString();
_existingRightsChannel = nullptr;
_bot->session().api().request(_existingRightsRequestId).cancel();
}
const auto requestedAddAdmin = (_scope == Scope::GroupAdmin)
|| (_scope == Scope::ChannelAdmin);
if (chat->isChannel()
&& requestedAddAdmin
&& !_existingRights.has_value()) {
requestExistingRights(chat->asChannel());
return;
}
const auto bot = _bot; const auto bot = _bot;
const auto close = [=](auto&&...) { const auto close = [=](auto&&...) {
Ui::hideLayer(); Ui::hideLayer();
Ui::showPeerHistory(chat, ShowAtUnreadMsgId); Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
}; };
const auto rights = (chat->isBroadcast() const auto rights = requestedAddAdmin
&& chat->asBroadcast()->canAddAdmins()) ? _requestedRights
: (chat->isBroadcast()
&& chat->asBroadcast()->canAddAdmins())
? bot->botInfo->channelAdminRights ? bot->botInfo->channelAdminRights
: ((chat->isMegagroup() && chat->asMegagroup()->canAddAdmins()) : ((chat->isMegagroup() && chat->asMegagroup()->canAddAdmins())
|| (chat->isChat() && chat->asChat()->canAddAdmins())) || (chat->isChat() && chat->asChat()->canAddAdmins()))
? bot->botInfo->groupAdminRights ? bot->botInfo->groupAdminRights
: ChatAdminRights(); : ChatAdminRights();
if (rights) { const auto addingAdmin = requestedAddAdmin || (rights != 0);
if (addingAdmin) {
const auto scope = _scope;
const auto token = _token;
const auto done = [=](
ChatAdminRightsInfo newRights,
const QString &rank) {
if (scope == Scope::GroupAdmin) {
chat->session().api().sendBotStart(bot, chat, token);
}
close();
};
const auto saveCallback = SaveAdminCallback( const auto saveCallback = SaveAdminCallback(
chat, chat,
bot, bot,
close, done,
close); close);
auto box = object_ptr<EditAdminBox>(nullptr); auto box = Box<EditAdminBox>(
box = Box<EditAdminBox>(
chat, chat,
bot, bot,
ChatAdminRightsInfo(rights), ChatAdminRightsInfo(rights),
QString(), _existingRank,
true); EditAdminBotFields{
_token,
_existingRights.value_or(ChatAdminRights()) });
box->setSaveCallback(saveCallback); box->setSaveCallback(saveCallback);
Ui::show(std::move(box), Ui::LayerOption::KeepOther); Ui::show(std::move(box), Ui::LayerOption::KeepOther);
} else { } else {
Ui::show( Ui::show(
Ui::MakeConfirmBox({ Ui::MakeConfirmBox({
tr::lng_bot_sure_invite(tr::now, lt_group, chat->name), tr::lng_bot_sure_invite(tr::now, lt_group, chat->name),
crl::guard(this, [=] { AddBotToGroup(bot, chat); }), crl::guard(this, [=] { AddBotToGroup(bot, chat, _token); }),
}), }),
Ui::LayerOption::KeepOther); Ui::LayerOption::KeepOther);
} }
@ -251,32 +329,33 @@ bool AddBotToGroupBoxController::needToCreateRow(
return true; return true;
} }
if (const auto chat = peer->asChat()) { if (const auto chat = peer->asChat()) {
if (_adminToGroup && chat->canAddAdmins()) { if (onlyAdminToGroup()) {
return chat->canAddAdmins();
} else if (_adminToGroup && chat->canAddAdmins()) {
_groups.fire_copy(peer); _groups.fire_copy(peer);
} else { } else if (!onlyAdminToChannel()) {
return chat->canAddMembers(); return chat->canAddMembers();
} }
} else if (const auto group = peer->asMegagroup()) { } else if (const auto group = peer->asMegagroup()) {
if (_adminToGroup && group->canAddAdmins()) { if (onlyAdminToGroup()) {
return group->canAddAdmins();
} else if (_adminToGroup && group->canAddAdmins()) {
_groups.fire_copy(peer); _groups.fire_copy(peer);
} else { } else if (!onlyAdminToChannel()) {
return group->canAddMembers(); return group->canAddMembers();
} }
} else if (const auto channel = peer->asBroadcast()) { } else if (const auto channel = peer->asBroadcast()) {
if (_adminToChannel && channel->canAddAdmins()) { if (onlyAdminToChannel()) {
return channel->canAddAdmins();
} else if (_adminToChannel && channel->canAddAdmins()) {
_channels.fire_copy(peer); _channels.fire_copy(peer);
} }
} }
return false; return false;
} }
bool AddBotToGroupBoxController::SharingBotGame(not_null<UserData*> bot) {
const auto &info = bot->botInfo;
return (info && !info->shareGameShortName.isEmpty());
}
bool AddBotToGroupBoxController::sharingBotGame() const { bool AddBotToGroupBoxController::sharingBotGame() const {
return SharingBotGame(_bot); return (_scope == Scope::ShareGame);
} }
QString AddBotToGroupBoxController::emptyBoxText() const { QString AddBotToGroupBoxController::emptyBoxText() const {
@ -310,29 +389,31 @@ object_ptr<Ui::RpWidget> AddBotToGroupBoxController::prepareAdminnedChats() {
const auto addList = [&]( const auto addList = [&](
tr::phrase<> subtitle, tr::phrase<> subtitle,
rpl::event_stream<not_null<PeerData*>> &items) { rpl::event_stream<not_null<PeerData*>> &items) {
container->add(CreatePeerListSectionSubtitle( const auto wrap = container->add(
container, object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
subtitle())); container,
container->add(object_ptr<Ui::FixedHeightWidget>( object_ptr<Ui::VerticalLayout>(container)));
container, wrap->hide(anim::type::instant);
st::membersMarginTop));
const auto delegate = container->lifetime().make_state< const auto inner = wrap->entity();
inner->add(CreatePeerListSectionSubtitle(inner, subtitle()));
const auto delegate = inner->lifetime().make_state<
PeerListContentDelegateSimple PeerListContentDelegateSimple
>(); >();
const auto controller = container->lifetime().make_state<Controller>( const auto controller = inner->lifetime().make_state<Controller>(
&session(), &session(),
items.events(), items.events(),
callback); callback);
const auto content = result->add(object_ptr<PeerListContent>( const auto content = inner->add(object_ptr<PeerListContent>(
container, container,
controller)); controller));
delegate->setContent(content); delegate->setContent(content);
controller->setDelegate(delegate); controller->setDelegate(delegate);
container->add(object_ptr<Ui::FixedHeightWidget>( items.events() | rpl::take(1) | rpl::start_with_next([=] {
container, wrap->show(anim::type::instant);
st::membersMarginBottom)); }, inner->lifetime());
}; };
if (_adminToChannel) { if (_adminToChannel) {
addList(tr::lng_bot_channels_manage, _channels); addList(tr::lng_bot_channels_manage, _channels);
@ -353,11 +434,20 @@ object_ptr<Ui::RpWidget> AddBotToGroupBoxController::prepareAdminnedChats() {
return result; return result;
} }
bool AddBotToGroupBoxController::onlyAdminToGroup() const {
return _adminToGroup && !_memberToGroup && !_adminToChannel;
}
bool AddBotToGroupBoxController::onlyAdminToChannel() const {
return _adminToChannel && !_memberToGroup && !_adminToGroup;
}
void AddBotToGroupBoxController::prepareViewHook() { void AddBotToGroupBoxController::prepareViewHook() {
delegate()->peerListSetTitle((sharingBotGame() || _adminToChannel) delegate()->peerListSetTitle((sharingBotGame() || _adminToChannel)
? tr::lng_bot_choose_chat() ? tr::lng_bot_choose_chat()
: tr::lng_bot_choose_group()); : tr::lng_bot_choose_group());
if (_adminToGroup || _adminToChannel) { if ((_adminToGroup && !onlyAdminToGroup())
|| (_adminToChannel && !onlyAdminToChannel())) {
delegate()->peerListSetAboveWidget(prepareAdminnedChats()); delegate()->peerListSetAboveWidget(prepareAdminnedChats());
} }
@ -370,9 +460,12 @@ void AddBotToGroupBoxController::prepareViewHook() {
}, lifetime()); }, lifetime());
} }
void AddBotToGroup(not_null<UserData*> bot, not_null<PeerData*> chat) { void AddBotToGroup(
if (bot->isBot() && !bot->botInfo->startGroupToken.isEmpty()) { not_null<UserData*> bot,
chat->session().api().sendBotStart(bot, chat); not_null<PeerData*> chat,
const QString &startToken) {
if (!startToken.isEmpty()) {
chat->session().api().sendBotStart(bot, chat, startToken);
} else { } else {
chat->session().api().chatParticipants().add(chat, { 1, bot }); chat->session().api().chatParticipants().add(chat, { 1, bot });
} }

View file

@ -8,14 +8,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "data/data_chat_participant_status.h"
class AddBotToGroupBoxController class AddBotToGroupBoxController
: public ChatsListBoxController : public ChatsListBoxController
, public base::has_weak_ptr { , public base::has_weak_ptr {
public: public:
static void Start(not_null<UserData*> bot); enum class Scope {
None,
GroupAdmin,
ChannelAdmin,
ShareGame,
All,
};
static void Start(
not_null<UserData*> bot,
Scope scope = Scope::All,
const QString &token = QString(),
ChatAdminRights requestedRights = {});
explicit AddBotToGroupBoxController(not_null<UserData*> bot); AddBotToGroupBoxController(
not_null<UserData*> bot,
Scope scope,
const QString &token,
ChatAdminRights requestedRights);
Main::Session &session() const override; Main::Session &session() const override;
void rowClicked(not_null<PeerListRow*> row) override; void rowClicked(not_null<PeerListRow*> row) override;
@ -26,9 +42,10 @@ protected:
QString emptyBoxText() const override; QString emptyBoxText() const override;
private: private:
static bool SharingBotGame(not_null<UserData*> bot); [[nodiscard]] object_ptr<Ui::RpWidget> prepareAdminnedChats();
object_ptr<Ui::RpWidget> prepareAdminnedChats(); [[nodiscard]] bool onlyAdminToGroup() const;
[[nodiscard]] bool onlyAdminToChannel() const;
bool needToCreateRow(not_null<PeerData*> peer) const; bool needToCreateRow(not_null<PeerData*> peer) const;
bool sharingBotGame() const; bool sharingBotGame() const;
@ -37,13 +54,28 @@ private:
void shareBotGame(not_null<PeerData*> chat); void shareBotGame(not_null<PeerData*> chat);
void addBotToGroup(not_null<PeerData*> chat); void addBotToGroup(not_null<PeerData*> chat);
void requestExistingRights(not_null<ChannelData*> channel);
const not_null<UserData*> _bot; const not_null<UserData*> _bot;
const Scope _scope = Scope::None;
const QString _token;
const ChatAdminRights _requestedRights;
ChannelData *_existingRightsChannel = nullptr;
mtpRequestId _existingRightsRequestId = 0;
std::optional<ChatAdminRights> _existingRights;
QString _existingRank;
rpl::event_stream<not_null<PeerData*>> _groups; rpl::event_stream<not_null<PeerData*>> _groups;
rpl::event_stream<not_null<PeerData*>> _channels; rpl::event_stream<not_null<PeerData*>> _channels;
bool _adminToGroup = false; bool _adminToGroup = false;
bool _adminToChannel = false; bool _adminToChannel = false;
bool _memberToGroup = false;
}; };
void AddBotToGroup(not_null<UserData*> bot, not_null<PeerData*> chat); void AddBotToGroup(
not_null<UserData*> bot,
not_null<PeerData*> chat,
const QString &startToken);

View file

@ -200,7 +200,7 @@ EditAdminBox::EditAdminBox(
not_null<UserData*> user, not_null<UserData*> user,
ChatAdminRightsInfo rights, ChatAdminRightsInfo rights,
const QString &rank, const QString &rank,
bool addingBot) std::optional<EditAdminBotFields> addingBot)
: EditParticipantBox( : EditParticipantBox(
nullptr, nullptr,
peer, peer,
@ -209,7 +209,7 @@ EditAdminBox::EditAdminBox(
, _show(this) , _show(this)
, _oldRights(rights) , _oldRights(rights)
, _oldRank(rank) , _oldRank(rank)
, _addingBot(addingBot) { , _addingBot(std::move(addingBot)) {
} }
ChatAdminRightsInfo EditAdminBox::defaultRights() const { ChatAdminRightsInfo EditAdminBox::defaultRights() const {
@ -240,12 +240,17 @@ void EditAdminBox::prepare() {
EditParticipantBox::prepare(); EditParticipantBox::prepare();
setTitle(_addingBot setTitle(_addingBot
? tr::lng_bot_add_title() ? (_addingBot->existing
? tr::lng_rights_edit_admin()
: tr::lng_bot_add_title())
: _oldRights.flags : _oldRights.flags
? tr::lng_rights_edit_admin() ? tr::lng_rights_edit_admin()
: tr::lng_channel_add_admin()); : tr::lng_channel_add_admin());
if (_addingBot && !peer()->isBroadcast() && _saveCallback) { if (_addingBot
&& !_addingBot->existing
&& !peer()->isBroadcast()
&& _saveCallback) {
addControl( addControl(
object_ptr<Ui::BoxContentDivider>(this), object_ptr<Ui::BoxContentDivider>(this),
st::rightsDividerMargin / 2); st::rightsDividerMargin / 2);
@ -277,7 +282,9 @@ void EditAdminBox::prepare() {
const auto chat = peer()->asChat(); const auto chat = peer()->asChat();
const auto channel = peer()->asChannel(); const auto channel = peer()->asChannel();
const auto prepareRights = _oldRights.flags const auto prepareRights = _addingBot
? ChatAdminRightsInfo(_oldRights.flags | _addingBot->existing)
: _oldRights.flags
? _oldRights ? _oldRights
: defaultRights(); : defaultRights();
const auto disabledByDefaults = (channel && !channel->isMegagroup()) const auto disabledByDefaults = (channel && !channel->isMegagroup())
@ -368,7 +375,7 @@ void EditAdminBox::prepare() {
? ~Flags(0) ? ~Flags(0)
: channel->adminRights()); : channel->adminRights());
_saveCallback( _saveCallback(
_addingBot ? ChatAdminRightsInfo() : _oldRights, _oldRights,
ChatAdminRightsInfo(newFlags), ChatAdminRightsInfo(newFlags),
_rank ? _rank->getLastText().trimmed() : QString()); _rank ? _rank->getLastText().trimmed() : QString());
}; };
@ -376,9 +383,9 @@ void EditAdminBox::prepare() {
if (!_saveCallback) { if (!_saveCallback) {
return; return;
} else if (_addAsAdmin && !_addAsAdmin->checked()) { } else if (_addAsAdmin && !_addAsAdmin->checked()) {
AddBotToGroup(user(), peer()); AddBotToGroup(user(), peer(), _addingBot->token);
return; return;
} else if (_addingBot) { } else if (_addingBot && !_addingBot->existing) {
const auto phrase = peer()->isBroadcast() const auto phrase = peer()->isBroadcast()
? tr::lng_bot_sure_add_text_channel ? tr::lng_bot_sure_add_text_channel
: tr::lng_bot_sure_add_text_group; : tr::lng_bot_sure_add_text_group;
@ -409,7 +416,7 @@ void EditAdminBox::finishAddAdmin() {
void EditAdminBox::refreshButtons() { void EditAdminBox::refreshButtons() {
clearButtons(); clearButtons();
if (canSave()) { if (canSave()) {
addButton(!_addingBot addButton((!_addingBot || _addingBot->existing)
? tr::lng_settings_save() ? tr::lng_settings_save()
: _adminControlsWrap->toggled() : _adminControlsWrap->toggled()
? tr::lng_bot_add_as_admin() ? tr::lng_bot_add_as_admin()

View file

@ -65,6 +65,11 @@ private:
}; };
struct EditAdminBotFields {
QString token;
ChatAdminRights existing;
};
class EditAdminBox : public EditParticipantBox { class EditAdminBox : public EditParticipantBox {
public: public:
EditAdminBox( EditAdminBox(
@ -73,7 +78,7 @@ public:
not_null<UserData*> user, not_null<UserData*> user,
ChatAdminRightsInfo rights, ChatAdminRightsInfo rights,
const QString &rank, const QString &rank,
bool addingBot = false); std::optional<EditAdminBotFields> addingBot = {});
void setSaveCallback( void setSaveCallback(
Fn<void( Fn<void(
@ -127,7 +132,7 @@ private:
mtpRequestId _transferRequestId = 0; mtpRequestId _transferRequestId = 0;
Fn<void()> _save, _finishSave; Fn<void()> _save, _finishSave;
bool _addingBot = false; std::optional<EditAdminBotFields> _addingBot;
}; };

View file

@ -251,6 +251,39 @@ bool ShowWallPaper(
params); params);
} }
[[nodiscard]] ChatAdminRights ParseRequestedAdminRights(
const QString &value) {
auto result = ChatAdminRights();
for (const auto &element : value.split(QRegularExpression("[+ ]"))) {
if (element == u"change_info"_q) {
result |= ChatAdminRight::ChangeInfo;
} else if (element == u"post_messages"_q) {
result |= ChatAdminRight::PostMessages;
} else if (element == u"edit_messages"_q) {
result |= ChatAdminRight::EditMessages;
} else if (element == u"delete_messages"_q) {
result |= ChatAdminRight::DeleteMessages;
} else if (element == u"restrict_members"_q) {
result |= ChatAdminRight::BanUsers;
} else if (element == u"invite_users"_q) {
result |= ChatAdminRight::InviteUsers;
} else if (element == u"pin_messages"_q) {
result |= ChatAdminRight::PinMessages;
} else if (element == u"promote_members"_q) {
result |= ChatAdminRight::AddAdmins;
} else if (element == u"manage_video_chats"_q) {
result |= ChatAdminRight::ManageCall;
} else if (element == u"anonymous"_q) {
result |= ChatAdminRight::Anonymous;
} else if (element == u"manage_chat"_q) {
result |= ChatAdminRight::Other;
} else {
return {};
}
}
return result;
}
bool ResolveUsernameOrPhone( bool ResolveUsernameOrPhone(
Window::SessionController *controller, Window::SessionController *controller,
const Match &match, const Match &match,
@ -278,18 +311,24 @@ bool ResolveUsernameOrPhone(
} else if (!validDomain(domain) && !validPhone(phone)) { } else if (!validDomain(domain) && !validPhone(phone)) {
return false; return false;
} }
auto start = qsl("start"); using BotStartType = Window::BotStartType;
auto startToken = params.value(start); auto startType = BotStartType::None;
if (startToken.isEmpty()) { auto startToken = params.value(u"start"_q);
start = qsl("startgroup"); if (!startToken.isEmpty()) {
startToken = params.value(start); startType = BotStartType::Personal;
if (startToken.isEmpty()) { } else if (params.contains(u"startgroup"_q)) {
start = QString(); startType = BotStartType::Group;
} startToken = params.value(u"startgroup"_q);
} else if (params.contains(u"startchannel"_q)) {
startType = BotStartType::Channel;
}
auto post = ShowAtUnreadMsgId;
auto adminRights = ChatAdminRights();
if (startType == BotStartType::Group
|| startType == BotStartType::Channel) {
post = ShowAtProfileMsgId;
adminRights = ParseRequestedAdminRights(params.value(u"admin"_q));
} }
auto post = (start == qsl("startgroup"))
? ShowAtProfileMsgId
: ShowAtUnreadMsgId;
const auto postParam = params.value(qsl("post")); const auto postParam = params.value(qsl("post"));
if (const auto postId = postParam.toInt()) { if (const auto postId = postParam.toInt()) {
post = postId; post = postId;
@ -301,7 +340,8 @@ bool ResolveUsernameOrPhone(
const auto gameParam = params.value(qsl("game")); const auto gameParam = params.value(qsl("game"));
if (!gameParam.isEmpty() && validDomain(gameParam)) { if (!gameParam.isEmpty() && validDomain(gameParam)) {
startToken = gameParam; startToken = gameParam;
post = ShowAtGameShareMsgId; post = ShowAtProfileMsgId;
startType = BotStartType::ShareGame;
} }
const auto fromMessageId = context.value<ClickHandlerContext>().itemId; const auto fromMessageId = context.value<ClickHandlerContext>().itemId;
using Navigation = Window::SessionNavigation; using Navigation = Window::SessionNavigation;
@ -318,7 +358,9 @@ bool ResolveUsernameOrPhone(
Navigation::ThreadId{ threadId } Navigation::ThreadId{ threadId }
} }
: Navigation::RepliesByLinkInfo{ v::null }, : Navigation::RepliesByLinkInfo{ v::null },
.startType = startType,
.startToken = startToken, .startToken = startToken,
.startAdminRights = adminRights,
.voicechatHash = (params.contains(u"livestream"_q) .voicechatHash = (params.contains(u"livestream"_q)
? std::make_optional(params.value(u"livestream"_q)) ? std::make_optional(params.value(u"livestream"_q))
: params.contains(u"videochat"_q) : params.contains(u"videochat"_q)

View file

@ -84,7 +84,6 @@ constexpr auto ShowAtTheEndMsgId = MsgId(SpecialMsgIdShift + 1);
constexpr auto SwitchAtTopMsgId = MsgId(SpecialMsgIdShift + 2); constexpr auto SwitchAtTopMsgId = MsgId(SpecialMsgIdShift + 2);
constexpr auto ShowAtProfileMsgId = MsgId(SpecialMsgIdShift + 3); constexpr auto ShowAtProfileMsgId = MsgId(SpecialMsgIdShift + 3);
constexpr auto ShowAndStartBotMsgId = MsgId(SpecialMsgIdShift + 4); constexpr auto ShowAndStartBotMsgId = MsgId(SpecialMsgIdShift + 4);
constexpr auto ShowAtGameShareMsgId = MsgId(SpecialMsgIdShift + 5);
constexpr auto ShowForChooseMessagesMsgId = MsgId(SpecialMsgIdShift + 6); constexpr auto ShowForChooseMessagesMsgId = MsgId(SpecialMsgIdShift + 6);
static_assert(SpecialMsgIdShift + 0xFF < 0); static_assert(SpecialMsgIdShift + 0xFF < 0);

View file

@ -20,7 +20,7 @@ struct BotInfo {
std::vector<BotCommand> commands; std::vector<BotCommand> commands;
Ui::Text::String text = { int(st::msgMinWidth) }; // description Ui::Text::String text = { int(st::msgMinWidth) }; // description
QString startToken, startGroupToken, shareGameShortName; QString startToken;
Dialogs::EntryState inlineReturnTo; Dialogs::EntryState inlineReturnTo;
ChatAdminRights groupAdminRights; ChatAdminRights groupAdminRights;

View file

@ -338,6 +338,7 @@ void SessionNavigation::showPeerByLinkResolved(
}).send(); }).send();
return; return;
} }
using Scope = AddBotToGroupBoxController::Scope;
const auto &replies = info.repliesInfo; const auto &replies = info.repliesInfo;
if (const auto threadId = std::get_if<ThreadId>(&replies)) { if (const auto threadId = std::get_if<ThreadId>(&replies)) {
showRepliesForMessage( showRepliesForMessage(
@ -351,31 +352,28 @@ void SessionNavigation::showPeerByLinkResolved(
info.messageId, info.messageId,
commentId->id, commentId->id,
params); params);
} else if (info.messageId == ShowAtGameShareMsgId) {
const auto user = peer->asUser();
if (user && user->isBot() && !info.startToken.isEmpty()) {
user->botInfo->shareGameShortName = info.startToken;
AddBotToGroupBoxController::Start(user);
} else {
crl::on_main(this, [=] {
showPeerHistory(peer->id, params);
});
}
} else if (info.messageId == ShowAtProfileMsgId && !peer->isChannel()) { } else if (info.messageId == ShowAtProfileMsgId && !peer->isChannel()) {
const auto user = peer->asUser(); const auto user = peer->asUser();
if (user const auto scope = (info.startType == BotStartType::ShareGame)
&& user->isBot() ? Scope::ShareGame
&& !user->botInfo->cantJoinGroups : (info.startType == BotStartType::Group)
&& !info.startToken.isEmpty()) { ? (info.startAdminRights ? Scope::GroupAdmin : Scope::All)
user->botInfo->startGroupToken = info.startToken; : (info.startType == BotStartType::Channel)
AddBotToGroupBoxController::Start(user); ? Scope::ChannelAdmin
} else if (user && user->isBot()) { : Scope::None;
if (!user || !user->isBot()) {
showPeerInfo(peer, params);
} else if (scope != Scope::None) {
AddBotToGroupBoxController::Start(
user,
scope,
info.startToken,
info.startAdminRights);
} else {
// Always open bot chats, even from mention links. // Always open bot chats, even from mention links.
crl::on_main(this, [=] { crl::on_main(this, [=] {
showPeerHistory(peer->id, params); showPeerHistory(peer->id, params);
}); });
} else {
showPeerInfo(peer, params);
} }
} else { } else {
const auto user = peer->asUser(); const auto user = peer->asUser();

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/observer.h" #include "base/observer.h"
#include "base/weak_ptr.h" #include "base/weak_ptr.h"
#include "base/timer.h" #include "base/timer.h"
#include "data/data_chat_participant_status.h"
#include "dialogs/dialogs_key.h" #include "dialogs/dialogs_key.h"
#include "ui/layers/layer_widget.h" #include "ui/layers/layer_widget.h"
#include "ui/layers/show.h" #include "ui/layers/show.h"
@ -86,6 +87,14 @@ enum class GifPauseReason {
using GifPauseReasons = base::flags<GifPauseReason>; using GifPauseReasons = base::flags<GifPauseReason>;
inline constexpr bool is_flag_type(GifPauseReason) { return true; }; inline constexpr bool is_flag_type(GifPauseReason) { return true; };
enum class BotStartType {
None,
Personal,
Group,
Channel,
ShareGame,
};
struct PeerThemeOverride { struct PeerThemeOverride {
PeerData *peer = nullptr; PeerData *peer = nullptr;
std::shared_ptr<Ui::ChatTheme> theme; std::shared_ptr<Ui::ChatTheme> theme;
@ -178,7 +187,9 @@ public:
QString phone; QString phone;
MsgId messageId = ShowAtUnreadMsgId; MsgId messageId = ShowAtUnreadMsgId;
RepliesByLinkInfo repliesInfo; RepliesByLinkInfo repliesInfo;
BotStartType startType = BotStartType::None;
QString startToken; QString startToken;
ChatAdminRights startAdminRights;
std::optional<QString> voicechatHash; std::optional<QString> voicechatHash;
FullMsgId clickFromMessageId; FullMsgId clickFromMessageId;
}; };