tlgr-cli 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. tlgr/__init__.py +3 -0
  2. tlgr/__main__.py +6 -0
  3. tlgr/actions/__init__.py +45 -0
  4. tlgr/actions/forward.py +74 -0
  5. tlgr/actions/reply.py +32 -0
  6. tlgr/cli/__init__.py +259 -0
  7. tlgr/cli/confirm.py +55 -0
  8. tlgr/cli/errors.py +84 -0
  9. tlgr/cli/gen.py +690 -0
  10. tlgr/cli/globals.py +273 -0
  11. tlgr/cli/introspect.py +170 -0
  12. tlgr/cli/params.py +189 -0
  13. tlgr/cli/render.py +418 -0
  14. tlgr/core/__init__.py +0 -0
  15. tlgr/core/accounts.py +384 -0
  16. tlgr/core/config.py +358 -0
  17. tlgr/core/custom_tl.py +170 -0
  18. tlgr/core/errors.py +687 -0
  19. tlgr/core/eventtypes.py +1170 -0
  20. tlgr/core/identity.py +127 -0
  21. tlgr/core/launchd.py +122 -0
  22. tlgr/core/logging.py +194 -0
  23. tlgr/core/media.py +134 -0
  24. tlgr/core/output.py +251 -0
  25. tlgr/core/pagination.py +227 -0
  26. tlgr/core/paths.py +360 -0
  27. tlgr/core/peers.py +427 -0
  28. tlgr/core/process.py +138 -0
  29. tlgr/core/signing.py +38 -0
  30. tlgr/core/systemd.py +96 -0
  31. tlgr/core/telethon_compat.py +295 -0
  32. tlgr/core/text.py +211 -0
  33. tlgr/core/timefmt.py +199 -0
  34. tlgr/core/tl.py +98 -0
  35. tlgr/daemon/__init__.py +0 -0
  36. tlgr/daemon/app.py +869 -0
  37. tlgr/daemon/dispatch.py +446 -0
  38. tlgr/daemon/events.py +723 -0
  39. tlgr/daemon/files.py +431 -0
  40. tlgr/daemon/idle.py +119 -0
  41. tlgr/daemon/jobs.py +68 -0
  42. tlgr/daemon/main.py +161 -0
  43. tlgr/daemon/peercred.py +75 -0
  44. tlgr/daemon/policy.py +113 -0
  45. tlgr/daemon/preauth.py +366 -0
  46. tlgr/daemon/ratelimit.py +391 -0
  47. tlgr/daemon/server.py +24 -0
  48. tlgr/daemon/session.py +648 -0
  49. tlgr/daemon/sessions.py +274 -0
  50. tlgr/daemon/singleton.py +114 -0
  51. tlgr/daemon/stream.py +193 -0
  52. tlgr/daemon/transfers.py +219 -0
  53. tlgr/daemon/webhook.py +390 -0
  54. tlgr/data/catalog_index.json +1 -0
  55. tlgr/data/parity_waivers.toml +90 -0
  56. tlgr/filters/__init__.py +42 -0
  57. tlgr/filters/compose.py +121 -0
  58. tlgr/filters/content.py +85 -0
  59. tlgr/filters/context.py +114 -0
  60. tlgr/filters/message.py +161 -0
  61. tlgr/filters/temporal.py +87 -0
  62. tlgr/filters/user.py +36 -0
  63. tlgr/gateway/__init__.py +1 -0
  64. tlgr/gateway/config.py +161 -0
  65. tlgr/gateway/engine.py +215 -0
  66. tlgr/gateway/event.py +22 -0
  67. tlgr/jobs/__init__.py +0 -0
  68. tlgr/jobs/base.py +81 -0
  69. tlgr/jobs/client.py +37 -0
  70. tlgr/models/__init__.py +1220 -0
  71. tlgr/models/admin.py +744 -0
  72. tlgr/models/auth.py +510 -0
  73. tlgr/models/base.py +81 -0
  74. tlgr/models/bot.py +576 -0
  75. tlgr/models/business.py +265 -0
  76. tlgr/models/call.py +586 -0
  77. tlgr/models/config.py +101 -0
  78. tlgr/models/contact.py +481 -0
  79. tlgr/models/daemon.py +336 -0
  80. tlgr/models/dialog.py +626 -0
  81. tlgr/models/envelope.py +68 -0
  82. tlgr/models/error.py +30 -0
  83. tlgr/models/event.py +79 -0
  84. tlgr/models/export.py +66 -0
  85. tlgr/models/gift.py +275 -0
  86. tlgr/models/inline.py +84 -0
  87. tlgr/models/location.py +115 -0
  88. tlgr/models/media.py +507 -0
  89. tlgr/models/message.py +584 -0
  90. tlgr/models/net.py +232 -0
  91. tlgr/models/notify.py +105 -0
  92. tlgr/models/page.py +32 -0
  93. tlgr/models/payment.py +172 -0
  94. tlgr/models/peer.py +400 -0
  95. tlgr/models/poll.py +119 -0
  96. tlgr/models/premium.py +161 -0
  97. tlgr/models/privacy.py +93 -0
  98. tlgr/models/profile.py +217 -0
  99. tlgr/models/reaction.py +160 -0
  100. tlgr/models/resolve.py +175 -0
  101. tlgr/models/settings.py +103 -0
  102. tlgr/models/stars.py +101 -0
  103. tlgr/models/sticker.py +243 -0
  104. tlgr/models/story.py +467 -0
  105. tlgr/models/sync.py +105 -0
  106. tlgr/models/todo.py +36 -0
  107. tlgr/models/webapp.py +89 -0
  108. tlgr/ops/__init__.py +63 -0
  109. tlgr/ops/_admin.py +313 -0
  110. tlgr/ops/_auth.py +599 -0
  111. tlgr/ops/_bots.py +586 -0
  112. tlgr/ops/_calls.py +535 -0
  113. tlgr/ops/_common.py +160 -0
  114. tlgr/ops/_layer.py +46 -0
  115. tlgr/ops/_media.py +592 -0
  116. tlgr/ops/_params.py +212 -0
  117. tlgr/ops/_rights.py +402 -0
  118. tlgr/ops/_send.py +593 -0
  119. tlgr/ops/_serialize.py +667 -0
  120. tlgr/ops/_settings.py +306 -0
  121. tlgr/ops/_spec.py +167 -0
  122. tlgr/ops/_story.py +743 -0
  123. tlgr/ops/account.py +2604 -0
  124. tlgr/ops/agent.py +937 -0
  125. tlgr/ops/auth.py +1282 -0
  126. tlgr/ops/bot.py +4880 -0
  127. tlgr/ops/business.py +1520 -0
  128. tlgr/ops/call.py +1610 -0
  129. tlgr/ops/chat.py +4025 -0
  130. tlgr/ops/chat_admin.py +929 -0
  131. tlgr/ops/chat_extra.py +1061 -0
  132. tlgr/ops/chat_invite.py +716 -0
  133. tlgr/ops/chat_manage.py +1691 -0
  134. tlgr/ops/chat_member.py +1357 -0
  135. tlgr/ops/chat_stats.py +902 -0
  136. tlgr/ops/chat_topic.py +905 -0
  137. tlgr/ops/conference.py +791 -0
  138. tlgr/ops/config.py +1698 -0
  139. tlgr/ops/contact.py +2330 -0
  140. tlgr/ops/daemon.py +1397 -0
  141. tlgr/ops/draft.py +299 -0
  142. tlgr/ops/emoji.py +343 -0
  143. tlgr/ops/events.py +1327 -0
  144. tlgr/ops/export.py +596 -0
  145. tlgr/ops/folder.py +1322 -0
  146. tlgr/ops/gif.py +522 -0
  147. tlgr/ops/gift.py +1546 -0
  148. tlgr/ops/giveaway.py +541 -0
  149. tlgr/ops/inline.py +773 -0
  150. tlgr/ops/job.py +799 -0
  151. tlgr/ops/location.py +917 -0
  152. tlgr/ops/media.py +4495 -0
  153. tlgr/ops/message.py +3769 -0
  154. tlgr/ops/net.py +536 -0
  155. tlgr/ops/notify.py +840 -0
  156. tlgr/ops/passport.py +464 -0
  157. tlgr/ops/payment.py +907 -0
  158. tlgr/ops/poll.py +1078 -0
  159. tlgr/ops/premium.py +488 -0
  160. tlgr/ops/privacy.py +794 -0
  161. tlgr/ops/profile.py +1481 -0
  162. tlgr/ops/proxy.py +750 -0
  163. tlgr/ops/reaction.py +1475 -0
  164. tlgr/ops/resolve.py +1140 -0
  165. tlgr/ops/search.py +521 -0
  166. tlgr/ops/settings.py +1066 -0
  167. tlgr/ops/stars.py +594 -0
  168. tlgr/ops/sticker.py +1602 -0
  169. tlgr/ops/story.py +3216 -0
  170. tlgr/ops/sync.py +788 -0
  171. tlgr/ops/todo.py +514 -0
  172. tlgr/ops/user.py +1406 -0
  173. tlgr/ops/vc.py +2351 -0
  174. tlgr/ops/webapp.py +717 -0
  175. tlgr/ops/webhook.py +418 -0
  176. tlgr/parity.py +386 -0
  177. tlgr/processors/__init__.py +125 -0
  178. tlgr/processors/regex.py +26 -0
  179. tlgr/processors/text.py +56 -0
  180. tlgr/registry.py +519 -0
  181. tlgr/schema.py +173 -0
  182. tlgr/transport/__init__.py +30 -0
  183. tlgr/transport/autostart.py +293 -0
  184. tlgr/transport/client.py +805 -0
  185. tlgr/transport/ndjson.py +44 -0
  186. tlgr/version.py +31 -0
  187. tlgr_cli-2.0.1.dist-info/METADATA +957 -0
  188. tlgr_cli-2.0.1.dist-info/RECORD +192 -0
  189. tlgr_cli-2.0.1.dist-info/WHEEL +5 -0
  190. tlgr_cli-2.0.1.dist-info/entry_points.txt +2 -0
  191. tlgr_cli-2.0.1.dist-info/licenses/LICENSE +21 -0
  192. tlgr_cli-2.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1170 @@
