Skip to content

Allow restricting and banning users from recent actions log in groups - #100

Open
CHUKEPC wants to merge 8 commits into
TelegramMessenger:masterfrom
CHUKEPC:fix/admin-log-ban-users
Open

CHUKEPC wants to merge 8 commits into
TelegramMessenger:masterfrom
CHUKEPC:fix/admin-log-ban-users

Conversation

@CHUKEPC

@CHUKEPC CHUKEPC commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Key change

  • Add localized Restrict and Ban actions to message context menus in Recent Actions only. Ordinary chat, pinned-message and thread message menus remain unchanged.
  • Keep Recent Actions avatar menus usable across refreshes of the same semantic row context, including message-version-only changes. Compare live chat identity, kind, participation/creator status and effective ban/promote/manage-ranks permissions rather than the complete group/channel object. Title, photo, member-count, boost and unrelated-rights updates preserve existing menus, including story/premium-video contexts. Meaningful account, interaction, author, row/message, mode/thread and story-availability changes or removal invalidate stale actions, including nested actions. Ordinary group-chat avatar Restrict keeps its existing flow.
  • Add moderation to service-row and service-text menus while preserving Copy, link copying and translation. Grouped deleted-message headers show at most 10 author submenus; larger collapsed groups can be expanded to reach the remaining authors.
  • Authorize moderation before enabling the actions, including when callers have no participant snapshot. Restrict/Ban start disabled while a fresh channel/participant lookup is pending, even with an allowed historical snapshot. Owners, uneditable admins and unavailable, mismatched or timed-out results remove these items; redundant separators and empty author submenus are pruned. Copy, Profile, Send Message and other existing actions remain immediately usable.
  • Enable the same item instances after authorization rather than replacing their handlers. This preserves existing avatar semantic-binding guards across delayed responses, same-context refreshes, actual row reuse and removal. The shared hook stays synchronous; no new screen-package dependency or generic menu API is introduced by this authorization fix.
  • Revalidate permissions and the participant again before opening or applying restrictions. Read-only authorization and post-update verification are bounded separately; no timeout covers the submitted mutation and local member-list replay. Success requires verified resulting rights, and uncertain verification has its own warning. Cancelling before submission prevents the update; a submitted engine operation can finish its local replay independently of the progress UI. The completion-only engine API retains its existing retry behavior.
  • Preserve localized Copy Selected Text and resolve the link under the right-click rather than another selected link. A feature-local TextView subclass uses an open rightMouseDown override; the shared implementation and unrelated text-menu behavior are unchanged.

Reason

Recent Actions previously supported Restrict through a message author's avatar menu, but not Restrict/Ban in message menus or moderation on service rows.

The owner/admin review identified that callers with no participant snapshot could offer actions against the group owner or an uneditable admin, only to fail at invocation. The shared authorization gate now keeps those actions disabled while checking and removes them after denial, without delaying unrelated menu actions. Disabled placeholders may briefly appear during the lookup; they cannot invoke moderation through their handlers or native menu validation.

Screenshot/video result (before/after)

The original PR screenshots illustrate the message-menu change. They are retained from the earlier revision and do not demonstrate the pending-authorization state introduced by this follow-up.

Before

The Recent Actions message context menu did not offer Restrict or Ban. Restrict was already available through the author's avatar menu.

Before - Message context menu without Restrict or Ban

After

Eligible members can be restricted or banned from their message context menus in Recent Actions.

After - Restrict and Ban in the message context menu

Confirmation

Verification environment: macOS 27.0 (26A428), Apple Silicon; Xcode 27.1. The existing client screenshots are from Telegram 13.1.0, build 283357 (Debug).

  • Native xcodebuild test -scheme PeerModerationUITests passes 20 tests: 11 ChannelParticipantModerationMenuTests and nine retained ChatAvatarMenuBindingTests, with zero failures or skips. Actual PeerModerationUI and ChatUI targets compile and link against real TelegramCore, Postbox, TGUIKit and SwiftSignalKit dependencies.
  • Nil-snapshot regression: create the real moderation items, invoke their handlers before resolution, then deliver owner/uneditable-admin results. Expected and actual: no moderation callback before authorization, denied items disappear, and Copy remains usable. Native menu validation also remains disabled while pending.
  • Further coverage: ordinary/editable members, stale allowed snapshots, missing/mismatched/revoked results, synchronous completion before attachment, timeout, disposal and late results, fresh invocation-time authorization, separator/submenu cleanup and delayed authorization across avatar semantic bindings. The retained avatar suite covers metadata refreshes, permission/context changes, nested actions, liveness and removal.
  • The first native run exposed missing app theme images during TGUIKit initialization in the isolated test process. The test bootstrap now registers five real existing asset images before initialization; the entire 20-case selection then passes. No production workaround was added.
  • python3 -I tools/tests/participant-moderation/run.py selects these native suites through the committed workspace scheme. Missing native prerequisites fail explicitly rather than falling back to extracted source or stub models.
  • The tested production/test sources and reported message/avatar/service callers match the feature worktree. Swift parsing, package-manifest parsing, runner/scheme validation and git diff --check pass. Local Xcode 27/macOS 12.0 settings and native-library link accommodations remain outside the PR.

