From f93e1f41881b645ca0e6ee84c6bd06c1553fab3a Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 24 Feb 2026 17:17:59 +0400 Subject: [PATCH] Support participant rank changes in admin log. --- Telegram/Resources/langs/lang.strings | 8 ++ .../boxes/peers/add_bot_to_chat_box.cpp | 2 +- .../boxes/peers/add_participants_box.cpp | 9 ++- .../boxes/peers/add_participants_box.h | 4 +- .../boxes/peers/edit_participant_box.cpp | 25 ++++-- .../boxes/peers/edit_participant_box.h | 4 +- .../boxes/peers/edit_participants_box.cpp | 52 +++++++----- .../boxes/peers/edit_participants_box.h | 6 +- .../admin_log/history_admin_log_filter.cpp | 1 + .../history_admin_log_filter_value.h | 3 +- .../admin_log/history_admin_log_inner.cpp | 3 +- .../admin_log/history_admin_log_item.cpp | 79 ++++++++++++++++++- Telegram/SourceFiles/mtproto/scheme/api.tl | 5 +- 13 files changed, 160 insertions(+), 41 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ec658928a1..a900c868ab 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -6300,6 +6300,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_admin_log_filter_subscribers_removed" = "Subscribers leaving"; "lng_admin_log_filter_topics" = "Topics"; "lng_admin_log_filter_sub_extend" = "Subscription Renewals"; +"lng_admin_log_filter_edit_rank" = "Tag Changes"; "lng_admin_log_filter_all_admins" = "All users and admins"; "lng_admin_log_filter_actions_admins_subtitle" = "Filter actions by admins"; "lng_admin_log_filter_actions_admins_section" = "Show Actions by All Admins"; @@ -6488,6 +6489,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_admin_log_admin_add_admins" = "Add new admins"; "lng_admin_log_subscription_extend" = "{name} renewed subscription until {date}"; +"lng_admin_log_changed_rank_from" = "{from} changed tag for {user} from \"{previous}\" to \"{tag}\""; +"lng_admin_log_set_rank" = "{from} set tag for {user} to \"{tag}\""; +"lng_admin_log_removed_rank" = "{from} removed tag for {user} (was \"{previous}\")"; +"lng_admin_log_changed_own_rank_from" = "{from} changed own tag from \"{previous}\" to \"{tag}\""; +"lng_admin_log_set_own_rank" = "{from} set own tag to \"{tag}\""; +"lng_admin_log_removed_own_rank" = "{from} removed own tag (was \"{previous}\")"; + "lng_admin_log_antispam_menu_report" = "Report False Positive"; "lng_admin_log_antispam_menu_report_toast" = "You can manage anti-spam settings in {link}."; "lng_admin_log_antispam_menu_report_toast_link" = "Group Info > Administrators"; diff --git a/Telegram/SourceFiles/boxes/peers/add_bot_to_chat_box.cpp b/Telegram/SourceFiles/boxes/peers/add_bot_to_chat_box.cpp index 956abf728c..de701a5215 100644 --- a/Telegram/SourceFiles/boxes/peers/add_bot_to_chat_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_bot_to_chat_box.cpp @@ -233,7 +233,7 @@ void AddBotToGroupBoxController::addBotToGroup(not_null chat) { const auto token = _token; const auto done = [=]( ChatAdminRightsInfo newRights, - const QString &rank) { + const std::optional &rank) { if (scope == Scope::GroupAdmin) { chat->session().api().sendBotStart(show, bot, chat, token); } diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index 4b08181573..d87ebcb487 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -1507,7 +1507,7 @@ void AddSpecialBoxController::showAdmin( if (_additional.canAddOrEditAdmin(user)) { const auto done = crl::guard(this, [=]( ChatAdminRightsInfo newRights, - const QString &rank) { + const std::optional &rank) { editAdminDone(user, newRights, rank); }); const auto fail = crl::guard(this, [=] { @@ -1524,12 +1524,15 @@ void AddSpecialBoxController::showAdmin( void AddSpecialBoxController::editAdminDone( not_null user, ChatAdminRightsInfo rights, - const QString &rank) { + const std::optional &rank) { if (_editParticipantBox) { _editParticipantBox->closeBox(); } - _additional.applyAdminLocally(user, rights, rank); + _additional.applyAdminLocally( + user, + rights, + rank.value_or(_additional.memberRank(user))); // _adminDoneCallback should call changes().chatAdminUpdated. if (const auto callback = _adminDoneCallback) { callback(user, rights, rank); diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.h b/Telegram/SourceFiles/boxes/peers/add_participants_box.h index dc06e8040f..7a6aafa24e 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.h +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.h @@ -101,7 +101,7 @@ public: using AdminDoneCallback = Fn user, ChatAdminRightsInfo adminRights, - const QString &rank)>; + const std::optional &rank)>; using BannedDoneCallback = Fn participant, ChatRestrictionsInfo bannedRights)>; @@ -133,7 +133,7 @@ private: void editAdminDone( not_null user, ChatAdminRightsInfo rights, - const QString &rank); + const std::optional &rank); void showRestricted(not_null user, bool sure = false); void editRestrictedDone( not_null participant, diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp index 88ab641fa0..e955b6a5c3 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp @@ -570,7 +570,7 @@ void EditAdminBox::prepare() { if (const auto callback = _saveCallback) { const auto old = _oldRights; const auto confirmed = [=](Fn close) { - callback(old, ChatAdminRightsInfo(), QString()); + callback(old, {}, {}); close(); }; uiShow()->showBox( @@ -595,8 +595,8 @@ void EditAdminBox::prepare() { _oldRights, ChatAdminRightsInfo(newFlags), _tagControl - ? _tagControl->currentRank() - : QString()); + ? std::optional(_tagControl->currentRank()) + : std::nullopt); }; _save = [=] { const auto show = uiShow(); @@ -877,13 +877,28 @@ void EditRestrictedBox::prepare() { const auto savedPeer = peer(); const auto done = [=]( ChatAdminRightsInfo newRights, - const QString &rank) { + const std::optional &rank) { closeBoth(); + const auto effectiveRank = rank.value_or([&] { + const auto ch = savedPeer->asChannel(); + if (!ch) { + return QString(); + } + const auto info = ch->mgInfo.get(); + if (!info) { + return QString(); + } + const auto i = info->memberRanks.find( + peerToUser(savedUser->id)); + return (i != end(info->memberRanks)) + ? i->second + : QString(); + }()); savedUser->session().changes().chatAdminChanged( savedPeer, savedUser, newRights.flags, - rank); + effectiveRank); }; const auto fail = closeBoth; adminBox->setSaveCallback( diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h index 1001f6961f..b3c1df3cfb 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h @@ -86,7 +86,7 @@ public: Fn callback) { + const std::optional &rank)> callback) { _saveCallback = std::move(callback); } @@ -112,7 +112,7 @@ private: Fn _saveCallback; + const std::optional &rank)> _saveCallback; base::weak_qptr _confirmBox; Ui::Checkbox *_addAsAdmin = nullptr; diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index f88326378f..14e4a687ba 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -64,7 +64,9 @@ void RemoveAdmin( Fn onDone, Fn onFail) { const auto newRights = MTP_chatAdminRights(MTP_flags(0)); + using Flag = MTPchannels_editAdmin::Flag; channel->session().api().request(MTPchannels_EditAdmin( + MTP_flags(Flag::f_rank), channel->inputChannel(), user->inputUser(), newRights, @@ -162,17 +164,31 @@ void SaveChannelAdmin( not_null user, ChatAdminRightsInfo oldRights, ChatAdminRightsInfo newRights, - const QString &rank, + const std::optional &rank, Fn onDone, Fn onFail) { + using Flag = MTPchannels_editAdmin::Flag; + const auto flags = Flag(0) + | (rank.has_value() ? Flag::f_rank : Flag(0)); channel->session().api().request(MTPchannels_EditAdmin( + MTP_flags(flags), channel->inputChannel(), user->inputUser(), AdminRightsToMTP(newRights), - MTP_string(rank) + rank ? MTP_string(*rank) : MTPstring() )).done([=](const MTPUpdates &result) { channel->session().api().applyUpdates(result); - channel->applyEditAdmin(user, oldRights, newRights, rank); + const auto effectiveRank = rank.value_or([&] { + if (const auto info = channel->mgInfo.get()) { + const auto i = info->memberRanks.find( + peerToUser(user->id)); + if (i != end(info->memberRanks)) { + return i->second; + } + } + return QString(); + }()); + channel->applyEditAdmin(user, oldRights, newRights, effectiveRank); if (onDone) { onDone(); } @@ -264,18 +280,18 @@ void SaveMemberRank( Fn SaveAdminCallback( + const std::optional &rank)> SaveAdminCallback( std::shared_ptr show, not_null peer, not_null user, Fn onDone, + const std::optional &rank)> onDone, Fn onFail) { return [=]( ChatAdminRightsInfo oldRights, ChatAdminRightsInfo newRights, - const QString &rank) { + const std::optional &rank) { const auto done = [=] { if (onDone) onDone(newRights, rank); }; const auto saveForChannel = [=](not_null channel) { SaveChannelAdmin( @@ -293,7 +309,7 @@ FndefaultAdminRights(user).flags - && rank.isEmpty()) { + && (!rank.has_value() || rank->isEmpty())) { saveChatAdmin(true); } else if (!newRights.flags) { saveChatAdmin(false); @@ -1121,7 +1137,7 @@ void ParticipantsBoxController::addNewItem() { const auto adminDone = crl::guard(this, [=]( not_null user, ChatAdminRightsInfo rights, - const QString &rank) { + const std::optional &rank) { editAdminDone(user, rights, rank); }); const auto restrictedDone = crl::guard(this, [=]( @@ -1990,7 +2006,7 @@ void ParticipantsBoxController::showAdmin(not_null user) { if (_additional.canAddOrEditAdmin(user)) { const auto done = crl::guard(this, [=]( ChatAdminRightsInfo newRights, - const QString &rank) { + const std::optional &rank) { editAdminDone(user, newRights, rank); }); const auto fail = crl::guard(this, [=] { @@ -2008,18 +2024,20 @@ void ParticipantsBoxController::showAdmin(not_null user) { void ParticipantsBoxController::editAdminDone( not_null user, ChatAdminRightsInfo rights, - const QString &rank) { + const std::optional &rank) { _addBox = nullptr; if (_editParticipantBox) { _editParticipantBox->closeBox(); } - _additional.applyAdminLocally(user, rights, rank); + const auto effectiveRank = rank.value_or( + _additional.memberRank(user)); + _additional.applyAdminLocally(user, rights, effectiveRank); recomputeTypeFor(user); refreshRows(); const auto flags = rights.flags; - user->session().changes().chatAdminChanged(_peer, user, flags, rank); + user->session().changes().chatAdminChanged(_peer, user, flags, effectiveRank); } void ParticipantsBoxController::showRestricted(not_null user) { @@ -2162,10 +2180,7 @@ void ParticipantsBoxController::removeAdminSure(not_null user) { if (const auto chat = _peer->asChat()) { const auto show = delegate()->peerListUiShow(); SaveChatAdmin(show, chat, user, false, crl::guard(this, [=] { - editAdminDone( - user, - ChatAdminRightsInfo(), - QString()); + editAdminDone(user, {}, {}); }), nullptr); } else if (const auto channel = _peer->asChannel()) { const auto adminRights = _additional.adminRights(user); @@ -2174,10 +2189,7 @@ void ParticipantsBoxController::removeAdminSure(not_null user) { } const auto show = delegate()->peerListUiShow(); RemoveAdmin(show, channel, user, *adminRights, crl::guard(this, [=] { - editAdminDone( - user, - ChatAdminRightsInfo(), - QString()); + editAdminDone(user, {}, {}); }), nullptr); } } diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h index 7306cd8e79..1d8a14dd80 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h @@ -32,13 +32,13 @@ class ChatParticipant; Fn SaveAdminCallback( + const std::optional &rank)> SaveAdminCallback( std::shared_ptr show, not_null peer, not_null user, Fn onDone, + const std::optional &rank)> onDone, Fn onFail); Fn user, ChatAdminRightsInfo rights, - const QString &rank); + const std::optional &rank); void showRestricted(not_null user); void editRestrictedDone( not_null participant, diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp index 9d5222d6eb..27613d8d43 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp @@ -34,6 +34,7 @@ EditFlagsDescriptor FilterValueLabels(bool isChannel) { auto members = std::vector