diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 0e5626642f..6a8b393857 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_peer_requests_box.h" #include "boxes/peers/edit_peer_reactions.h" #include "boxes/stickers_box.h" +#include "boxes/username_box.h" #include "ui/boxes/single_choice_box.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "core/application.h" @@ -67,6 +68,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { +constexpr auto kBotManagerUsername = "BotFather"_cs; + [[nodiscard]] auto ToPositiveNumberString() { return rpl::map([](int count) { return count ? QString::number(count) : QString(); @@ -306,6 +309,11 @@ private: void fillManageSection(); void fillPendingRequestsButton(); + void fillBotUsernamesButton(); + void fillBotEditIntroButton(); + void fillBotEditCommandsButton(); + void fillBotEditSettingsButton(); + void submitTitle(); void submitDescription(); void deleteWithConfirmation(); @@ -341,6 +349,8 @@ private: void continueSave(); void cancelSave(); + void toggleBotManager(const QString &command); + void togglePreHistoryHidden( not_null channel, bool hidden, @@ -363,6 +373,7 @@ private: not_null _peer; MTP::Sender _api; const bool _isGroup = false; + const bool _isBot = false; base::unique_qptr _wrap; Controls _controls; @@ -386,8 +397,11 @@ Controller::Controller( , _box(box) , _peer(peer) , _api(&_peer->session().mtp()) -, _isGroup(_peer->isChat() || _peer->isMegagroup()) { - _box->setTitle(_isGroup +, _isGroup(_peer->isChat() || _peer->isMegagroup()) +, _isBot(_peer->isUser() && _peer->asUser()->botInfo) { + _box->setTitle(_isBot + ? tr::lng_edit_bot_title() + : _isGroup ? tr::lng_edit_group() : tr::lng_edit_channel_title()); _box->addButton(tr::lng_settings_save(), [=] { @@ -491,7 +505,9 @@ object_ptr Controller::createTitleEdit() { object_ptr( _wrap, st::defaultInputField, - (_isGroup + (_isBot + ? tr::lng_dlg_new_bot_name + : _isGroup ? tr::lng_dlg_new_group_name : tr::lng_dlg_new_channel_name)(), _peer->name()), @@ -603,7 +619,9 @@ object_ptr Controller::createStickersEdit() { } bool Controller::canEditInformation() const { - if (const auto channel = _peer->asChannel()) { + if (_isBot) { + return _peer->asUser()->botInfo->canEditInformation; + } else if (const auto channel = _peer->asChannel()) { return channel->canEditInformation(); } else if (const auto chat = _peer->asChat()) { return chat->canEditInformation(); @@ -985,12 +1003,41 @@ void Controller::fillHistoryVisibilityButton() { void Controller::fillManageSection() { Expects(_controls.buttonsLayout != nullptr); - using namespace rpl::mappers; + if (_isBot) { + const auto &container = _controls.buttonsLayout; + + AddSkip( + container, + st::editPeerTopButtonsLayoutSkipCustomTop); + fillBotUsernamesButton(); + fillBotEditIntroButton(); + fillBotEditCommandsButton(); + fillBotEditSettingsButton(); + Settings::AddSkip( + container, + st::editPeerTopButtonsLayoutSkipCustomBottom); + container->add(object_ptr( + container, + object_ptr( + container, + tr::lng_manage_peer_bot_about( + lt_bot, + rpl::single(Ui::Text::Link( + '@' + kBotManagerUsername.utf16(), + _peer->session().createInternalLinkFull( + kBotManagerUsername.utf16()))), + Ui::Text::RichLangValue), + st::boxDividerLabel), + st::settingsDividerLabelPadding)); + return; + } const auto chat = _peer->asChat(); const auto channel = _peer->asChannel(); const auto isChannel = (!chat); - if (!chat && !channel) return; + if (!chat && !channel) { + return; + } const auto canEditType = [&] { return isChannel @@ -1043,7 +1090,6 @@ void Controller::fillManageSection() { }(); const auto canEditStickers = [&] { - // return true; return isChannel ? channel->canEditStickers() : false; @@ -1308,6 +1354,98 @@ void Controller::fillPendingRequestsButton() { }, wrap->lifetime()); } +void Controller::fillBotUsernamesButton() { + Expects(_isBot); + + const auto user = _peer->asUser(); + + auto localUsernames = rpl::single( + user->usernames() + ) | rpl::map([](const std::vector &usernames) { + return ranges::views::all( + usernames + ) | ranges::views::transform([](const QString &u) { + return Data::Username{ u }; + }) | ranges::to_vector; + }); + auto usernamesValue = std::move( + localUsernames + ) | rpl::then( + _peer->session().api().usernames().loadUsernames(_peer) + ); + auto rightLabel = rpl::duplicate( + usernamesValue + ) | rpl::map([=](const Data::Usernames &usernames) { + if (usernames.size() <= 1) { + return user->session().createInternalLink(user->username()); + } else { + const auto active = ranges::count_if( + usernames, + [](const Data::Username &u) { return u.active; }); + return u"%1/%2"_q.arg(active).arg(usernames.size()); + } + }); + auto leftLabel = std::move( + usernamesValue + ) | rpl::map([=](const Data::Usernames &usernames) { + return (usernames.size() <= 1) + ? tr::lng_manage_peer_bot_public_link() + : tr::lng_manage_peer_bot_public_links(); + }) | rpl::flatten_latest(); + + const auto wrap = _controls.buttonsLayout->add( + object_ptr>( + _controls.buttonsLayout, + object_ptr( + _controls.buttonsLayout))); + AddButtonWithCount( + _controls.buttonsLayout, + std::move(leftLabel), + std::move(rightLabel), + [=] { + Window::Show(_navigation).showBox( + Box(UsernamesBox, user), + Ui::LayerOption::KeepOther); + }, + { &st::infoRoundedIconInviteLinks, Settings::kIconLightOrange }); +} + +void Controller::fillBotEditIntroButton() { + Expects(_isBot); + + const auto user = _peer->asUser(); + AddButtonWithCount( + _controls.buttonsLayout, + tr::lng_manage_peer_bot_edit_intro(), + rpl::never(), + [=] { toggleBotManager(u"%1-intro"_q.arg(user->username())); }, + { &st::settingsIconChat, Settings::kIconLightBlue }); +} + +void Controller::fillBotEditCommandsButton() { + Expects(_isBot); + + const auto user = _peer->asUser(); + AddButtonWithCount( + _controls.buttonsLayout, + tr::lng_manage_peer_bot_edit_commands(), + rpl::never(), + [=] { toggleBotManager(u"%1-commands"_q.arg(user->username())); }, + { &st::settingsIconChat, Settings::kIconLightBlue }); +} + +void Controller::fillBotEditSettingsButton() { + Expects(_isBot); + + const auto user = _peer->asUser(); + AddButtonWithCount( + _controls.buttonsLayout, + tr::lng_manage_peer_bot_edit_settings(), + rpl::never(), + [=] { toggleBotManager(user->username()); }, + { &st::settingsIconChat, Settings::kIconLightBlue }); +} + void Controller::submitTitle() { Expects(_controls.title != nullptr); @@ -1670,6 +1808,19 @@ void Controller::saveTitle() { )).done(std::move(onDone) ).fail(std::move(onFail) ).send(); + } else if (_isBot) { + using Flag = MTPbots_SetBotInfo::Flag; + _api.request(MTPbots_SetBotInfo( + MTP_flags(Flag::f_bot | Flag::f_name), + _peer->asUser()->inputUser, + MTPstring(), // Lang code. + MTP_string(*_savingData.title), // Name. + MTPstring(), // About. + MTPstring() // Description. + )).done([=] { + continueSave(); + }).fail(std::move(onFail) + ).send(); } else { continueSave(); } @@ -1684,6 +1835,23 @@ void Controller::saveDescription() { _peer->setAbout(*_savingData.description); continueSave(); }; + if (_isBot) { + using Flag = MTPbots_SetBotInfo::Flag; + _api.request(MTPbots_SetBotInfo( + MTP_flags(Flag::f_bot | Flag::f_about), + _peer->asUser()->inputUser, + MTPstring(), // Lang code. + MTPstring(), // Name. + MTP_string(*_savingData.description), // About. + MTPstring() // Description. + )).done([=] { + successCallback(); + }).fail([=] { + _controls.description->showError(); + cancelSave(); + }).send(); + return; + } _api.request(MTPmessages_EditChatAbout( _peer->input, MTP_string(*_savingData.description) @@ -1726,6 +1894,22 @@ void Controller::saveHistoryVisibility() { [=] { cancelSave(); }); } +void Controller::toggleBotManager(const QString &command) { + const auto controller = _navigation->parentController(); + _api.request(MTPcontacts_ResolveUsername( + MTP_string(kBotManagerUsername.utf16()) + )).done([=](const MTPcontacts_ResolvedPeer &result) { + _peer->owner().processUsers(result.data().vusers()); + _peer->owner().processChats(result.data().vchats()); + const auto botPeer = _peer->owner().peerLoaded( + peerFromMTP(result.data().vpeer())); + if (const auto bot = botPeer ? botPeer->asUser() : nullptr) { + _peer->session().api().sendBotStart(bot, bot, command); + controller->showPeerHistory(bot); + } + }).send(); +} + void Controller::togglePreHistoryHidden( not_null channel, bool hidden,