These are native menu-object, model, signal and handler tests; they do not simulate displayed-menu mouse/keyboard tracking or Telegram server mutations. Earlier extracted-source moderation/service/text harness results are historical and are not evidence for the new gate. Their former immediate-enabled-menu expectation is no longer the intended behavior. The native build targets macOS 12.0 rather than providing macOS 10.13 runtime verification.

  • I confirm that I have tested the affected behavior and verified that it works properly, or verified the content and links for a documentation-only change.
@macosswift

Copy link
Copy Markdown
Collaborator

Issues:

CONFIRMED: ChannelEventLogItem.swift:957-958, 969-975: the service-row menu offers Restrict/Ban for the acting or promoted admin (including the owner on rows like title changes) because only the current user and non-user peers are filtered; Ban on the owner or on an admin you did not promote will normally be rejected by the server, yet the success checkmark is shown immediately and the request is retried indefinitely, and the same missing owner/admin check applies to the new message-menu (ChatMessageMenuItems.swift:1475-1476) and avatar-menu (ChatRowView.swift:1518) items

CONFIRMED: ChannelEventLogItem.swift:939, 1011-1018: the service row's text views handle right-click themselves (Copy menu) and never pass it on to the row, so the new Restrict/Ban items only show when the click lands on blank row area or the avatar, not on the row text

CONFIRMED: ChatRowView.swift:1518-1523: the new avatar-menu Ban has no !channel.isChannel check, no user/bot filter and no participant fetch, so on an anonymous-admin message it targets the group itself, in a broadcast channel's log it is offered to the owner, and the success checkmark is shown regardless

CONFIRMED: ChatMessageMenuItems.swift:1474-1486: Restrict is gated only on banMembers, so every admin with that right now gets Restrict on every member message in ordinary supergroups (also in pinned and thread views), not only in Recent Actions, and it duplicates the avatar-menu Restrict; only Ban is limited to the log

CONFIRMED: ChannelEventLogItem.swift:949-950, 981-1005: for a grouped "deleted messages" row the menu lists a Restrict and a Ban item for every distinct author (80 items for 40 authors) with no cap, and the "Restrict - Name" / "Ban - Name" titles are hard-coded literals without a //TODOLANG marker

CONFIRMED: PR description: the "before" claim that admins had no moderation actions is inaccurate for message rows, since the avatar menu in Recent Actions already had Restrict; what the PR adds is Ban on message rows, Restrict in the message menu, and Restrict/Ban on service rows

CONFIRMED: ChannelEventLogItem.swift:994: ChannelAdminUI opens another feature's restricted modal through ChatHistoryUIHooks, although it has its own ChannelAdminUIHooks for cross-feature screens

CONFIRMED: ChannelEventLogItem.swift:951-974: prev is bound but never used in three cases (compiler warnings), and the join/leave case has the same body as default

@CHUKEPC

CHUKEPC commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review. Added fresh participant authorization to all three menus, including owner/admin, self, user/bot and broadcast checks. Moderation is revalidated before acting; retries are bounded and success is shown only after the resulting rights are confirmed. Message-menu additions are now limited to Recent Actions.

Service text menus now include the actions while retaining Copy/link copying and translation. Grouped deletions are limited to 10 named author submenus, with an expansion option for the remaining messages. The actions use ChannelAdminUI's own hook, and the unused bindings and duplicate switch case are removed. The description now correctly notes that the avatar menu already had Restrict.

Also merged current upstream and resolved the message-menu conflict while retaining its new selected-photo save flow alongside the log-only moderation signal. The full Debug workspace build passed again on that integration, along with 40 moderation assertions, 18 service-menu assertions and 15 source-wiring checks. The harnesses use model/UI doubles; no new live server or manual UI testing is claimed.

