diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 29bcf9dd6..4f79f2f3e 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -4089,6 +4089,50 @@ void ApiWrap::saveContactSignupSilent(bool silent) { _contactSignupSilentRequestId = requestId; } +auto ApiWrap::botCommonGroups(not_null bot) const +-> std::optional>> { + const auto i = _botCommonGroups.find(bot); + return (i != end(_botCommonGroups)) + ? i->second + : std::optional>>(); +} + +void ApiWrap::requestBotCommonGroups( + not_null bot, + Fn done) { + if (_botCommonGroupsRequests.contains(bot)) { + return; + } + _botCommonGroupsRequests.emplace(bot, done); + const auto finish = [=](std::vector> list) { + _botCommonGroups.emplace(bot, std::move(list)); + if (const auto callback = _botCommonGroupsRequests.take(bot)) { + (*callback)(); + } + }; + const auto limit = 100; + request(MTPmessages_GetCommonChats( + bot->inputUser, + MTP_long(0), // max_id + MTP_int(limit) + )).done([=](const MTPmessages_Chats &result) { + const auto chats = result.match([](const auto &data) { + return &data.vchats().v; + }); + auto &owner = session().data(); + auto list = std::vector>(); + list.reserve(chats->size()); + for (const auto &chat : *chats) { + if (const auto peer = owner.processChat(chat)) { + list.push_back(peer); + } + } + finish(std::move(list)); + }).fail([=] { + finish({}); + }).send(); +} + void ApiWrap::saveSelfBio(const QString &text) { if (_bio.requestId) { if (text != _bio.requestedText) { diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 1ae6ab288..ec71c781b 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -352,6 +352,10 @@ public: std::optional contactSignupSilentCurrent() const; void saveContactSignupSilent(bool silent); + [[nodiscard]] auto botCommonGroups(not_null bot) const + -> std::optional>>; + void requestBotCommonGroups(not_null bot, Fn done); + void saveSelfBio(const QString &text); [[nodiscard]] Api::Authorizations &authorizations(); @@ -692,6 +696,11 @@ private: std::optional _contactSignupSilent; rpl::event_stream _contactSignupSilentChanges; + base::flat_map< + not_null, + std::vector>> _botCommonGroups; + base::flat_map, Fn> _botCommonGroupsRequests; + base::flat_map _unlikelyMessageLinks; }; diff --git a/Telegram/SourceFiles/boxes/peers/choose_peer_box.cpp b/Telegram/SourceFiles/boxes/peers/choose_peer_box.cpp index 1a2f8fe30..81741473f 100644 --- a/Telegram/SourceFiles/boxes/peers/choose_peer_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/choose_peer_box.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/peers/choose_peer_box.h" +#include "apiwrap.h" // ApiWrap::botCommonGroups / requestBotCommonGroups. #include "boxes/add_contact_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/premium_limits_box.h" @@ -18,8 +19,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_reply_markup.h" #include "info/profile/info_profile_icon.h" #include "lang/lang_keys.h" +#include "main/main_session.h" // Session::api(). #include "settings/settings_common.h" #include "ui/boxes/confirm_box.h" +#include "ui/text/text_utilities.h" #include "ui/widgets/buttons.h" #include "ui/wrap/vertical_layout.h" #include "window/window_session_controller.h" @@ -55,10 +58,87 @@ private: const not_null _navigation; not_null _bot; RequestPeerQuery _query; + base::flat_set> _commonGroups; Fn)> _callback; }; +using RightsMap = std::vector>>; + +[[nodiscard]] RightsMap GroupRights() { + using Flag = ChatAdminRight; + return { + { Flag::ChangeInfo, tr::lng_request_group_change_info }, + { + Flag::DeleteMessages, + tr::lng_request_group_delete_messages }, + { Flag::BanUsers, tr::lng_request_group_ban_users }, + { Flag::InviteByLinkOrAdd, tr::lng_request_group_invite }, + { Flag::PinMessages, tr::lng_request_group_pin_messages }, + { Flag::ManageTopics, tr::lng_request_group_manage_topics }, + { + Flag::ManageCall, + tr::lng_request_group_manage_video_chats }, + { Flag::Anonymous, tr::lng_request_group_anonymous }, + { Flag::AddAdmins, tr::lng_request_group_add_admins }, + }; +} + +[[nodiscard]] RightsMap BroadcastRights() { + using Flag = ChatAdminRight; + return { + { Flag::ChangeInfo, tr::lng_request_channel_change_info }, + { + Flag::PostMessages, + tr::lng_request_channel_post_messages }, + { + Flag::EditMessages, + tr::lng_request_channel_edit_messages }, + { + Flag::DeleteMessages, + tr::lng_request_channel_delete_messages }, + { + Flag::InviteByLinkOrAdd, + tr::lng_request_channel_add_subscribers }, + { + Flag::ManageCall, + tr::lng_request_channel_manage_livestreams }, + { Flag::AddAdmins, tr::lng_request_channel_add_admins }, + }; +} + +[[nodiscard]] QString RightsText( + ChatAdminRights rights, + const RightsMap &phrases) { + auto list = QStringList(); + for (const auto &[flag, phrase] : phrases) { + if (rights & flag) { + list.push_back(phrase(tr::now)); + } + } + const auto count = list.size(); + if (!count) { + return QString(); + } + const auto last = list.back(); + return (count > 1) + ? tr::lng_request_peer_rights_and( + tr::now, + lt_rights, + list.mid(0, count - 1).join(", "), + lt_last, + last) + : last; +} + +[[nodiscard]] QString GroupRightsText(ChatAdminRights rights) { + return RightsText(rights, GroupRights()); +} + +[[nodiscard]] QString BroadcastRightsText(ChatAdminRights rights) { + return RightsText(rights, BroadcastRights()); +} + [[nodiscard]] QStringList RestrictionsList(RequestPeerQuery query) { using Flag = ChatAdminRight; using Type = RequestPeerQuery::Type; @@ -74,30 +154,11 @@ private: result.push_back(no(tr::now)); } }; - const auto addRights = [&]( - ChatAdminRights rights, - std::vector>> phrases) { - auto list = QStringList(); - for (const auto &[flag, phrase] : phrases) { - if (rights & flag) { - list.push_back(phrase(tr::now)); - } + const auto addRights = [&](const QString &rights) { + if (!rights.isEmpty()) { + result.push_back( + tr::lng_request_peer_rights(tr::now, lt_rights, rights)); } - const auto count = list.size(); - if (!count) { - return; - } - const auto last = list.back(); - const auto full = (count > 1) - ? tr::lng_request_peer_rights_and( - tr::now, - lt_rights, - list.mid(0, count - 1).join(", "), - lt_last, - last) - : last; - result.push_back( - tr::lng_request_peer_rights(tr::now, lt_rights, full)); }; switch (query.type) { case Type::User: @@ -120,21 +181,7 @@ private: if (query.amCreator) { result.push_back(tr::lng_request_group_am_owner(tr::now)); } else { - addRights(query.myRights, { - { Flag::ChangeInfo, tr::lng_request_group_change_info }, - { - Flag::DeleteMessages, - tr::lng_request_group_delete_messages }, - { Flag::BanUsers, tr::lng_request_group_ban_users }, - { Flag::InviteByLinkOrAdd, tr::lng_request_group_invite }, - { Flag::PinMessages, tr::lng_request_group_pin_messages }, - { Flag::ManageTopics, tr::lng_request_group_manage_topics }, - { - Flag::ManageCall, - tr::lng_request_group_manage_video_chats }, - { Flag::Anonymous, tr::lng_request_group_anonymous }, - { Flag::AddAdmins, tr::lng_request_group_add_admins }, - }); + addRights(GroupRightsText(query.myRights)); } break; case Type::Broadcast: @@ -145,25 +192,7 @@ private: if (query.amCreator) { result.push_back(tr::lng_request_channel_am_owner(tr::now)); } else { - addRights(query.myRights, { - { Flag::ChangeInfo, tr::lng_request_channel_change_info }, - { - Flag::PostMessages, - tr::lng_request_channel_post_messages }, - { - Flag::EditMessages, - tr::lng_request_channel_edit_messages }, - { - Flag::DeleteMessages, - tr::lng_request_channel_delete_messages }, - { - Flag::InviteByLinkOrAdd, - tr::lng_request_channel_add_subscribers }, - { - Flag::ManageCall, - tr::lng_request_channel_manage_livestreams }, - { Flag::AddAdmins, tr::lng_request_channel_add_admins }, - }); + addRights(BroadcastRightsText(query.myRights)); } break; } @@ -175,10 +204,48 @@ object_ptr MakeConfirmBox( not_null peer, RequestPeerQuery query, Fn confirmed) { - auto text = TextWithEntities{ "Sure?.." }; + const auto user = peer->asUser(); + const auto name = user ? user->firstName : peer->name(); + const auto botName = bot->name(); + auto text = tr::lng_request_peer_confirm( + tr::now, + lt_chat, + Ui::Text::Bold(name), + lt_bot, + Ui::Text::Bold(botName), + Ui::Text::WithEntities); + if (!user) { + const auto rights = peer->isBroadcast() + ? BroadcastRightsText(query.botRights) + : GroupRightsText(query.botRights); + if (!rights.isEmpty()) { + text.append('\n').append('\n').append( + tr::lng_request_peer_confirm_rights( + tr::now, + lt_bot, + Ui::Text::Bold(botName), + lt_chat, + Ui::Text::Bold(name), + lt_rights, + TextWithEntities{ rights }, + Ui::Text::WithEntities)); + } else if (!peer->isBroadcast() && query.isBotParticipant) { + const auto common = bot->session().api().botCommonGroups(bot); + if (!common || !ranges::contains(*common, peer)) { + text.append('\n').append('\n').append( + tr::lng_request_peer_confirm_add( + tr::now, + lt_bot, + Ui::Text::Bold(botName), + lt_chat, + Ui::Text::Bold(name), + Ui::Text::WithEntities)); + } + } + } return Ui::MakeConfirmBox({ .text = std::move(text), - .confirmed = std::move(confirmed), + .confirmed = [=](Fn close) { confirmed(); close(); }, .confirmText = tr::lng_request_peer_confirm_send(tr::now), }); } @@ -206,7 +273,8 @@ object_ptr CreatePeerByQueryBox( [[nodiscard]] bool FilterPeerByQuery( not_null peer, - RequestPeerQuery query) { + RequestPeerQuery query, + const base::flat_set> &commonGroups) { using Type = RequestPeerQuery::Type; using Restriction = RequestPeerQuery::Restriction; const auto checkRestriction = [](Restriction restriction, bool value) { @@ -239,7 +307,13 @@ object_ptr CreatePeerByQueryBox( && checkRights( query.myRights, chat ? chat->amCreator() : megagroup->amCreator(), - chat ? chat->adminRights() : megagroup->adminRights()); + chat ? chat->adminRights() : megagroup->adminRights()) + && (!query.isBotParticipant + || query.myRights + || commonGroups.contains(peer) + || (chat + ? chat->canAddMembers() + : megagroup->canAddMembers())); } case Type::Broadcast: { const auto broadcast = peer->asBroadcast(); @@ -265,6 +339,9 @@ ChoosePeerBoxController::ChoosePeerBoxController( , _bot(bot) , _query(query) , _callback(std::move(callback)) { + if (const auto list = _bot->session().api().botCommonGroups(_bot)) { + _commonGroups = { begin(*list), end(*list) }; + } } Main::Session &ChoosePeerBoxController::session() const { @@ -358,7 +435,7 @@ void ChoosePeerBoxController::rowClicked(not_null row) { auto ChoosePeerBoxController::createRow(not_null history) -> std::unique_ptr { - return FilterPeerByQuery(history->peer, _query) + return FilterPeerByQuery(history->peer, _query, _commonGroups) ? std::make_unique(history) : nullptr; } @@ -388,11 +465,23 @@ QString ChoosePeerBoxController::emptyBoxText() const { } // namespace -QPointer ShowChoosePeerBox( +void ShowChoosePeerBox( not_null navigation, not_null bot, RequestPeerQuery query, - Fn)> &&chosen) { + Fn)> chosen) { + const auto needCommonGroups = query.isBotParticipant + && (query.type == RequestPeerQuery::Type::Group) + && !query.myRights; + if (needCommonGroups && !bot->session().api().botCommonGroups(bot)) { + const auto weak = base::make_weak(navigation); + bot->session().api().requestBotCommonGroups(bot, [=] { + if (const auto strong = weak.get()) { + ShowChoosePeerBox(strong, bot, query, chosen); + } + }); + return; + } const auto weak = std::make_shared>(); auto initBox = [=](not_null box) { box->addButton(tr::lng_cancel(), [box] { @@ -412,5 +501,4 @@ QPointer ShowChoosePeerBox( query, std::move(callback)), std::move(initBox)), Ui::LayerOption::KeepOther); - return weak->data(); } diff --git a/Telegram/SourceFiles/boxes/peers/choose_peer_box.h b/Telegram/SourceFiles/boxes/peers/choose_peer_box.h index 7b1f86678..2c9ab7a1d 100644 --- a/Telegram/SourceFiles/boxes/peers/choose_peer_box.h +++ b/Telegram/SourceFiles/boxes/peers/choose_peer_box.h @@ -17,8 +17,8 @@ namespace Window { class SessionNavigation; } // namespace Window -QPointer ShowChoosePeerBox( +void ShowChoosePeerBox( not_null navigation, not_null bot, RequestPeerQuery query, - Fn)> &&chosen); + Fn)> chosen);