tdelegram 0.1.0__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.
- tdelegram/__init__.py +42 -0
- tdelegram/api/__init__.py +47 -0
- tdelegram/api/account.py +69 -0
- tdelegram/api/admin.py +225 -0
- tdelegram/api/bots.py +134 -0
- tdelegram/api/chats.py +314 -0
- tdelegram/api/contacts.py +78 -0
- tdelegram/api/drafts.py +49 -0
- tdelegram/api/export.py +275 -0
- tdelegram/api/folders.py +77 -0
- tdelegram/api/inbox.py +124 -0
- tdelegram/api/media.py +202 -0
- tdelegram/api/messages.py +400 -0
- tdelegram/api/polls.py +51 -0
- tdelegram/api/proxies.py +222 -0
- tdelegram/api/reactions.py +43 -0
- tdelegram/api/search.py +103 -0
- tdelegram/api/secret.py +46 -0
- tdelegram/api/stories.py +42 -0
- tdelegram/api/topics.py +161 -0
- tdelegram/api/updates.py +106 -0
- tdelegram/api/users.py +82 -0
- tdelegram/auth.py +433 -0
- tdelegram/cli/__init__.py +0 -0
- tdelegram/cli/context.py +301 -0
- tdelegram/cli/main.py +1050 -0
- tdelegram/cli/output.py +107 -0
- tdelegram/client.py +198 -0
- tdelegram/config.py +113 -0
- tdelegram/credentials.py +227 -0
- tdelegram/dates.py +65 -0
- tdelegram/entities.py +91 -0
- tdelegram/errors.py +127 -0
- tdelegram/files.py +100 -0
- tdelegram/loop.py +197 -0
- tdelegram/methods.json +4090 -0
- tdelegram/normalize.py +378 -0
- tdelegram/paging.py +127 -0
- tdelegram/py.typed +0 -0
- tdelegram/safety.py +43 -0
- tdelegram/schema.json +3211 -0
- tdelegram/schema.py +233 -0
- tdelegram/tdjson.py +54 -0
- tdelegram/transport.py +196 -0
- tdelegram-0.1.0.dist-info/METADATA +200 -0
- tdelegram-0.1.0.dist-info/RECORD +50 -0
- tdelegram-0.1.0.dist-info/WHEEL +4 -0
- tdelegram-0.1.0.dist-info/entry_points.txt +2 -0
- tdelegram-0.1.0.dist-info/licenses/LICENSE +201 -0
- tdelegram-0.1.0.dist-info/licenses/NOTICE +5 -0
tdelegram/__init__.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""TDelegram: a full-featured Telegram client library + CLI backed by TDLib."""
|
|
2
|
+
|
|
3
|
+
from importlib import metadata as _metadata
|
|
4
|
+
|
|
5
|
+
from tdelegram.client import TelegramClient
|
|
6
|
+
from tdelegram.config import ClientConfig
|
|
7
|
+
from tdelegram.errors import (
|
|
8
|
+
AuthError,
|
|
9
|
+
FloodWait,
|
|
10
|
+
InvalidRequest,
|
|
11
|
+
NotFoundError,
|
|
12
|
+
TelegramError,
|
|
13
|
+
TelegramPermissionError,
|
|
14
|
+
TransportError,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"TelegramClient",
|
|
19
|
+
"ClientConfig",
|
|
20
|
+
"TelegramError",
|
|
21
|
+
"AuthError",
|
|
22
|
+
"TelegramPermissionError",
|
|
23
|
+
"NotFoundError",
|
|
24
|
+
"InvalidRequest",
|
|
25
|
+
"FloodWait",
|
|
26
|
+
"TransportError",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _installed_version() -> str:
|
|
31
|
+
# pyproject.toml is the only place the version is written. A second copy
|
|
32
|
+
# here had to be bumped by hand and nothing checked that it was, so a
|
|
33
|
+
# release that bumped only pyproject.toml would have reported the version
|
|
34
|
+
# before it. Reinstall after a bump for a dev checkout to see it.
|
|
35
|
+
try:
|
|
36
|
+
return _metadata.version("tdelegram")
|
|
37
|
+
except _metadata.PackageNotFoundError:
|
|
38
|
+
# Imported from a source tree that was never installed.
|
|
39
|
+
return "0+unknown"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
__version__ = _installed_version()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Domain APIs over TelegramClient."""
|
|
2
|
+
|
|
3
|
+
from tdelegram.api import (
|
|
4
|
+
account,
|
|
5
|
+
admin,
|
|
6
|
+
bots,
|
|
7
|
+
chats,
|
|
8
|
+
contacts,
|
|
9
|
+
drafts,
|
|
10
|
+
export,
|
|
11
|
+
folders,
|
|
12
|
+
inbox,
|
|
13
|
+
media,
|
|
14
|
+
messages,
|
|
15
|
+
polls,
|
|
16
|
+
proxies,
|
|
17
|
+
reactions,
|
|
18
|
+
search,
|
|
19
|
+
secret,
|
|
20
|
+
stories,
|
|
21
|
+
topics,
|
|
22
|
+
updates,
|
|
23
|
+
users,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"account",
|
|
28
|
+
"admin",
|
|
29
|
+
"bots",
|
|
30
|
+
"chats",
|
|
31
|
+
"contacts",
|
|
32
|
+
"drafts",
|
|
33
|
+
"export",
|
|
34
|
+
"folders",
|
|
35
|
+
"inbox",
|
|
36
|
+
"media",
|
|
37
|
+
"messages",
|
|
38
|
+
"polls",
|
|
39
|
+
"proxies",
|
|
40
|
+
"reactions",
|
|
41
|
+
"search",
|
|
42
|
+
"secret",
|
|
43
|
+
"stories",
|
|
44
|
+
"topics",
|
|
45
|
+
"updates",
|
|
46
|
+
"users",
|
|
47
|
+
]
|
tdelegram/api/account.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Account: info, profile, privacy, sessions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from tdelegram.client import TelegramClient
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def info(client: TelegramClient) -> dict[str, Any]:
|
|
11
|
+
return client.call("getMe", {})
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def full_info(client: TelegramClient, user_id: int) -> dict[str, Any]:
|
|
15
|
+
return client.call("getUserFullInfo", {"user_id": user_id})
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def set_profile(
|
|
19
|
+
client: TelegramClient,
|
|
20
|
+
*,
|
|
21
|
+
first_name: str | None = None,
|
|
22
|
+
last_name: str | None = None,
|
|
23
|
+
bio: str | None = None,
|
|
24
|
+
allow_write: bool = False,
|
|
25
|
+
) -> dict[str, Any]:
|
|
26
|
+
results: dict[str, Any] = {}
|
|
27
|
+
if first_name is not None or last_name is not None:
|
|
28
|
+
results["name"] = client.call(
|
|
29
|
+
"setName",
|
|
30
|
+
{"first_name": first_name or "", "last_name": last_name or ""},
|
|
31
|
+
allow_write=allow_write,
|
|
32
|
+
)
|
|
33
|
+
if bio is not None:
|
|
34
|
+
results["bio"] = client.call("setBio", {"bio": bio}, allow_write=allow_write)
|
|
35
|
+
return results or {"@type": "ok"}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def sessions(client: TelegramClient) -> dict[str, Any]:
|
|
39
|
+
return client.call("getActiveSessions", {})
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def terminate_session(
|
|
43
|
+
client: TelegramClient,
|
|
44
|
+
session_id: int,
|
|
45
|
+
*,
|
|
46
|
+
allow_write: bool = False,
|
|
47
|
+
allow_destructive: bool = False,
|
|
48
|
+
) -> dict[str, Any]:
|
|
49
|
+
return client.call(
|
|
50
|
+
"terminateSession",
|
|
51
|
+
{"session_id": session_id},
|
|
52
|
+
allow_write=allow_write,
|
|
53
|
+
allow_destructive=allow_destructive,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def terminate_others(
|
|
58
|
+
client: TelegramClient, *, allow_write: bool = False, allow_destructive: bool = False
|
|
59
|
+
) -> dict[str, Any]:
|
|
60
|
+
return client.call(
|
|
61
|
+
"terminateAllOtherSessions",
|
|
62
|
+
{},
|
|
63
|
+
allow_write=allow_write,
|
|
64
|
+
allow_destructive=allow_destructive,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def privacy(client: TelegramClient, setting: str) -> dict[str, Any]:
|
|
69
|
+
return client.call("getUserPrivacySettingRules", {"setting": {"@type": setting}})
|
tdelegram/api/admin.py
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""Admin: promote/demote/ban/restrict/permissions/slowmode/invites."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from tdelegram.api.chats import resolve, resolve_id
|
|
9
|
+
from tdelegram.client import TelegramClient
|
|
10
|
+
|
|
11
|
+
# The least an administrator can hold, and what `promote` grants unless told
|
|
12
|
+
# otherwise: every other right implies it.
|
|
13
|
+
DEFAULT_RIGHTS = ("manage_chat",)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def administrator_rights(rights: Iterable[str]) -> dict[str, Any]:
|
|
17
|
+
"""chatAdministratorRights granting exactly `rights`, and nothing else.
|
|
18
|
+
|
|
19
|
+
Names are the schema's fields with or without the `can_` prefix, so
|
|
20
|
+
`delete_messages` and `can_delete_messages` both work; `is_anonymous` is
|
|
21
|
+
taken as it is.
|
|
22
|
+
"""
|
|
23
|
+
from tdelegram import schema
|
|
24
|
+
|
|
25
|
+
entry = schema.constructor("chatAdministratorRights")
|
|
26
|
+
known = list(entry["fields"]) if entry else []
|
|
27
|
+
granted: dict[str, Any] = {"@type": "chatAdministratorRights"}
|
|
28
|
+
for right in rights:
|
|
29
|
+
name = right.strip().lower().replace("-", "_")
|
|
30
|
+
field = name if name in known else f"can_{name}"
|
|
31
|
+
if field not in known:
|
|
32
|
+
short = ", ".join(k.removeprefix("can_") for k in known)
|
|
33
|
+
raise ValueError(f"Unknown administrator right {right!r}; choose from: {short}.")
|
|
34
|
+
granted[field] = True
|
|
35
|
+
return granted
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _set_status(
|
|
39
|
+
client: TelegramClient,
|
|
40
|
+
chat_id: int,
|
|
41
|
+
user_id: int,
|
|
42
|
+
status: dict[str, Any],
|
|
43
|
+
*,
|
|
44
|
+
allow_write: bool,
|
|
45
|
+
allow_destructive: bool,
|
|
46
|
+
) -> dict[str, Any]:
|
|
47
|
+
return client.call(
|
|
48
|
+
"setChatMemberStatus",
|
|
49
|
+
{
|
|
50
|
+
"chat_id": chat_id,
|
|
51
|
+
"member_id": {"@type": "messageSenderUser", "user_id": user_id},
|
|
52
|
+
"status": status,
|
|
53
|
+
},
|
|
54
|
+
allow_write=allow_write,
|
|
55
|
+
allow_destructive=allow_destructive,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def set_member_status(
|
|
60
|
+
client: TelegramClient,
|
|
61
|
+
chat_ref: str,
|
|
62
|
+
user_id: int,
|
|
63
|
+
status: dict[str, Any],
|
|
64
|
+
*,
|
|
65
|
+
allow_write: bool = False,
|
|
66
|
+
allow_destructive: bool = False,
|
|
67
|
+
) -> dict[str, Any]:
|
|
68
|
+
"""Set a member's status. Can ban, so it is gated like banChatMember."""
|
|
69
|
+
return _set_status(
|
|
70
|
+
client,
|
|
71
|
+
resolve_id(client, chat_ref),
|
|
72
|
+
user_id,
|
|
73
|
+
status,
|
|
74
|
+
allow_write=allow_write,
|
|
75
|
+
allow_destructive=allow_destructive,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def promote(
|
|
80
|
+
client: TelegramClient,
|
|
81
|
+
chat_ref: str,
|
|
82
|
+
user_id: int,
|
|
83
|
+
*,
|
|
84
|
+
rights: Iterable[str] = DEFAULT_RIGHTS,
|
|
85
|
+
title: str = "",
|
|
86
|
+
allow_write: bool = False,
|
|
87
|
+
allow_destructive: bool = False,
|
|
88
|
+
) -> dict[str, Any]:
|
|
89
|
+
"""Make a member an administrator with exactly `rights`.
|
|
90
|
+
|
|
91
|
+
The status used to carry `custom_title` and no `rights`, neither of which
|
|
92
|
+
TDLib's administrator status takes any more, so the promotion granted
|
|
93
|
+
nothing. A title is now the member's tag, set by a second call -- which
|
|
94
|
+
channels do not have, so a title there is refused before anything changes
|
|
95
|
+
rather than failing after the promotion went through.
|
|
96
|
+
"""
|
|
97
|
+
chat = resolve(client, chat_ref)
|
|
98
|
+
if title and (chat.get("type") or {}).get("is_channel"):
|
|
99
|
+
raise ValueError("Channels have no member titles; promote without --title.")
|
|
100
|
+
chat_id = int(chat["id"])
|
|
101
|
+
result = _set_status(
|
|
102
|
+
client,
|
|
103
|
+
chat_id,
|
|
104
|
+
user_id,
|
|
105
|
+
{"@type": "chatMemberStatusAdministrator", "rights": administrator_rights(rights)},
|
|
106
|
+
allow_write=allow_write,
|
|
107
|
+
allow_destructive=allow_destructive,
|
|
108
|
+
)
|
|
109
|
+
if title:
|
|
110
|
+
client.call(
|
|
111
|
+
"setChatMemberTag",
|
|
112
|
+
{"chat_id": chat_id, "user_id": user_id, "tag": title},
|
|
113
|
+
allow_write=allow_write,
|
|
114
|
+
)
|
|
115
|
+
return result
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def demote(
|
|
119
|
+
client: TelegramClient,
|
|
120
|
+
chat_ref: str,
|
|
121
|
+
user_id: int,
|
|
122
|
+
*,
|
|
123
|
+
allow_write: bool = False,
|
|
124
|
+
allow_destructive: bool = False,
|
|
125
|
+
) -> dict[str, Any]:
|
|
126
|
+
return set_member_status(
|
|
127
|
+
client,
|
|
128
|
+
chat_ref,
|
|
129
|
+
user_id,
|
|
130
|
+
{"@type": "chatMemberStatusMember"},
|
|
131
|
+
allow_write=allow_write,
|
|
132
|
+
allow_destructive=allow_destructive,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def ban(
|
|
137
|
+
client: TelegramClient,
|
|
138
|
+
chat_ref: str,
|
|
139
|
+
user_id: int,
|
|
140
|
+
*,
|
|
141
|
+
until: int = 0,
|
|
142
|
+
allow_write: bool = False,
|
|
143
|
+
allow_destructive: bool = False,
|
|
144
|
+
) -> dict[str, Any]:
|
|
145
|
+
return client.call(
|
|
146
|
+
"banChatMember",
|
|
147
|
+
{
|
|
148
|
+
"chat_id": resolve_id(client, chat_ref),
|
|
149
|
+
"member_id": {"@type": "messageSenderUser", "user_id": user_id},
|
|
150
|
+
"banned_until_date": until,
|
|
151
|
+
},
|
|
152
|
+
allow_write=allow_write,
|
|
153
|
+
allow_destructive=allow_destructive,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def restrict(
|
|
158
|
+
client: TelegramClient,
|
|
159
|
+
chat_ref: str,
|
|
160
|
+
user_id: int,
|
|
161
|
+
*,
|
|
162
|
+
permissions: dict[str, Any] | None = None,
|
|
163
|
+
allow_write: bool = False,
|
|
164
|
+
allow_destructive: bool = False,
|
|
165
|
+
) -> dict[str, Any]:
|
|
166
|
+
return set_member_status(
|
|
167
|
+
client,
|
|
168
|
+
chat_ref,
|
|
169
|
+
user_id,
|
|
170
|
+
{
|
|
171
|
+
"@type": "chatMemberStatusRestricted",
|
|
172
|
+
"permissions": permissions
|
|
173
|
+
or {"@type": "chatPermissions", "can_send_basic_messages": False},
|
|
174
|
+
"is_member": True,
|
|
175
|
+
},
|
|
176
|
+
allow_write=allow_write,
|
|
177
|
+
allow_destructive=allow_destructive,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def unrestrict(
|
|
182
|
+
client: TelegramClient,
|
|
183
|
+
chat_ref: str,
|
|
184
|
+
user_id: int,
|
|
185
|
+
*,
|
|
186
|
+
allow_write: bool = False,
|
|
187
|
+
allow_destructive: bool = False,
|
|
188
|
+
) -> dict[str, Any]:
|
|
189
|
+
return demote(
|
|
190
|
+
client, chat_ref, user_id, allow_write=allow_write, allow_destructive=allow_destructive
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def slowmode(
|
|
195
|
+
client: TelegramClient, chat_ref: str, delay_seconds: int, *, allow_write: bool = False
|
|
196
|
+
) -> dict[str, Any]:
|
|
197
|
+
return client.call(
|
|
198
|
+
"setChatSlowModeDelay",
|
|
199
|
+
{"chat_id": resolve_id(client, chat_ref), "slow_mode_delay": delay_seconds},
|
|
200
|
+
allow_write=allow_write,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def create_invite(
|
|
205
|
+
client: TelegramClient, chat_ref: str, *, allow_write: bool = False
|
|
206
|
+
) -> dict[str, Any]:
|
|
207
|
+
return client.call(
|
|
208
|
+
"createChatInviteLink", {"chat_id": resolve_id(client, chat_ref)}, allow_write=allow_write
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def revoke_invite(
|
|
213
|
+
client: TelegramClient,
|
|
214
|
+
chat_ref: str,
|
|
215
|
+
invite_link: str,
|
|
216
|
+
*,
|
|
217
|
+
allow_write: bool = False,
|
|
218
|
+
allow_destructive: bool = False,
|
|
219
|
+
) -> dict[str, Any]:
|
|
220
|
+
return client.call(
|
|
221
|
+
"revokeChatInviteLink",
|
|
222
|
+
{"chat_id": resolve_id(client, chat_ref), "invite_link": invite_link},
|
|
223
|
+
allow_write=allow_write,
|
|
224
|
+
allow_destructive=allow_destructive,
|
|
225
|
+
)
|
tdelegram/api/bots.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Bots: inline queries, inline results, callback answers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from tdelegram.api.chats import resolve_id
|
|
8
|
+
from tdelegram.client import TelegramClient
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def start_bot(
|
|
12
|
+
client: TelegramClient,
|
|
13
|
+
bot_ref: str,
|
|
14
|
+
chat_ref: str,
|
|
15
|
+
*,
|
|
16
|
+
parameter: str = "",
|
|
17
|
+
allow_write: bool = False,
|
|
18
|
+
) -> dict[str, Any]:
|
|
19
|
+
bot_id = resolve_id(client, bot_ref)
|
|
20
|
+
chat_id = resolve_id(client, chat_ref)
|
|
21
|
+
return client.call(
|
|
22
|
+
"sendBotStartMessage",
|
|
23
|
+
{"bot_user_id": bot_id, "chat_id": chat_id, "parameter": parameter},
|
|
24
|
+
allow_write=allow_write,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def inline_results(
|
|
29
|
+
client: TelegramClient,
|
|
30
|
+
bot_user_id: int,
|
|
31
|
+
query: str,
|
|
32
|
+
*,
|
|
33
|
+
offset: str = "",
|
|
34
|
+
allow_write: bool = False,
|
|
35
|
+
) -> dict[str, Any]:
|
|
36
|
+
"""Send an inline query to a bot. The bot is notified, so this is a write."""
|
|
37
|
+
return client.call(
|
|
38
|
+
"getInlineQueryResults",
|
|
39
|
+
{"bot_user_id": bot_user_id, "chat_id": 0, "query": query, "offset": offset},
|
|
40
|
+
allow_write=allow_write,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def send_inline_result(
|
|
45
|
+
client: TelegramClient,
|
|
46
|
+
chat_ref: str,
|
|
47
|
+
query_id: int | str,
|
|
48
|
+
result_id: str,
|
|
49
|
+
*,
|
|
50
|
+
allow_write: bool = False,
|
|
51
|
+
) -> dict[str, Any]:
|
|
52
|
+
"""Send one result of an inline query. `query_id` comes from inline_results()."""
|
|
53
|
+
return client.call(
|
|
54
|
+
"sendInlineQueryResultMessage",
|
|
55
|
+
{"chat_id": resolve_id(client, chat_ref), "query_id": query_id, "result_id": result_id},
|
|
56
|
+
allow_write=allow_write,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def press_button(
|
|
61
|
+
client: TelegramClient,
|
|
62
|
+
chat_ref: str,
|
|
63
|
+
message_id: int,
|
|
64
|
+
label: str,
|
|
65
|
+
*,
|
|
66
|
+
allow_write: bool = False,
|
|
67
|
+
) -> dict[str, Any]:
|
|
68
|
+
"""Press the inline button labelled `label` under a bot's message.
|
|
69
|
+
|
|
70
|
+
The bot sees the press and acts on it, which is why getCallbackQueryAnswer
|
|
71
|
+
is a write. A URL button has nothing to press, so its link comes back
|
|
72
|
+
instead; a button that needs a password, a game or a payment is refused.
|
|
73
|
+
"""
|
|
74
|
+
chat_id = resolve_id(client, chat_ref)
|
|
75
|
+
message = client.call("getMessage", {"chat_id": chat_id, "message_id": message_id})
|
|
76
|
+
markup = message.get("reply_markup") or {}
|
|
77
|
+
rows = markup.get("rows") if markup.get("@type") == "replyMarkupInlineKeyboard" else None
|
|
78
|
+
buttons = [button for row in rows or [] for button in row or []]
|
|
79
|
+
chosen = [b for b in buttons if b.get("text") == label] or [
|
|
80
|
+
b for b in buttons if str(b.get("text", "")).casefold() == label.casefold()
|
|
81
|
+
]
|
|
82
|
+
if not chosen:
|
|
83
|
+
offered = ", ".join(repr(b.get("text")) for b in buttons) or "none"
|
|
84
|
+
raise ValueError(f"Message {message_id} has no button {label!r}; its buttons: {offered}.")
|
|
85
|
+
kind = chosen[0].get("type") or {}
|
|
86
|
+
found = {"chat_id": chat_id, "message_id": message_id, "button": chosen[0].get("text")}
|
|
87
|
+
if kind.get("@type") == "inlineKeyboardButtonTypeUrl":
|
|
88
|
+
return {**found, "pressed": False, "url": kind.get("url")}
|
|
89
|
+
if kind.get("@type") != "inlineKeyboardButtonTypeCallback":
|
|
90
|
+
raise ValueError(f"{label!r} is a {kind.get('@type')} button, which only an app can use.")
|
|
91
|
+
answer = client.call(
|
|
92
|
+
"getCallbackQueryAnswer",
|
|
93
|
+
{
|
|
94
|
+
"chat_id": chat_id,
|
|
95
|
+
"message_id": message_id,
|
|
96
|
+
"payload": {"@type": "callbackQueryPayloadData", "data": kind.get("data", "")},
|
|
97
|
+
},
|
|
98
|
+
allow_write=allow_write,
|
|
99
|
+
)
|
|
100
|
+
return {
|
|
101
|
+
**found,
|
|
102
|
+
"pressed": True,
|
|
103
|
+
"answer": answer.get("text") or None,
|
|
104
|
+
"show_alert": bool(answer.get("show_alert")),
|
|
105
|
+
"url": answer.get("url") or None,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def answer_callback(
|
|
110
|
+
client: TelegramClient, query_id: int, *, text: str = "", allow_write: bool = False
|
|
111
|
+
) -> dict[str, Any]:
|
|
112
|
+
"""For bot accounts only: a user account cannot answer callback queries."""
|
|
113
|
+
return client.call(
|
|
114
|
+
"answerCallbackQuery",
|
|
115
|
+
{"callback_query_id": query_id, "text": text},
|
|
116
|
+
allow_write=allow_write,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def answer_inline(
|
|
121
|
+
client: TelegramClient,
|
|
122
|
+
inline_query_id: int,
|
|
123
|
+
results: list[dict[str, Any]],
|
|
124
|
+
*,
|
|
125
|
+
allow_write: bool = False,
|
|
126
|
+
) -> dict[str, Any]:
|
|
127
|
+
return client.call(
|
|
128
|
+
"answerInlineQuery",
|
|
129
|
+
{"inline_query_id": inline_query_id, "results": results},
|
|
130
|
+
allow_write=allow_write,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# -- stories ----------------------------------------------------------
|