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.
- tlgr/__init__.py +3 -0
- tlgr/__main__.py +6 -0
- tlgr/actions/__init__.py +45 -0
- tlgr/actions/forward.py +74 -0
- tlgr/actions/reply.py +32 -0
- tlgr/cli/__init__.py +259 -0
- tlgr/cli/confirm.py +55 -0
- tlgr/cli/errors.py +84 -0
- tlgr/cli/gen.py +690 -0
- tlgr/cli/globals.py +273 -0
- tlgr/cli/introspect.py +170 -0
- tlgr/cli/params.py +189 -0
- tlgr/cli/render.py +418 -0
- tlgr/core/__init__.py +0 -0
- tlgr/core/accounts.py +384 -0
- tlgr/core/config.py +358 -0
- tlgr/core/custom_tl.py +170 -0
- tlgr/core/errors.py +687 -0
- tlgr/core/eventtypes.py +1170 -0
- tlgr/core/identity.py +127 -0
- tlgr/core/launchd.py +122 -0
- tlgr/core/logging.py +194 -0
- tlgr/core/media.py +134 -0
- tlgr/core/output.py +251 -0
- tlgr/core/pagination.py +227 -0
- tlgr/core/paths.py +360 -0
- tlgr/core/peers.py +427 -0
- tlgr/core/process.py +138 -0
- tlgr/core/signing.py +38 -0
- tlgr/core/systemd.py +96 -0
- tlgr/core/telethon_compat.py +295 -0
- tlgr/core/text.py +211 -0
- tlgr/core/timefmt.py +199 -0
- tlgr/core/tl.py +98 -0
- tlgr/daemon/__init__.py +0 -0
- tlgr/daemon/app.py +869 -0
- tlgr/daemon/dispatch.py +446 -0
- tlgr/daemon/events.py +723 -0
- tlgr/daemon/files.py +431 -0
- tlgr/daemon/idle.py +119 -0
- tlgr/daemon/jobs.py +68 -0
- tlgr/daemon/main.py +161 -0
- tlgr/daemon/peercred.py +75 -0
- tlgr/daemon/policy.py +113 -0
- tlgr/daemon/preauth.py +366 -0
- tlgr/daemon/ratelimit.py +391 -0
- tlgr/daemon/server.py +24 -0
- tlgr/daemon/session.py +648 -0
- tlgr/daemon/sessions.py +274 -0
- tlgr/daemon/singleton.py +114 -0
- tlgr/daemon/stream.py +193 -0
- tlgr/daemon/transfers.py +219 -0
- tlgr/daemon/webhook.py +390 -0
- tlgr/data/catalog_index.json +1 -0
- tlgr/data/parity_waivers.toml +90 -0
- tlgr/filters/__init__.py +42 -0
- tlgr/filters/compose.py +121 -0
- tlgr/filters/content.py +85 -0
- tlgr/filters/context.py +114 -0
- tlgr/filters/message.py +161 -0
- tlgr/filters/temporal.py +87 -0
- tlgr/filters/user.py +36 -0
- tlgr/gateway/__init__.py +1 -0
- tlgr/gateway/config.py +161 -0
- tlgr/gateway/engine.py +215 -0
- tlgr/gateway/event.py +22 -0
- tlgr/jobs/__init__.py +0 -0
- tlgr/jobs/base.py +81 -0
- tlgr/jobs/client.py +37 -0
- tlgr/models/__init__.py +1220 -0
- tlgr/models/admin.py +744 -0
- tlgr/models/auth.py +510 -0
- tlgr/models/base.py +81 -0
- tlgr/models/bot.py +576 -0
- tlgr/models/business.py +265 -0
- tlgr/models/call.py +586 -0
- tlgr/models/config.py +101 -0
- tlgr/models/contact.py +481 -0
- tlgr/models/daemon.py +336 -0
- tlgr/models/dialog.py +626 -0
- tlgr/models/envelope.py +68 -0
- tlgr/models/error.py +30 -0
- tlgr/models/event.py +79 -0
- tlgr/models/export.py +66 -0
- tlgr/models/gift.py +275 -0
- tlgr/models/inline.py +84 -0
- tlgr/models/location.py +115 -0
- tlgr/models/media.py +507 -0
- tlgr/models/message.py +584 -0
- tlgr/models/net.py +232 -0
- tlgr/models/notify.py +105 -0
- tlgr/models/page.py +32 -0
- tlgr/models/payment.py +172 -0
- tlgr/models/peer.py +400 -0
- tlgr/models/poll.py +119 -0
- tlgr/models/premium.py +161 -0
- tlgr/models/privacy.py +93 -0
- tlgr/models/profile.py +217 -0
- tlgr/models/reaction.py +160 -0
- tlgr/models/resolve.py +175 -0
- tlgr/models/settings.py +103 -0
- tlgr/models/stars.py +101 -0
- tlgr/models/sticker.py +243 -0
- tlgr/models/story.py +467 -0
- tlgr/models/sync.py +105 -0
- tlgr/models/todo.py +36 -0
- tlgr/models/webapp.py +89 -0
- tlgr/ops/__init__.py +63 -0
- tlgr/ops/_admin.py +313 -0
- tlgr/ops/_auth.py +599 -0
- tlgr/ops/_bots.py +586 -0
- tlgr/ops/_calls.py +535 -0
- tlgr/ops/_common.py +160 -0
- tlgr/ops/_layer.py +46 -0
- tlgr/ops/_media.py +592 -0
- tlgr/ops/_params.py +212 -0
- tlgr/ops/_rights.py +402 -0
- tlgr/ops/_send.py +593 -0
- tlgr/ops/_serialize.py +667 -0
- tlgr/ops/_settings.py +306 -0
- tlgr/ops/_spec.py +167 -0
- tlgr/ops/_story.py +743 -0
- tlgr/ops/account.py +2604 -0
- tlgr/ops/agent.py +937 -0
- tlgr/ops/auth.py +1282 -0
- tlgr/ops/bot.py +4880 -0
- tlgr/ops/business.py +1520 -0
- tlgr/ops/call.py +1610 -0
- tlgr/ops/chat.py +4025 -0
- tlgr/ops/chat_admin.py +929 -0
- tlgr/ops/chat_extra.py +1061 -0
- tlgr/ops/chat_invite.py +716 -0
- tlgr/ops/chat_manage.py +1691 -0
- tlgr/ops/chat_member.py +1357 -0
- tlgr/ops/chat_stats.py +902 -0
- tlgr/ops/chat_topic.py +905 -0
- tlgr/ops/conference.py +791 -0
- tlgr/ops/config.py +1698 -0
- tlgr/ops/contact.py +2330 -0
- tlgr/ops/daemon.py +1397 -0
- tlgr/ops/draft.py +299 -0
- tlgr/ops/emoji.py +343 -0
- tlgr/ops/events.py +1327 -0
- tlgr/ops/export.py +596 -0
- tlgr/ops/folder.py +1322 -0
- tlgr/ops/gif.py +522 -0
- tlgr/ops/gift.py +1546 -0
- tlgr/ops/giveaway.py +541 -0
- tlgr/ops/inline.py +773 -0
- tlgr/ops/job.py +799 -0
- tlgr/ops/location.py +917 -0
- tlgr/ops/media.py +4495 -0
- tlgr/ops/message.py +3769 -0
- tlgr/ops/net.py +536 -0
- tlgr/ops/notify.py +840 -0
- tlgr/ops/passport.py +464 -0
- tlgr/ops/payment.py +907 -0
- tlgr/ops/poll.py +1078 -0
- tlgr/ops/premium.py +488 -0
- tlgr/ops/privacy.py +794 -0
- tlgr/ops/profile.py +1481 -0
- tlgr/ops/proxy.py +750 -0
- tlgr/ops/reaction.py +1475 -0
- tlgr/ops/resolve.py +1140 -0
- tlgr/ops/search.py +521 -0
- tlgr/ops/settings.py +1066 -0
- tlgr/ops/stars.py +594 -0
- tlgr/ops/sticker.py +1602 -0
- tlgr/ops/story.py +3216 -0
- tlgr/ops/sync.py +788 -0
- tlgr/ops/todo.py +514 -0
- tlgr/ops/user.py +1406 -0
- tlgr/ops/vc.py +2351 -0
- tlgr/ops/webapp.py +717 -0
- tlgr/ops/webhook.py +418 -0
- tlgr/parity.py +386 -0
- tlgr/processors/__init__.py +125 -0
- tlgr/processors/regex.py +26 -0
- tlgr/processors/text.py +56 -0
- tlgr/registry.py +519 -0
- tlgr/schema.py +173 -0
- tlgr/transport/__init__.py +30 -0
- tlgr/transport/autostart.py +293 -0
- tlgr/transport/client.py +805 -0
- tlgr/transport/ndjson.py +44 -0
- tlgr/version.py +31 -0
- tlgr_cli-2.0.1.dist-info/METADATA +957 -0
- tlgr_cli-2.0.1.dist-info/RECORD +192 -0
- tlgr_cli-2.0.1.dist-info/WHEEL +5 -0
- tlgr_cli-2.0.1.dist-info/entry_points.txt +2 -0
- tlgr_cli-2.0.1.dist-info/licenses/LICENSE +21 -0
- tlgr_cli-2.0.1.dist-info/top_level.txt +1 -0
tlgr/models/story.py
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
"""Stories: the item, its audience, its overlays, and who watched it.
|
|
2
|
+
|
|
3
|
+
Four shapes carry most of this group and each exists because the API makes a
|
|
4
|
+
distinction the GUI hides.
|
|
5
|
+
|
|
6
|
+
* **`Story`** is one story item. Telegram returns three different TL classes
|
|
7
|
+
for one id — the full `storyItem`, a `storyItemSkipped` placeholder in a
|
|
8
|
+
feed, and `storyItemDeleted` for one that is gone — so the model carries
|
|
9
|
+
`skipped` and `deleted` flags rather than pretending a placeholder is a
|
|
10
|
+
story with no caption.
|
|
11
|
+
* **`StoryPrivacy`** is the audience as a *base rule plus exceptions*, which
|
|
12
|
+
is how `inputPrivacyRule*` vectors actually work and how the GUI's
|
|
13
|
+
"Contacts, except Bob" is expressed. Flattening it into a single string
|
|
14
|
+
would make that sentence inexpressible.
|
|
15
|
+
* **`MediaArea`** is one overlay pill. All eight TL variants are flattened
|
|
16
|
+
into one struct with a `type` discriminator, because `story get --areas-out`
|
|
17
|
+
has to write JSON that `story post --areas` reads back verbatim: a nested
|
|
18
|
+
one-of would make that round trip depend on the caller knowing the variant.
|
|
19
|
+
* **`StoryViewer`** is one row of the viewers screen, which mixes plain views,
|
|
20
|
+
public forwards and public reposts — `kind` says which, so a caller counting
|
|
21
|
+
"people who watched" is not silently counting reposts too.
|
|
22
|
+
|
|
23
|
+
Coordinates on a media area are percentages of the media (0–100), which is
|
|
24
|
+
what the TL type stores; they are never pixels, because a story's rendered
|
|
25
|
+
size is a client decision.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from typing import Any, Literal
|
|
31
|
+
|
|
32
|
+
from tlgr.models.base import Model
|
|
33
|
+
from tlgr.models.message import MediaSummary, Message, MessageEntity
|
|
34
|
+
from tlgr.models.peer import Peer, Photo, User
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"AlbumDeleted",
|
|
38
|
+
"AlbumOrder",
|
|
39
|
+
"BlockedStoryUser",
|
|
40
|
+
"BlocklistChange",
|
|
41
|
+
"LiveStory",
|
|
42
|
+
"MediaArea",
|
|
43
|
+
"StealthMode",
|
|
44
|
+
"StoriesDeleted",
|
|
45
|
+
"Story",
|
|
46
|
+
"StoryAlbum",
|
|
47
|
+
"StoryEvent",
|
|
48
|
+
"StoryExport",
|
|
49
|
+
"StoryFeedPeer",
|
|
50
|
+
"StoryHidden",
|
|
51
|
+
"StoryLimits",
|
|
52
|
+
"StoryPinned",
|
|
53
|
+
"StoryPostCheck",
|
|
54
|
+
"StoryPrivacy",
|
|
55
|
+
"StoryReactionResult",
|
|
56
|
+
"StoryRead",
|
|
57
|
+
"StoryReply",
|
|
58
|
+
"StoryReport",
|
|
59
|
+
"StoryShared",
|
|
60
|
+
"StoryStats",
|
|
61
|
+
"StoryViewer",
|
|
62
|
+
"StoryViews",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
#: The eight `mediaArea*` variants, as one flat vocabulary.
|
|
66
|
+
MediaAreaKind = Literal[
|
|
67
|
+
"geo",
|
|
68
|
+
"venue",
|
|
69
|
+
"reaction",
|
|
70
|
+
"channel_post",
|
|
71
|
+
"url",
|
|
72
|
+
"weather",
|
|
73
|
+
"star_gift",
|
|
74
|
+
"unknown",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
#: The base audience rule. `selected` is Telegram's "Only these people": an
|
|
78
|
+
#: empty allow list with this rule shows the story to nobody.
|
|
79
|
+
PrivacyBase = Literal["everyone", "contacts", "close-friends", "selected"]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class MediaArea(Model):
|
|
83
|
+
"""One overlay pill on a story, in the round-trippable JSON form.
|
|
84
|
+
|
|
85
|
+
`x`/`y` are the centre of the area and `w`/`h` its size, all as
|
|
86
|
+
percentages of the media, exactly as `mediaAreaCoordinates` stores them.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
type: MediaAreaKind = "unknown"
|
|
90
|
+
x: float = 0.0
|
|
91
|
+
y: float = 0.0
|
|
92
|
+
w: float = 0.0
|
|
93
|
+
h: float = 0.0
|
|
94
|
+
rotation: float = 0.0
|
|
95
|
+
radius: float | None = None
|
|
96
|
+
# geo / venue
|
|
97
|
+
latitude: float | None = None
|
|
98
|
+
longitude: float | None = None
|
|
99
|
+
address: dict[str, str] | None = None
|
|
100
|
+
title: str | None = None
|
|
101
|
+
address_text: str | None = None
|
|
102
|
+
provider: str | None = None
|
|
103
|
+
venue_id: str | None = None
|
|
104
|
+
venue_type: str | None = None
|
|
105
|
+
# suggested reaction
|
|
106
|
+
reaction: str | None = None
|
|
107
|
+
dark: bool = False
|
|
108
|
+
flipped: bool = False
|
|
109
|
+
# channel post
|
|
110
|
+
chat_id: int | None = None
|
|
111
|
+
msg_id: int | None = None
|
|
112
|
+
# url
|
|
113
|
+
url: str | None = None
|
|
114
|
+
# weather
|
|
115
|
+
emoji: str | None = None
|
|
116
|
+
temperature_c: float | None = None
|
|
117
|
+
color: int | None = None
|
|
118
|
+
# collectible star gift
|
|
119
|
+
slug: str | None = None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class StoryPrivacy(Model):
|
|
123
|
+
"""Who may see a story: a base rule, then the exceptions on top of it.
|
|
124
|
+
|
|
125
|
+
The lists are *marked* ids, like every other id tlgr emits. They are only
|
|
126
|
+
populated on your own stories — the server never tells you the audience of
|
|
127
|
+
somebody else's.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
base: PrivacyBase = "everyone"
|
|
131
|
+
allow_users: list[int] = []
|
|
132
|
+
allow_chats: list[int] = []
|
|
133
|
+
disallow_users: list[int] = []
|
|
134
|
+
disallow_chats: list[int] = []
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class StoryViews(Model):
|
|
138
|
+
"""The counters under a story.
|
|
139
|
+
|
|
140
|
+
`has_viewers` is the honest half: a non-Premium account loses the viewer
|
|
141
|
+
list `story_viewers_expire_period` seconds after the story expires, and
|
|
142
|
+
an empty list then means "no longer available", not "nobody watched".
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
views_count: int = 0
|
|
146
|
+
forwards_count: int | None = None
|
|
147
|
+
reactions_count: int | None = None
|
|
148
|
+
has_viewers: bool = False
|
|
149
|
+
recent_viewers: list[int] = []
|
|
150
|
+
reactions: dict[str, int] = {}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class StoryFwdHeader(Model):
|
|
154
|
+
"""Where a reposted story came from."""
|
|
155
|
+
|
|
156
|
+
from_id: int | None = None
|
|
157
|
+
from_name: str | None = None
|
|
158
|
+
story_id: int | None = None
|
|
159
|
+
modified: bool = False
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class Story(Model):
|
|
163
|
+
"""One story.
|
|
164
|
+
|
|
165
|
+
`skipped` and `deleted` are the two placeholder states the API has:
|
|
166
|
+
a feed hands back `storyItemSkipped` (id, dates and the close-friends flag
|
|
167
|
+
only) and a gone story comes back as `storyItemDeleted`. Reporting either
|
|
168
|
+
as an ordinary story with empty fields is how a caller ends up believing a
|
|
169
|
+
deleted story is a caption-less one.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
id: int
|
|
173
|
+
peer_id: int = 0
|
|
174
|
+
peer: Peer | None = None
|
|
175
|
+
date: str | None = None
|
|
176
|
+
date_unix: int | None = None
|
|
177
|
+
expire_date: str | None = None
|
|
178
|
+
expire_date_unix: int | None = None
|
|
179
|
+
caption: str = ""
|
|
180
|
+
entities: list[MessageEntity] = []
|
|
181
|
+
media: MediaSummary | None = None
|
|
182
|
+
media_areas: list[MediaArea] = []
|
|
183
|
+
privacy: StoryPrivacy | None = None
|
|
184
|
+
public: bool = False
|
|
185
|
+
close_friends: bool = False
|
|
186
|
+
contacts: bool = False
|
|
187
|
+
selected_contacts: bool = False
|
|
188
|
+
pinned: bool = False
|
|
189
|
+
noforwards: bool = False
|
|
190
|
+
edited: bool = False
|
|
191
|
+
out: bool = False
|
|
192
|
+
min: bool = False
|
|
193
|
+
#: A placeholder row in a feed: only id, dates and `close_friends` are set.
|
|
194
|
+
skipped: bool = False
|
|
195
|
+
#: The story is gone; nothing but `id` is meaningful.
|
|
196
|
+
deleted: bool = False
|
|
197
|
+
#: A live story (an ongoing broadcast rather than a recorded item).
|
|
198
|
+
live: bool = False
|
|
199
|
+
fwd_from: StoryFwdHeader | None = None
|
|
200
|
+
sent_reaction: str | None = None
|
|
201
|
+
albums: list[int] = []
|
|
202
|
+
music: MediaSummary | None = None
|
|
203
|
+
views: StoryViews | None = None
|
|
204
|
+
link: str | None = None
|
|
205
|
+
translation: str | None = None
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class StoryAlbum(Model):
|
|
209
|
+
"""A profile album — the named chips above the story grid."""
|
|
210
|
+
|
|
211
|
+
id: int
|
|
212
|
+
title: str = ""
|
|
213
|
+
icon: Photo | None = None
|
|
214
|
+
stories: list[int] = []
|
|
215
|
+
stories_count: int | None = None
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class AlbumDeleted(Model):
|
|
219
|
+
peer: int = 0
|
|
220
|
+
album_id: int = 0
|
|
221
|
+
ok: bool = True
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class AlbumOrder(Model):
|
|
225
|
+
peer: int = 0
|
|
226
|
+
order: list[int] = []
|
|
227
|
+
ok: bool = True
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class BlockedStoryUser(Model):
|
|
231
|
+
"""A row of "Hide my stories from" — a blocklist of its own."""
|
|
232
|
+
|
|
233
|
+
user_id: int
|
|
234
|
+
username: str | None = None
|
|
235
|
+
name: str = ""
|
|
236
|
+
date: str | None = None
|
|
237
|
+
date_unix: int | None = None
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class BlocklistChange(Model):
|
|
241
|
+
added: list[int] = []
|
|
242
|
+
removed: list[int] = []
|
|
243
|
+
total: int | None = None
|
|
244
|
+
already: bool = False
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class StoryLimits(Model):
|
|
248
|
+
"""The app-config numbers that decide what a story may be.
|
|
249
|
+
|
|
250
|
+
Every one of them is read from `help.getAppConfig`; none is hardcoded,
|
|
251
|
+
because Telegram changes them without changing the layer.
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
expiring_limit: int | None = None
|
|
255
|
+
sent_weekly_limit: int | None = None
|
|
256
|
+
sent_monthly_limit: int | None = None
|
|
257
|
+
caption_length_limit: int | None = None
|
|
258
|
+
suggested_reactions_limit: int | None = None
|
|
259
|
+
area_url_max: int | None = None
|
|
260
|
+
albums_limit: int | None = None
|
|
261
|
+
album_stories_limit: int | None = None
|
|
262
|
+
pinned_to_top_max: int | None = None
|
|
263
|
+
viewers_expire_period: int | None = None
|
|
264
|
+
stealth_past_period: int | None = None
|
|
265
|
+
stealth_future_period: int | None = None
|
|
266
|
+
stealth_cooldown_period: int | None = None
|
|
267
|
+
#: Which of the above this account's Premium status unlocks.
|
|
268
|
+
premium_unlocks: list[str] = []
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class StoryPostCheck(Model):
|
|
272
|
+
"""`story can-post`: the pre-flight the GUI runs before opening the camera."""
|
|
273
|
+
|
|
274
|
+
can_post: bool = False
|
|
275
|
+
reason: str = ""
|
|
276
|
+
#: Seconds to wait, for a weekly/monthly flood; boosts missing, for a channel.
|
|
277
|
+
retry_after: int | None = None
|
|
278
|
+
boosts_required: int | None = None
|
|
279
|
+
free_slots: int | None = None
|
|
280
|
+
count_remains: int | None = None
|
|
281
|
+
premium: bool = False
|
|
282
|
+
limits: StoryLimits | None = None
|
|
283
|
+
chats: list[Peer] = []
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class StoryHiddenPeer(Model):
|
|
287
|
+
"""One row of a bulk `story hide`, in the same keys the single call uses.
|
|
288
|
+
|
|
289
|
+
`hidden` and `already` carry no default on purpose: they are the answer,
|
|
290
|
+
and `omit_defaults` would drop the false ones (see `StoryHidden`).
|
|
291
|
+
"""
|
|
292
|
+
|
|
293
|
+
hidden: bool
|
|
294
|
+
already: bool
|
|
295
|
+
user_id: int = 0
|
|
296
|
+
username: str | None = None
|
|
297
|
+
peer_id: int = 0
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class StoryHidden(Model):
|
|
301
|
+
"""The per-account "Hide Stories" toggle, in v1's keys.
|
|
302
|
+
|
|
303
|
+
`user_id`/`username` are what `tlgr user hide-stories` printed and stay
|
|
304
|
+
spelled that way; `peer_id` is the marked id, for the channels the same
|
|
305
|
+
RPC accepts. A single target answers exactly as v1 did; extra targets
|
|
306
|
+
appear in `peers`, so a bulk pass stays one command without changing the
|
|
307
|
+
shape the documented single-peer call returns.
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
#: Neither has a default: `hidden: false` and `already: false` are the
|
|
311
|
+
#: answer AGENT.md publishes for `user hide-stories`, and `omit_defaults`
|
|
312
|
+
#: would drop exactly the two values a caller has to read.
|
|
313
|
+
hidden: bool
|
|
314
|
+
already: bool
|
|
315
|
+
user_id: int = 0
|
|
316
|
+
username: str | None = None
|
|
317
|
+
peer_id: int = 0
|
|
318
|
+
#: Set instead of the peer fields when `--all` collapsed the whole bar.
|
|
319
|
+
all: bool = False
|
|
320
|
+
peers: list[StoryHiddenPeer] = []
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class StoriesDeleted(Model):
|
|
324
|
+
peer: int = 0
|
|
325
|
+
deleted_ids: list[int] = []
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class StoryPinned(Model):
|
|
329
|
+
peer: int = 0
|
|
330
|
+
ids: list[int] = []
|
|
331
|
+
pinned: bool = False
|
|
332
|
+
pinned_to_top: list[int] = []
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
class StoryRead(Model):
|
|
336
|
+
peer: int = 0
|
|
337
|
+
max_id: int = 0
|
|
338
|
+
ids: list[int] = []
|
|
339
|
+
ok: bool = True
|
|
340
|
+
already: bool = False
|
|
341
|
+
#: Ids whose view counter was incremented (`--register-view`).
|
|
342
|
+
viewed_ids: list[int] = []
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
class StoryReactionResult(Model):
|
|
346
|
+
peer: int = 0
|
|
347
|
+
story_id: int = 0
|
|
348
|
+
reaction: str = ""
|
|
349
|
+
removed: bool = False
|
|
350
|
+
#: Set when `--as-message` sent an ordinary reply instead.
|
|
351
|
+
msg_id: int | None = None
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class StoryReply(Model):
|
|
355
|
+
chat_id: int = 0
|
|
356
|
+
msg_id: int = 0
|
|
357
|
+
reply_to_story: int = 0
|
|
358
|
+
text: str = ""
|
|
359
|
+
message: Message | None = None
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class StoryShared(Model):
|
|
363
|
+
sent: list[Message] = []
|
|
364
|
+
story_id: int = 0
|
|
365
|
+
peer: int = 0
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class StoryReport(Model):
|
|
369
|
+
"""One step of the multi-step report flow."""
|
|
370
|
+
|
|
371
|
+
result: str = ""
|
|
372
|
+
title: str = ""
|
|
373
|
+
options: list[dict[str, str]] = []
|
|
374
|
+
comment_required: bool = False
|
|
375
|
+
reported: bool = False
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
class StealthMode(Model):
|
|
379
|
+
active_until_date: str | None = None
|
|
380
|
+
active_until_unix: int | None = None
|
|
381
|
+
cooldown_until_date: str | None = None
|
|
382
|
+
cooldown_until_unix: int | None = None
|
|
383
|
+
past: bool = False
|
|
384
|
+
future: bool = False
|
|
385
|
+
active: bool = False
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
class StoryViewer(Model):
|
|
389
|
+
"""One row of the viewers screen."""
|
|
390
|
+
|
|
391
|
+
kind: Literal["view", "forward", "repost"] = "view"
|
|
392
|
+
user_id: int = 0
|
|
393
|
+
user: User | None = None
|
|
394
|
+
peer: Peer | None = None
|
|
395
|
+
date: str | None = None
|
|
396
|
+
date_unix: int | None = None
|
|
397
|
+
reaction: str | None = None
|
|
398
|
+
blocked: bool = False
|
|
399
|
+
blocked_my_stories_from: bool = False
|
|
400
|
+
#: For a forward/repost row: where it landed.
|
|
401
|
+
msg_id: int | None = None
|
|
402
|
+
story_id: int | None = None
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
class StoryFeedPeer(Model):
|
|
406
|
+
"""One peer in the stories bar."""
|
|
407
|
+
|
|
408
|
+
peer_id: int = 0
|
|
409
|
+
peer: Peer | None = None
|
|
410
|
+
max_read_id: int = 0
|
|
411
|
+
stories: list[Story] = []
|
|
412
|
+
unread_count: int = 0
|
|
413
|
+
has_unread: bool = False
|
|
414
|
+
live: bool = False
|
|
415
|
+
hidden: bool = False
|
|
416
|
+
#: Set by `--peers`: the compact `stories.getPeerMaxIDs` answer.
|
|
417
|
+
max_id: int | None = None
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class LiveStory(Model):
|
|
421
|
+
"""A live story: the ongoing-broadcast form of a story."""
|
|
422
|
+
|
|
423
|
+
story_id: int = 0
|
|
424
|
+
peer: int = 0
|
|
425
|
+
live: bool = True
|
|
426
|
+
date: str | None = None
|
|
427
|
+
expire_date: str | None = None
|
|
428
|
+
call_id: int | None = None
|
|
429
|
+
participants_count: int | None = None
|
|
430
|
+
streamer: int | None = None
|
|
431
|
+
rtmp_stream: bool = False
|
|
432
|
+
rtmp_url: str | None = None
|
|
433
|
+
rtmp_key: str | None = None
|
|
434
|
+
stream_dc_id: int | None = None
|
|
435
|
+
messages_enabled: bool | None = None
|
|
436
|
+
send_paid_messages_stars: int | None = None
|
|
437
|
+
record_start_date: str | None = None
|
|
438
|
+
listeners_hidden: bool | None = None
|
|
439
|
+
pinned: bool = False
|
|
440
|
+
noforwards: bool = False
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
class StoryExport(Model):
|
|
444
|
+
count: int = 0
|
|
445
|
+
out_dir: str = ""
|
|
446
|
+
files: list[str] = []
|
|
447
|
+
stories: list[Story] = []
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
class StoryStats(Model):
|
|
451
|
+
views_graph: dict[str, Any] | None = None
|
|
452
|
+
reactions_by_emotion_graph: dict[str, Any] | None = None
|
|
453
|
+
forwards: list[dict[str, Any]] = []
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
class StoryEvent(Model):
|
|
457
|
+
"""One frame of `story watch`."""
|
|
458
|
+
|
|
459
|
+
event: str = "story"
|
|
460
|
+
kind: str = ""
|
|
461
|
+
peer: int = 0
|
|
462
|
+
story_id: int | None = None
|
|
463
|
+
ids: list[int] = []
|
|
464
|
+
reaction: str | None = None
|
|
465
|
+
max_read_id: int | None = None
|
|
466
|
+
stealth_mode: StealthMode | None = None
|
|
467
|
+
at: str = ""
|
tlgr/models/sync.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Update-state shapes: pts/qts/seq, per-channel boxes, and difference runs.
|
|
2
|
+
|
|
3
|
+
`sync` is deliberately a different noun from `catchup`. `chat catchup` is the
|
|
4
|
+
unread digest a human reads; `sync catch-up` is `updates.getDifference`, the
|
|
5
|
+
plumbing that decides whether an event ever existed for the daemon at all.
|
|
6
|
+
Confusing them is how v1 ended up with an idle timeout that guaranteed a
|
|
7
|
+
permanent sync hole and a `catchup` command that could not close it.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from tlgr.models.base import Model
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"BackfillPage",
|
|
18
|
+
"CatchUpResult",
|
|
19
|
+
"ChannelState",
|
|
20
|
+
"DifferenceResult",
|
|
21
|
+
"ResetResult",
|
|
22
|
+
"SyncStatus",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ChannelState(Model):
|
|
27
|
+
chat_id: int
|
|
28
|
+
pts: int = 0
|
|
29
|
+
#: False means catch-up will silently *skip* this channel: Telethon needs
|
|
30
|
+
#: an access hash in the session to call getChannelDifference at all.
|
|
31
|
+
access_hash_known: bool = False
|
|
32
|
+
last_difference_at: str | None = None
|
|
33
|
+
title: str | None = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class SyncStatus(Model):
|
|
37
|
+
account: str = ""
|
|
38
|
+
pts: int | None = None
|
|
39
|
+
qts: int | None = None
|
|
40
|
+
seq: int | None = None
|
|
41
|
+
date: str | None = None
|
|
42
|
+
date_unix: int | None = None
|
|
43
|
+
unread_count: int | None = None
|
|
44
|
+
server_pts: int | None = None
|
|
45
|
+
server_seq: int | None = None
|
|
46
|
+
behind_pts: int | None = None
|
|
47
|
+
behind_seconds: int | None = None
|
|
48
|
+
phase: str = "unknown"
|
|
49
|
+
getting_difference: bool = False
|
|
50
|
+
channels: list[ChannelState] = []
|
|
51
|
+
last_update_at: str | None = None
|
|
52
|
+
no_updates_for_seconds: int | None = None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class CatchUpResult(Model):
|
|
56
|
+
account: str = ""
|
|
57
|
+
events_replayed: int = 0
|
|
58
|
+
pts_before: int | None = None
|
|
59
|
+
pts_after: int | None = None
|
|
60
|
+
duration_ms: int = 0
|
|
61
|
+
too_long: bool = False
|
|
62
|
+
timed_out: bool = False
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class DifferenceResult(Model):
|
|
66
|
+
"""One `updates.getDifference` / `getChannelDifference` run, reported raw.
|
|
67
|
+
|
|
68
|
+
Without `--apply` this is a *probe*: it does not advance the stored pts,
|
|
69
|
+
so running it cannot create the gap it was meant to diagnose.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
#: `common` or `channel`. No default: the discriminator must never be the
|
|
73
|
+
#: field `omit_defaults` drops.
|
|
74
|
+
kind: str
|
|
75
|
+
#: Nor this one: "was that the whole difference" is what a caller loops on.
|
|
76
|
+
final: bool
|
|
77
|
+
new_pts: int | None = None
|
|
78
|
+
new_qts: int | None = None
|
|
79
|
+
new_seq: int | None = None
|
|
80
|
+
new_date: str | None = None
|
|
81
|
+
messages: int = 0
|
|
82
|
+
other_updates: int = 0
|
|
83
|
+
users: int = 0
|
|
84
|
+
chats: int = 0
|
|
85
|
+
timeout: int | None = None
|
|
86
|
+
too_long: bool = False
|
|
87
|
+
applied: bool = False
|
|
88
|
+
dry_run: bool = False
|
|
89
|
+
requests: list[dict[str, Any]] = []
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class ResetResult(Model):
|
|
93
|
+
account: str = ""
|
|
94
|
+
reset: bool = False
|
|
95
|
+
pts_before: int | None = None
|
|
96
|
+
pts_after: int | None = None
|
|
97
|
+
channels_reset: list[int] = []
|
|
98
|
+
dry_run: bool = False
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class BackfillPage(Model):
|
|
102
|
+
"""The extra field a plain `Page[Message]` cannot carry: what was missing."""
|
|
103
|
+
|
|
104
|
+
fetched: int = 0
|
|
105
|
+
missing_ids: list[int] = []
|
tlgr/models/todo.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Checklists.
|
|
2
|
+
|
|
3
|
+
A task's `id` is assigned by the client and is the only thing that ties a
|
|
4
|
+
completion to a task: `messages.editMessage` rewrites the whole `todoList`, so
|
|
5
|
+
renumbering the survivors while deleting one would silently move every tick to
|
|
6
|
+
the wrong row. Nothing in this group ever renumbers.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from tlgr.models.base import Model
|
|
12
|
+
from tlgr.models.message import MessageEntity
|
|
13
|
+
|
|
14
|
+
__all__ = ["Todo", "TodoTask"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TodoTask(Model):
|
|
18
|
+
id: int
|
|
19
|
+
title: str = ""
|
|
20
|
+
entities: list[MessageEntity] = []
|
|
21
|
+
done: bool = False
|
|
22
|
+
completed_by: int | None = None
|
|
23
|
+
completed_date: str | None = None
|
|
24
|
+
completed_date_unix: int | None = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Todo(Model):
|
|
28
|
+
chat_id: int = 0
|
|
29
|
+
msg_id: int = 0
|
|
30
|
+
title: str = ""
|
|
31
|
+
entities: list[MessageEntity] = []
|
|
32
|
+
others_can_add: bool = False
|
|
33
|
+
others_can_complete: bool = False
|
|
34
|
+
tasks: list[TodoTask] = []
|
|
35
|
+
done_count: int = 0
|
|
36
|
+
already: bool = False
|
tlgr/models/webapp.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Mini apps: the manifest, the signed session URL, and the session's lifetime.
|
|
2
|
+
|
|
3
|
+
`WebAppSession.url` is a short-lived *credential*, not a link: it carries the
|
|
4
|
+
user's signed init data, and anyone holding it can act as the app on that
|
|
5
|
+
user's behalf until it expires. It is therefore printed once, with that
|
|
6
|
+
warning in human output, and never opened — tlgr has no browser and hosting
|
|
7
|
+
the `window.Telegram.WebApp` bridge is not a CLI's job.
|
|
8
|
+
|
|
9
|
+
`needs_prolong` exists because the two request families differ in a way that
|
|
10
|
+
is invisible from the URL: a session that came back with a `query_id` dies in
|
|
11
|
+
about a minute unless `webapp watch` keeps prolonging it, and one that did not
|
|
12
|
+
simply has no session to lose.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from tlgr.models.base import Model
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"WebAppDownload",
|
|
23
|
+
"WebAppInfo",
|
|
24
|
+
"WebAppProlong",
|
|
25
|
+
"WebAppSent",
|
|
26
|
+
"WebAppSession",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class WebAppInfo(Model):
|
|
31
|
+
"""A mini app's manifest, as `messages.botApp` and `botAppSettings` hold it."""
|
|
32
|
+
|
|
33
|
+
bot: str | None = None
|
|
34
|
+
short_name: str | None = None
|
|
35
|
+
title: str | None = None
|
|
36
|
+
description: str | None = None
|
|
37
|
+
photo: int | None = None
|
|
38
|
+
document: int | None = None
|
|
39
|
+
inactive: bool = False
|
|
40
|
+
request_write_access: bool = False
|
|
41
|
+
has_settings: bool = False
|
|
42
|
+
terms_url: str | None = None
|
|
43
|
+
privacy_policy_url: str | None = None
|
|
44
|
+
link: str | None = None
|
|
45
|
+
installed_in_attach_menu: bool = False
|
|
46
|
+
installed_in_side_menu: bool = False
|
|
47
|
+
#: The placeholder is an SVG-like path blob; its length is reported
|
|
48
|
+
#: rather than its bytes, because nothing on a terminal can render it.
|
|
49
|
+
placeholder_path: int | None = None
|
|
50
|
+
bg_color: int | None = None
|
|
51
|
+
bg_dark_color: int | None = None
|
|
52
|
+
header_color: int | None = None
|
|
53
|
+
header_dark_color: int | None = None
|
|
54
|
+
button_request: dict[str, Any] | None = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class WebAppSession(Model):
|
|
58
|
+
bot: str | None = None
|
|
59
|
+
kind: str = ""
|
|
60
|
+
url: str = ""
|
|
61
|
+
query_id: str | None = None
|
|
62
|
+
expires_at: str | None = None
|
|
63
|
+
fullsize: bool = False
|
|
64
|
+
fullscreen: bool = False
|
|
65
|
+
same_origin: bool = False
|
|
66
|
+
needs_prolong: bool = False
|
|
67
|
+
prolong_every: int | None = None
|
|
68
|
+
write_allowed: bool = False
|
|
69
|
+
inactive_confirmed: bool = False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class WebAppSent(Model):
|
|
73
|
+
bot_id: int = 0
|
|
74
|
+
sent: bool = False
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class WebAppDownload(Model):
|
|
78
|
+
allowed: bool | None = None
|
|
79
|
+
file_name: str = ""
|
|
80
|
+
url: str = ""
|
|
81
|
+
downloaded: bool | None = None
|
|
82
|
+
path: str | None = None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class WebAppProlong(Model):
|
|
86
|
+
query_id: str = ""
|
|
87
|
+
prolonged_at: str | None = None
|
|
88
|
+
alive: bool = True
|
|
89
|
+
reason: str | None = None
|