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
tlgr/models/message.py ADDED
@@ -0,0 +1,584 @@
1
+ """Message and everything hanging off it.
2
+
3
+ The shape answers, without a second RPC, the questions an agent actually asks
4
+ of a message: is it a sticker or a voice note (`media.kind`), did we already
5
+ react (`reactions.mine`), is it an event rather than text (`kind`/`action`),
6
+ did it come from a forum topic (`reply_to.top_message_id`), may it be
7
+ forwarded (`noforwards`). v1 assembled most of that ad hoc in three places as
8
+ loose `media_*` keys; here it is one struct built by one function.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any, Literal
14
+
15
+ from tlgr.models.base import Model
16
+ from tlgr.models.peer import Peer, Photo
17
+
18
+ __all__ = [
19
+ "Button",
20
+ "ComposeResult",
21
+ "DeleteResult",
22
+ "DiceCatalog",
23
+ "DraftCleared",
24
+ "EditResult",
25
+ "Effect",
26
+ "EntityReport",
27
+ "FactCheck",
28
+ "Forward",
29
+ "ForwardedMessage",
30
+ "GameInfo",
31
+ "GameScore",
32
+ "LinkResult",
33
+ "MediaKind",
34
+ "MediaSummary",
35
+ "Message",
36
+ "MessageEntity",
37
+ "PaidMessageSettings",
38
+ "PinResult",
39
+ "ReactionSummary",
40
+ "ReadReceipts",
41
+ "ReadResult",
42
+ "ReplyHeader",
43
+ "ReplyMarkup",
44
+ "ReportResult",
45
+ "ScheduledSent",
46
+ "ServiceAction",
47
+ "SponsoredHidden",
48
+ "SponsoredMessage",
49
+ "SuggestedPostState",
50
+ "SummaryResult",
51
+ "Tone",
52
+ "Transcription",
53
+ "Translation",
54
+ "ViewCount",
55
+ "WebPagePreview",
56
+ ]
57
+
58
+ MediaKind = Literal[
59
+ "photo",
60
+ "video",
61
+ "gif",
62
+ "audio",
63
+ "voice",
64
+ "video_note",
65
+ "sticker",
66
+ "file",
67
+ "contact",
68
+ "geo",
69
+ "geo_live",
70
+ "venue",
71
+ "poll",
72
+ "dice",
73
+ "game",
74
+ "invoice",
75
+ "webpage",
76
+ "story",
77
+ "giveaway",
78
+ "paid",
79
+ "todo",
80
+ "unsupported",
81
+ ]
82
+
83
+
84
+ class MediaSummary(Model):
85
+ """What the media IS, from attributes the message already carries.
86
+
87
+ Nothing here downloads a byte. `tl_type` alone says `MessageMediaDocument`
88
+ for a thumbs-up sticker, a voice note, a GIF and a PDF alike — and a
89
+ caption-less one of those IS the message, so the distinction cannot be
90
+ left to the caller.
91
+ """
92
+
93
+ kind: MediaKind
94
+ tl_type: str
95
+ mime_type: str | None = None
96
+ file_name: str | None = None
97
+ size: int | None = None
98
+ duration: int | None = None
99
+ width: int | None = None
100
+ height: int | None = None
101
+ alt: str | None = None
102
+ sticker_set: str | None = None
103
+ performer: str | None = None
104
+ title: str | None = None
105
+ waveform: bool = False
106
+ is_animated: bool = False
107
+ supports_streaming: bool = False
108
+ spoiler: bool = False
109
+ ttl_seconds: int | None = None
110
+ round: bool = False
111
+ stripped_thumb_b64: str | None = None
112
+ thumbs: list[str] = []
113
+ dc_id: int | None = None
114
+ downloadable: bool = True
115
+ # Non-file media, flattened rather than nested as a one-of: a caller
116
+ # projecting `--select media.latitude` should not have to know the variant.
117
+ latitude: float | None = None
118
+ longitude: float | None = None
119
+ venue_title: str | None = None
120
+ contact_phone: str | None = None
121
+ contact_name: str | None = None
122
+ dice_emoji: str | None = None
123
+ dice_value: int | None = None
124
+ webpage_url: str | None = None
125
+ webpage_title: str | None = None
126
+ story_peer_id: int | None = None
127
+ story_id: int | None = None
128
+ paid_stars: int | None = None
129
+
130
+
131
+ class MessageEntity(Model):
132
+ """Formatting run. Offsets are UTF-16 code units, as Telegram defines them."""
133
+
134
+ type: str
135
+ offset: int
136
+ length: int
137
+ url: str | None = None
138
+ user_id: int | None = None
139
+ language: str | None = None
140
+ document_id: int | None = None
141
+ collapsed: bool | None = None
142
+
143
+
144
+ class Button(Model):
145
+ text: str
146
+ type: str
147
+ #: Flat row-major index across the whole markup. `bot press --button <n>`
148
+ #: takes exactly this number, which is why it is printed rather than left
149
+ #: for the caller to count. The default is -1 rather than 0 because
150
+ #: `omit_defaults` would drop the first button's index otherwise, and an
151
+ #: index that disappears for exactly one button is worse than none.
152
+ n: int = -1
153
+ data_b64: str | None = None
154
+ url: str | None = None
155
+ query: str | None = None
156
+ user_id: int | None = None
157
+ requires_password: bool = False
158
+ same_peer: bool = False
159
+ copy_text: str | None = None
160
+ button_id: int | None = None
161
+
162
+
163
+ class ReplyMarkup(Model):
164
+ kind: Literal["inline", "keyboard", "hide", "force_reply"]
165
+ rows: list[list[Button]] = []
166
+ resize: bool | None = None
167
+ single_use: bool | None = None
168
+ selective: bool | None = None
169
+ persistent: bool | None = None
170
+ placeholder: str | None = None
171
+
172
+
173
+ class ReactionSummary(Model):
174
+ """Compact reaction state including whether WE already reacted.
175
+
176
+ `mine` matters: Telegram answers a duplicate reaction with
177
+ MESSAGE_NOT_MODIFIED, so without it the only way to learn that a reaction
178
+ is already there is to send one and read the failure. It comes from
179
+ `ReactionCount.chosen_order`, which Telegram sets only for this account.
180
+ """
181
+
182
+ counts: dict[str, int] = {}
183
+ mine: list[str] = []
184
+ total: int = 0
185
+ can_see_list: bool | None = None
186
+ as_tags: bool = False
187
+ recent: list[dict[str, Any]] = []
188
+ paid_stars: int | None = None
189
+
190
+
191
+ class Forward(Model):
192
+ from_id: int | None = None
193
+ from_name: str | None = None
194
+ date: str | None = None
195
+ channel_post_id: int | None = None
196
+ post_author: str | None = None
197
+ saved_from_peer_id: int | None = None
198
+ saved_from_msg_id: int | None = None
199
+ imported: bool = False
200
+
201
+
202
+ class ReplyHeader(Model):
203
+ message_id: int | None = None
204
+ peer_id: int | None = None
205
+ top_message_id: int | None = None
206
+ forum_topic: bool = False
207
+ quote_text: str | None = None
208
+ quote_entities: list[MessageEntity] = []
209
+ quote_offset: int | None = None
210
+ story_peer_id: int | None = None
211
+ story_id: int | None = None
212
+ todo_item_id: int | None = None
213
+
214
+
215
+ class ServiceAction(Model):
216
+ """A service message is not "a message with empty text". It is an event."""
217
+
218
+ type: str
219
+ tl_type: str
220
+ user_ids: list[int] = []
221
+ title: str | None = None
222
+ photo: Photo | None = None
223
+ duration: int | None = None
224
+ call_id: int | None = None
225
+ ttl_seconds: int | None = None
226
+ boosts: int | None = None
227
+ stars: int | None = None
228
+ payload: dict[str, Any] = {}
229
+
230
+
231
+ class FactCheck(Model):
232
+ """A country-scoped fact-check attached to a message.
233
+
234
+ Readable by everyone; writable only by accounts Telegram has flagged as
235
+ independent fact-checkers for that country, which is why the write half
236
+ of `message fact-check set` usually fails with PERMISSION_DENIED.
237
+ """
238
+
239
+ msg_id: int | None = None
240
+ country: str | None = None
241
+ text: str = ""
242
+ entities: list[MessageEntity] = []
243
+ hash: int | None = None
244
+ need_check: bool = False
245
+
246
+
247
+ class DeleteResult(Model):
248
+ chat_id: int
249
+ deleted: int = 0
250
+ ids: list[int] = []
251
+ pts: int | None = None
252
+ #: `messages.AffectedHistory.offset` loops: how many messages the server
253
+ #: reported as affected across every round trip.
254
+ affected: int | None = None
255
+ scheduled: bool = False
256
+
257
+
258
+ class EditResult(Model):
259
+ """The edited message, or — with `--check` — whether it may be edited.
260
+
261
+ `can_edit` and `edit_time_limit` come from `messages.getMessageEditData`
262
+ and are the only honest answer to "can I still fix this?": the 48-hour
263
+ window does not apply to pinned, scheduled or Saved-Messages posts, so it
264
+ cannot be computed from the date on the client side.
265
+ """
266
+
267
+ id: int
268
+ chat_id: int
269
+ #: v1's `{"edited": true, …}`, kept for the shape AGENT.md documents.
270
+ #: False by default so `omit_defaults` does not drop it when it is true.
271
+ edited: bool = False
272
+ edit_date: str | None = None
273
+ text: str = ""
274
+ entities: list[MessageEntity] = []
275
+ can_edit: bool | None = None
276
+ edit_time_limit: int | None = None
277
+ caption: bool | None = None
278
+ already: bool = False
279
+
280
+
281
+ class DraftCleared(Model):
282
+ """What `draft clear` reports: v1's `{"cleared": true, "chat_id": …}`."""
283
+
284
+ cleared: bool = False
285
+ chat_id: int | None = None
286
+ count: int = 0
287
+
288
+
289
+ class ForwardedMessage(Model):
290
+ id: int
291
+ chat_id: int
292
+ date: str = ""
293
+ date_unix: int = 0
294
+ from_chat_id: int | None = None
295
+ from_msg_id: int | None = None
296
+
297
+
298
+ class PinResult(Model):
299
+ chat_id: int
300
+ msg_id: int | None = None
301
+ pinned: bool = False
302
+ #: `unpin --all` reports how many were unpinned; a single unpin reports 1.
303
+ unpinned: int | None = None
304
+ count: int | None = None
305
+ already: bool = False
306
+
307
+
308
+ class ReadResult(Model):
309
+ chat_id: int
310
+ #: v1 answered `{"read": true, "chat_id": …}` and AGENT.md documents it,
311
+ #: so the flag stays. Defaulted to False rather than True because
312
+ #: `omit_defaults` would drop a field whose value equals its default —
313
+ #: and a compatibility key that is absent is not a compatibility key.
314
+ read: bool = False
315
+ read_up_to: int | None = None
316
+ still_unread: int | None = None
317
+ mentions_read: int | None = None
318
+ reactions_read: int | None = None
319
+ contents_read: list[int] = []
320
+
321
+
322
+ class ReadReceipts(Model):
323
+ """Who read it, and when.
324
+
325
+ `unavailable_reason` exists because Telegram refuses this in three
326
+ different ways — too many members, too long ago, either side hiding read
327
+ dates — and none of them is an error the caller can fix by retrying.
328
+ """
329
+
330
+ msg_id: int
331
+ readers: list[int] = []
332
+ users: list[Peer] = []
333
+ read_date: str | None = None
334
+ read_date_unix: int | None = None
335
+ expired: bool = False
336
+ unavailable_reason: str | None = None
337
+
338
+
339
+ class LinkResult(Model):
340
+ link: str
341
+ public: bool = False
342
+ thread: bool = False
343
+ chat_id: int = 0
344
+ msg_id: int = 0
345
+
346
+
347
+ class ViewCount(Model):
348
+ msg_id: int
349
+ views: int | None = None
350
+ forwards: int | None = None
351
+ replies: int | None = None
352
+
353
+
354
+ class Translation(Model):
355
+ msg_id: int | None = None
356
+ text: str = ""
357
+ entities: list[MessageEntity] = []
358
+ lang: str = ""
359
+
360
+
361
+ class Transcription(Model):
362
+ msg_id: int
363
+ text: str = ""
364
+ pending: bool = False
365
+ transcription_id: int | None = None
366
+ rated: str | None = None
367
+
368
+
369
+ class SummaryResult(Model):
370
+ msg_id: int
371
+ text: str = ""
372
+ entities: list[MessageEntity] = []
373
+ quota_left: int | None = None
374
+
375
+
376
+ class ComposeResult(Model):
377
+ """An AI rewrite, plus the diff entities a proofread produces.
378
+
379
+ `diff_*` is only populated when proofreading is the sole mode: Telegram
380
+ emits `messageEntityDiffInsert/Delete/Replace` against the original text,
381
+ and mixing a translation into the same call makes the diff meaningless.
382
+ """
383
+
384
+ text: str = ""
385
+ entities: list[MessageEntity] = []
386
+ diff_text: str | None = None
387
+ diff_entities: list[MessageEntity] = []
388
+ quota_left: int | None = None
389
+ sent: Message | None = None
390
+
391
+
392
+ class EntityReport(Model):
393
+ """What `--parse` did to a piece of text, in the units Telegram counts in."""
394
+
395
+ text: str = ""
396
+ entities: list[MessageEntity] = []
397
+ #: Entities the *server* re-derives (url, email, mention, hashtag,
398
+ #: cashtag, bot_command, phone, bank card). They must not be sent, which
399
+ #: is why they are reported separately rather than merged in.
400
+ auto_entities: list[MessageEntity] = []
401
+ length_utf16: int = 0
402
+ would_split: int = 1
403
+ rendered: str | None = None
404
+
405
+
406
+ class Effect(Model):
407
+ id: int
408
+ emoticon: str = ""
409
+ premium_required: bool = False
410
+ static_icon_id: int | None = None
411
+ effect_animation_id: int | None = None
412
+ effect_sticker_id: int | None = None
413
+
414
+
415
+ class DiceCatalog(Model):
416
+ """Which dice emoji exist and what counts as a win, read from appConfig.
417
+
418
+ Never hardcoded: `emojies_send_dice` and `emojies_send_dice_success` are
419
+ server-side, and a client that hardcodes them reports a new dice emoji as
420
+ "unsupported media" the day Telegram adds one.
421
+ """
422
+
423
+ emojis: list[str] = []
424
+ success_values: dict[str, int] = {}
425
+ stake: dict[str, Any] | None = None
426
+
427
+
428
+ class WebPagePreview(Model):
429
+ url: str = ""
430
+ type: str | None = None
431
+ site_name: str | None = None
432
+ title: str | None = None
433
+ description: str | None = None
434
+ photo: Photo | None = None
435
+ document: MediaSummary | None = None
436
+ has_large_media: bool = False
437
+ cached_page: bool = False
438
+ pending: bool = False
439
+
440
+
441
+ class ReportResult(Model):
442
+ """A report, or the next menu of options it wants.
443
+
444
+ `messages.report` is a state machine: the first call answers with a list
445
+ of options, and each `--option` walks one level deeper until the server
446
+ reports the report was accepted.
447
+ """
448
+
449
+ ok: bool = False
450
+ title: str | None = None
451
+ options: list[dict[str, Any]] = []
452
+ comment_required: bool = False
453
+ already: bool = False
454
+
455
+
456
+ class ScheduledSent(Model):
457
+ id: int
458
+ chat_id: int
459
+ date: str = ""
460
+ date_unix: int = 0
461
+
462
+
463
+ class PaidMessageSettings(Model):
464
+ user_id: int | None = None
465
+ exempt: bool | None = None
466
+ refunded_stars: int | None = None
467
+ revenue_stars: int | None = None
468
+ revenue_ton: int | None = None
469
+
470
+
471
+ class SponsoredMessage(Model):
472
+ random_id: str
473
+ title: str | None = None
474
+ message: str = ""
475
+ entities: list[MessageEntity] = []
476
+ url: str | None = None
477
+ button_text: str | None = None
478
+ sponsor_info: str | None = None
479
+ additional_info: str | None = None
480
+ recommended: bool = False
481
+ can_report: bool = False
482
+ viewed: bool = False
483
+
484
+
485
+ class SponsoredHidden(Model):
486
+ hidden: bool = False
487
+ chat_id: int | None = None
488
+ already: bool = False
489
+
490
+
491
+ class SuggestedPostState(Model):
492
+ chat_id: int
493
+ msg_id: int
494
+ state: str = ""
495
+ publish_at: str | None = None
496
+ price: dict[str, Any] | None = None
497
+
498
+
499
+ class Tone(Model):
500
+ slug: str
501
+ title: str = ""
502
+ prompt: str | None = None
503
+ emoji_id: int | None = None
504
+ installed: bool = False
505
+ author: str | None = None
506
+ examples: list[str] = []
507
+
508
+
509
+ class GameScore(Model):
510
+ position: int | None = None
511
+ user_id: int | None = None
512
+ score: int = 0
513
+
514
+
515
+ class GameInfo(Model):
516
+ title: str = ""
517
+ short_name: str = ""
518
+ description: str | None = None
519
+ url: str | None = None
520
+ scores: list[GameScore] = []
521
+
522
+
523
+ class Message(Model):
524
+ id: int
525
+ chat_id: int
526
+ date: str
527
+ date_unix: int
528
+ text: str = ""
529
+ #: Only global search fills this: a hit in an unknown chat needs a name,
530
+ #: and `chat_id` alone makes the caller resolve every row itself.
531
+ chat: Peer | None = None
532
+ out: bool = False
533
+ kind: Literal["message", "service"] = "message"
534
+ # --- who ---
535
+ sender_id: int | None = None
536
+ sender: Peer | None = None
537
+ post_author: str | None = None
538
+ via_bot_id: int | None = None
539
+ from_rank: str | None = None
540
+ send_as_id: int | None = None
541
+ # --- what ---
542
+ entities: list[MessageEntity] = []
543
+ media: MediaSummary | None = None
544
+ reply_markup: ReplyMarkup | None = None
545
+ reactions: ReactionSummary | None = None
546
+ forward: Forward | None = None
547
+ reply_to: ReplyHeader | None = None
548
+ action: ServiceAction | None = None
549
+ grouped_id: int | None = None
550
+ # --- state ---
551
+ edit_date: str | None = None
552
+ pinned: bool = False
553
+ silent: bool = False
554
+ noforwards: bool = False
555
+ mentioned: bool = False
556
+ media_unread: bool = False
557
+ scheduled: bool = False
558
+ ttl_period: int | None = None
559
+ effect_id: int | None = None
560
+ views: int | None = None
561
+ forwards: int | None = None
562
+ replies_count: int | None = None
563
+ edit_hide: bool = False
564
+ restriction_reason: list[str] = []
565
+ link: str | None = None
566
+ paid_stars: int | None = None
567
+ factcheck: FactCheck | None = None
568
+ # Only `message thread list` fills these: a comment lives in the linked
569
+ # discussion group, not in the channel the caller named, and saying so is
570
+ # the difference between "id 42" and "id 42 *of what*".
571
+ thread_root: int | None = None
572
+ discussion_chat_id: int | None = None
573
+ # `--with-reply` / `--context` hang the neighbouring messages off the one
574
+ # that was asked for, rather than making the caller issue three requests.
575
+ reply: Message | None = None
576
+ context: list[Message] = []
577
+ raw: dict[str, Any] | None = None
578
+ #: `--delivery`: sent / read, derived from the *dialog's*
579
+ #: `read_outbox_max_id` rather than from the message, which carries no
580
+ #: such field.
581
+ delivery: str | None = None
582
+ #: The sibling ids one send produced — album items, or the parts of a
583
+ #: `--split` text. Empty for the ordinary one-message send.
584
+ batch: list[int] = []