From a2a16f3f238a35a87a6bbb7232dd5fa2548fd7cb Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 18 Mar 2025 16:46:24 +0400 Subject: [PATCH] Implement new business bots permissions. --- Telegram/Resources/langs/lang.strings | 23 +++++++++- .../boxes/peers/edit_peer_permissions_box.cpp | 25 +++++++++- .../boxes/peers/edit_peer_permissions_box.h | 10 ++++ .../data/business/data_business_chatbots.cpp | 43 +++++++++++++++-- .../data/business/data_business_chatbots.h | 8 +++- .../data/business/data_business_common.cpp | 41 +++++++++++++++++ .../data/business/data_business_common.h | 23 ++++++++++ .../settings/business/settings_chatbots.cpp | 46 ++++++++++++------- .../settings/settings_business.cpp | 3 -- .../settings/settings_privacy_controllers.cpp | 2 +- 10 files changed, 197 insertions(+), 27 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 4530375071..23e533b492 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2942,8 +2942,29 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_chatbots_include_button" = "Select Chats"; "lng_chatbots_exclude_about" = "Select chats or entire chat categories which the bot will not have access to."; "lng_chatbots_permissions_title" = "Bot permissions"; + +"lng_chatbots_manage_messages" = "Manage Messages"; +"lng_chatbots_read" = "Read Messages"; "lng_chatbots_reply" = "Reply to Messages"; -"lng_chatbots_reply_about" = "The bot can only reply on your behalf in chats that were active during the last 24h."; +"lng_chatbots_mark_as_read" = "Mark Messages as Read"; +"lng_chatbots_delete_sent" = "Delete Sent Messages"; +"lng_chatbots_delete_received" = "Delete Received Messages"; + +"lng_chatbots_manage_profile" = "Manage Profile"; +"lng_chatbots_edit_name" = "Edit Name"; +"lng_chatbots_edit_bio" = "Edit Bio"; +"lng_chatbots_edit_userpic" = "Edit Profile Picture"; +"lng_chatbots_edit_username" = "Edit Username"; + +"lng_chatbots_manage_gifts" = "Manage Gifts and Stars"; +"lng_chatbots_view_gifts" = "View Gifts"; +"lng_chatbots_sell_gifts" = "Sell Gifts"; +"lng_chatbots_gift_settings" = "Change Gift Settings"; +"lng_chatbots_transfer_gifts" = "Transfer and Upgrade Gifts"; +"lng_chatbots_transfer_stars" = "Transfer Stars"; + +"lng_chatbots_manage_stories" = "Manage Stories"; + "lng_chatbots_remove" = "Remove Bot"; "lng_chatbots_not_found" = "Chatbot not found."; "lng_chatbots_not_supported" = "This bot doesn't support Telegram Business yet."; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index 6e824ac35d..5f562f5313 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/admin_log/history_admin_log_filter.h" #include "core/ui_integration.h" #include "data/stickers/data_custom_emoji.h" +#include "data/business/data_business_chatbots.h" #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_session.h" @@ -63,6 +64,11 @@ constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000); return std::vector>{}; } +[[nodiscard]] auto Dependencies(Data::ChatbotsPermissions) { + using Flag = Data::ChatbotsPermission; + return std::vector>{}; +} + [[nodiscard]] auto NestedRestrictionLabelsList( Data::RestrictionsSetOptions options) -> std::vector> { @@ -678,7 +684,7 @@ template checkView->setChecked(false, anim::type::instant); } else if (locked.has_value()) { if (checked != toggled) { - if (!state->toast) { + if (!state->toast && !locked->isEmpty()) { state->toast = Ui::Toast::Show(container, { .text = { *locked }, .duration = kForceDisableTooltipDuration, @@ -1488,3 +1494,20 @@ EditFlagsControl CreateEditAdminLogFilter( return result; } + +EditFlagsControl CreateEditChatbotPermissions( + QWidget *parent, + Data::ChatbotsPermissions flags) { + auto widget = object_ptr(parent); + auto descriptor = Data::ChatbotsPermissionsLabels(); + descriptor.disabledMessages.emplace( + Data::ChatbotsPermission::ViewMessages, + QString()); + auto result = CreateEditFlags( + widget.data(), + flags | Data::ChatbotsPermission::ViewMessages, + std::move(descriptor)); + result.widget = std::move(widget); + + return result; +} diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h index 6c4863b7bf..018b5523dc 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h @@ -27,6 +27,11 @@ enum Flag : uint32; using Flags = base::flags; } // namespace PowerSaving +namespace Data { +enum class ChatbotsPermission; +using ChatbotsPermissions = base::flags; +} // namespace Data + template class object_ptr; @@ -120,3 +125,8 @@ using AdminRightLabel = EditFlagsLabel; AdminLog::FilterValue::Flags flags, bool isChannel ) -> EditFlagsControl; + +[[nodiscard]] auto CreateEditChatbotPermissions( + QWidget *parent, + Data::ChatbotsPermissions flags +) -> EditFlagsControl; diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.cpp b/Telegram/SourceFiles/data/business/data_business_chatbots.cpp index 7c0ac6ea91..665e5617c2 100644 --- a/Telegram/SourceFiles/data/business/data_business_chatbots.cpp +++ b/Telegram/SourceFiles/data/business/data_business_chatbots.cpp @@ -8,10 +8,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/business/data_business_chatbots.h" #include "apiwrap.h" +#include "boxes/peers/edit_peer_permissions_box.h" #include "data/business/data_business_common.h" #include "data/business/data_business_info.h" #include "data/data_session.h" #include "data/data_user.h" +#include "lang/lang_keys.h" #include "main/main_session.h" namespace Data { @@ -41,7 +43,7 @@ void Chatbots::preload() { _settings = ChatbotsSettings{ .bot = _owner->session().data().user(botId), .recipients = FromMTP(_owner, bot.vrecipients()), - .repliesAllowed = bot.vrights().data().is_reply(), + .permissions = FromMTP(bot.vrights()), }; } else { _settings.force_assign(ChatbotsSettings()); @@ -83,9 +85,7 @@ void Chatbots::save( const auto api = &_owner->session().api(); api->request(MTPaccount_UpdateConnectedBot( MTP_flags(!settings.bot ? Flag::f_deleted : Flag::f_rights), - MTP_businessBotRights(MTP_flags(settings.repliesAllowed - ? RightFlag::f_reply - : RightFlag())), + ToMTP(settings.permissions), (settings.bot ? settings.bot : was.bot)->inputUser, ForBotsToMTP(settings.recipients) )).done([=](const MTPUpdates &result) { @@ -180,4 +180,39 @@ void Chatbots::reload() { preload(); } +EditFlagsDescriptor ChatbotsPermissionsLabels() { + using Flag = ChatbotsPermission; + + using PermissionLabel = EditFlagsLabel; + auto messages = std::vector{ + { Flag::ViewMessages, tr::lng_chatbots_read(tr::now) }, + { Flag::ReplyToMessages, tr::lng_chatbots_reply(tr::now) }, + { Flag::MarkAsRead, tr::lng_chatbots_mark_as_read(tr::now) }, + { Flag::DeleteSent, tr::lng_chatbots_delete_sent(tr::now) }, + { Flag::DeleteReceived, tr::lng_chatbots_delete_received(tr::now) }, + }; + auto manage = std::vector{ + { Flag::EditName, tr::lng_chatbots_edit_name(tr::now) }, + { Flag::EditBio, tr::lng_chatbots_edit_bio(tr::now) }, + { Flag::EditUserpic, tr::lng_chatbots_edit_userpic(tr::now) }, + { Flag::EditUsername, tr::lng_chatbots_edit_username(tr::now) }, + }; + auto gifts = std::vector{ + { Flag::ViewGifts, tr::lng_chatbots_view_gifts(tr::now) }, + { Flag::SellGifts, tr::lng_chatbots_sell_gifts(tr::now) }, + { Flag::GiftSettings, tr::lng_chatbots_gift_settings(tr::now) }, + { Flag::TransferGifts, tr::lng_chatbots_transfer_gifts(tr::now) }, + { Flag::TransferStars, tr::lng_chatbots_transfer_stars(tr::now) }, + }; + auto stories = std::vector{ + { Flag::ManageStories, tr::lng_chatbots_manage_stories(tr::now) }, + }; + return { .labels = { + { tr::lng_chatbots_manage_messages(), std::move(messages) }, + { tr::lng_chatbots_manage_profile(), std::move(manage) }, + { tr::lng_chatbots_manage_gifts(), std::move(gifts) }, + { std::nullopt, std::move(stories) }, + }, .st = nullptr }; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.h b/Telegram/SourceFiles/data/business/data_business_chatbots.h index 902cdd4206..97a034845e 100644 --- a/Telegram/SourceFiles/data/business/data_business_chatbots.h +++ b/Telegram/SourceFiles/data/business/data_business_chatbots.h @@ -11,6 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class UserData; +template +struct EditFlagsDescriptor; + namespace Data { class Session; @@ -18,7 +21,7 @@ class Session; struct ChatbotsSettings { UserData *bot = nullptr; BusinessRecipients recipients; - bool repliesAllowed = false; + ChatbotsPermissions permissions; friend inline bool operator==( const ChatbotsSettings &, @@ -67,4 +70,7 @@ private: }; +[[nodiscard]] auto ChatbotsPermissionsLabels() +-> EditFlagsDescriptor; + } // namespace Data diff --git a/Telegram/SourceFiles/data/business/data_business_common.cpp b/Telegram/SourceFiles/data/business/data_business_common.cpp index 149a3ee136..dc6619f5e7 100644 --- a/Telegram/SourceFiles/data/business/data_business_common.cpp +++ b/Telegram/SourceFiles/data/business/data_business_common.cpp @@ -159,6 +159,47 @@ BusinessRecipients FromMTP( return result; } +ChatbotsPermissions FromMTP(const MTPBusinessBotRights &rights) { + using Flag = ChatbotsPermission; + const auto &data = rights.data(); + + return Flag::ViewMessages + | (data.is_reply() ? Flag::ReplyToMessages : Flag()) + | (data.is_read_messages() ? Flag::MarkAsRead : Flag()) + | (data.is_delete_sent_messages() ? Flag::DeleteSent : Flag()) + | (data.is_delete_received_messages() ? Flag::DeleteReceived : Flag()) + | (data.is_edit_name() ? Flag::EditName : Flag()) + | (data.is_edit_bio() ? Flag::EditBio : Flag()) + | (data.is_edit_profile_photo() ? Flag::EditUserpic : Flag()) + | (data.is_edit_username() ? Flag::EditUsername : Flag()) + | (data.is_view_gifts() ? Flag::ViewGifts : Flag()) + | (data.is_sell_gifts() ? Flag::SellGifts : Flag()) + | (data.is_change_gift_settings() ? Flag::GiftSettings : Flag()) + | (data.is_transfer_and_upgrade_gifts() ? Flag::TransferGifts : Flag()) + | (data.is_transfer_stars() ? Flag::TransferStars : Flag()) + | (data.is_manage_stories() ? Flag::ManageStories : Flag()); +} + +MTPBusinessBotRights ToMTP(ChatbotsPermissions rights) { + using Flag = MTPDbusinessBotRights::Flag; + using Right = ChatbotsPermission; + return MTP_businessBotRights(MTP_flags(Flag() + | ((rights & Right::ReplyToMessages) ? Flag::f_reply : Flag()) + | ((rights & Right::MarkAsRead) ? Flag::f_read_messages : Flag()) + | ((rights & Right::DeleteSent) ? Flag::f_delete_sent_messages : Flag()) + | ((rights & Right::DeleteReceived) ? Flag::f_delete_received_messages : Flag()) + | ((rights & Right::EditName) ? Flag::f_edit_name : Flag()) + | ((rights & Right::EditBio) ? Flag::f_edit_bio : Flag()) + | ((rights & Right::EditUserpic) ? Flag::f_edit_profile_photo : Flag()) + | ((rights & Right::EditUsername) ? Flag::f_edit_username : Flag()) + | ((rights & Right::ViewGifts) ? Flag::f_view_gifts : Flag()) + | ((rights & Right::SellGifts) ? Flag::f_sell_gifts : Flag()) + | ((rights & Right::GiftSettings) ? Flag::f_change_gift_settings : Flag()) + | ((rights & Right::TransferGifts) ? Flag::f_transfer_and_upgrade_gifts : Flag()) + | ((rights & Right::TransferStars) ? Flag::f_transfer_stars : Flag()) + | ((rights & Right::ManageStories) ? Flag::f_manage_stories : Flag()))); +} + BusinessDetails FromMTP( not_null owner, const tl::conditional &hours, diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h index 600d5ff79d..90ae82b074 100644 --- a/Telegram/SourceFiles/data/business/data_business_common.h +++ b/Telegram/SourceFiles/data/business/data_business_common.h @@ -57,6 +57,26 @@ enum class BusinessRecipientsType : uchar { Bots, }; +enum class ChatbotsPermission { + ViewMessages = 0x0001, + ReplyToMessages = 0x0002, + MarkAsRead = 0x0004, + DeleteSent = 0x0008, + DeleteReceived = 0x0010, + EditName = 0x0020, + EditBio = 0x0040, + EditUserpic = 0x0080, + EditUsername = 0x0100, + ViewGifts = 0x0200, + SellGifts = 0x0400, + GiftSettings = 0x0800, + TransferGifts = 0x1000, + TransferStars = 0x2000, + ManageStories = 0x4000, +}; +inline constexpr bool is_flag_type(ChatbotsPermission) { return true; } +using ChatbotsPermissions = base::flags; + [[nodiscard]] MTPInputBusinessRecipients ForMessagesToMTP( const BusinessRecipients &data); [[nodiscard]] MTPInputBusinessBotRecipients ForBotsToMTP( @@ -67,6 +87,9 @@ enum class BusinessRecipientsType : uchar { [[nodiscard]] BusinessRecipients FromMTP( not_null owner, const MTPBusinessBotRecipients &recipients); +[[nodiscard]] ChatbotsPermissions FromMTP( + const MTPBusinessBotRights &rights); +[[nodiscard]] MTPBusinessBotRights ToMTP(ChatbotsPermissions rights); struct Timezone { QString id; diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp index e85df4a996..02362ec772 100644 --- a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp +++ b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/business/settings_chatbots.h" #include "apiwrap.h" +#include "boxes/peers/edit_peer_permissions_box.h" #include "boxes/peers/prepare_short_info_box.h" #include "boxes/peer_list_box.h" #include "core/application.h" @@ -47,6 +48,25 @@ struct BotState { LookupState state = LookupState::Empty; }; +[[nodiscard]] constexpr Data::ChatbotsPermissions Defaults() { + using Flag = Data::ChatbotsPermission; + return Flag::ViewMessages + | Flag::ReplyToMessages + | Flag::MarkAsRead + | Flag::DeleteSent + | Flag::DeleteReceived + | Flag::EditName + | Flag::EditBio + | Flag::EditUserpic + | Flag::EditUsername + | Flag::ViewGifts + | Flag::SellGifts + | Flag::GiftSettings + | Flag::TransferGifts + | Flag::TransferStars + | Flag::ManageStories; +} + class Chatbots final : public BusinessSection { public: Chatbots( @@ -70,7 +90,8 @@ private: rpl::variable _recipients; rpl::variable _usernameValue; rpl::variable _botValue; - rpl::variable _repliesAllowed = true; + rpl::variable _permissions = Defaults(); + Fn _resolvePermissions; }; @@ -410,7 +431,7 @@ void Chatbots::setupContent( const auto current = controller->session().data().chatbots().current(); _recipients = Data::BusinessRecipients::MakeValid(current.recipients); - _repliesAllowed = current.repliesAllowed; + _permissions = current.permissions; AddDividerTextWithLottie(content, { .lottie = u"robot"_q, @@ -472,21 +493,14 @@ void Chatbots::setupContent( Ui::AddSkip(content); Ui::AddSubsectionTitle(content, tr::lng_chatbots_permissions_title()); - content->add(object_ptr( - content, - tr::lng_chatbots_reply(), - st::settingsButtonNoIcon - ))->toggleOn(_repliesAllowed.value())->toggledChanges( - ) | rpl::start_with_next([=](bool value) { - _repliesAllowed = value; - }, content->lifetime()); - Ui::AddSkip(content); - Ui::AddDividerText( + auto permissions = CreateEditChatbotPermissions( content, - tr::lng_chatbots_reply_about(), - st::settingsChatbotsBottomTextMargin, - RectPart::Top); + _permissions.current()); + content->add(std::move(permissions.widget)); + _resolvePermissions = permissions.value; + + Ui::AddSkip(content); Ui::ResizeFitChild(this, content); } @@ -503,7 +517,7 @@ void Chatbots::save() { controller()->session().data().chatbots().save({ .bot = _botValue.current().bot, .recipients = _recipients.current(), - .repliesAllowed = _repliesAllowed.current(), + .permissions = _resolvePermissions(), }, [=] { }, fail); } diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp index b1bee49426..a59c30b63f 100644 --- a/Telegram/SourceFiles/settings/settings_business.cpp +++ b/Telegram/SourceFiles/settings/settings_business.cpp @@ -148,7 +148,6 @@ using Order = std::vector; tr::lng_business_subtitle_chat_intro(), tr::lng_business_about_chat_intro(), PremiumFeature::ChatIntro, - true, }, }, { @@ -158,7 +157,6 @@ using Order = std::vector; tr::lng_business_subtitle_chat_links(), tr::lng_business_about_chat_links(), PremiumFeature::ChatLinks, - true, }, }, { @@ -168,7 +166,6 @@ using Order = std::vector; tr::lng_premium_summary_subtitle_filter_tags(), tr::lng_premium_summary_about_filter_tags(), PremiumFeature::FilterTags, - true, }, }, }; diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp index 20a9d36459..00a09723fb 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp @@ -1734,10 +1734,10 @@ object_ptr GiftsAutoSavePrivacyController::setupBelowWidget( content, tr::lng_edit_privacy_gifts_types()); const auto types = base::flat_map>{ - { Type::Premium, tr::lng_edit_privacy_gifts_premium() }, { Type::Unlimited, tr::lng_edit_privacy_gifts_unlimited() }, { Type::Limited, tr::lng_edit_privacy_gifts_limited() }, { Type::Unique, tr::lng_edit_privacy_gifts_unique() }, + { Type::Premium, tr::lng_edit_privacy_gifts_premium() }, }; for (const auto &[type, title] : types) { const auto button = content->add(object_ptr(