Skip to content

chat admin: members, admins, invites, topics, permissions and the admin log on the registry - #11

Merged
erfnzdeh merged 11 commits into
mainfrom
feat/pr7-chat-admin
Sep 4, 2026
Merged

chat admin: members, admins, invites, topics, permissions and the admin log on the registry#11
erfnzdeh merged 11 commits into
mainfrom
feat/pr7-chat-admin

Conversation

@erfnzdeh

@erfnzdeh erfnzdeh commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

PR-7 of the v2 plan: group and channel administration on the operation registry — 86 operations: chat create/edit/join/leave/convert/transfer/delete/import, chat member, chat admin, chat permission, chat invite, chat request, chat topic, chat admin-log, chat stats, chat setting/username/photo/discussion/send-as/revenue/sponsored/direct/suggested-post/similar/verification, boost. The last legacy chat commands (chat create, chat members) and their routes are deleted.

  • One rights vocabulary (tlgr/ops/_rights.py): the canonical hyphenated names for admin and banned rights, printed by chat permission list, consumed by chat admin promote --rights/--grant/--revoke, chat member restrict --allow/--deny and chat permission set; every mask write is read-modify-write and every get round-trips into set.
  • Basic group vs supergroup dispatch happens inside each op; supergroup-only operations offer --upgrade instead of migrating silently; chat leave probes the future-owner rule before an owner leaves.
  • Invites with expiry/limits/approval/subscription pricing, join-request handling, importers; topics lifecycle incl. general-topic hiding; admin log with all filters and cursors; stats with async graph loading; boosts status/list/apply/links.
  • Layer 229 only, refused with exit 13: communities and ephemeral welcome messages, plus the two admin rights that have no field in layer 227.

Numbers

11 commits, 9,890 tests green (164 new against a fake Telegram that now models participants, invites, topics, admin log, boosts and stats), ruff + mypy clean, make docs parity with no diff. Parity after the rebase onto main: groups_channels_admin P0 12/12, 156/162 covered (96.3%), 100% accounted; registry-wide 479 operations, 1,285 covered ids, P0 floor 148.

Rebase notes

The branch was written against the PR-3 merge and landed on a main that has since taken PR-2/4/5/6/9/11. Where the two met:

  • models.PhotoResult was claimed by both this branch (a chat photo) and PR-5 (a profile photo); the chat one is ChatPhotoResult.
  • The fake client's World already had participants (call participants), so this branch's chat participants are World.members / World.add_member.
  • tlgr/ops/__init__.py auto-discovers modules on main, so the hand list this branch touched is gone; tlgr/core/client.py and tlgr/daemon/ipc.py lose the chat methods here and had already lost the contact/user ones.
  • LEGACY_EXTRAS is now empty: chat create and chat members were the last entries.
  • Two test expectations moved for reasons that belong to the rebase rather than to this branch, and are corrected in their own commit: the registry-contract module check (ids' first segment vs the module the impl lives in — chat_stats registers boost.* too), and vc.link's fallback link (the merged fake mints a real invite instead of a fixed one).

…hapes, and one rights vocabulary

Rights are stored inverted by Telegram and spelled four different ways across
the admin surface. models/peer.Rights gains the four flags layer 227 added,
ops/_rights.py owns the canonical hyphenated names and both conversions, and
ops/_admin.py keeps the participant wrapper v1 flattened away.
…asks are read before they are written

chat member list/get/add/remove/ban/unban/restrict/edit/delete-history/report,
chat request list/approve/deny, chat admin list/promote/demote, chat permission
get/list/set, chat admin-log list/report and chat transfer. Every mask-writing
call reads the current mask first, because channels.editBanned replaces it
whole and a flag you omit is a flag you cleared.
…ent objects

chat invite create/edit/revoke/delete/get/list/open and chat join. The replaced
permanent link is reported beside the new one, join reports its three separate
successes, and the QR code is rendered locally through the optional tlgr[qr]
extra rather than by carrying an encoder.
…xceptions honoured

General is id 1, cannot be deleted, is the only topic that may be hidden, and
is the one topic whose top_msg_id must be omitted rather than sent. Deleting a
topic drains affectedHistory; muting one writes an absolute mute_until, so a
timed topic mute cannot resolve to 1970 the way v1's chat mute did.
…to, send-as and discussion

The Manage screen is one screen and twenty MTProto methods, so chat setting set
is a batch: a toggle already in the requested state is reported in already and
never sent, and a refusal lands in failed per key instead of hiding the nine
changes that did work. v1's hand-written chat create/members and their IPC
routes are deleted; both paths stay invocable through the registry.
…s, affiliates — and seven layer-229 refusals

tlgr reports sponsored messages as data and never sends viewSponsoredMessage:
a headless CLI has no viewport and an invented impression is a lie. chat
community * and chat welcome * are registered and exit 13 with the reason,
because a command that refuses teaches an agent what is missing while a command
that is absent teaches it nothing.
…ted verbatim

Every stats.* call follows STATS_MIGRATE to channelFull.stats_dc, because a
client that stays on the home DC reports 'no statistics' for a channel that has
plenty. Graph payloads are Telegram's own chart spec, never redrawn; revenue is
read-only because the withdrawal call moves money and wants the 2FA password.
…and reopens

The fake grows a participant/invite/topic/log/boost world, so a ban really moves
somebody into the banned set and a second restrict really patches the mask it
read. Three properties get the attention: mask completeness (editBanned replaces
the whole thing), polarity (banned rights are inverted), and the seven
layer-229 refusals exiting 13 with a reason.
… retired

86 operations land in docs/reference/chat.md and boost.md, generated. The
groups_channels_admin domain waiver is gone: the eight ids left in it each name
the group that owns them, which is what makes 'the groups-and-channels group is
done' checkable. P0 floor 53 -> 65, covered floor 350 -> 500. Also drops the
scratch directory an earlier commit picked up.
Both broke only when this branch met `main`, so they are corrected here
rather than folded into the commit that "caused" them.

`test_every_registered_op_comes_from_a_discovered_module` asserted that
every id's first segment names a module. That was a usable proxy while
each module owned exactly one noun; `chat_stats` registers `chat stats`,
`chat revenue` *and* `boost`, because all three read the statistics DC —
so the proxy failed on a layout that is correct. It now checks the module
the spec's `impl` is defined in, which is what discovery actually finds.

`test_a_private_chat_falls_back_to_the_chat_invite` pinned the fake's
fixed `+fallback` link. The merged fake holds invites as state — PR-7's
`messages.exportChatInvite` mints and stores a real one — so the test now
compares against the invite the world actually holds.
The floor only means something if a PR that adds coverage raises it: 1132
was PR-6's number, and leaving it there would let this branch's 153 new
covered ids be dropped again without the gate noticing.
@erfnzdeh
erfnzdeh merged commit b8873ca into main Sep 4, 2026
20 checks passed
@erfnzdeh
erfnzdeh deleted the feat/pr7-chat-admin branch September 4, 2026 00:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant