baleropy 0.2.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.
- baleropy/__init__.py +21 -0
- baleropy/__meta__.py +1 -0
- baleropy/client/__init__.py +3 -0
- baleropy/client/bot.py +568 -0
- baleropy/client/server.py +32 -0
- baleropy/client/session/__init__.py +2 -0
- baleropy/client/session/aiohttp.py +84 -0
- baleropy/client/session/base.py +124 -0
- baleropy/dispatcher/__init__.py +5 -0
- baleropy/dispatcher/dispatcher.py +230 -0
- baleropy/dispatcher/event/__init__.py +0 -0
- baleropy/dispatcher/event/bases.py +32 -0
- baleropy/dispatcher/event/handler.py +86 -0
- baleropy/dispatcher/event/observer.py +86 -0
- baleropy/dispatcher/flags.py +19 -0
- baleropy/dispatcher/middlewares/__init__.py +2 -0
- baleropy/dispatcher/middlewares/base.py +33 -0
- baleropy/dispatcher/middlewares/builtin.py +106 -0
- baleropy/dispatcher/middlewares/user_context.py +29 -0
- baleropy/dispatcher/router.py +129 -0
- baleropy/exceptions.py +88 -0
- baleropy/filters/__init__.py +7 -0
- baleropy/filters/base.py +79 -0
- baleropy/filters/callback_data.py +92 -0
- baleropy/filters/chat.py +64 -0
- baleropy/filters/command.py +124 -0
- baleropy/filters/extra.py +49 -0
- baleropy/filters/magic.py +155 -0
- baleropy/filters/state.py +37 -0
- baleropy/fsm/__init__.py +4 -0
- baleropy/fsm/context.py +39 -0
- baleropy/fsm/middleware.py +78 -0
- baleropy/fsm/state.py +108 -0
- baleropy/fsm/storage/__init__.py +3 -0
- baleropy/fsm/storage/base.py +46 -0
- baleropy/fsm/storage/memory.py +37 -0
- baleropy/fsm/storage/redis.py +68 -0
- baleropy/methods/__init__.py +14 -0
- baleropy/methods/base.py +64 -0
- baleropy/methods/chats.py +118 -0
- baleropy/methods/limits.py +32 -0
- baleropy/methods/media_group.py +59 -0
- baleropy/methods/messages.py +168 -0
- baleropy/methods/misc.py +137 -0
- baleropy/py.typed +0 -0
- baleropy/types/__init__.py +50 -0
- baleropy/types/base.py +35 -0
- baleropy/types/chat.py +127 -0
- baleropy/types/input_file.py +53 -0
- baleropy/types/markup.py +73 -0
- baleropy/types/media.py +104 -0
- baleropy/types/message.py +321 -0
- baleropy/types/payments.py +53 -0
- baleropy/utils/__init__.py +5 -0
- baleropy/utils/chat_action.py +75 -0
- baleropy/utils/deep_linking.py +43 -0
- baleropy/utils/keyboard.py +157 -0
- baleropy/utils/media_group.py +71 -0
- baleropy/utils/text.py +47 -0
- baleropy/webhook/__init__.py +1 -0
- baleropy/webhook/aiohttp_server.py +93 -0
- baleropy-0.2.0.dist-info/METADATA +315 -0
- baleropy-0.2.0.dist-info/RECORD +65 -0
- baleropy-0.2.0.dist-info/WHEEL +4 -0
- baleropy-0.2.0.dist-info/licenses/LICENSE +21 -0
baleropy/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from . import exceptions, methods, types
|
|
2
|
+
from .__meta__ import __version__
|
|
3
|
+
from .client import BUSINESS, PRODUCTION, AiohttpSession, ApiServer, BaseSession, Bot
|
|
4
|
+
from .dispatcher import Dispatcher, Router
|
|
5
|
+
from .filters import F
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"BUSINESS",
|
|
9
|
+
"PRODUCTION",
|
|
10
|
+
"AiohttpSession",
|
|
11
|
+
"ApiServer",
|
|
12
|
+
"BaseSession",
|
|
13
|
+
"Bot",
|
|
14
|
+
"F",
|
|
15
|
+
"Dispatcher",
|
|
16
|
+
"Router",
|
|
17
|
+
"__version__",
|
|
18
|
+
"exceptions",
|
|
19
|
+
"methods",
|
|
20
|
+
"types",
|
|
21
|
+
]
|
baleropy/__meta__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
baleropy/client/bot.py
ADDED
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import io
|
|
5
|
+
import re
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, BinaryIO, TypeVar
|
|
8
|
+
|
|
9
|
+
from .. import methods as m
|
|
10
|
+
from ..methods.base import BaleMethod
|
|
11
|
+
from ..methods.media_group import AnyInputMedia
|
|
12
|
+
from ..methods.misc import InputSticker
|
|
13
|
+
from ..types import (
|
|
14
|
+
ChatAction,
|
|
15
|
+
ChatFullInfo,
|
|
16
|
+
ChatMember,
|
|
17
|
+
File,
|
|
18
|
+
InlineKeyboardMarkup,
|
|
19
|
+
LabeledPrice,
|
|
20
|
+
Message,
|
|
21
|
+
MessageId,
|
|
22
|
+
ReplyMarkup,
|
|
23
|
+
StickerSet,
|
|
24
|
+
Transaction,
|
|
25
|
+
Update,
|
|
26
|
+
User,
|
|
27
|
+
WebhookInfo,
|
|
28
|
+
)
|
|
29
|
+
from ..types.input_file import InputFile
|
|
30
|
+
from .server import BUSINESS, PRODUCTION, ApiServer
|
|
31
|
+
from .session.aiohttp import AiohttpSession
|
|
32
|
+
from .session.base import BaseSession
|
|
33
|
+
|
|
34
|
+
T = TypeVar("T")
|
|
35
|
+
ChatId = int | str
|
|
36
|
+
FileInput = InputFile | str
|
|
37
|
+
|
|
38
|
+
_TOKEN_RE = re.compile(r"^\d+:[\w-]+$")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def validate_token(token: str) -> str:
|
|
42
|
+
if not isinstance(token, str) or not _TOKEN_RE.match(token.strip()):
|
|
43
|
+
raise ValueError("Token has an invalid format, expected '<digits>:<secret>'")
|
|
44
|
+
return token.strip()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _write_file(path: Path, data: bytes) -> None:
|
|
48
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
49
|
+
path.write_bytes(data)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class Bot:
|
|
53
|
+
"""Entry point to the Bale Bot API.
|
|
54
|
+
|
|
55
|
+
Use shortcuts (``await bot.send_message(...)``) or method objects
|
|
56
|
+
(``await bot(SendMessage(...))``).
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
token: str,
|
|
62
|
+
session: BaseSession | None = None,
|
|
63
|
+
server: ApiServer = PRODUCTION,
|
|
64
|
+
) -> None:
|
|
65
|
+
self.token = validate_token(token)
|
|
66
|
+
self.session = session or AiohttpSession()
|
|
67
|
+
self.server = server
|
|
68
|
+
self._me: User | None = None
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def business(cls, token: str, session: BaseSession | None = None) -> Bot:
|
|
72
|
+
"""A bot for the Business API (bulk messaging with a separate, paid rate quota).
|
|
73
|
+
|
|
74
|
+
Only getMe, sendMessage, forwardMessage, sendPhoto, sendVideo, sendAudio and sendDocument
|
|
75
|
+
are available; ``chat_id`` must be a private chat and files can only be sent by ``file_id``.
|
|
76
|
+
"""
|
|
77
|
+
return cls(token, session, server=BUSINESS)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def id(self) -> int:
|
|
81
|
+
return int(self.token.split(":", 1)[0])
|
|
82
|
+
|
|
83
|
+
async def __call__(self, method: BaleMethod[T]) -> T:
|
|
84
|
+
return await self.session(self, method)
|
|
85
|
+
|
|
86
|
+
async def __aenter__(self) -> Bot:
|
|
87
|
+
return self
|
|
88
|
+
|
|
89
|
+
async def __aexit__(self, *exc: object) -> None:
|
|
90
|
+
await self.close()
|
|
91
|
+
|
|
92
|
+
async def close(self) -> None:
|
|
93
|
+
await self.session.close()
|
|
94
|
+
|
|
95
|
+
async def me(self) -> User:
|
|
96
|
+
"""Cached result of ``getMe``."""
|
|
97
|
+
if self._me is None:
|
|
98
|
+
self._me = await self.get_me()
|
|
99
|
+
return self._me
|
|
100
|
+
|
|
101
|
+
# --- updates ---------------------------------------------------------------------------
|
|
102
|
+
async def get_updates(
|
|
103
|
+
self, offset: int | None = None, limit: int | None = None, timeout: int | None = None
|
|
104
|
+
) -> list[Update]:
|
|
105
|
+
return await self(m.GetUpdates(offset=offset, limit=limit, timeout=timeout))
|
|
106
|
+
|
|
107
|
+
async def set_webhook(self, url: str) -> bool:
|
|
108
|
+
return await self(m.SetWebhook(url=url))
|
|
109
|
+
|
|
110
|
+
async def delete_webhook(self) -> bool:
|
|
111
|
+
return await self(m.DeleteWebhook())
|
|
112
|
+
|
|
113
|
+
async def get_webhook_info(self) -> WebhookInfo:
|
|
114
|
+
return await self(m.GetWebhookInfo())
|
|
115
|
+
|
|
116
|
+
# --- bot -------------------------------------------------------------------------------
|
|
117
|
+
async def get_me(self) -> User:
|
|
118
|
+
return await self(m.GetMe())
|
|
119
|
+
|
|
120
|
+
async def ask_review(self, user_id: int, delay_seconds: int) -> bool:
|
|
121
|
+
return await self(m.AskReview(user_id=user_id, delay_seconds=delay_seconds))
|
|
122
|
+
|
|
123
|
+
# --- sending ---------------------------------------------------------------------------
|
|
124
|
+
async def send_message(
|
|
125
|
+
self,
|
|
126
|
+
chat_id: ChatId,
|
|
127
|
+
text: str,
|
|
128
|
+
reply_to_message_id: int | None = None,
|
|
129
|
+
reply_markup: ReplyMarkup | None = None,
|
|
130
|
+
) -> Message:
|
|
131
|
+
return await self(
|
|
132
|
+
m.SendMessage(
|
|
133
|
+
chat_id=chat_id,
|
|
134
|
+
text=text,
|
|
135
|
+
reply_to_message_id=reply_to_message_id,
|
|
136
|
+
reply_markup=reply_markup,
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
async def forward_message(
|
|
141
|
+
self, chat_id: ChatId, from_chat_id: ChatId, message_id: int
|
|
142
|
+
) -> Message:
|
|
143
|
+
return await self(
|
|
144
|
+
m.ForwardMessage(chat_id=chat_id, from_chat_id=from_chat_id, message_id=message_id)
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
async def copy_message(
|
|
148
|
+
self, chat_id: ChatId, from_chat_id: ChatId, message_id: int
|
|
149
|
+
) -> MessageId:
|
|
150
|
+
return await self(
|
|
151
|
+
m.CopyMessage(chat_id=chat_id, from_chat_id=from_chat_id, message_id=message_id)
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
async def send_photo(
|
|
155
|
+
self,
|
|
156
|
+
chat_id: ChatId,
|
|
157
|
+
photo: FileInput,
|
|
158
|
+
caption: str | None = None,
|
|
159
|
+
reply_to_message_id: int | None = None,
|
|
160
|
+
reply_markup: ReplyMarkup | None = None,
|
|
161
|
+
) -> Message:
|
|
162
|
+
return await self(
|
|
163
|
+
m.SendPhoto(
|
|
164
|
+
chat_id=chat_id,
|
|
165
|
+
photo=photo,
|
|
166
|
+
caption=caption,
|
|
167
|
+
reply_to_message_id=reply_to_message_id,
|
|
168
|
+
reply_markup=reply_markup,
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
async def send_audio(
|
|
173
|
+
self,
|
|
174
|
+
chat_id: ChatId,
|
|
175
|
+
audio: FileInput,
|
|
176
|
+
caption: str | None = None,
|
|
177
|
+
reply_to_message_id: int | None = None,
|
|
178
|
+
reply_markup: ReplyMarkup | None = None,
|
|
179
|
+
) -> Message:
|
|
180
|
+
return await self(
|
|
181
|
+
m.SendAudio(
|
|
182
|
+
chat_id=chat_id,
|
|
183
|
+
audio=audio,
|
|
184
|
+
caption=caption,
|
|
185
|
+
reply_to_message_id=reply_to_message_id,
|
|
186
|
+
reply_markup=reply_markup,
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
async def send_document(
|
|
191
|
+
self,
|
|
192
|
+
chat_id: ChatId,
|
|
193
|
+
document: FileInput,
|
|
194
|
+
caption: str | None = None,
|
|
195
|
+
reply_to_message_id: int | None = None,
|
|
196
|
+
reply_markup: ReplyMarkup | None = None,
|
|
197
|
+
) -> Message:
|
|
198
|
+
return await self(
|
|
199
|
+
m.SendDocument(
|
|
200
|
+
chat_id=chat_id,
|
|
201
|
+
document=document,
|
|
202
|
+
caption=caption,
|
|
203
|
+
reply_to_message_id=reply_to_message_id,
|
|
204
|
+
reply_markup=reply_markup,
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
async def send_video(
|
|
209
|
+
self,
|
|
210
|
+
chat_id: ChatId,
|
|
211
|
+
video: FileInput,
|
|
212
|
+
caption: str | None = None,
|
|
213
|
+
reply_to_message_id: int | None = None,
|
|
214
|
+
reply_markup: ReplyMarkup | None = None,
|
|
215
|
+
) -> Message:
|
|
216
|
+
return await self(
|
|
217
|
+
m.SendVideo(
|
|
218
|
+
chat_id=chat_id,
|
|
219
|
+
video=video,
|
|
220
|
+
caption=caption,
|
|
221
|
+
reply_to_message_id=reply_to_message_id,
|
|
222
|
+
reply_markup=reply_markup,
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
async def send_animation(
|
|
227
|
+
self,
|
|
228
|
+
chat_id: ChatId,
|
|
229
|
+
animation: FileInput,
|
|
230
|
+
caption: str | None = None,
|
|
231
|
+
reply_to_message_id: int | None = None,
|
|
232
|
+
reply_markup: ReplyMarkup | None = None,
|
|
233
|
+
) -> Message:
|
|
234
|
+
return await self(
|
|
235
|
+
m.SendAnimation(
|
|
236
|
+
chat_id=chat_id,
|
|
237
|
+
animation=animation,
|
|
238
|
+
caption=caption,
|
|
239
|
+
reply_to_message_id=reply_to_message_id,
|
|
240
|
+
reply_markup=reply_markup,
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
async def send_voice(
|
|
245
|
+
self,
|
|
246
|
+
chat_id: ChatId,
|
|
247
|
+
voice: FileInput,
|
|
248
|
+
caption: str | None = None,
|
|
249
|
+
reply_to_message_id: int | None = None,
|
|
250
|
+
reply_markup: ReplyMarkup | None = None,
|
|
251
|
+
) -> Message:
|
|
252
|
+
return await self(
|
|
253
|
+
m.SendVoice(
|
|
254
|
+
chat_id=chat_id,
|
|
255
|
+
voice=voice,
|
|
256
|
+
caption=caption,
|
|
257
|
+
reply_to_message_id=reply_to_message_id,
|
|
258
|
+
reply_markup=reply_markup,
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
async def send_sticker(
|
|
263
|
+
self,
|
|
264
|
+
chat_id: ChatId,
|
|
265
|
+
sticker: FileInput,
|
|
266
|
+
reply_to_message_id: int | None = None,
|
|
267
|
+
reply_markup: ReplyMarkup | None = None,
|
|
268
|
+
) -> Message:
|
|
269
|
+
return await self(
|
|
270
|
+
m.SendSticker(
|
|
271
|
+
chat_id=chat_id,
|
|
272
|
+
sticker=sticker,
|
|
273
|
+
reply_to_message_id=reply_to_message_id,
|
|
274
|
+
reply_markup=reply_markup,
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
async def send_media_group(
|
|
279
|
+
self,
|
|
280
|
+
chat_id: ChatId,
|
|
281
|
+
media: list[AnyInputMedia],
|
|
282
|
+
reply_to_message_id: int | None = None,
|
|
283
|
+
) -> list[Message]:
|
|
284
|
+
return await self(
|
|
285
|
+
m.SendMediaGroup(chat_id=chat_id, media=media, reply_to_message_id=reply_to_message_id)
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
async def send_location(
|
|
289
|
+
self,
|
|
290
|
+
chat_id: ChatId,
|
|
291
|
+
latitude: float,
|
|
292
|
+
longitude: float,
|
|
293
|
+
horizontal_accuracy: float | None = None,
|
|
294
|
+
reply_to_message_id: int | None = None,
|
|
295
|
+
reply_markup: ReplyMarkup | None = None,
|
|
296
|
+
) -> Message:
|
|
297
|
+
return await self(
|
|
298
|
+
m.SendLocation(
|
|
299
|
+
chat_id=chat_id,
|
|
300
|
+
latitude=latitude,
|
|
301
|
+
longitude=longitude,
|
|
302
|
+
horizontal_accuracy=horizontal_accuracy,
|
|
303
|
+
reply_to_message_id=reply_to_message_id,
|
|
304
|
+
reply_markup=reply_markup,
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
async def send_contact(
|
|
309
|
+
self,
|
|
310
|
+
chat_id: ChatId,
|
|
311
|
+
phone_number: str | int,
|
|
312
|
+
first_name: str,
|
|
313
|
+
last_name: str | None = None,
|
|
314
|
+
reply_to_message_id: int | None = None,
|
|
315
|
+
reply_markup: ReplyMarkup | None = None,
|
|
316
|
+
) -> Message:
|
|
317
|
+
return await self(
|
|
318
|
+
m.SendContact(
|
|
319
|
+
chat_id=chat_id,
|
|
320
|
+
phone_number=phone_number,
|
|
321
|
+
first_name=first_name,
|
|
322
|
+
last_name=last_name,
|
|
323
|
+
reply_to_message_id=reply_to_message_id,
|
|
324
|
+
reply_markup=reply_markup,
|
|
325
|
+
)
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
async def send_chat_action(self, chat_id: ChatId, action: ChatAction | str) -> bool:
|
|
329
|
+
return await self(m.SendChatAction(chat_id=chat_id, action=action))
|
|
330
|
+
|
|
331
|
+
# --- editing ---------------------------------------------------------------------------
|
|
332
|
+
async def edit_message_text(
|
|
333
|
+
self,
|
|
334
|
+
chat_id: ChatId,
|
|
335
|
+
message_id: int,
|
|
336
|
+
text: str,
|
|
337
|
+
reply_markup: InlineKeyboardMarkup | None = None,
|
|
338
|
+
) -> Message | bool:
|
|
339
|
+
return await self(
|
|
340
|
+
m.EditMessageText(
|
|
341
|
+
chat_id=chat_id, message_id=message_id, text=text, reply_markup=reply_markup
|
|
342
|
+
)
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
async def edit_message_caption(
|
|
346
|
+
self,
|
|
347
|
+
chat_id: ChatId,
|
|
348
|
+
message_id: int,
|
|
349
|
+
caption: str | None = None,
|
|
350
|
+
reply_markup: InlineKeyboardMarkup | None = None,
|
|
351
|
+
) -> Message | bool:
|
|
352
|
+
return await self(
|
|
353
|
+
m.EditMessageCaption(
|
|
354
|
+
chat_id=chat_id, message_id=message_id, caption=caption, reply_markup=reply_markup
|
|
355
|
+
)
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
async def edit_message_reply_markup(
|
|
359
|
+
self,
|
|
360
|
+
chat_id: ChatId,
|
|
361
|
+
message_id: int,
|
|
362
|
+
reply_markup: InlineKeyboardMarkup | None = None,
|
|
363
|
+
) -> Message | bool:
|
|
364
|
+
return await self(
|
|
365
|
+
m.EditMessageReplyMarkup(
|
|
366
|
+
chat_id=chat_id, message_id=message_id, reply_markup=reply_markup
|
|
367
|
+
)
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
async def delete_message(self, chat_id: ChatId, message_id: int) -> bool:
|
|
371
|
+
return await self(m.DeleteMessage(chat_id=chat_id, message_id=message_id))
|
|
372
|
+
|
|
373
|
+
async def answer_callback_query(
|
|
374
|
+
self, callback_query_id: str, text: str | None = None, show_alert: bool | None = None
|
|
375
|
+
) -> bool:
|
|
376
|
+
return await self(
|
|
377
|
+
m.AnswerCallbackQuery(
|
|
378
|
+
callback_query_id=callback_query_id, text=text, show_alert=show_alert
|
|
379
|
+
)
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
# --- chats -----------------------------------------------------------------------------
|
|
383
|
+
async def get_chat(self, chat_id: ChatId) -> ChatFullInfo:
|
|
384
|
+
return await self(m.GetChat(chat_id=chat_id))
|
|
385
|
+
|
|
386
|
+
async def get_chat_administrators(self, chat_id: ChatId) -> list[ChatMember]:
|
|
387
|
+
return await self(m.GetChatAdministrators(chat_id=chat_id))
|
|
388
|
+
|
|
389
|
+
async def get_chat_members_count(self, chat_id: ChatId) -> int:
|
|
390
|
+
return await self(m.GetChatMembersCount(chat_id=chat_id))
|
|
391
|
+
|
|
392
|
+
async def get_chat_member(self, chat_id: ChatId, user_id: int) -> ChatMember:
|
|
393
|
+
return await self(m.GetChatMember(chat_id=chat_id, user_id=user_id))
|
|
394
|
+
|
|
395
|
+
async def ban_chat_member(self, chat_id: ChatId, user_id: int) -> bool:
|
|
396
|
+
return await self(m.BanChatMember(chat_id=chat_id, user_id=user_id))
|
|
397
|
+
|
|
398
|
+
async def unban_chat_member(
|
|
399
|
+
self, chat_id: ChatId, user_id: int, only_if_banned: bool | None = None
|
|
400
|
+
) -> bool:
|
|
401
|
+
return await self(
|
|
402
|
+
m.UnbanChatMember(chat_id=chat_id, user_id=user_id, only_if_banned=only_if_banned)
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
async def promote_chat_member(
|
|
406
|
+
self,
|
|
407
|
+
chat_id: ChatId,
|
|
408
|
+
user_id: int,
|
|
409
|
+
*,
|
|
410
|
+
can_change_info: bool | None = None,
|
|
411
|
+
can_post_messages: bool | None = None,
|
|
412
|
+
can_edit_messages: bool | None = None,
|
|
413
|
+
can_delete_messages: bool | None = None,
|
|
414
|
+
can_manage_video_chats: bool | None = None,
|
|
415
|
+
can_invite_users: bool | None = None,
|
|
416
|
+
can_restrict_members: bool | None = None,
|
|
417
|
+
) -> bool:
|
|
418
|
+
return await self(
|
|
419
|
+
m.PromoteChatMember(
|
|
420
|
+
chat_id=chat_id,
|
|
421
|
+
user_id=user_id,
|
|
422
|
+
can_change_info=can_change_info,
|
|
423
|
+
can_post_messages=can_post_messages,
|
|
424
|
+
can_edit_messages=can_edit_messages,
|
|
425
|
+
can_delete_messages=can_delete_messages,
|
|
426
|
+
can_manage_video_chats=can_manage_video_chats,
|
|
427
|
+
can_invite_users=can_invite_users,
|
|
428
|
+
can_restrict_members=can_restrict_members,
|
|
429
|
+
)
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
async def leave_chat(self, chat_id: ChatId) -> bool:
|
|
433
|
+
return await self(m.LeaveChat(chat_id=chat_id))
|
|
434
|
+
|
|
435
|
+
async def set_chat_photo(self, chat_id: ChatId, photo: InputFile) -> bool:
|
|
436
|
+
return await self(m.SetChatPhoto(chat_id=chat_id, photo=photo))
|
|
437
|
+
|
|
438
|
+
async def delete_chat_photo(self, chat_id: ChatId) -> bool:
|
|
439
|
+
return await self(m.DeleteChatPhoto(chat_id=chat_id))
|
|
440
|
+
|
|
441
|
+
async def set_chat_title(self, chat_id: ChatId, title: str) -> bool:
|
|
442
|
+
return await self(m.SetChatTitle(chat_id=chat_id, title=title))
|
|
443
|
+
|
|
444
|
+
async def set_chat_description(self, chat_id: ChatId, description: str) -> bool:
|
|
445
|
+
return await self(m.SetChatDescription(chat_id=chat_id, description=description))
|
|
446
|
+
|
|
447
|
+
async def pin_chat_message(self, chat_id: ChatId, message_id: int) -> bool:
|
|
448
|
+
return await self(m.PinChatMessage(chat_id=chat_id, message_id=message_id))
|
|
449
|
+
|
|
450
|
+
async def unpin_chat_message(self, chat_id: ChatId, message_id: int) -> bool:
|
|
451
|
+
return await self(m.UnpinChatMessage(chat_id=chat_id, message_id=message_id))
|
|
452
|
+
|
|
453
|
+
async def unpin_all_chat_messages(self, chat_id: ChatId) -> bool:
|
|
454
|
+
return await self(m.UnpinAllChatMessages(chat_id=chat_id))
|
|
455
|
+
|
|
456
|
+
async def create_chat_invite_link(self, chat_id: ChatId) -> Any:
|
|
457
|
+
return await self(m.CreateChatInviteLink(chat_id=chat_id))
|
|
458
|
+
|
|
459
|
+
async def revoke_chat_invite_link(self, chat_id: ChatId, invite_link: str) -> Any:
|
|
460
|
+
return await self(m.RevokeChatInviteLink(chat_id=chat_id, invite_link=invite_link))
|
|
461
|
+
|
|
462
|
+
async def export_chat_invite_link(self, chat_id: ChatId) -> str:
|
|
463
|
+
return await self(m.ExportChatInviteLink(chat_id=chat_id))
|
|
464
|
+
|
|
465
|
+
# --- files -----------------------------------------------------------------------------
|
|
466
|
+
async def get_file(self, file_id: str) -> File:
|
|
467
|
+
return await self(m.GetFile(file_id=file_id))
|
|
468
|
+
|
|
469
|
+
async def download(
|
|
470
|
+
self,
|
|
471
|
+
file: str | File,
|
|
472
|
+
destination: BinaryIO | str | Path | None = None,
|
|
473
|
+
chunk_size: int = 65536,
|
|
474
|
+
) -> BinaryIO | None:
|
|
475
|
+
"""Download a file by ``file_id`` or :class:`File`.
|
|
476
|
+
|
|
477
|
+
Returns an in-memory buffer when ``destination`` is omitted, otherwise ``None``.
|
|
478
|
+
"""
|
|
479
|
+
if isinstance(file, File) and file.file_path:
|
|
480
|
+
info = file
|
|
481
|
+
else:
|
|
482
|
+
info = await self.get_file(file.file_id if isinstance(file, File) else file)
|
|
483
|
+
if not info.file_path:
|
|
484
|
+
raise ValueError("Bale did not return file_path for this file")
|
|
485
|
+
url = self.server.file_url(self.token, info.file_path)
|
|
486
|
+
buffer = io.BytesIO()
|
|
487
|
+
async for chunk in self.session.stream_content(url, chunk_size):
|
|
488
|
+
buffer.write(chunk)
|
|
489
|
+
if destination is None:
|
|
490
|
+
buffer.seek(0)
|
|
491
|
+
return buffer
|
|
492
|
+
if isinstance(destination, (str, Path)):
|
|
493
|
+
await asyncio.to_thread(_write_file, Path(destination), buffer.getvalue())
|
|
494
|
+
else:
|
|
495
|
+
destination.write(buffer.getvalue()) # noqa: ASYNC240
|
|
496
|
+
return None
|
|
497
|
+
|
|
498
|
+
# --- stickers --------------------------------------------------------------------------
|
|
499
|
+
async def upload_sticker_file(self, user_id: int, sticker: InputFile) -> File:
|
|
500
|
+
return await self(m.UploadStickerFile(user_id=user_id, sticker=sticker))
|
|
501
|
+
|
|
502
|
+
async def create_new_sticker_set(
|
|
503
|
+
self, user_id: int, name: str, title: str, stickers: list[InputSticker]
|
|
504
|
+
) -> bool:
|
|
505
|
+
return await self(
|
|
506
|
+
m.CreateNewStickerSet(user_id=user_id, name=name, title=title, stickers=stickers)
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
async def add_sticker_to_set(self, user_id: int, name: str, sticker: InputSticker) -> bool:
|
|
510
|
+
return await self(m.AddStickerToSet(user_id=user_id, name=name, sticker=sticker))
|
|
511
|
+
|
|
512
|
+
async def get_sticker_set(self, name: str) -> StickerSet:
|
|
513
|
+
return await self(m.GetStickerSet(name=name))
|
|
514
|
+
|
|
515
|
+
# --- payments --------------------------------------------------------------------------
|
|
516
|
+
async def send_invoice(
|
|
517
|
+
self,
|
|
518
|
+
chat_id: ChatId,
|
|
519
|
+
title: str,
|
|
520
|
+
description: str,
|
|
521
|
+
payload: str,
|
|
522
|
+
provider_token: str,
|
|
523
|
+
prices: list[LabeledPrice],
|
|
524
|
+
photo_url: str | None = None,
|
|
525
|
+
reply_to_message_id: int | None = None,
|
|
526
|
+
) -> Message:
|
|
527
|
+
return await self(
|
|
528
|
+
m.SendInvoice(
|
|
529
|
+
chat_id=chat_id,
|
|
530
|
+
title=title,
|
|
531
|
+
description=description,
|
|
532
|
+
payload=payload,
|
|
533
|
+
provider_token=provider_token,
|
|
534
|
+
prices=prices,
|
|
535
|
+
photo_url=photo_url,
|
|
536
|
+
reply_to_message_id=reply_to_message_id,
|
|
537
|
+
)
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
async def create_invoice_link(
|
|
541
|
+
self,
|
|
542
|
+
title: str,
|
|
543
|
+
description: str,
|
|
544
|
+
payload: str,
|
|
545
|
+
provider_token: str,
|
|
546
|
+
prices: list[LabeledPrice],
|
|
547
|
+
) -> str:
|
|
548
|
+
return await self(
|
|
549
|
+
m.CreateInvoiceLink(
|
|
550
|
+
title=title,
|
|
551
|
+
description=description,
|
|
552
|
+
payload=payload,
|
|
553
|
+
provider_token=provider_token,
|
|
554
|
+
prices=prices,
|
|
555
|
+
)
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
async def answer_pre_checkout_query(
|
|
559
|
+
self, pre_checkout_query_id: str, ok: bool, error_message: str | None = None
|
|
560
|
+
) -> bool:
|
|
561
|
+
return await self(
|
|
562
|
+
m.AnswerPreCheckoutQuery(
|
|
563
|
+
pre_checkout_query_id=pre_checkout_query_id, ok=ok, error_message=error_message
|
|
564
|
+
)
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
async def inquire_transaction(self, transaction_id: str) -> Transaction:
|
|
568
|
+
return await self(m.InquireTransaction(transaction_id=transaction_id))
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class ApiServer:
|
|
8
|
+
"""Describes where the Bale Bot API lives. Subclass or build your own for proxies/mirrors."""
|
|
9
|
+
|
|
10
|
+
base: str = "https://tapi.bale.ai/bot{token}/{method}"
|
|
11
|
+
file: str = "https://tapi.bale.ai/file/bot{token}/{path}"
|
|
12
|
+
|
|
13
|
+
def api_url(self, token: str, method: str) -> str:
|
|
14
|
+
return self.base.format(token=token, method=method)
|
|
15
|
+
|
|
16
|
+
def file_url(self, token: str, path: str) -> str:
|
|
17
|
+
return self.file.format(token=token, path=path)
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def from_base(cls, base: str) -> ApiServer:
|
|
21
|
+
base = base.rstrip("/")
|
|
22
|
+
return cls(
|
|
23
|
+
base=f"{base}/bot{{token}}/{{method}}",
|
|
24
|
+
file=f"{base}/file/bot{{token}}/{{path}}",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
PRODUCTION = ApiServer()
|
|
29
|
+
BUSINESS = ApiServer(
|
|
30
|
+
base="https://tapi.bale.ai/business/bot{token}/{method}",
|
|
31
|
+
file=PRODUCTION.file,
|
|
32
|
+
)
|