1
+ """The event taxonomy: every `Update*` constructor, named or explained.
2
+
3
+ This is the table `docs/design/EVENTS.md` documents, `tlgr events list` prints,
4
+ `tlgr watch --events` selects from and `tlgr/daemon/events.py` normalises
5
+ against. It lives in `core/` because all three of `ops/`, `daemon/` and the
6
+ doc generator need it and none of them may import each other (§2.2).
7
+
8
+ Two rules make the table worth trusting, and both are asserted by
9
+ `tests/test_event_taxonomy.py`:
10
+
11
+ * **every constructor is accounted for.** A constructor is either mapped to a
12
+ tlgr event type or listed in `INTERNAL` with the reason it is not surfaced.
13
+ A constructor that is merely missing would be an update tlgr silently drops
14
+ with nobody able to tell — which is exactly what v1's polling watch did to
15
+ everything that was not a new message.
16
+ * **no type is invented.** A name here is a name a consumer can filter on
17
+ forever. An update whose meaning we have not worked out is `INTERNAL`, not
18
+ a type called `unknown`.
19
+
20
+ The table is written against Telethon 1.44 (layer 227). Constructors Telegram
21
+ has added since carry `since_layer=229` and `available=False`: they are listed
22
+ so that `tlgr events list` can say "this exists and this build cannot parse
23
+ it", which is a far more useful answer than their absence.
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ from collections.abc import Iterable
29
+ from dataclasses import dataclass, field
30
+
31
+ __all__ = [
32
+ "ALIASES",
33
+ "CONSTRUCTORS",
34
+ "GROUPS",
35
+ "INTERNAL",
36
+ "TYPES",
37
+ "EventTypeSpec",
38
+ "constructors_for",
39
+ "group_of",
40
+ "resolve_selectors",
41
+ "type_for_constructor",
42
+ ]
43
+
44
+ #: The families `--events` accepts as a shorthand, and `events list --group`
45
+ #: filters by. Derived from the first segment of a type name, with the two
46
+ #: presence types folded in by hand.
47
+ GROUPS: tuple[str, ...] = (
48
+ "message",
49
+ "read",
50
+ "presence",
51
+ "peer",
52
+ "member",
53
+ "dialog",
54
+ "story",
55
+ "collection",
56
+ "call",
57
+ "bot",
58
+ "stars",
59
+ "secret",
60
+ "account",
61
+ "sync",
62
+ )
63
+
64
+
65
+ @dataclass(frozen=True, slots=True)
66
+ class EventTypeSpec:
67
+ """One tlgr event type: what it means and where it comes from."""
68
+
69
+ type: str
70
+ group: str
71
+ summary: str
72
+ #: field name → a short type description. Documented rather than a JSON
73
+ #: Schema because most payloads are the update's own fields, made
74
+ #: JSON-safe; `events get --json-schema` renders this into one.
75
+ payload: dict[str, str] = field(default_factory=dict)
76
+ #: Which sequence box orders it: pts, qts, seq, channel_pts, version or
77
+ #: none. A consumer that wants gap-free delivery needs to know.
78
+ box: str = "none"
79
+ bot_only: bool = False
80
+ #: 0 when Telethon 1.44 can parse every source constructor.
81
+ since_layer: int = 0
82
+ telethon: str = "raw"
83
+ #: Set on the handful of types no constructor produces on its own: they
84
+ #: are derived by tlgr (a service message inside `updateNewMessage`) or
85
+ #: synthesised by the daemon (health, a revoked session from a push).
86
+ derived: str = ""
87
+
88
+
89
+ def _t(
90
+ type_: str,
91
+ group: str,
92
+ summary: str,
93
+ *,
94
+ payload: dict[str, str] | None = None,
95
+ box: str = "none",
96
+ bot_only: bool = False,
97
+ since_layer: int = 0,
98
+ telethon: str = "raw",
99
+ derived: str = "",
100
+ ) -> EventTypeSpec:
101
+ return EventTypeSpec(
102
+ type=type_,
103
+ group=group,
104
+ summary=summary,
105
+ payload=payload or {},
106
+ box=box,
107
+ bot_only=bot_only,
108
+ since_layer=since_layer,
109
+ telethon=telethon,
110
+ derived=derived,
111
+ )
112
+
113
+
114
+ #: The payload every generically-normalised event carries: the update's own
115
+ #: fields, made JSON-safe (datetimes as RFC-3339, bytes as hex, nested TL
116
+ #: objects as `{"_": "ClassName", …}`).
117
+ _RAW_PAYLOAD = {
118
+ "_": "str — the source TL constructor name",
119
+ "…": "the update's own fields, JSON-safe",
120
+ }
121
+
122
+ _TYPES: tuple[EventTypeSpec, ...] = (
123
+ # -- message ----------------------------------------------------------
124
+ _t(
125
+ "message_new",
126
+ "message",
127
+ "A message arrived in any chat the account can see",
128
+ payload={"message": "Message"},
129
+ box="pts",
130
+ telethon="high-level: events.NewMessage",
131
+ ),
132
+ _t(
133
+ "message_service",
134
+ "message",
135
+ "A service message: a join, a pin, a title change, a call",
136
+ payload={"message": "Message", "action": "str — the MessageAction name"},
137
+ box="pts",
138
+ telethon="high-level: events.ChatAction (subset)",
139
+ derived="updateNewMessage / updateNewChannelMessage carrying a messageService",
140
+ ),
141
+ _t(
142
+ "message_edited",
143
+ "message",
144
+ "A message was edited",
145
+ payload={"message": "Message — post-edit"},
146
+ box="pts",
147
+ telethon="high-level: events.MessageEdited",
148
+ ),
149
+ _t(
150
+ "message_deleted",
151
+ "message",
152
+ "Messages were deleted",
153
+ payload={"message_ids": "list[int]", "channel_id": "int | null"},
154
+ box="pts",
155
+ telethon="high-level: events.MessageDeleted",
156
+ ),
157
+ _t(
158
+ "message_id_assigned",
159
+ "message",
160
+ "An outgoing message got its server id (random_id reconciliation)",
161
+ payload={"msg_id": "int", "random_id": "int | null"},
162
+ box="pts",
163
+ ),
164
+ _t(
165
+ "message_pinned",
166
+ "message",
167
+ "Messages were pinned or unpinned",
168
+ payload={"message_ids": "list[int]", "pinned": "bool"},
169
+ box="pts",
170
+ ),
171
+ _t(
172
+ "message_views",
173
+ "message",
174
+ "A channel post's view counter moved",
175
+ payload={"msg_id": "int", "views": "int"},
176
+ box="channel_pts",
177
+ ),
178
+ _t(
179
+ "message_forwards",
180
+ "message",
181
+ "A channel post's forward counter moved",
182
+ payload={"msg_id": "int", "forwards": "int"},
183
+ box="channel_pts",
184
+ ),
185
+ _t(
186
+ "message_poll",
187
+ "message",
188
+ "A poll's results changed",
189
+ payload={"poll_id": "int", "poll": "object | null", "results": "object"},
190
+ box="pts",
191
+ ),
192
+ _t(
193
+ "message_poll_vote",
194
+ "message",
195
+ "Somebody voted in a poll you can see the votes of",
196
+ payload={"poll_id": "int", "peer": "object", "options": "list[str]"},
197
+ box="qts",
198
+ ),
199
+ _t(
200
+ "message_reactions",
201
+ "message",
202
+ "Reactions on a message changed",
203
+ payload={"msg_id": "int", "reactions": "object", "top_msg_id": "int | null"},
204
+ box="pts",
205
+ ),
206
+ _t(
207
+ "message_extended_media",
208
+ "message",
209
+ "Paid media on a message was unlocked",
210
+ payload={"msg_id": "int", "extended_media": "list[object]"},
211
+ box="pts",
212
+ ),
213
+ _t(
214
+ "message_transcribed",
215
+ "message",
216
+ "A voice or video note transcription finished",
217
+ payload={
218
+ "msg_id": "int",
219
+ "transcription_id": "int",
220
+ "text": "str",
221
+ "pending": "bool",
222
+ },
223
+ ),
224
+ _t(
225
+ "message_webpage",
226
+ "message",
227
+ "A link preview finished resolving",
228
+ payload={"webpage": "object"},
229
+ box="pts",
230
+ ),
231
+ _t(
232
+ "message_scheduled_new",
233
+ "message",
234
+ "A scheduled message was queued",
235
+ payload={"message": "Message"},
236
+ ),
237
+ _t(
238
+ "message_scheduled_deleted",
239
+ "message",
240
+ "A scheduled message fired or was cancelled",
241
+ payload={"message_ids": "list[int]", "sent": "bool"},
242
+ ),
243
+ _t(
244
+ "message_available_min",
245
+ "message",
246
+ "A channel's history was cleared below a point",
247
+ payload={"available_min_id": "int"},
248
+ ),
249
+ _t(
250
+ "message_emoji_game",
251
+ "message",
252
+ "An emoji game (dice, dart, slot) resolved",
253
+ payload=dict(_RAW_PAYLOAD),
254
+ ),
255
+ _t(
256
+ "message_geo_live_viewed",
257
+ "message",
258
+ "Somebody viewed a live location I am sharing",
259
+ payload={"peer": "object", "msg_id": "int"},
260
+ ),
261
+ # -- read -------------------------------------------------------------
262
+ _t(
263
+ "read_inbox",
264
+ "read",
265
+ "My read position moved: messages I have now seen",
266
+ payload={"max_id": "int", "still_unread_count": "int | null", "outbox": "false"},
267
+ box="pts",
268
+ telethon="high-level: events.MessageRead(inbox=True)",
269
+ ),
270
+ _t(
271
+ "read_outbox",
272
+ "read",
273
+ "The other side read my messages",
274
+ payload={"max_id": "int", "outbox": "true"},
275
+ box="pts",
276
+ telethon="high-level: events.MessageRead(inbox=False)",
277
+ ),
278
+ _t(
279
+ "read_contents",
280
+ "read",
281
+ "Media or a mention was marked read (the media_unread flag)",
282
+ payload={"message_ids": "list[int]"},
283
+ box="pts",
284
+ ),
285
+ _t(
286
+ "read_discussion",
287
+ "read",
288
+ "A comment thread's read position moved",
289
+ payload={"msg_id": "int", "read_max_id": "int", "outbox": "bool"},
290
+ box="pts",
291
+ ),
292
+ _t(
293
+ "read_monoforum",
294
+ "read",
295
+ "A direct-messages (monoforum) channel's read position moved",
296
+ payload={"saved_peer_id": "object", "read_max_id": "int", "outbox": "bool"},
297
+ box="pts",
298
+ ),
299
+ # -- presence ---------------------------------------------------------
300
+ _t(
301
+ "user_status",
302
+ "presence",
303
+ "A user came online, or their last-seen changed",
304
+ payload={"user_id": "int", "status": "str", "online": "bool", "was_online": "int | null"},
305
+ telethon="high-level: events.UserUpdate",
306
+ ),
307
+ _t(
308
+ "typing",
309
+ "presence",
310
+ "Somebody is typing, recording or uploading",
311
+ payload={
312
+ "user_id": "int | null",
313
+ "action": "str — the SendMessageAction name",
314
+ "progress": "int | null",
315
+ "top_msg_id": "int | null",
316
+ },
317
+ telethon="high-level: events.UserUpdate",
318
+ ),
319
+ # -- peer -------------------------------------------------------------
320
+ _t(
321
+ "peer_user_changed",
322
+ "peer",
323
+ "A user record was invalidated and should be refetched",
324
+ payload={"user_id": "int"},
325
+ ),
326
+ _t(
327
+ "peer_user_name",
328
+ "peer",
329
+ "A user changed their name or username",
330
+ payload={
331
+ "user_id": "int",
332
+ "first_name": "str",
333
+ "last_name": "str",
334
+ "usernames": "list[str]",
335
+ },
336
+ ),
337
+ _t(
338
+ "peer_user_phone",
339
+ "peer",
340
+ "A contact's phone number changed",
341
+ payload={"user_id": "int", "phone": "str"},
342
+ ),
343
+ _t(
344
+ "peer_user_emoji_status",
345
+ "peer",
346
+ "A user's emoji status changed",
347
+ payload={"user_id": "int", "emoji_status": "object"},
348
+ ),
349
+ _t(
350
+ "peer_chat_changed",
351
+ "peer",
352
+ "A chat or channel record was invalidated (refetch; may mean kicked)",
353
+ payload={"chat_id": "int"},
354
+ ),
355
+ _t(
356
+ "peer_blocked",
357
+ "peer",
358
+ "A peer was blocked or unblocked",
359
+ payload={"peer_id": "object", "blocked": "bool", "blocked_my_stories_from": "bool"},
360
+ ),
361
+ _t(
362
+ "peer_settings",
363
+ "peer",
364
+ "A peer's action-bar settings changed (anti-scam hints included)",
365
+ payload={"peer": "object", "settings": "object"},
366
+ ),
367
+ _t(
368
+ "peer_located",
369
+ "peer",
370
+ "The people/groups-nearby list changed",
371
+ payload={"peers": "list[object]"},
372
+ ),
373
+ _t(
374
+ "peer_wallpaper",
375
+ "peer",
376
+ "A chat wallpaper changed",
377
+ payload={"peer": "object", "wallpaper": "object | null"},
378
+ ),
379
+ _t(
380
+ "peer_history_ttl",
381
+ "peer",
382
+ "A chat's auto-delete timer changed",
383
+ payload={"peer": "object", "ttl_period": "int | null"},
384
+ ),
385
+ _t(
386
+ "peer_notify_settings",
387
+ "peer",
388
+ "Notification settings changed for a peer or a scope",
389
+ payload={"peer": "object", "notify_settings": "object"},
390
+ ),
391
+ # -- member -----------------------------------------------------------
392
+ _t(
393
+ "member_channel",
394
+ "member",
395
+ "A channel or supergroup member or admin changed",
396
+ payload={
397
+ "channel_id": "int",
398
+ "user_id": "int",
399
+ "actor_id": "int | null",
400
+ "prev_participant": "object | null",
401
+ "new_participant": "object | null",
402
+ },
403
+ box="qts",
404
+ ),
405
+ _t(
406
+ "member_chat",
407
+ "member",
408
+ "A basic group's membership or admin list changed",
409
+ payload={
410
+ "chat_id": "int",
411
+ "user_id": "int | null",
412
+ "version": "int | null",
413
+ "participants": "object | null",
414
+ },
415
+ box="version",
416
+ ),
417
+ _t(
418
+ "member_default_rights",
419
+ "member",
420
+ "A group's default permissions changed",
421
+ payload={"peer": "object", "default_banned_rights": "object", "version": "int"},
422
+ box="version",
423
+ ),
424
+ _t(
425
+ "member_join_request",
426
+ "member",
427
+ "A pending join request arrived or was resolved",
428
+ payload={"peer": "object", "requests_pending": "int | null", "recent_requesters": "list"},
429
+ ),
430
+ _t(
431
+ "member_boost",
432
+ "member",
433
+ "A channel boost was applied",
434
+ payload={"peer": "object", "boost": "object"},
435
+ bot_only=True,
436
+ ),
437
+ # -- dialog -----------------------------------------------------------
438
+ _t(
439
+ "dialog_pinned",
440
+ "dialog",
441
+ "A chat was pinned, unpinned or reordered in the list",
442
+ payload={"peer": "object | null", "order": "list[object] | null", "pinned": "bool"},
443
+ ),
444
+ _t(
445
+ "dialog_unread_mark",
446
+ "dialog",
447
+ "A chat was manually marked unread (or the mark was cleared)",
448
+ payload={"peer": "object", "unread": "bool"},
449
+ ),
450
+ _t(
451
+ "dialog_folder",
452
+ "dialog",
453
+ "A chat moved into or out of the Archive",
454
+ payload={"folder_peers": "list[object]"},
455
+ box="pts",
456
+ ),
457
+ _t(
458
+ "dialog_filters",
459
+ "dialog",
460
+ "Chat folders (dialog filters) changed",
461
+ payload={"id": "int | null", "filter": "object | null", "order": "list[int] | null"},
462
+ ),
463
+ _t(
464
+ "dialog_draft",
465
+ "dialog",
466
+ "A cloud draft was set or cleared",
467
+ payload={"peer": "object", "draft": "object", "top_msg_id": "int | null"},
468
+ ),
469
+ _t(
470
+ "dialog_saved_pinned",
471
+ "dialog",
472
+ "A Saved Messages sub-dialog was pinned or reordered",
473
+ payload={"peer": "object | null", "order": "list[object] | null", "pinned": "bool"},
474
+ ),
475
+ _t(
476
+ "dialog_saved_tags",
477
+ "dialog",
478
+ "Saved-message reaction tags changed",
479
+ payload={"saved_peer_id": "object | null"},
480
+ ),
481
+ _t(
482
+ "dialog_forum_pinned",
483
+ "dialog",
484
+ "Forum topics were pinned or reordered",
485
+ payload={"channel_id": "int", "topic_id": "int | null", "order": "list[int] | null"},
486
+ ),
487
+ _t(
488
+ "dialog_forum_view",
489
+ "dialog",
490
+ "A forum's display mode was toggled",
491
+ payload={"channel_id": "int", "enabled": "bool"},
492
+ ),
493
+ _t(
494
+ "dialog_quick_reply",
495
+ "dialog",
496
+ "Business quick-reply shortcuts changed",
497
+ payload=dict(_RAW_PAYLOAD),
498
+ ),
499
+ _t(
500
+ "dialog_monoforum_no_paid",
501
+ "dialog",
502
+ "A direct-messages channel's paid-message exception changed",
503
+ payload={"channel_id": "int", "saved_peer_id": "object", "exception": "bool"},
504
+ ),
505
+ # -- story ------------------------------------------------------------
506
+ _t(
507
+ "story_new",
508
+ "story",
509
+ "A story was posted, edited or deleted",
510
+ payload={"peer": "object", "story": "object"},
511
+ ),
512
+ _t(
513
+ "story_id",
514
+ "story",
515
+ "A story you posted got its server id",
516
+ payload={"id": "int", "random_id": "int"},
517
+ ),
518
+ _t(
519
+ "story_read",
520
+ "story",
521
+ "Stories were marked read",
522
+ payload={"peer": "object", "max_id": "int"},
523
+ ),
524
+ _t(
525
+ "story_reaction",
526
+ "story",
527
+ "A story was reacted to",
528
+ payload={"peer": "object", "story_id": "int", "reaction": "object"},
529
+ ),
530
+ _t(
531
+ "story_stealth",
532
+ "story",
533
+ "Story stealth mode changed",
534
+ payload={"stealth_mode": "object"},
535
+ ),
536
+ # -- collection -------------------------------------------------------
537
+ _t(
538
+ "collection_stickers",
539
+ "collection",
540
+ "Sticker or custom-emoji sets changed",
541
+ payload=dict(_RAW_PAYLOAD),
542
+ ),
543
+ _t(
544
+ "collection_stickers_read",
545
+ "collection",
546
+ "Featured sticker or emoji sets were marked read",
547
+ payload={"message_ids": "list[int]"},
548
+ ),
549
+ _t("collection_gifs", "collection", "Saved GIFs changed", payload={}),
550
+ _t("collection_ringtones", "collection", "Notification sounds changed", payload={}),
551
+ _t("collection_reactions", "collection", "Recent or top reactions changed", payload={}),
552
+ _t("collection_emoji_statuses", "collection", "Recent emoji statuses changed", payload={}),
553
+ _t(
554
+ "collection_themes",
555
+ "collection",
556
+ "A theme changed",
557
+ payload={"theme": "object"},
558
+ ),
559
+ _t(
560
+ "collection_attach_menu",
561
+ "collection",
562
+ "The attachment-menu bot list changed",
563
+ payload={},
564
+ ),
565
+ # -- call -------------------------------------------------------------
566
+ _t(
567
+ "call_phone",
568
+ "call",
569
+ "An incoming or updated 1:1 call (signalling only; tlgr carries no media)",
570
+ payload={"phone_call": "object"},
571
+ ),
572
+ _t(
573
+ "call_signaling",
574
+ "call",
575
+ "Raw call signalling data",
576
+ payload={"phone_call_id": "int", "data": "str — hex"},
577
+ ),
578
+ _t(
579
+ "call_group",
580
+ "call",
581
+ "A group call, video chat or live stream changed",
582
+ payload={"chat_id": "int | null", "call": "object"},
583
+ ),
584
+ _t(
585
+ "call_group_participants",
586
+ "call",
587
+ "Group-call participants changed",
588
+ payload={"call": "object", "participants": "list[object]", "version": "int"},
589
+ box="version",
590
+ ),
591
+ _t(
592
+ "call_group_message",
593
+ "call",
594
+ "A message inside a group call was posted or deleted",
595
+ payload=dict(_RAW_PAYLOAD),
596
+ ),
597
+ _t(
598
+ "call_group_encrypted",
599
+ "call",
600
+ "Encrypted group-call key material (conference calls)",
601
+ payload=dict(_RAW_PAYLOAD),
602
+ ),
603
+ # -- bot --------------------------------------------------------------
604
+ _t(
605
+ "bot_callback_query",
606
+ "bot",
607
+ "An inline-keyboard button was pressed",
608
+ payload=dict(_RAW_PAYLOAD),
609
+ bot_only=True,
610
+ telethon="high-level: events.CallbackQuery",
611
+ ),
612
+ _t(
613
+ "bot_inline_query",
614
+ "bot",
615
+ "An inline query arrived, or a result was chosen",
616
+ payload=dict(_RAW_PAYLOAD),
617
+ bot_only=True,
618
+ telethon="high-level: events.InlineQuery",
619
+ ),
620
+ _t(
621
+ "bot_precheckout",
622
+ "bot",
623
+ "A pre-checkout query arrived",
624
+ payload=dict(_RAW_PAYLOAD),
625
+ bot_only=True,
626
+ ),
627
+ _t(
628
+ "bot_shipping",
629
+ "bot",
630
+ "A shipping query arrived",
631
+ payload=dict(_RAW_PAYLOAD),
632
+ bot_only=True,
633
+ ),
634
+ _t(
635
+ "bot_paid_media_purchased",
636
+ "bot",
637
+ "A user bought paid media from this bot",
638
+ payload=dict(_RAW_PAYLOAD),
639
+ bot_only=True,
640
+ ),
641
+ _t(
642
+ "bot_stopped",
643
+ "bot",
644
+ "A user started or stopped this bot",
645
+ payload={"user_id": "int", "stopped": "bool", "date": "str"},
646
+ box="qts",
647
+ bot_only=True,
648
+ ),
649
+ _t("bot_commands", "bot", "A bot's command list changed", payload=dict(_RAW_PAYLOAD)),
650
+ _t("bot_menu_button", "bot", "A bot's menu button changed", payload=dict(_RAW_PAYLOAD)),
651
+ _t(
652
+ "bot_webhook",
653
+ "bot",
654
+ "A bot-webhook JSON passthrough arrived",
655
+ payload=dict(_RAW_PAYLOAD),
656
+ bot_only=True,
657
+ ),
658
+ _t(
659
+ "bot_business_connection",
660
+ "bot",
661
+ "A business connection was created or changed",
662
+ payload=dict(_RAW_PAYLOAD),
663
+ bot_only=True,
664
+ ),
665
+ _t(
666
+ "bot_business_message",
667
+ "bot",
668
+ "A message on a connected business account arrived, changed or went",
669
+ payload=dict(_RAW_PAYLOAD),
670
+ bot_only=True,
671
+ ),
672
+ _t(
673
+ "bot_message_reaction",
674
+ "bot",
675
+ "A reaction on a message this bot can see changed",
676
+ payload=dict(_RAW_PAYLOAD),
677
+ bot_only=True,
678
+ ),
679
+ _t(
680
+ "bot_guest_chat_query",
681
+ "bot",
682
+ "A guest-mode chat query arrived",
683
+ payload=dict(_RAW_PAYLOAD),
684
+ bot_only=True,
685
+ ),
686
+ _t(
687
+ "bot_managed",
688
+ "bot",
689
+ "A bot you manage changed",
690
+ payload=dict(_RAW_PAYLOAD),
691
+ ),
692
+ _t(
693
+ "bot_webview_result",
694
+ "bot",
695
+ "A mini app sent data back",
696
+ payload=dict(_RAW_PAYLOAD),
697
+ bot_only=True,
698
+ ),
699
+ _t(
700
+ "bot_webview_join_decision",
701
+ "bot",
702
+ "A join-chat decision was made inside a mini app",
703
+ payload=dict(_RAW_PAYLOAD),
704
+ ),
705
+ _t(
706
+ "bot_stars_subscription",
707
+ "bot",
708
+ "A Stars subscription to this bot changed",
709
+ payload=dict(_RAW_PAYLOAD),
710
+ bot_only=True,
711
+ since_layer=229,
712
+ ),
713
+ _t(
714
+ "bot_ephemeral_callback",
715
+ "bot",
716
+ "A callback button on an ephemeral message was pressed",
717
+ payload=dict(_RAW_PAYLOAD),
718
+ bot_only=True,
719
+ since_layer=229,
720
+ ),
721
+ # -- stars ------------------------------------------------------------
722
+ _t(
723
+ "stars_balance",
724
+ "stars",
725
+ "The Telegram Stars balance changed",
726
+ payload={"balance": "object"},
727
+ ),
728
+ _t(
729
+ "stars_revenue",
730
+ "stars",
731
+ "Star revenue or withdrawal status changed",
732
+ payload={"peer": "object", "status": "object"},
733
+ ),
734
+ _t(
735
+ "stars_gift_auction",
736
+ "stars",
737
+ "A star-gift auction or craft changed state",
738
+ payload=dict(_RAW_PAYLOAD),
739
+ ),
740
+ _t(
741
+ "stars_paid_reaction_privacy",
742
+ "stars",
743
+ "Paid-reaction privacy changed",
744
+ payload=dict(_RAW_PAYLOAD),
745
+ ),
746
+ # -- secret -----------------------------------------------------------
747
+ _t(
748
+ "secret_chat",
749
+ "secret",
750
+ "A secret chat was requested, accepted or discarded",
751
+ payload={"chat": "object"},
752
+ box="qts",
753
+ ),
754
+ _t(
755
+ "secret_message",
756
+ "secret",
757
+ "Encrypted traffic arrived; tlgr acknowledges it but cannot decrypt it",
758
+ payload={"chat_id": "int", "date": "str", "decrypted": "false"},
759
+ box="qts",
760
+ ),
761
+ _t(
762
+ "secret_read",
763
+ "secret",
764
+ "Secret-chat messages were read or expired",
765
+ payload={"chat_id": "int", "max_date": "str"},
766
+ box="qts",
767
+ ),
768
+ # -- account ----------------------------------------------------------
769
+ _t(
770
+ "account_privacy",
771
+ "account",
772
+ "A privacy rule changed",
773
+ payload={"key": "str", "rules": "list[object]"},
774
+ ),
775
+ _t(
776
+ "account_new_authorization",
777
+ "account",
778
+ "A new login on this account",
779
+ payload={"hash": "int", "device": "str", "location": "str", "unconfirmed": "bool"},
780
+ ),
781
+ _t(
782
+ "account_service_notification",
783
+ "account",
784
+ "An official service notification (from 777000)",
785
+ payload={"type": "str", "message": "str", "popup": "bool", "inbox_date": "str | null"},
786
+ ),
787
+ _t(
788
+ "account_login_token",
789
+ "account",
790
+ "A QR login token was accepted",
791
+ payload={},
792
+ ),
793
+ _t(
794
+ "account_sent_phone_code",
795
+ "account",
796
+ "A login code was delivered in-app",
797
+ payload=dict(_RAW_PAYLOAD),
798
+ ),
799
+ _t(
800
+ "account_autosave",
801
+ "account",
802
+ "Media auto-save settings changed",
803
+ payload=dict(_RAW_PAYLOAD),
804
+ ),
805
+ _t(
806
+ "account_browser_settings",
807
+ "account",
808
+ "In-app browser settings or a per-domain exception changed",
809
+ payload=dict(_RAW_PAYLOAD),
810
+ ),
811
+ _t("account_contacts_reset", "account", "The contact list was wiped", payload={}),
812
+ _t(
813
+ "account_ai_tones",
814
+ "account",
815
+ "The AI compose tone list changed",
816
+ payload=dict(_RAW_PAYLOAD),
817
+ ),
818
+ _t(
819
+ "account_sms_job",
820
+ "account",
821
+ "An SMS-relay job arrived (Telegram's peer-to-peer login SMS programme)",
822
+ payload={"job_id": "str"},
823
+ ),
824
+ _t(
825
+ "account_langpack",
826
+ "account",
827
+ "The language pack changed",
828
+ payload=dict(_RAW_PAYLOAD),
829
+ ),
830
+ _t(
831
+ "account_session_revoked",
832
+ "account",
833
+ "This session was terminated elsewhere (from a push payload)",
834
+ payload={"reason": "str"},
835
+ derived="the SESSION_REVOKE push payload, decoded by `tlgr events decode`",
836
+ ),
837
+ # -- sync -------------------------------------------------------------
838
+ _t(
839
+ "sync_config",
840
+ "sync",
841
+ "The server configuration was invalidated; re-read help.getConfig",
842
+ payload={},
843
+ ),
844
+ _t(
845
+ "sync_dc_options",
846
+ "sync",
847
+ "The data-centre address list changed",
848
+ payload={"dc_options": "list[object]"},
849
+ ),
850
+ _t(
851
+ "sync_pts_changed",
852
+ "sync",
853
+ "The pts sequence was reset; some updates are unrecoverable",
854
+ payload={},
855
+ ),
856
+ _t(
857
+ "sync_channel_too_long",
858
+ "sync",
859
+ "A channel's gap is unrecoverable from its pts; a resync is needed",
860
+ payload={"channel_id": "int", "pts": "int | null"},
861
+ ),
862
+ _t(
863
+ "daemon_health",
864
+ "sync",
865
+ "An account changed state, or the circuit breaker opened",
866
+ payload={"state": "str", "reason": "str", "account": "str"},
867
+ telethon="n/a — synthesised by the daemon",
868
+ derived="the session state machine (ARCHITECTURE §6.2)",
869
+ ),
870
+ )
871
+
872
+ TYPES: dict[str, EventTypeSpec] = {spec.type: spec for spec in _TYPES}
873
+
874
+
875
+ # ---------------------------------------------------------------------------
876
+ # Constructor → type
877
+ # ---------------------------------------------------------------------------
878
+
879
+ #: `Update*` constructor name → the tlgr event type it becomes.
880
+ CONSTRUCTORS: dict[str, str] = {
881
+ # message
882
+ "UpdateNewMessage": "message_new",
883
+ "UpdateNewChannelMessage": "message_new",
884
+ "UpdateShortMessage": "message_new",
885
+ "UpdateShortChatMessage": "message_new",
886
+ "UpdateQuickReplyMessage": "dialog_quick_reply",
887
+ "UpdateEditMessage": "message_edited",
888
+ "UpdateEditChannelMessage": "message_edited",
889
+ "UpdateDeleteMessages": "message_deleted",
890
+ "UpdateDeleteChannelMessages": "message_deleted",
891
+ "UpdateMessageID": "message_id_assigned",
892
+ "UpdateShortSentMessage": "message_id_assigned",
893
+ "UpdatePinnedMessages": "message_pinned",
894
+ "UpdatePinnedChannelMessages": "message_pinned",
895
+ "UpdateChannelMessageViews": "message_views",
896
+ "UpdateChannelMessageForwards": "message_forwards",
897
+ "UpdateMessagePoll": "message_poll",
898
+ "UpdateMessagePollVote": "message_poll_vote",
899
+ "UpdateMessageReactions": "message_reactions",
900
+ "UpdateMessageExtendedMedia": "message_extended_media",
901
+ "UpdateTranscribedAudio": "message_transcribed",
902
+ "UpdateWebPage": "message_webpage",
903
+ "UpdateChannelWebPage": "message_webpage",
904
+ "UpdateNewScheduledMessage": "message_scheduled_new",
905
+ "UpdateDeleteScheduledMessages": "message_scheduled_deleted",
906
+ "UpdateChannelAvailableMessages": "message_available_min",
907
+ "UpdateEmojiGameInfo": "message_emoji_game",
908
+ "UpdateGeoLiveViewed": "message_geo_live_viewed",
909
+ # read
910
+ "UpdateReadHistoryInbox": "read_inbox",
911
+ "UpdateReadChannelInbox": "read_inbox",
912
+ "UpdateReadHistoryOutbox": "read_outbox",
913
+ "UpdateReadChannelOutbox": "read_outbox",
914
+ "UpdateReadMessagesContents": "read_contents",
915
+ "UpdateChannelReadMessagesContents": "read_contents",
916
+ "UpdateReadChannelDiscussionInbox": "read_discussion",
917
+ "UpdateReadChannelDiscussionOutbox": "read_discussion",
918
+ "UpdateReadMonoForumInbox": "read_monoforum",
919
+ "UpdateReadMonoForumOutbox": "read_monoforum",
920
+ # presence
921
+ "UpdateUserStatus": "user_status",
922
+ "UpdateUserTyping": "typing",
923
+ "UpdateChatUserTyping": "typing",
924
+ "UpdateChannelUserTyping": "typing",
925
+ "UpdateEncryptedChatTyping": "typing",
926
+ # peer
927
+ "UpdateUser": "peer_user_changed",
928
+ "UpdateUserName": "peer_user_name",
929
+ "UpdateUserPhone": "peer_user_phone",
930
+ "UpdateUserEmojiStatus": "peer_user_emoji_status",
931
+ "UpdateChat": "peer_chat_changed",
932
+ "UpdateChannel": "peer_chat_changed",
933
+ "UpdatePeerBlocked": "peer_blocked",
934
+ "UpdatePeerSettings": "peer_settings",
935
+ "UpdatePeerLocated": "peer_located",
936
+ "UpdatePeerWallpaper": "peer_wallpaper",
937
+ "UpdatePeerHistoryTTL": "peer_history_ttl",
938
+ "UpdateNotifySettings": "peer_notify_settings",
939
+ # member
940
+ "UpdateChannelParticipant": "member_channel",
941
+ "UpdateChatParticipant": "member_chat",
942
+ "UpdateChatParticipants": "member_chat",
943
+ "UpdateChatParticipantAdd": "member_chat",
944
+ "UpdateChatParticipantDelete": "member_chat",
945
+ "UpdateChatParticipantAdmin": "member_chat",
946
+ "UpdateChatParticipantRank": "member_chat",
947
+ "UpdateChatDefaultBannedRights": "member_default_rights",
948
+ "UpdatePendingJoinRequests": "member_join_request",
949
+ "UpdateBotChatInviteRequester": "member_join_request",
950
+ "UpdateBotChatBoost": "member_boost",
951
+ # dialog
952
+ "UpdateDialogPinned": "dialog_pinned",
953
+ "UpdatePinnedDialogs": "dialog_pinned",
954
+ "UpdateDialogUnreadMark": "dialog_unread_mark",
955
+ "UpdateFolderPeers": "dialog_folder",
956
+ "UpdateDialogFilter": "dialog_filters",
957
+ "UpdateDialogFilterOrder": "dialog_filters",
958
+ "UpdateDialogFilters": "dialog_filters",
959
+ "UpdateDraftMessage": "dialog_draft",
960
+ "UpdateSavedDialogPinned": "dialog_saved_pinned",
961
+ "UpdatePinnedSavedDialogs": "dialog_saved_pinned",
962
+ "UpdateSavedReactionTags": "dialog_saved_tags",
963
+ "UpdatePinnedForumTopic": "dialog_forum_pinned",
964
+ "UpdatePinnedForumTopics": "dialog_forum_pinned",
965
+ "UpdateChannelViewForumAsMessages": "dialog_forum_view",
966
+ "UpdateQuickReplies": "dialog_quick_reply",
967
+ "UpdateNewQuickReply": "dialog_quick_reply",
968
+ "UpdateDeleteQuickReply": "dialog_quick_reply",
969
+ "UpdateDeleteQuickReplyMessages": "dialog_quick_reply",
970
+ "UpdateMonoForumNoPaidException": "dialog_monoforum_no_paid",
971
+ # story
972
+ "UpdateStory": "story_new",
973
+ "UpdateStoryID": "story_id",
974
+ "UpdateReadStories": "story_read",
975
+ "UpdateNewStoryReaction": "story_reaction",
976
+ "UpdateSentStoryReaction": "story_reaction",
977
+ "UpdateStoriesStealthMode": "story_stealth",
978
+ # collection
979
+ "UpdateStickerSets": "collection_stickers",
980
+ "UpdateStickerSetsOrder": "collection_stickers",
981
+ "UpdateNewStickerSet": "collection_stickers",
982
+ "UpdateRecentStickers": "collection_stickers",
983
+ "UpdateFavedStickers": "collection_stickers",
984
+ "UpdateMoveStickerSetToTop": "collection_stickers",
985
+ "UpdateReadFeaturedStickers": "collection_stickers_read",
986
+ "UpdateReadFeaturedEmojiStickers": "collection_stickers_read",
987
+ "UpdateSavedGifs": "collection_gifs",
988
+ "UpdateSavedRingtones": "collection_ringtones",
989
+ "UpdateRecentReactions": "collection_reactions",
990
+ "UpdateRecentEmojiStatuses": "collection_emoji_statuses",
991
+ "UpdateTheme": "collection_themes",
992
+ "UpdateAttachMenuBots": "collection_attach_menu",
993
+ # call
994
+ "UpdatePhoneCall": "call_phone",
995
+ "UpdatePhoneCallSignalingData": "call_signaling",
996
+ "UpdateGroupCall": "call_group",
997
+ "UpdateGroupCallConnection": "call_group",
998
+ "UpdateGroupCallParticipants": "call_group_participants",
999
+ "UpdateGroupCallMessage": "call_group_message",
1000
+ "UpdateDeleteGroupCallMessages": "call_group_message",
1001
+ "UpdateGroupCallChainBlocks": "call_group_encrypted",
1002
+ "UpdateGroupCallEncryptedMessage": "call_group_encrypted",
1003
+ # bot
1004
+ "UpdateBotCallbackQuery": "bot_callback_query",
1005
+ "UpdateInlineBotCallbackQuery": "bot_callback_query",
1006
+ "UpdateBusinessBotCallbackQuery": "bot_callback_query",
1007
+ "UpdateBotInlineQuery": "bot_inline_query",
1008
+ "UpdateBotInlineSend": "bot_inline_query",
1009
+ "UpdateBotPrecheckoutQuery": "bot_precheckout",
1010
+ "UpdateBotShippingQuery": "bot_shipping",
1011
+ "UpdateBotPurchasedPaidMedia": "bot_paid_media_purchased",
1012
+ "UpdateBotStopped": "bot_stopped",
1013
+ "UpdateBotCommands": "bot_commands",
1014
+ "UpdateBotMenuButton": "bot_menu_button",
1015
+ "UpdateBotWebhookJSON": "bot_webhook",
1016
+ "UpdateBotWebhookJSONQuery": "bot_webhook",
1017
+ "UpdateBotBusinessConnect": "bot_business_connection",
1018
+ "UpdateNewBotConnection": "bot_business_connection",
1019
+ "UpdateBotNewBusinessMessage": "bot_business_message",
1020
+ "UpdateBotEditBusinessMessage": "bot_business_message",
1021
+ "UpdateBotDeleteBusinessMessage": "bot_business_message",
1022
+ "UpdateBotMessageReaction": "bot_message_reaction",
1023
+ "UpdateBotMessageReactions": "bot_message_reaction",
1024
+ "UpdateBotGuestChatQuery": "bot_guest_chat_query",
1025
+ "UpdateManagedBot": "bot_managed",
1026
+ "UpdateWebViewResultSent": "bot_webview_result",
1027
+ "UpdateJoinChatWebViewDecision": "bot_webview_join_decision",
1028
+ # stars
1029
+ "UpdateStarsBalance": "stars_balance",
1030
+ "UpdateStarsRevenueStatus": "stars_revenue",
1031
+ "UpdateStarGiftAuctionState": "stars_gift_auction",
1032
+ "UpdateStarGiftAuctionUserState": "stars_gift_auction",
1033
+ "UpdateStarGiftCraftFail": "stars_gift_auction",
1034
+ "UpdatePaidReactionPrivacy": "stars_paid_reaction_privacy",
1035
+ # secret
1036
+ "UpdateEncryption": "secret_chat",
1037
+ "UpdateNewEncryptedMessage": "secret_message",
1038
+ "UpdateEncryptedMessagesRead": "secret_read",
1039
+ # account
1040
+ "UpdatePrivacy": "account_privacy",
1041
+ "UpdateNewAuthorization": "account_new_authorization",
1042
+ "UpdateServiceNotification": "account_service_notification",
1043
+ "UpdateLoginToken": "account_login_token",
1044
+ "UpdateSentPhoneCode": "account_sent_phone_code",
1045
+ "UpdateAutoSaveSettings": "account_autosave",
1046
+ "UpdateWebBrowserSettings": "account_browser_settings",
1047
+ "UpdateWebBrowserException": "account_browser_settings",
1048
+ "UpdateContactsReset": "account_contacts_reset",
1049
+ "UpdateAiComposeTones": "account_ai_tones",
1050
+ "UpdateSmsJob": "account_sms_job",
1051
+ "UpdateLangPack": "account_langpack",
1052
+ "UpdateLangPackTooLong": "account_langpack",
1053
+ # sync
1054
+ "UpdateConfig": "sync_config",
1055
+ "UpdateDcOptions": "sync_dc_options",
1056
+ "UpdatePtsChanged": "sync_pts_changed",
1057
+ "UpdateChannelTooLong": "sync_channel_too_long",
1058
+ }
1059
+
1060
+ #: Constructors that carry no event of their own, and why. Being on this list
1061
+ #: is a decision; being on neither list is a bug the taxonomy test catches.
1062
+ INTERNAL: dict[str, str] = {
1063
+ "UpdateShort": ("container: carries exactly one Update, which is normalised in its place"),
1064
+ "Updates": "container: a batch of updates plus their users/chats arrays",
1065
+ "UpdatesCombined": "container: a batch of updates spanning a seq range",
1066
+ "UpdatesTooLong": (
1067
+ "transport signal: the common box overflowed, handled by the supervisor "
1068
+ "with updates.getDifference (see `tlgr sync catch-up`)"
1069
+ ),
1070
+ }
1071
+
1072
+ #: Constructors Telegram ships that Telethon 1.44 (layer 227) cannot parse.
1073
+ #: Listed so `events list` can say "exists, unavailable here" rather than
1074
+ #: leaving a silent hole; a raw handler sees only an unknown constructor id.
1075
+ NEWER_THAN_LAYER_227: dict[str, str] = {
1076
+ "UpdateNewEphemeralMessage": "message_new",
1077
+ "UpdateEditEphemeralMessage": "message_edited",
1078
+ "UpdateDeleteEphemeralMessages": "message_deleted",
1079
+ "UpdateBotStarsSubscription": "bot_stars_subscription",
1080
+ "UpdateBotEphemeralCallbackQuery": "bot_ephemeral_callback",
1081
+ }
1082
+
1083
+ #: Names a consumer may still be using, and what they mean now. v1's `watch`
1084
+ #: and `jobs.yaml` spelled the message events differently, and the foundation
1085
+ #: shipped a nine-name starter set; both keep working (§12.4).
1086
+ ALIASES: dict[str, tuple[str, ...]] = {
1087
+ "new_message": ("message_new",),
1088
+ "user_joined": ("message_service", "member_chat", "member_channel"),
1089
+ "chat_action": ("message_service", "member_chat", "member_channel"),
1090
+ "message_read": ("read_inbox", "read_outbox"),
1091
+ "reaction_changed": ("message_reactions",),
1092
+ "draft_changed": ("dialog_draft",),
1093
+ "message_edit": ("message_edited",),
1094
+ }
1095
+
1096
+
1097
+ def group_of(event_type: str) -> str:
1098
+ spec = TYPES.get(event_type)
1099
+ return spec.group if spec is not None else ""
1100
+
1101
+
1102
+ def type_for_constructor(name: str) -> str | None:
1103
+ """The event type a `Update*` class name maps to, or None when internal."""
1104
+ found = CONSTRUCTORS.get(name)
1105
+ if found is not None:
1106
+ return found
1107
+ return NEWER_THAN_LAYER_227.get(name)
1108
+
1109
+
1110
+ def constructors_for(event_type: str) -> tuple[str, ...]:
1111
+ """Every `Update*` constructor that produces *event_type*."""
1112
+ return tuple(
1113
+ sorted(
1114
+ name
1115
+ for mapping in (CONSTRUCTORS, NEWER_THAN_LAYER_227)
1116
+ for name, mapped in mapping.items()
1117
+ if mapped == event_type
1118
+ )
1119
+ )
1120
+
1121
+
1122
+ def resolve_selectors(
1123
+ selectors: str | Iterable[str] | None, *, allow_all: bool = True
1124
+ ) -> frozenset[str]:
1125
+ """Expand `--events`/`--exclude` values into a set of event type names.
1126
+
1127
+ Accepts type names, group names, the legacy names in `ALIASES`,
1128
+ `raw:UpdateFoo` (the type that constructor maps to) and `all`. An unknown
1129
+ value is a `USAGE` error rather than an empty selection: silently watching
1130
+ nothing is the failure mode that makes a user think the daemon is broken.
1131
+ """
1132
+ from tlgr.core.errors import UsageError
1133
+
1134
+ if isinstance(selectors, str):
1135
+ values = [part.strip() for part in selectors.split(",")]
1136
+ else:
1137
+ values = [str(part).strip() for part in (selectors or ())]
1138
+ wanted: set[str] = set()
1139
+ for value in values:
1140
+ if not value:
1141
+ continue
1142
+ lowered = value.lower()
1143
+ if lowered in ("all", "*"):
1144
+ if not allow_all:
1145
+ raise UsageError("'all' is not accepted here", field="events")
1146
+ return frozenset(TYPES)
1147
+ if lowered.startswith("raw:"):
1148
+ constructor = value[4:]
1149
+ mapped = type_for_constructor(constructor)
1150
+ if mapped is None:
1151
+ raise UsageError(
1152
+ f"raw:{constructor} is not an update tlgr names; see `tlgr events list --raw`",
1153
+ field="events",
1154
+ )
1155
+ wanted.add(mapped)
1156
+ continue
1157
+ if lowered in TYPES:
1158
+ wanted.add(lowered)
1159
+ continue
1160
+ if lowered in ALIASES:
1161
+ wanted.update(ALIASES[lowered])
1162
+ continue
1163
+ if lowered in GROUPS:
1164
+ wanted.update(name for name, spec in TYPES.items() if spec.group == lowered)
1165
+ continue
1166
+ raise UsageError(
1167
+ f"unknown event selector {value!r}; run `tlgr events list` for the vocabulary",
1168
+ field="events",
1169
+ )
1170
+ return frozenset(wanted)