@macosswift

Copy link
Copy Markdown
Collaborator

This pull request is not filled in correctly: the Confirmation section does not state the macOS version. Please complete every section as described in CONTRIBUTING.md and update the PR so it can be reviewed.

@macosswift

Copy link
Copy Markdown
Collaborator

Issues:

CONFIRMED: ChatRowView.swift:1515-1530: the avatar-menu Restrict/Ban items are now added in menu.onShow after a network fetch, but the custom menu has already been sized by then and is never resized, so the new rows land below the visible area; with the native context menu onShow never runs, so they are never added. Because this path replaces the old synchronous Restrict in every group chat, admins effectively lose avatar-menu Restrict everywhere.

CONFIRMED: ChatMessageMenuItems.swift:343-358: in Recent Actions the whole message context menu (Copy, Save and the rest) now waits for a channels.getParticipant request with a 10 s timeout, because the moderation items are joined into the menu signal with combineLatest, so on a slow or offline connection right-clicking a logged message shows nothing for up to 10 seconds.

CONFIRMED: ChannelEventLogItem.swift:929-966, 980-1041: every service-row text now gets a menuItems closure that waits for up to 10 participant fetches, so right-clicking service text in Recent Actions shows no menu at all, not even Copy, until every request answers or times out (before, Copy appeared at once).

CONFIRMED: ChannelParticipantController.swift:475-502: the 15 s timeout covers four sequential network round trips, and when it fires it cancels the chain and shows "unknown error" even if the restriction or ban was already applied on the server, and it can skip the local member-list update.

CONFIRMED: ChatHistoryUIHooks.swift:274, 366, 418: the PR removes the only caller of makeRestrictedModalController but leaves the hook property, its init parameter and the app installer (ChatHistoryUIHooksInstall.swift:327) in place as dead code.

CONFIRMED: PR description: the description says the avatar menu only gains Ban next to the existing Restrict in Recent Actions, but the avatar-menu Restrict is rewritten in every group chat: it now loads after a network fetch at the end of the menu, is hidden for the owner, uneditable admins and channels, and saving shows a progress modal and an alert instead of the silent update.

CONFIRMED: ChannelEventLogItem.swift:937-957: the new service-row text menu looks up the link at the start of the current selection instead of under the click, so with some text selected, right-clicking a mention elsewhere shows "Copy Username" but copies the selected text or a different link.

@CHUKEPC

CHUKEPC commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Completed the Confirmation environment details: macOS 27.0 (Apple Silicon), Telegram 13.1.0, build 283357 (Debug).

Also addressed the subsequent review: Recent Actions moderation items are built synchronously before either menu implementation opens, so existing Copy/Save/text actions no longer wait for participant requests. Ordinary group avatar Restrict is restored to its original behavior, including its existing hook. Local eligibility uses available participant snapshots; fresh authorization happens when choosing an action and again before applying restrictions.

Removed the whole-operation timeout: only read-only authorization and verification are bounded, while a submitted mutation completes its local member-list replay. Verification uncertainty has a distinct warning rather than a misleading generic failure. Service text menus resolve the link under the actual click, independently of another selection. Updated the description to match this scope. The focused harnesses pass 50 moderation, 19 service-menu, 13 clicked-text-menu, and 22 wiring assertions; four old-fail/new-pass controls detect the reviewed regressions. The full Debug workspace build passes with all seven review corrections integrated against upstream 70516222 (local macOS 12 compatibility settings). All 21 functional TextView menu-lifecycle tests also pass; the opt-in benchmark was skipped.

@macosswift

Copy link
Copy Markdown
Collaborator

Issues:

CONFIRMED: ChatRowView.swift:1438, 1515-1517, 1543-1549: the avatar context menu is built once with the row's peer when the avatar view is created and is not rebuilt when the row view is reused for another author, so in Recent Actions right-clicking user B's avatar can show the menu for user A, and the new Ban item (immediate, no confirmation, no name shown) bans A.

CONFIRMED: ChannelEventLogItem.swift:155-198: the new service-row text menu is installed in every Recent Actions log even when there are no moderation items (broadcast channels, admins without ban rights), and it renames the copy item from "Copy Selected Text" to "Copy Text" although it still copies only the word under the cursor.

@CHUKEPC

CHUKEPC commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Avatar menu bindings now refresh with every peer/row assignment, and binding tokens invalidate stale factories and actions—including nested submenu actions—after reuse or removal. The actual avatar binding test covers A→B reuse, same-peer replacement and cleared bindings.

Service text menus now delegate entirely to the original TextView behavior when no moderation actions exist. Augmented menus retain the original localized Copy Selected Text label while keeping clicked-link copying. The existing moderation/service harnesses pass, including 15 text-menu assertions covering fallback behavior. The full Debug workspace build passed against upstream 30097e3b.

@macosswift

Copy link
Copy Markdown
Collaborator

Issues:

  • ChatAvatarView.swift:34-50: an avatar menu that is already open stops working when its row is refreshed: every setPeer call, even for the same peer, issues a new binding token, so choosing Open Profile, Send Message, Mention, Restrict or Ban from the open menu does nothing.
@CHUKEPC

CHUKEPC commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Fixed avatar-menu invalidation on same-context refreshes. The binding now survives row refreshes (including message-version-only updates) and is invalidated only by a changed semantic context or removal. Open Profile, Send Message, Mention, Restrict, Ban and nested actions remain usable; real row/author/account changes and live permission changes still reject stale actions. Six focused XCTest cases pass with the extracted production binding and real NSMenu objects; restoring unconditional token rotation fails two cases. The description and verification scope are updated.

@macosswift

Copy link
Copy Markdown
Collaborator

This doesn't compile. Please make sure the project builds before opening a PR.

Issues:

  • ChatAvatarMenuBindingTests.swift:23: TelegramChatAdminRights(flags: [.canBanUsers]) does not exist because the type's only initializer taking rights is init(rights:), so the new ChatAvatarMenuBindingTests does not compile and the whole ChatUITests target stops building.

  • ChatAvatarView.swift:119-122: the new live check compares the whole current chat peer (interaction.peer) with the snapshot taken in the last setPeer, so in ordinary list-mode chats and for story-ring or premium-video avatars the avatar menu stops opening as soon as the group or channel object changes in any field (member count, title, photo, rights, boost level), not only when ban permission changes.

@CHUKEPC

CHUKEPC commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

Fixed both issues. The test fixture now calls TelegramChatAdminRights(rights:), and the live chat snapshot compares action-relevant identity/kind/participation/creator state and effective ban/promote/manage-ranks permissions instead of the full peer object. Title, photo, member-count, boost and unrelated-rights refreshes keep menus usable before and after rebinding; meaningful permission/context changes still invalidate stale actions. Removed the adapter runner that replaced constructors and hid the compilation error. Reproduced the old failure with native xcodebuild build-for-testing, then built the complete ChatUI/ChatUITests target and ran all nine ChatAvatarMenuBindingTests against real TelegramCore/Postbox/TGUIKit: TEST SUCCEEDED, zero failures. The description and test documentation are updated.

@macosswift

Copy link
Copy Markdown
Collaborator

Issues:

  • ChannelParticipantController.swift:467-469: Restrict and Ban are offered for the group owner and for admins the user cannot edit, because the owner/admin check only runs when a participant snapshot is passed, and the message menu, the avatar menu and the service rows showing the acting admin all pass nil; choosing the item then ends in the generic strings().unknownError alert.
@CHUKEPC

CHUKEPC commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Fixed the nil-participant owner/admin path in the shared moderation menu implementation used by message, avatar and service-row menus.

Restrict/Ban now start disabled while fresh channel/participant authorization is pending, including with an allowed historical snapshot. Owners, uneditable admins and unavailable/mismatched/timed-out results remove the actions and prune redundant separators or empty author submenus. Other menu actions stay immediately usable. Authorized results enable the existing item instances, preserving the avatar's semantic-binding wrappers; action-time authorization is still performed separately.

Added native PeerModerationUI coverage and a workspace test scheme/runner. The real moderation gate, ContextMenu/NSMenuItem, TelegramCore/Postbox types and SwiftSignalKit callbacks are exercised without extraction or model stubs. 20 native XCTest cases pass: 11 new moderation tests and nine existing avatar-binding tests, zero failures. Coverage includes the reported nil-snapshot cases, pending-handler protection, stale snapshots, synchronous responses, timeout/disposal, submenu cleanup and delayed responses after avatar reuse/removal.

The description now reflects the disabled-pending behavior and current native verification; older extracted-source results are identified as historical. No shared menu implementation or unrelated caller behavior changed in this follow-up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants