maxkit 2.13.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.
- aiomax/__init__.py +9 -0
- aiomax/bot.py +1166 -0
- aiomax/buttons.py +303 -0
- aiomax/cache.py +31 -0
- aiomax/exceptions.py +85 -0
- aiomax/filters.py +179 -0
- aiomax/fsm.py +107 -0
- aiomax/router.py +383 -0
- aiomax/russian_trusted_root_ca.cer +33 -0
- aiomax/types.py +1512 -0
- aiomax/utils.py +125 -0
- maxkit-2.13.0.dist-info/METADATA +45 -0
- maxkit-2.13.0.dist-info/RECORD +16 -0
- maxkit-2.13.0.dist-info/WHEEL +5 -0
- maxkit-2.13.0.dist-info/licenses/LICENSE.md +22 -0
- maxkit-2.13.0.dist-info/top_level.txt +1 -0
aiomax/types.py
ADDED
|
@@ -0,0 +1,1512 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Callable, Literal, Optional
|
|
3
|
+
|
|
4
|
+
from . import buttons, exceptions, utils
|
|
5
|
+
|
|
6
|
+
type_logger = logging.getLogger("aiomax.types")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BotCommand:
|
|
10
|
+
def __init__(self, name: str, description: str, **kwargs):
|
|
11
|
+
self.name = name
|
|
12
|
+
self.description = description
|
|
13
|
+
|
|
14
|
+
def as_dict(self):
|
|
15
|
+
return {"name": self.name, "description": self.description}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class User:
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
user_id: int,
|
|
22
|
+
first_name: str,
|
|
23
|
+
name: str,
|
|
24
|
+
is_bot: bool,
|
|
25
|
+
last_activity_time: int,
|
|
26
|
+
last_name: "str | None" = None,
|
|
27
|
+
username: "str | None" = None,
|
|
28
|
+
description: "str | None" = None,
|
|
29
|
+
avatar_url: "str | None" = None,
|
|
30
|
+
full_avatar_url: "str | None" = None,
|
|
31
|
+
commands: "list[BotCommand] | None" = None,
|
|
32
|
+
last_access_time: "int | None" = None,
|
|
33
|
+
is_owner: "bool | None" = None,
|
|
34
|
+
is_admin: "bool | None" = None,
|
|
35
|
+
join_time: "int | None" = None,
|
|
36
|
+
permissions: "list[str] | None" = None,
|
|
37
|
+
**kwargs,
|
|
38
|
+
):
|
|
39
|
+
self.user_id: int = user_id
|
|
40
|
+
self.first_name: str = first_name
|
|
41
|
+
self.last_name: str = last_name
|
|
42
|
+
self.name: str = name
|
|
43
|
+
self.username: "str | None" = username
|
|
44
|
+
self.is_bot: bool = is_bot
|
|
45
|
+
self.last_activity_time: float | None = (
|
|
46
|
+
last_activity_time / 1000 if last_activity_time else None
|
|
47
|
+
)
|
|
48
|
+
self.description: "str | None" = description
|
|
49
|
+
self.avatar_url: "str | None" = avatar_url
|
|
50
|
+
self.full_avatar_url: "str | None" = full_avatar_url
|
|
51
|
+
self.commands: "list[BotCommand] | None" = (
|
|
52
|
+
[BotCommand(**i) for i in commands] if commands else None
|
|
53
|
+
)
|
|
54
|
+
self.last_access_time: "int | None" = (
|
|
55
|
+
last_access_time / 1000 if last_access_time else None
|
|
56
|
+
)
|
|
57
|
+
self.is_owner: "bool | None" = is_owner
|
|
58
|
+
self.is_admin: "bool | None" = is_admin
|
|
59
|
+
self.join_time: "float | None" = (
|
|
60
|
+
join_time / 1000 if join_time else None
|
|
61
|
+
)
|
|
62
|
+
self.permissions: "list[str] | None" = permissions
|
|
63
|
+
|
|
64
|
+
def __repr__(self):
|
|
65
|
+
return (
|
|
66
|
+
f"{type(self).__name__}(user_id={self.user_id!r},"
|
|
67
|
+
f"name={self.name!r})"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def __eq__(self, other):
|
|
71
|
+
if isinstance(other, User):
|
|
72
|
+
return self.user_id == other.user_id
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def from_json(data: dict) -> "User | None":
|
|
77
|
+
if data is None:
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
return User(**data)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Attachment:
|
|
84
|
+
def __init__(self, type: str):
|
|
85
|
+
self.type: str = type
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def from_json(data: dict) -> "Attachment | None":
|
|
89
|
+
if data["type"] == "image":
|
|
90
|
+
return PhotoAttachment.from_json(data)
|
|
91
|
+
elif data["type"] == "video":
|
|
92
|
+
return VideoAttachment.from_json(data)
|
|
93
|
+
elif data["type"] == "audio":
|
|
94
|
+
return AudioAttachment.from_json(data)
|
|
95
|
+
elif data["type"] == "file":
|
|
96
|
+
return FileAttachment.from_json(data)
|
|
97
|
+
elif data["type"] == "sticker":
|
|
98
|
+
return StickerAttachment.from_json(data)
|
|
99
|
+
elif data["type"] == "contact":
|
|
100
|
+
return ContactAttachment.from_json(data)
|
|
101
|
+
elif data["type"] == "share":
|
|
102
|
+
return ShareAttachment.from_json(data)
|
|
103
|
+
elif data["type"] == "location":
|
|
104
|
+
return LocationAttachment.from_json(data)
|
|
105
|
+
elif data["type"] == "inline_keyboard":
|
|
106
|
+
return InlineKeyboardAttachment.from_json(data)
|
|
107
|
+
else:
|
|
108
|
+
# Unknown/newly-added attachment type: keep parsing the rest of the
|
|
109
|
+
# update instead of crashing handle_update (and losing the batch).
|
|
110
|
+
type_logger.warning(
|
|
111
|
+
"Unknown attachment type: %s", data.get("type")
|
|
112
|
+
)
|
|
113
|
+
return Attachment(data["type"])
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class PhotoAttachment(Attachment):
|
|
117
|
+
def __init__(
|
|
118
|
+
self,
|
|
119
|
+
url: "str | None" = None,
|
|
120
|
+
token: "str | None" = None,
|
|
121
|
+
photo_id: "int | None" = None,
|
|
122
|
+
):
|
|
123
|
+
"""
|
|
124
|
+
A photo attachment. Use either `url` or `token` when uploading.
|
|
125
|
+
|
|
126
|
+
:param url: Image URL
|
|
127
|
+
:param token: Attachment token got while uploading the image
|
|
128
|
+
:param photo_id: Unique photo ID. Not used when sending the attachment
|
|
129
|
+
"""
|
|
130
|
+
super().__init__("image")
|
|
131
|
+
self.url: "str | None" = url
|
|
132
|
+
self.token: "str | None" = token
|
|
133
|
+
self.photo_id: "int | None" = photo_id
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def from_json(data: dict) -> "PhotoAttachment | None":
|
|
137
|
+
photo = PhotoAttachment(
|
|
138
|
+
url=data["payload"].get("url"),
|
|
139
|
+
token=data["payload"].get("token"),
|
|
140
|
+
photo_id=data["payload"].get("photo_id"),
|
|
141
|
+
)
|
|
142
|
+
return photo
|
|
143
|
+
|
|
144
|
+
def as_dict(self):
|
|
145
|
+
data = {"type": self.type, "payload": {}}
|
|
146
|
+
if self.token:
|
|
147
|
+
data["payload"]["token"] = self.token
|
|
148
|
+
if self.url:
|
|
149
|
+
data["payload"]["url"] = self.url
|
|
150
|
+
return data
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class VideoAttachment(Attachment):
|
|
154
|
+
def __init__(
|
|
155
|
+
self,
|
|
156
|
+
token: "str | None" = None,
|
|
157
|
+
url: "str | None" = None,
|
|
158
|
+
thumbnail: "str | None" = None,
|
|
159
|
+
width: "int | None" = None,
|
|
160
|
+
height: "int | None" = None,
|
|
161
|
+
duration: "int | None" = None,
|
|
162
|
+
):
|
|
163
|
+
"""
|
|
164
|
+
A video attachment. Use `token` when uploading.
|
|
165
|
+
|
|
166
|
+
:param token: Attachment token got while uploading video
|
|
167
|
+
:param url: Video URL that can be used for downloading the video
|
|
168
|
+
:param thumbnail: Video thumbnail URL
|
|
169
|
+
:param width: Video width
|
|
170
|
+
:param height: Video height
|
|
171
|
+
:param duration: Video duration
|
|
172
|
+
"""
|
|
173
|
+
super().__init__("video")
|
|
174
|
+
self.token: "str | None" = token
|
|
175
|
+
self.url: "str | None" = url
|
|
176
|
+
self.thumbnail: "str | None" = thumbnail
|
|
177
|
+
self.width: "int | None" = width
|
|
178
|
+
self.height: "int | None" = height
|
|
179
|
+
self.duration: "int | None" = duration
|
|
180
|
+
|
|
181
|
+
@staticmethod
|
|
182
|
+
def from_json(data: dict) -> "VideoAttachment | None":
|
|
183
|
+
return VideoAttachment(
|
|
184
|
+
data["payload"].get("token", None),
|
|
185
|
+
data["payload"].get("url", None),
|
|
186
|
+
data.get("thumbnail", {}).get("url"),
|
|
187
|
+
data.get("width"),
|
|
188
|
+
data.get("height"),
|
|
189
|
+
data.get("duration"),
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def as_dict(self):
|
|
193
|
+
return {"type": self.type, "payload": {"token": self.token}}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class AudioAttachment(Attachment):
|
|
197
|
+
def __init__(
|
|
198
|
+
self,
|
|
199
|
+
url: "str | None" = None,
|
|
200
|
+
token: "str | None" = None,
|
|
201
|
+
transcription: "str | None" = None,
|
|
202
|
+
):
|
|
203
|
+
"""
|
|
204
|
+
An audio attachment. Use `token` when uploading.
|
|
205
|
+
|
|
206
|
+
:param token: Attachment token got while uploading audio
|
|
207
|
+
:param transcription: Audio transcription
|
|
208
|
+
"""
|
|
209
|
+
super().__init__("audio")
|
|
210
|
+
self.url: str = url
|
|
211
|
+
self.token: str = token
|
|
212
|
+
self.transcription: "str | None" = transcription
|
|
213
|
+
|
|
214
|
+
@staticmethod
|
|
215
|
+
def from_json(data: dict) -> "AudioAttachment | None":
|
|
216
|
+
payload = data.get("payload") or {}
|
|
217
|
+
return AudioAttachment(
|
|
218
|
+
payload.get("url"),
|
|
219
|
+
payload.get("token"),
|
|
220
|
+
data.get("transcription"),
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
def as_dict(self):
|
|
224
|
+
return {"type": self.type, "payload": {"token": self.token}}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class FileAttachment(Attachment):
|
|
228
|
+
def __init__(
|
|
229
|
+
self,
|
|
230
|
+
token: str,
|
|
231
|
+
url: "str | None" = None,
|
|
232
|
+
filename: "str | None" = None,
|
|
233
|
+
size: "int | None" = None,
|
|
234
|
+
):
|
|
235
|
+
"""
|
|
236
|
+
A file attachment. Use `token` when uploading.
|
|
237
|
+
|
|
238
|
+
:param token: Attachment token got while uploading the file
|
|
239
|
+
:param url: File URL that can be used for downloading the file
|
|
240
|
+
:param filename: File name
|
|
241
|
+
:param size: File size
|
|
242
|
+
"""
|
|
243
|
+
super().__init__("file")
|
|
244
|
+
self.url: "str | None" = url
|
|
245
|
+
self.token: str = token
|
|
246
|
+
self.filename: "str | None" = filename
|
|
247
|
+
self.size: "int | None" = size
|
|
248
|
+
|
|
249
|
+
@staticmethod
|
|
250
|
+
def from_json(data: dict) -> "FileAttachment | None":
|
|
251
|
+
return FileAttachment(
|
|
252
|
+
data["payload"]["token"],
|
|
253
|
+
data["payload"].get("url"),
|
|
254
|
+
data.get("filename"),
|
|
255
|
+
data.get("size"),
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
def as_dict(self):
|
|
259
|
+
return {"type": self.type, "payload": {"token": self.token}}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class StickerAttachment(Attachment):
|
|
263
|
+
def __init__(
|
|
264
|
+
self,
|
|
265
|
+
code: str,
|
|
266
|
+
url: "str | None" = None,
|
|
267
|
+
width: "int | None" = None,
|
|
268
|
+
height: "int | None" = None,
|
|
269
|
+
):
|
|
270
|
+
"""
|
|
271
|
+
A sticker attachment. Use `code` when uploading.
|
|
272
|
+
|
|
273
|
+
:param code: Sticker code
|
|
274
|
+
:param url: Sticker URL that can be used for downloading the sticker
|
|
275
|
+
:param width: Sticker width
|
|
276
|
+
:param height: Sticker height
|
|
277
|
+
"""
|
|
278
|
+
super().__init__("sticker")
|
|
279
|
+
self.code: str = code
|
|
280
|
+
self.url: "str | None" = url
|
|
281
|
+
self.width: int = width
|
|
282
|
+
self.height: int = height
|
|
283
|
+
|
|
284
|
+
@staticmethod
|
|
285
|
+
def from_json(data: dict) -> "StickerAttachment | None":
|
|
286
|
+
sticker = StickerAttachment(
|
|
287
|
+
data["payload"]["code"],
|
|
288
|
+
data["payload"].get("url"),
|
|
289
|
+
data.get("width"),
|
|
290
|
+
data.get("height"),
|
|
291
|
+
)
|
|
292
|
+
return sticker
|
|
293
|
+
|
|
294
|
+
def as_dict(self) -> dict:
|
|
295
|
+
return {"type": "sticker", "payload": {"code": self.code}}
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class ContactAttachment(Attachment):
|
|
299
|
+
def __init__(
|
|
300
|
+
self,
|
|
301
|
+
name: "str | None" = None,
|
|
302
|
+
contact_id: "int | None" = None,
|
|
303
|
+
vcf_info: "str | None" = None,
|
|
304
|
+
vcf_phone: "str | None" = None,
|
|
305
|
+
max_info: "User | None" = None,
|
|
306
|
+
):
|
|
307
|
+
"""
|
|
308
|
+
A contact attachment.
|
|
309
|
+
|
|
310
|
+
:param name: Contact name. Only used when sending
|
|
311
|
+
:param contact_id: Contact user ID (if sending a Max user).
|
|
312
|
+
Only used when sending contacts
|
|
313
|
+
:param vcf_info: Contact's information in vCard format
|
|
314
|
+
:param vcf_phone: Contact's phone number.
|
|
315
|
+
Only used when sending contacts
|
|
316
|
+
:param max_info: User object if contact is a user.
|
|
317
|
+
Only used when receiving contacts
|
|
318
|
+
"""
|
|
319
|
+
super().__init__("contact")
|
|
320
|
+
self.name: "str | None" = name
|
|
321
|
+
self.contact_id: "int | None" = contact_id
|
|
322
|
+
self.vcf_info: "str | None" = vcf_info
|
|
323
|
+
self.vcf_phone: "str | None" = vcf_phone
|
|
324
|
+
self.max_info: "User | None" = max_info
|
|
325
|
+
|
|
326
|
+
@staticmethod
|
|
327
|
+
def from_json(data: dict) -> "ContactAttachment | None":
|
|
328
|
+
if not data:
|
|
329
|
+
return None
|
|
330
|
+
|
|
331
|
+
payload = data.get("payload") or {}
|
|
332
|
+
|
|
333
|
+
return ContactAttachment(
|
|
334
|
+
name=payload.get("name"),
|
|
335
|
+
contact_id=payload.get("contact_id"),
|
|
336
|
+
vcf_info=payload.get("vcf_info"),
|
|
337
|
+
vcf_phone=payload.get("vcf_phone"),
|
|
338
|
+
max_info=User.from_json(payload.get("max_info")),
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
def as_dict(self) -> dict:
|
|
342
|
+
return {
|
|
343
|
+
"type": self.type,
|
|
344
|
+
"payload": {
|
|
345
|
+
"name": self.name,
|
|
346
|
+
"contact_id": self.contact_id,
|
|
347
|
+
"vcf_info": self.vcf_info,
|
|
348
|
+
"vcf_phone": self.vcf_phone,
|
|
349
|
+
},
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class ShareAttachment(Attachment):
|
|
354
|
+
def __init__(
|
|
355
|
+
self,
|
|
356
|
+
url: "str | None" = None,
|
|
357
|
+
token: "str | None" = None,
|
|
358
|
+
title: "str | None" = None,
|
|
359
|
+
description: "str | None" = None,
|
|
360
|
+
image_url: "str | None" = None,
|
|
361
|
+
):
|
|
362
|
+
"""
|
|
363
|
+
Link preview. Use `url` and `token` when uploading
|
|
364
|
+
|
|
365
|
+
:param url: Link URL
|
|
366
|
+
:param token: Attachment token
|
|
367
|
+
:param title: Preview title
|
|
368
|
+
:param description: Preview description
|
|
369
|
+
:param image_url: Preview image URL
|
|
370
|
+
"""
|
|
371
|
+
super().__init__("share")
|
|
372
|
+
self.url: "str | None" = url
|
|
373
|
+
self.token: "str | None" = token
|
|
374
|
+
self.title: "str | None" = title
|
|
375
|
+
self.description: "str | None" = description
|
|
376
|
+
self.image_url: "str | None" = image_url
|
|
377
|
+
|
|
378
|
+
@staticmethod
|
|
379
|
+
def from_json(data: dict) -> "ShareAttachment | None":
|
|
380
|
+
return ShareAttachment(
|
|
381
|
+
data["payload"].get("url", None),
|
|
382
|
+
data["payload"].get("token", None),
|
|
383
|
+
data.get("title"),
|
|
384
|
+
data.get("description"),
|
|
385
|
+
data.get("image_url"),
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
def as_dict(self) -> dict:
|
|
389
|
+
return {
|
|
390
|
+
"type": self.type,
|
|
391
|
+
"payload": {"url": self.url, "token": self.token},
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
class LocationAttachment(Attachment):
|
|
396
|
+
def __init__(
|
|
397
|
+
self,
|
|
398
|
+
latitude: float,
|
|
399
|
+
longitude: float,
|
|
400
|
+
):
|
|
401
|
+
super().__init__("location")
|
|
402
|
+
self.latitude: float = latitude
|
|
403
|
+
self.longitude: float = longitude
|
|
404
|
+
|
|
405
|
+
@staticmethod
|
|
406
|
+
def from_json(data: dict) -> "LocationAttachment | None":
|
|
407
|
+
return LocationAttachment(data["latitude"], data["longitude"])
|
|
408
|
+
|
|
409
|
+
def as_dict(self) -> dict:
|
|
410
|
+
return {
|
|
411
|
+
"type": "location",
|
|
412
|
+
"latitude": self.latitude,
|
|
413
|
+
"longitude": self.longitude,
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
class InlineKeyboardAttachment(Attachment):
|
|
418
|
+
def __init__(
|
|
419
|
+
self,
|
|
420
|
+
payload: list[list[buttons.Button]],
|
|
421
|
+
):
|
|
422
|
+
super().__init__("inline_keyboard")
|
|
423
|
+
self.payload: list[list[buttons.Button]] = payload
|
|
424
|
+
|
|
425
|
+
@staticmethod
|
|
426
|
+
def from_json(data: dict) -> "InlineKeyboardAttachment | None":
|
|
427
|
+
return InlineKeyboardAttachment(
|
|
428
|
+
[
|
|
429
|
+
[buttons.Button.from_json(j) for j in i]
|
|
430
|
+
for i in data["payload"]["buttons"]
|
|
431
|
+
]
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
class MessageRecipient:
|
|
436
|
+
def __init__(self, chat_id: "int | None", chat_type: str):
|
|
437
|
+
self.chat_id: "int | None" = chat_id
|
|
438
|
+
self.chat_type: str = chat_type
|
|
439
|
+
|
|
440
|
+
def __repr__(self):
|
|
441
|
+
return (
|
|
442
|
+
f"{type(self).__name__}(chat_id={self.chat_id!r},"
|
|
443
|
+
f"chat_type={self.chat_type!r})"
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
def __eq__(self, other):
|
|
447
|
+
if isinstance(other, MessageRecipient):
|
|
448
|
+
return self.chat_id == other.chat_id
|
|
449
|
+
return False
|
|
450
|
+
|
|
451
|
+
@staticmethod
|
|
452
|
+
def from_json(data: dict) -> "MessageRecipient | None":
|
|
453
|
+
if data is None:
|
|
454
|
+
return None
|
|
455
|
+
|
|
456
|
+
return MessageRecipient(
|
|
457
|
+
chat_id=data["chat_id"], chat_type=data["chat_type"]
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
class Markup:
|
|
462
|
+
def __init__(
|
|
463
|
+
self,
|
|
464
|
+
type: Literal[
|
|
465
|
+
"strong",
|
|
466
|
+
"emphasized",
|
|
467
|
+
"monospaced",
|
|
468
|
+
"link",
|
|
469
|
+
"strikethrough",
|
|
470
|
+
"underline",
|
|
471
|
+
"user_mention",
|
|
472
|
+
"heading",
|
|
473
|
+
"highlighted",
|
|
474
|
+
],
|
|
475
|
+
start: int,
|
|
476
|
+
length: int,
|
|
477
|
+
user_link: "str | None" = None,
|
|
478
|
+
user_id: "int | None" = None,
|
|
479
|
+
url: "str | None" = None,
|
|
480
|
+
):
|
|
481
|
+
"""
|
|
482
|
+
A markup element
|
|
483
|
+
|
|
484
|
+
:param type: Markup type
|
|
485
|
+
:param start: Start position
|
|
486
|
+
:param length: Length
|
|
487
|
+
:param user_link: Username. `None` if markup type is not `user_link`
|
|
488
|
+
:param user_id: User ID. `None` if markup type is not `user_link`
|
|
489
|
+
:param url: URL. `None` if markup type is not `link`
|
|
490
|
+
"""
|
|
491
|
+
self.type: Literal[
|
|
492
|
+
"strong",
|
|
493
|
+
"emphasized",
|
|
494
|
+
"monospaced",
|
|
495
|
+
"link",
|
|
496
|
+
"strikethrough",
|
|
497
|
+
"underline",
|
|
498
|
+
"user_mention",
|
|
499
|
+
"heading",
|
|
500
|
+
"highlighted",
|
|
501
|
+
] = type
|
|
502
|
+
self.start: int = start
|
|
503
|
+
self.length: int = length
|
|
504
|
+
|
|
505
|
+
self.user_link: "str | None" = user_link
|
|
506
|
+
self.user_id: "int | None" = user_id
|
|
507
|
+
self.url: "str | None" = url
|
|
508
|
+
|
|
509
|
+
@staticmethod
|
|
510
|
+
def from_json(data: dict) -> "Markup | None":
|
|
511
|
+
if data is None:
|
|
512
|
+
return None
|
|
513
|
+
|
|
514
|
+
if data["type"] == "user_mention":
|
|
515
|
+
return Markup(
|
|
516
|
+
data["type"],
|
|
517
|
+
data["from"],
|
|
518
|
+
data["length"],
|
|
519
|
+
user_link=data.get("user_link"),
|
|
520
|
+
user_id=data.get("user_id"),
|
|
521
|
+
)
|
|
522
|
+
elif data["type"] == "link":
|
|
523
|
+
return Markup(
|
|
524
|
+
data["type"], data["from"], data["length"], url=data["url"]
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
return Markup(data["type"], data["from"], data["length"])
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
class MessageBody:
|
|
531
|
+
def __init__(
|
|
532
|
+
self,
|
|
533
|
+
mid: str,
|
|
534
|
+
seq: int,
|
|
535
|
+
text: "str | None",
|
|
536
|
+
attachments: "list[Attachment] | None",
|
|
537
|
+
markup: "list[Markup] | None" = None,
|
|
538
|
+
):
|
|
539
|
+
self.message_id: str = mid
|
|
540
|
+
self.seq: int = seq
|
|
541
|
+
self.text: "str | None" = text
|
|
542
|
+
self.attachments: "list[Attachment] | None" = attachments
|
|
543
|
+
self.markup: "list[Markup] | None" = markup
|
|
544
|
+
|
|
545
|
+
@staticmethod
|
|
546
|
+
def from_json(data: dict) -> "MessageBody | None":
|
|
547
|
+
if data is None:
|
|
548
|
+
return None
|
|
549
|
+
|
|
550
|
+
return MessageBody(
|
|
551
|
+
mid=data["mid"],
|
|
552
|
+
seq=data["seq"],
|
|
553
|
+
text=data["text"],
|
|
554
|
+
attachments=[
|
|
555
|
+
Attachment.from_json(x) for x in data.get("attachments", [])
|
|
556
|
+
],
|
|
557
|
+
markup=[Markup.from_json(x) for x in data.get("markup", [])],
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
class LinkedMessage:
|
|
562
|
+
def __init__(
|
|
563
|
+
self,
|
|
564
|
+
type: str,
|
|
565
|
+
message: MessageBody,
|
|
566
|
+
sender: User,
|
|
567
|
+
chat_id: "int | None" = None,
|
|
568
|
+
):
|
|
569
|
+
self.type: str = type
|
|
570
|
+
self.message: "MessageBody | None" = message
|
|
571
|
+
self.sender: User = sender
|
|
572
|
+
self.chat_id: "int | None" = chat_id
|
|
573
|
+
|
|
574
|
+
@staticmethod
|
|
575
|
+
def from_json(data: dict) -> "LinkedMessage | None":
|
|
576
|
+
if data is None:
|
|
577
|
+
return None
|
|
578
|
+
|
|
579
|
+
return LinkedMessage(
|
|
580
|
+
type=data["type"],
|
|
581
|
+
message=MessageBody.from_json(data.get("message")),
|
|
582
|
+
sender=User.from_json(data.get("sender")),
|
|
583
|
+
chat_id=data.get("chat_id"),
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
@property
|
|
587
|
+
def user_id(self):
|
|
588
|
+
return self.sender.user_id
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
class Message:
|
|
592
|
+
def __init__(
|
|
593
|
+
self,
|
|
594
|
+
recipient: MessageRecipient,
|
|
595
|
+
body: MessageBody,
|
|
596
|
+
timestamp: float,
|
|
597
|
+
sender: User,
|
|
598
|
+
link: "LinkedMessage | None" = None,
|
|
599
|
+
views: "int | None" = None,
|
|
600
|
+
url: "str | None" = None,
|
|
601
|
+
bot=None,
|
|
602
|
+
):
|
|
603
|
+
self.recipient: MessageRecipient = recipient
|
|
604
|
+
self.body: "MessageBody | None" = body
|
|
605
|
+
self.timestamp: "float | None" = (
|
|
606
|
+
timestamp / 1000 if timestamp else None
|
|
607
|
+
)
|
|
608
|
+
self.sender: "User | None" = sender
|
|
609
|
+
self.link: "LinkedMessage | None" = link
|
|
610
|
+
self.views: "int | None" = views
|
|
611
|
+
self.url: "str | None" = url
|
|
612
|
+
self.user_locale: "str | None" = None
|
|
613
|
+
self.bot = bot
|
|
614
|
+
|
|
615
|
+
def __repr__(self):
|
|
616
|
+
return f"{type(self).__name__}(text={self.body.text!r})"
|
|
617
|
+
|
|
618
|
+
def __str__(self):
|
|
619
|
+
return self.body.text
|
|
620
|
+
|
|
621
|
+
def __eq__(self, other):
|
|
622
|
+
if isinstance(other, Message):
|
|
623
|
+
return self.id == other.id
|
|
624
|
+
return False
|
|
625
|
+
|
|
626
|
+
@property
|
|
627
|
+
def id(self) -> str:
|
|
628
|
+
return self.body.message_id
|
|
629
|
+
|
|
630
|
+
@property
|
|
631
|
+
def content(self) -> str:
|
|
632
|
+
return self.body.text
|
|
633
|
+
|
|
634
|
+
@property
|
|
635
|
+
def user_id(self):
|
|
636
|
+
return self.sender.user_id
|
|
637
|
+
|
|
638
|
+
@staticmethod
|
|
639
|
+
def from_json(data: dict) -> "Message":
|
|
640
|
+
return Message(
|
|
641
|
+
recipient=MessageRecipient.from_json(data.get("recipient")),
|
|
642
|
+
body=MessageBody.from_json(data.get("body")),
|
|
643
|
+
timestamp=data.get("timestamp"),
|
|
644
|
+
sender=User.from_json(data.get("sender")),
|
|
645
|
+
link=LinkedMessage.from_json(data.get("link")),
|
|
646
|
+
views=data.get("stat", {}).get("views", None),
|
|
647
|
+
url=data.get("url"),
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
def resolve_mention(
|
|
651
|
+
self,
|
|
652
|
+
replies: bool = True,
|
|
653
|
+
message_text: bool = True,
|
|
654
|
+
skip_bot: bool = True,
|
|
655
|
+
) -> "int | None":
|
|
656
|
+
"""
|
|
657
|
+
Finds who was mentioned in this message
|
|
658
|
+
and returns the user ID if found.
|
|
659
|
+
|
|
660
|
+
:param replies: Whether to check for this message's link author.
|
|
661
|
+
:param message_text: Whether to check for mentions in message text.
|
|
662
|
+
:param skip_bot: Whether to ignore mentions of the bot.
|
|
663
|
+
"""
|
|
664
|
+
if replies and self.link and self.link.type == "reply":
|
|
665
|
+
if (
|
|
666
|
+
skip_bot
|
|
667
|
+
and self.bot
|
|
668
|
+
and self.link.sender.user_id == self.bot.id
|
|
669
|
+
):
|
|
670
|
+
pass
|
|
671
|
+
else:
|
|
672
|
+
return self.link.sender.user_id
|
|
673
|
+
|
|
674
|
+
if message_text and self.body.markup:
|
|
675
|
+
for i in self.body.markup:
|
|
676
|
+
if i.type != "user_mention":
|
|
677
|
+
continue
|
|
678
|
+
if skip_bot and self.bot and i.user_id == self.bot.id:
|
|
679
|
+
continue
|
|
680
|
+
if skip_bot and self.bot and i.user_link == self.bot.username:
|
|
681
|
+
continue
|
|
682
|
+
return i.user_id
|
|
683
|
+
|
|
684
|
+
async def send(
|
|
685
|
+
self,
|
|
686
|
+
text: "str | None" = None,
|
|
687
|
+
format: "Literal['html', 'markdown', 'default'] | None" = "default",
|
|
688
|
+
notify: bool = True,
|
|
689
|
+
disable_link_preview: bool = False,
|
|
690
|
+
keyboard: """list[list[buttons.Button]] \
|
|
691
|
+
| buttons.KeyboardBuilder \
|
|
692
|
+
| None""" = None,
|
|
693
|
+
attachments: "list[Attachment] | Attachment | None" = None,
|
|
694
|
+
) -> "Message":
|
|
695
|
+
"""
|
|
696
|
+
Send a message to the chat that the message is sent.
|
|
697
|
+
|
|
698
|
+
:param text: Message text. Up to 4000 characters
|
|
699
|
+
:param format: Message format. Bot.default_format by default
|
|
700
|
+
:param notify: Whether to notify users about the message.
|
|
701
|
+
True by default.
|
|
702
|
+
:param disable_link_preview: Whether to disable link preview.
|
|
703
|
+
False by default
|
|
704
|
+
:param keyboard: An inline keyboard to attach to the message
|
|
705
|
+
:param attachments: List of attachments
|
|
706
|
+
"""
|
|
707
|
+
if self.bot is None:
|
|
708
|
+
return
|
|
709
|
+
return await self.bot.send_message(
|
|
710
|
+
text,
|
|
711
|
+
chat_id=self.recipient.chat_id,
|
|
712
|
+
format=format,
|
|
713
|
+
notify=notify,
|
|
714
|
+
disable_link_preview=disable_link_preview,
|
|
715
|
+
keyboard=keyboard,
|
|
716
|
+
attachments=attachments,
|
|
717
|
+
)
|
|
718
|
+
|
|
719
|
+
async def reply(
|
|
720
|
+
self,
|
|
721
|
+
text: "str | None" = None,
|
|
722
|
+
format: "Literal['html', 'markdown', 'default'] | None" = "default",
|
|
723
|
+
notify: bool = True,
|
|
724
|
+
disable_link_preview: bool = False,
|
|
725
|
+
keyboard: """list[list[buttons.Button]] \
|
|
726
|
+
| buttons.KeyboardBuilder \
|
|
727
|
+
| None""" = None,
|
|
728
|
+
attachments: "list[Attachment] | Attachment | None" = None,
|
|
729
|
+
) -> "Message | None":
|
|
730
|
+
"""
|
|
731
|
+
Reply to this message.
|
|
732
|
+
|
|
733
|
+
:param text: Message text. Up to 4000 characters
|
|
734
|
+
:param format: Message format. Bot.default_format by default
|
|
735
|
+
:param notify: Whether to notify users about the message.
|
|
736
|
+
True by default.
|
|
737
|
+
:param disable_link_preview: Whether to disable link preview.
|
|
738
|
+
False by default
|
|
739
|
+
:param keyboard: An inline keyboard to attach to the message
|
|
740
|
+
:param attachments: List of attachments
|
|
741
|
+
"""
|
|
742
|
+
if self.bot is None:
|
|
743
|
+
return
|
|
744
|
+
return await self.bot.send_message(
|
|
745
|
+
text,
|
|
746
|
+
chat_id=self.recipient.chat_id,
|
|
747
|
+
format=format,
|
|
748
|
+
notify=notify,
|
|
749
|
+
disable_link_preview=disable_link_preview,
|
|
750
|
+
keyboard=keyboard,
|
|
751
|
+
attachments=attachments,
|
|
752
|
+
reply_to=self.id,
|
|
753
|
+
)
|
|
754
|
+
|
|
755
|
+
async def edit(
|
|
756
|
+
self,
|
|
757
|
+
text: "str | None" = None,
|
|
758
|
+
format: "Literal['html', 'markdown', 'default'] | None" = "default",
|
|
759
|
+
reply_to: "int | None" = None,
|
|
760
|
+
notify: bool = True,
|
|
761
|
+
keyboard: """list[list[buttons.Button]] \
|
|
762
|
+
| buttons.KeyboardBuilder \
|
|
763
|
+
| None""" = None,
|
|
764
|
+
attachments: "list[Attachment] | Attachment | None" = None,
|
|
765
|
+
) -> "Message | None":
|
|
766
|
+
"""
|
|
767
|
+
Edit a message
|
|
768
|
+
|
|
769
|
+
:param text: Message text. Up to 4000 characters
|
|
770
|
+
:param format: Message format. Bot.default_format by default
|
|
771
|
+
:param notify: Whether to notify users about the message.
|
|
772
|
+
True by default.
|
|
773
|
+
:param disable_link_preview: Whether to disable link preview.
|
|
774
|
+
False by default
|
|
775
|
+
:param keyboard: An inline keyboard to attach to the message
|
|
776
|
+
:param attachments: List of attachments
|
|
777
|
+
"""
|
|
778
|
+
if self.bot is None:
|
|
779
|
+
return
|
|
780
|
+
return await self.bot.edit_message(
|
|
781
|
+
self.id,
|
|
782
|
+
text,
|
|
783
|
+
format=format,
|
|
784
|
+
notify=notify,
|
|
785
|
+
keyboard=keyboard,
|
|
786
|
+
reply_to=reply_to,
|
|
787
|
+
attachments=attachments,
|
|
788
|
+
)
|
|
789
|
+
|
|
790
|
+
async def delete(self):
|
|
791
|
+
if self.bot is None:
|
|
792
|
+
return
|
|
793
|
+
return await self.bot.delete_message(self.id)
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
class BotStartPayload:
|
|
797
|
+
def __init__(
|
|
798
|
+
self,
|
|
799
|
+
chat_id: int,
|
|
800
|
+
user: User,
|
|
801
|
+
payload: "str | None",
|
|
802
|
+
user_locale: "str | None",
|
|
803
|
+
bot=None,
|
|
804
|
+
):
|
|
805
|
+
self.chat_id: int = chat_id
|
|
806
|
+
self.user: User = user
|
|
807
|
+
self.payload: "str | None" = payload
|
|
808
|
+
self.user_locale: "str | None" = user_locale
|
|
809
|
+
self.bot = bot
|
|
810
|
+
|
|
811
|
+
@staticmethod
|
|
812
|
+
def from_json(data: dict, bot) -> "BotStartPayload":
|
|
813
|
+
return BotStartPayload(
|
|
814
|
+
chat_id=data["chat_id"],
|
|
815
|
+
user=User.from_json(data["user"]),
|
|
816
|
+
payload=data.get("payload"),
|
|
817
|
+
user_locale=data.get("user_locale"),
|
|
818
|
+
bot=bot,
|
|
819
|
+
)
|
|
820
|
+
|
|
821
|
+
@property
|
|
822
|
+
def user_id(self):
|
|
823
|
+
return self.user.user_id
|
|
824
|
+
|
|
825
|
+
async def send(
|
|
826
|
+
self,
|
|
827
|
+
text: "str | None" = None,
|
|
828
|
+
format: "Literal['html', 'markdown', 'default'] | None" = "default",
|
|
829
|
+
notify: bool = True,
|
|
830
|
+
disable_link_preview: bool = False,
|
|
831
|
+
keyboard: """list[list[buttons.Button]] \
|
|
832
|
+
| buttons.KeyboardBuilder \
|
|
833
|
+
| None""" = None,
|
|
834
|
+
attachments: "list[Attachment] | Attachment | None" = None,
|
|
835
|
+
) -> "Message | None":
|
|
836
|
+
"""
|
|
837
|
+
Send a message to the chat where bot was started.
|
|
838
|
+
|
|
839
|
+
:param text: Message text. Up to 4000 characters
|
|
840
|
+
:param format: Message format. Bot.default_format by default
|
|
841
|
+
:param notify: Whether to notify users about the message.
|
|
842
|
+
True by default.
|
|
843
|
+
:param disable_link_preview: Whether to disable link preview.
|
|
844
|
+
False by default
|
|
845
|
+
:param keyboard: An inline keyboard to attach to the message
|
|
846
|
+
:param attachments: List of attachments
|
|
847
|
+
"""
|
|
848
|
+
if self.bot is None:
|
|
849
|
+
return
|
|
850
|
+
return await self.bot.send_message(
|
|
851
|
+
text,
|
|
852
|
+
chat_id=self.chat_id,
|
|
853
|
+
format=format,
|
|
854
|
+
notify=notify,
|
|
855
|
+
disable_link_preview=disable_link_preview,
|
|
856
|
+
keyboard=keyboard,
|
|
857
|
+
attachments=attachments,
|
|
858
|
+
)
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
class CommandContext:
|
|
862
|
+
def __init__(self, bot, message: Message, command_name: str, args: str):
|
|
863
|
+
self.bot = bot
|
|
864
|
+
self.message: Message = message
|
|
865
|
+
self.sender: User = message.sender
|
|
866
|
+
self.recipient: MessageRecipient = message.recipient
|
|
867
|
+
self.command_name: str = command_name
|
|
868
|
+
self.args_raw: str = args
|
|
869
|
+
self.args: list[str] = args.split()
|
|
870
|
+
|
|
871
|
+
async def send(
|
|
872
|
+
self,
|
|
873
|
+
text: "str | None" = None,
|
|
874
|
+
format: "Literal['html', 'markdown', 'default'] | None" = "default",
|
|
875
|
+
notify: bool = True,
|
|
876
|
+
disable_link_preview: bool = False,
|
|
877
|
+
keyboard: """list[list[buttons.Button]] \
|
|
878
|
+
| buttons.KeyboardBuilder \
|
|
879
|
+
| None""" = None,
|
|
880
|
+
attachments: "list[Attachment] | Attachment | None" = None,
|
|
881
|
+
) -> Message:
|
|
882
|
+
"""
|
|
883
|
+
Send a message to the chat that the user sent the command.
|
|
884
|
+
|
|
885
|
+
:param text: Message text. Up to 4000 characters
|
|
886
|
+
:param format: Message format. Bot.default_format by default
|
|
887
|
+
:param notify: Whether to notify users about the message.
|
|
888
|
+
True by default.
|
|
889
|
+
:param disable_link_preview: Whether to disable link preview.
|
|
890
|
+
False by default
|
|
891
|
+
:param keyboard: An inline keyboard to attach to the message
|
|
892
|
+
:param attachments: List of attachments
|
|
893
|
+
"""
|
|
894
|
+
return await self.bot.send_message(
|
|
895
|
+
text,
|
|
896
|
+
chat_id=self.message.recipient.chat_id,
|
|
897
|
+
format=format,
|
|
898
|
+
notify=notify,
|
|
899
|
+
disable_link_preview=disable_link_preview,
|
|
900
|
+
keyboard=keyboard,
|
|
901
|
+
attachments=attachments,
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
async def reply(
|
|
905
|
+
self,
|
|
906
|
+
text: "str | None" = None,
|
|
907
|
+
format: "Literal['html', 'markdown', 'default'] | None" = "default",
|
|
908
|
+
notify: bool = True,
|
|
909
|
+
disable_link_preview: bool = False,
|
|
910
|
+
keyboard: """list[list[buttons.Button]] \
|
|
911
|
+
| buttons.KeyboardBuilder \
|
|
912
|
+
| None""" = None,
|
|
913
|
+
attachments: "list[Attachment] | Attachment | None" = None,
|
|
914
|
+
) -> Message:
|
|
915
|
+
"""
|
|
916
|
+
Reply to the message that the user sent.
|
|
917
|
+
|
|
918
|
+
:param text: Message text. Up to 4000 characters
|
|
919
|
+
:param format: Message format. Bot.default_format by default
|
|
920
|
+
:param notify: Whether to notify users about the message.
|
|
921
|
+
True by default.
|
|
922
|
+
:param disable_link_preview: Whether to disable link preview.
|
|
923
|
+
False by default
|
|
924
|
+
:param keyboard: An inline keyboard to attach to the message
|
|
925
|
+
:param attachments: List of attachments
|
|
926
|
+
"""
|
|
927
|
+
return await self.bot.send_message(
|
|
928
|
+
text,
|
|
929
|
+
chat_id=self.message.recipient.chat_id,
|
|
930
|
+
format=format,
|
|
931
|
+
notify=notify,
|
|
932
|
+
disable_link_preview=disable_link_preview,
|
|
933
|
+
keyboard=keyboard,
|
|
934
|
+
attachments=attachments,
|
|
935
|
+
reply_to=self.message.id,
|
|
936
|
+
)
|
|
937
|
+
|
|
938
|
+
@property
|
|
939
|
+
def user_id(self):
|
|
940
|
+
return self.sender.user_id
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
class CommandHandler:
|
|
944
|
+
def __init__(
|
|
945
|
+
self,
|
|
946
|
+
call: Callable,
|
|
947
|
+
as_message: bool = False,
|
|
948
|
+
):
|
|
949
|
+
self.call = call
|
|
950
|
+
self.as_message: bool = as_message
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
class Handler:
|
|
954
|
+
def __init__(
|
|
955
|
+
self,
|
|
956
|
+
call: Callable,
|
|
957
|
+
deco_filter: "Callable | None" = None,
|
|
958
|
+
router_filters: Optional[list[Callable]] = None,
|
|
959
|
+
):
|
|
960
|
+
if router_filters is None:
|
|
961
|
+
router_filters = []
|
|
962
|
+
|
|
963
|
+
self.call = call
|
|
964
|
+
self.deco_filter: "Callable | None" = deco_filter
|
|
965
|
+
self.router_filters: list[Callable] = router_filters
|
|
966
|
+
|
|
967
|
+
@property
|
|
968
|
+
def filters(self) -> list[Callable]:
|
|
969
|
+
if self.deco_filter:
|
|
970
|
+
return [self.deco_filter, *self.router_filters]
|
|
971
|
+
return self.router_filters
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
class MessageHandler(Handler):
|
|
975
|
+
def __init__(
|
|
976
|
+
self,
|
|
977
|
+
call: Callable,
|
|
978
|
+
deco_filter: "Callable | None" = None,
|
|
979
|
+
router_filters: Optional[list[Callable]] = None,
|
|
980
|
+
detect_commands: bool = False,
|
|
981
|
+
):
|
|
982
|
+
if router_filters is None:
|
|
983
|
+
router_filters = []
|
|
984
|
+
|
|
985
|
+
super().__init__(call, deco_filter, router_filters)
|
|
986
|
+
self.detect_commands: bool = detect_commands
|
|
987
|
+
|
|
988
|
+
|
|
989
|
+
class Image:
|
|
990
|
+
def __init__(
|
|
991
|
+
self,
|
|
992
|
+
url: str,
|
|
993
|
+
**kwargs,
|
|
994
|
+
):
|
|
995
|
+
"""
|
|
996
|
+
An image.
|
|
997
|
+
|
|
998
|
+
:param url: Image URL
|
|
999
|
+
"""
|
|
1000
|
+
self.url: str = url
|
|
1001
|
+
|
|
1002
|
+
@staticmethod
|
|
1003
|
+
def from_json(data: dict) -> "Image | None":
|
|
1004
|
+
if data is None:
|
|
1005
|
+
return None
|
|
1006
|
+
|
|
1007
|
+
return Image(**data)
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
class ImageRequestPayload:
|
|
1011
|
+
def __init__(
|
|
1012
|
+
self,
|
|
1013
|
+
url: "str | None" = None,
|
|
1014
|
+
token: "str | None" = None,
|
|
1015
|
+
**kwargs,
|
|
1016
|
+
):
|
|
1017
|
+
"""
|
|
1018
|
+
A payload with the info about an image or avatar to send to the bot.
|
|
1019
|
+
|
|
1020
|
+
Only url or token must be specified.
|
|
1021
|
+
|
|
1022
|
+
:param url: Image URL
|
|
1023
|
+
:param token: Attachment token generated by Bot.upload_image().token
|
|
1024
|
+
"""
|
|
1025
|
+
if url is None and token is None:
|
|
1026
|
+
raise exceptions.AiomaxException("Token or URL must be specified")
|
|
1027
|
+
if not (url is None or token is None):
|
|
1028
|
+
raise exceptions.AiomaxException(
|
|
1029
|
+
"Token and URL cannot be specified at the same time"
|
|
1030
|
+
)
|
|
1031
|
+
|
|
1032
|
+
self.url: "str | None" = url
|
|
1033
|
+
self.token: "str | None" = token
|
|
1034
|
+
|
|
1035
|
+
@staticmethod
|
|
1036
|
+
def from_json(data: dict) -> "ImageRequestPayload | None":
|
|
1037
|
+
if data is None:
|
|
1038
|
+
return None
|
|
1039
|
+
|
|
1040
|
+
return ImageRequestPayload(**data)
|
|
1041
|
+
|
|
1042
|
+
def as_dict(self):
|
|
1043
|
+
return {"url": self.url} if self.url else {"token": self.token}
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
class Chat:
|
|
1047
|
+
def __init__(
|
|
1048
|
+
self,
|
|
1049
|
+
chat_id: int,
|
|
1050
|
+
type: str,
|
|
1051
|
+
status: str,
|
|
1052
|
+
last_event_time: int,
|
|
1053
|
+
participants_count: int,
|
|
1054
|
+
is_public: bool,
|
|
1055
|
+
title: "str | None" = None,
|
|
1056
|
+
icon: "Image | None" = None,
|
|
1057
|
+
description: "str | None" = None,
|
|
1058
|
+
pinned_message: "Message | None" = None,
|
|
1059
|
+
owner_id: "int | None" = None,
|
|
1060
|
+
participants: "dict[str, int] | None" = None,
|
|
1061
|
+
link: "str | None" = None,
|
|
1062
|
+
messages_count: "str | None" = None,
|
|
1063
|
+
chat_message_id: "str | None" = None,
|
|
1064
|
+
dialog_with_user: "User | None" = None,
|
|
1065
|
+
**kwargs,
|
|
1066
|
+
):
|
|
1067
|
+
self.chat_id: int = chat_id
|
|
1068
|
+
self.type: str = type
|
|
1069
|
+
self.status: str = status
|
|
1070
|
+
self.last_event_time: float | None = (
|
|
1071
|
+
last_event_time / 1000 if last_event_time else None
|
|
1072
|
+
)
|
|
1073
|
+
self.participants_count: int = participants_count
|
|
1074
|
+
self.title: "str | None" = title
|
|
1075
|
+
self.icon: "Image | None" = icon
|
|
1076
|
+
self.is_public: bool = is_public
|
|
1077
|
+
self.dialog_with_user: "User | None" = dialog_with_user
|
|
1078
|
+
self.description: "str | None" = description
|
|
1079
|
+
self.pinned_message: "Message | None" = pinned_message
|
|
1080
|
+
self.owner_id: "int | None" = owner_id
|
|
1081
|
+
self.participants: "dict[int, int] | None" = (
|
|
1082
|
+
{int(k): v for k, v in participants.items()}
|
|
1083
|
+
if participants
|
|
1084
|
+
else None
|
|
1085
|
+
)
|
|
1086
|
+
self.link: "str | None" = link
|
|
1087
|
+
self.messages_count: "str | None" = messages_count
|
|
1088
|
+
self.chat_message_id: "str | None" = chat_message_id
|
|
1089
|
+
|
|
1090
|
+
def __eq__(self, other):
|
|
1091
|
+
if isinstance(other, Chat):
|
|
1092
|
+
return self.chat_id == other.chat_id
|
|
1093
|
+
return False
|
|
1094
|
+
|
|
1095
|
+
def __repr__(self):
|
|
1096
|
+
return (
|
|
1097
|
+
f"{self.__class__.__name__}(chat_id={self.chat_id!r},"
|
|
1098
|
+
f"title={self.title!r})"
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
@staticmethod
|
|
1102
|
+
def from_json(data: dict) -> "Chat | None":
|
|
1103
|
+
if data is None:
|
|
1104
|
+
return None
|
|
1105
|
+
|
|
1106
|
+
data = dict(data)
|
|
1107
|
+
if data.get("icon") is not None:
|
|
1108
|
+
data["icon"] = Image.from_json(data["icon"])
|
|
1109
|
+
if data.get("pinned_message") is not None:
|
|
1110
|
+
data["pinned_message"] = Message.from_json(data["pinned_message"])
|
|
1111
|
+
if data.get("dialog_with_user") is not None:
|
|
1112
|
+
data["dialog_with_user"] = User.from_json(data["dialog_with_user"])
|
|
1113
|
+
|
|
1114
|
+
return Chat(**data)
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
class Callback:
|
|
1118
|
+
def __init__(
|
|
1119
|
+
self,
|
|
1120
|
+
bot,
|
|
1121
|
+
timestamp: int,
|
|
1122
|
+
callback_id: str,
|
|
1123
|
+
message: "Message | None",
|
|
1124
|
+
user: User,
|
|
1125
|
+
user_locale: "str | None",
|
|
1126
|
+
payload: "str | None" = None,
|
|
1127
|
+
):
|
|
1128
|
+
self.bot = bot
|
|
1129
|
+
self.timestamp: float = timestamp / 1000
|
|
1130
|
+
self.callback_id: str = callback_id
|
|
1131
|
+
self.message: "Message | None" = message
|
|
1132
|
+
self.user: User = user
|
|
1133
|
+
self.payload: "str | None" = payload
|
|
1134
|
+
self.user_locale: "str | None" = user_locale
|
|
1135
|
+
|
|
1136
|
+
if self.message is not None:
|
|
1137
|
+
self.message.bot = bot
|
|
1138
|
+
|
|
1139
|
+
@property
|
|
1140
|
+
def content(self) -> str:
|
|
1141
|
+
return self.payload
|
|
1142
|
+
|
|
1143
|
+
async def send(
|
|
1144
|
+
self,
|
|
1145
|
+
text: "str | None" = None,
|
|
1146
|
+
format: "Literal['html', 'markdown', 'default'] | None" = "default",
|
|
1147
|
+
notify: bool = True,
|
|
1148
|
+
disable_link_preview: bool = False,
|
|
1149
|
+
keyboard: """list[list[buttons.Button]] \
|
|
1150
|
+
| buttons.KeyboardBuilder \
|
|
1151
|
+
| None""" = None,
|
|
1152
|
+
attachments: "list[Attachment] | Attachment | None" = None,
|
|
1153
|
+
) -> "Message | None":
|
|
1154
|
+
"""
|
|
1155
|
+
Send a message to the chat that contains the message
|
|
1156
|
+
with the pressed button.
|
|
1157
|
+
|
|
1158
|
+
:param text: Message text. Up to 4000 characters
|
|
1159
|
+
:param format: Message format. Bot.default_format by default
|
|
1160
|
+
:param notify: Whether to notify users about the message.
|
|
1161
|
+
True by default.
|
|
1162
|
+
:param disable_link_preview: Whether to disable link preview.
|
|
1163
|
+
False by default
|
|
1164
|
+
:param keyboard: An inline keyboard to attach to the message
|
|
1165
|
+
:param attachments: List of attachments
|
|
1166
|
+
"""
|
|
1167
|
+
if self.bot is None:
|
|
1168
|
+
return
|
|
1169
|
+
|
|
1170
|
+
if self.message is None:
|
|
1171
|
+
raise exceptions.AiomaxException("Original message not found")
|
|
1172
|
+
|
|
1173
|
+
return await self.bot.send_message(
|
|
1174
|
+
text,
|
|
1175
|
+
chat_id=self.message.recipient.chat_id,
|
|
1176
|
+
format=format,
|
|
1177
|
+
notify=notify,
|
|
1178
|
+
disable_link_preview=disable_link_preview,
|
|
1179
|
+
keyboard=keyboard,
|
|
1180
|
+
attachments=attachments,
|
|
1181
|
+
)
|
|
1182
|
+
|
|
1183
|
+
async def reply(
|
|
1184
|
+
self,
|
|
1185
|
+
text: "str | None" = None,
|
|
1186
|
+
format: "Literal['html', 'markdown', 'default'] | None" = "default",
|
|
1187
|
+
notify: bool = True,
|
|
1188
|
+
disable_link_preview: bool = False,
|
|
1189
|
+
keyboard: """list[list[buttons.Button]] \
|
|
1190
|
+
| buttons.KeyboardBuilder \
|
|
1191
|
+
| None""" = None,
|
|
1192
|
+
attachments: "list[Attachment] | Attachment | None" = None,
|
|
1193
|
+
) -> "Message | None":
|
|
1194
|
+
"""
|
|
1195
|
+
Reply to the message with the button.
|
|
1196
|
+
|
|
1197
|
+
:param text: Message text. Up to 4000 characters
|
|
1198
|
+
:param format: Message format. Bot.default_format by default
|
|
1199
|
+
:param notify: Whether to notify users about the message.
|
|
1200
|
+
True by default.
|
|
1201
|
+
:param disable_link_preview: Whether to disable link preview.
|
|
1202
|
+
False by default
|
|
1203
|
+
:param keyboard: An inline keyboard to attach to the message
|
|
1204
|
+
:param attachments: List of attachments
|
|
1205
|
+
"""
|
|
1206
|
+
if self.bot is None:
|
|
1207
|
+
return
|
|
1208
|
+
|
|
1209
|
+
if self.message is None:
|
|
1210
|
+
raise exceptions.AiomaxException("Original message not found")
|
|
1211
|
+
|
|
1212
|
+
return await self.bot.send_message(
|
|
1213
|
+
text,
|
|
1214
|
+
chat_id=self.message.recipient.chat_id,
|
|
1215
|
+
format=format,
|
|
1216
|
+
notify=notify,
|
|
1217
|
+
disable_link_preview=disable_link_preview,
|
|
1218
|
+
keyboard=keyboard,
|
|
1219
|
+
attachments=attachments,
|
|
1220
|
+
reply_to=self.message.id,
|
|
1221
|
+
)
|
|
1222
|
+
|
|
1223
|
+
async def answer(
|
|
1224
|
+
self,
|
|
1225
|
+
notification: "str | None" = None,
|
|
1226
|
+
text: "str | None" = None,
|
|
1227
|
+
format: "Literal['html', 'markdown', 'default'] | None" = "default",
|
|
1228
|
+
notify: bool = True,
|
|
1229
|
+
keyboard: """list[list[buttons.Button]] \
|
|
1230
|
+
| buttons.KeyboardBuilder \
|
|
1231
|
+
| None""" = None,
|
|
1232
|
+
attachments: "list[Attachment] | Attachment | None" = None,
|
|
1233
|
+
):
|
|
1234
|
+
"""
|
|
1235
|
+
Answer the callback.
|
|
1236
|
+
|
|
1237
|
+
:param notification: Notification to display to the user
|
|
1238
|
+
:param text: Message text. Up to 4000 characters
|
|
1239
|
+
:param format: Message format. Bot.default_format by default
|
|
1240
|
+
:param notify: Whether to notify users about the message.
|
|
1241
|
+
True by default.
|
|
1242
|
+
:param keyboard: An inline keyboard to attach to the message
|
|
1243
|
+
:param attachments: List of attachments
|
|
1244
|
+
"""
|
|
1245
|
+
if (
|
|
1246
|
+
notification is None
|
|
1247
|
+
and text is None
|
|
1248
|
+
and attachments is None
|
|
1249
|
+
and keyboard is None
|
|
1250
|
+
):
|
|
1251
|
+
raise exceptions.AiomaxException(
|
|
1252
|
+
"Either notification, text or attachments must be specified"
|
|
1253
|
+
)
|
|
1254
|
+
body = {"notification": notification, "message": None}
|
|
1255
|
+
if keyboard is None and self.message is not None:
|
|
1256
|
+
keyboard = [
|
|
1257
|
+
i
|
|
1258
|
+
for i in self.message.body.attachments
|
|
1259
|
+
if i.type == "inline_keyboard"
|
|
1260
|
+
]
|
|
1261
|
+
keyboard = None if len(keyboard) == 0 else keyboard[0].payload
|
|
1262
|
+
|
|
1263
|
+
if text is not None or attachments is not None or keyboard is not None:
|
|
1264
|
+
format = self.bot.default_format if format == "default" else format
|
|
1265
|
+
body["message"] = utils.get_message_body(
|
|
1266
|
+
text,
|
|
1267
|
+
format,
|
|
1268
|
+
notify=notify,
|
|
1269
|
+
keyboard=keyboard,
|
|
1270
|
+
attachments=attachments,
|
|
1271
|
+
)
|
|
1272
|
+
|
|
1273
|
+
out = await self.bot.post(
|
|
1274
|
+
"answers",
|
|
1275
|
+
params={"callback_id": self.callback_id},
|
|
1276
|
+
json=body,
|
|
1277
|
+
)
|
|
1278
|
+
return await out.json()
|
|
1279
|
+
|
|
1280
|
+
@property
|
|
1281
|
+
def user_id(self):
|
|
1282
|
+
return self.user.user_id
|
|
1283
|
+
|
|
1284
|
+
@staticmethod
|
|
1285
|
+
def from_json(
|
|
1286
|
+
data: dict,
|
|
1287
|
+
message: "dict | None",
|
|
1288
|
+
user_locale: "str | None" = None,
|
|
1289
|
+
bot=None,
|
|
1290
|
+
) -> "Callback | None":
|
|
1291
|
+
if data is None:
|
|
1292
|
+
return None
|
|
1293
|
+
|
|
1294
|
+
return Callback(
|
|
1295
|
+
bot,
|
|
1296
|
+
data["timestamp"],
|
|
1297
|
+
data["callback_id"],
|
|
1298
|
+
Message.from_json(message) if message is not None else None,
|
|
1299
|
+
User.from_json(data["user"]),
|
|
1300
|
+
user_locale,
|
|
1301
|
+
data.get("payload"),
|
|
1302
|
+
)
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
class ChatCreatePayload:
|
|
1306
|
+
def __init__(
|
|
1307
|
+
self,
|
|
1308
|
+
timestamp: int,
|
|
1309
|
+
chat: Chat,
|
|
1310
|
+
message_id: "str | None" = None,
|
|
1311
|
+
start_payload: "str | None" = None,
|
|
1312
|
+
):
|
|
1313
|
+
"""
|
|
1314
|
+
Payload that is sent to the `Bot.on_button_chat_create` decorator.
|
|
1315
|
+
|
|
1316
|
+
:param timestamp: Timestamp of the button press
|
|
1317
|
+
:param chat: Created chat
|
|
1318
|
+
:param message_id: Message ID on which the button was
|
|
1319
|
+
:param start_payload: Start payload specified by the button
|
|
1320
|
+
"""
|
|
1321
|
+
self.timestamp: float = timestamp / 1000
|
|
1322
|
+
self.chat: Chat = chat
|
|
1323
|
+
self.message_id: "str | None" = message_id
|
|
1324
|
+
self.start_payload: "str | None" = start_payload
|
|
1325
|
+
|
|
1326
|
+
@staticmethod
|
|
1327
|
+
def from_json(data: dict) -> "ChatCreatePayload | None":
|
|
1328
|
+
if data is None:
|
|
1329
|
+
return None
|
|
1330
|
+
|
|
1331
|
+
return ChatCreatePayload(
|
|
1332
|
+
data["timestamp"],
|
|
1333
|
+
Chat.from_json(data["chat"]),
|
|
1334
|
+
data.get("message_id"),
|
|
1335
|
+
data.get("start_payload"),
|
|
1336
|
+
)
|
|
1337
|
+
|
|
1338
|
+
|
|
1339
|
+
class MessageDeletePayload:
|
|
1340
|
+
def __init__(
|
|
1341
|
+
self,
|
|
1342
|
+
timestamp: int,
|
|
1343
|
+
message: "Message | None" = None,
|
|
1344
|
+
message_id: "str | None" = None,
|
|
1345
|
+
chat_id: "int | None" = None,
|
|
1346
|
+
user_id: "int | None" = None,
|
|
1347
|
+
bot=None,
|
|
1348
|
+
):
|
|
1349
|
+
"""
|
|
1350
|
+
Payload that is sent to the `Bot.on_message_delete` decorator.
|
|
1351
|
+
|
|
1352
|
+
:param timestamp: Timestamp of the message deletion.
|
|
1353
|
+
:param message: Cached Message object.
|
|
1354
|
+
May be None if message was not cached
|
|
1355
|
+
:param message_id: ID of the deleted message
|
|
1356
|
+
:param chat_id: ID of the chat the message was deleted in
|
|
1357
|
+
:param user_id: ID of the user who deleted the message
|
|
1358
|
+
"""
|
|
1359
|
+
self.timestamp: float = timestamp / 1000
|
|
1360
|
+
self.message: "Message | None" = message
|
|
1361
|
+
self.message_id: "str | None" = message_id
|
|
1362
|
+
self.chat_id: "int | None" = chat_id
|
|
1363
|
+
self.user_id: "int | None" = user_id
|
|
1364
|
+
self.bot = bot
|
|
1365
|
+
|
|
1366
|
+
@staticmethod
|
|
1367
|
+
def from_json(data: dict, bot) -> "MessageDeletePayload | None":
|
|
1368
|
+
if data is None:
|
|
1369
|
+
return None
|
|
1370
|
+
|
|
1371
|
+
return MessageDeletePayload(
|
|
1372
|
+
data["timestamp"],
|
|
1373
|
+
(
|
|
1374
|
+
bot.cache.get_message(data.get("message_id"))
|
|
1375
|
+
if bot.cache
|
|
1376
|
+
else None
|
|
1377
|
+
),
|
|
1378
|
+
data.get("message_id"),
|
|
1379
|
+
data.get("chat_id"),
|
|
1380
|
+
data.get("user_id"),
|
|
1381
|
+
bot=bot,
|
|
1382
|
+
)
|
|
1383
|
+
|
|
1384
|
+
@property
|
|
1385
|
+
def content(self) -> "str | None":
|
|
1386
|
+
if self.message is None:
|
|
1387
|
+
return None
|
|
1388
|
+
|
|
1389
|
+
return self.message.content
|
|
1390
|
+
|
|
1391
|
+
|
|
1392
|
+
class ChatTitleEditPayload:
|
|
1393
|
+
def __init__(
|
|
1394
|
+
self,
|
|
1395
|
+
timestamp: int,
|
|
1396
|
+
user: User,
|
|
1397
|
+
chat_id: "int | None" = None,
|
|
1398
|
+
title: "str | None" = None,
|
|
1399
|
+
):
|
|
1400
|
+
"""
|
|
1401
|
+
Payload that is sent to the `Bot.on_chat_title_change` decorator.
|
|
1402
|
+
|
|
1403
|
+
:param timestamp: Timestamp of the title edit.
|
|
1404
|
+
:param user: User that edited the chat name.
|
|
1405
|
+
:param chat_id: Chat ID that had its title edited.
|
|
1406
|
+
:param title: New chat title
|
|
1407
|
+
"""
|
|
1408
|
+
self.timestamp: float = timestamp / 1000
|
|
1409
|
+
self.user: User = user
|
|
1410
|
+
self.chat_id: "int | None" = chat_id
|
|
1411
|
+
self.title: "str | None" = title
|
|
1412
|
+
|
|
1413
|
+
@property
|
|
1414
|
+
def user_id(self):
|
|
1415
|
+
return self.user.user_id
|
|
1416
|
+
|
|
1417
|
+
@staticmethod
|
|
1418
|
+
def from_json(data: dict) -> "ChatTitleEditPayload | None":
|
|
1419
|
+
if data is None:
|
|
1420
|
+
return None
|
|
1421
|
+
|
|
1422
|
+
return ChatTitleEditPayload(
|
|
1423
|
+
data["timestamp"],
|
|
1424
|
+
User.from_json(data["user"]),
|
|
1425
|
+
data.get("chat_id"),
|
|
1426
|
+
data.get("title"),
|
|
1427
|
+
)
|
|
1428
|
+
|
|
1429
|
+
|
|
1430
|
+
class ChatMembershipPayload:
|
|
1431
|
+
def __init__(
|
|
1432
|
+
self,
|
|
1433
|
+
timestamp: int,
|
|
1434
|
+
user: User,
|
|
1435
|
+
chat_id: "int | None" = None,
|
|
1436
|
+
is_channel: bool = False,
|
|
1437
|
+
):
|
|
1438
|
+
"""
|
|
1439
|
+
Payload that is sent to the `Bot.on_bot_add`
|
|
1440
|
+
or `Bot.on_bot_remove` decorator.
|
|
1441
|
+
|
|
1442
|
+
:param timestamp: Timestamp of the action.
|
|
1443
|
+
:param user: User that invited or kicked the bot.
|
|
1444
|
+
:param chat_id: Chat ID that the bot was invited to / kicked from.
|
|
1445
|
+
:param is_channel: Whether the bot got added to / kicked
|
|
1446
|
+
from a channel or not
|
|
1447
|
+
"""
|
|
1448
|
+
self.timestamp: float = timestamp / 1000
|
|
1449
|
+
self.user: User = user
|
|
1450
|
+
self.chat_id: "int | None" = chat_id
|
|
1451
|
+
self.is_channel: bool = is_channel
|
|
1452
|
+
|
|
1453
|
+
@property
|
|
1454
|
+
def user_id(self):
|
|
1455
|
+
return self.user.user_id
|
|
1456
|
+
|
|
1457
|
+
@staticmethod
|
|
1458
|
+
def from_json(data: dict) -> "ChatMembershipPayload | None":
|
|
1459
|
+
if data is None:
|
|
1460
|
+
return None
|
|
1461
|
+
|
|
1462
|
+
return ChatMembershipPayload(
|
|
1463
|
+
data["timestamp"],
|
|
1464
|
+
User.from_json(data["user"]),
|
|
1465
|
+
data.get("chat_id"),
|
|
1466
|
+
data.get("is_channel", False),
|
|
1467
|
+
)
|
|
1468
|
+
|
|
1469
|
+
|
|
1470
|
+
class UserMembershipPayload:
|
|
1471
|
+
def __init__(
|
|
1472
|
+
self,
|
|
1473
|
+
timestamp: int,
|
|
1474
|
+
user: User,
|
|
1475
|
+
chat_id: "int | None" = None,
|
|
1476
|
+
is_channel: bool = False,
|
|
1477
|
+
initiator: "int | None" = None,
|
|
1478
|
+
):
|
|
1479
|
+
"""
|
|
1480
|
+
Payload that is sent to the `Bot.on_user_add` or
|
|
1481
|
+
`Bot.on_user_remove` decorator.
|
|
1482
|
+
|
|
1483
|
+
:param timestamp: Timestamp of the action.
|
|
1484
|
+
:param user: User that joined or left the chat.
|
|
1485
|
+
:param chat_id: Chat ID that the user joined / left.
|
|
1486
|
+
:param is_channel: Whether the user was added to / kicked
|
|
1487
|
+
from a channel or not.
|
|
1488
|
+
:param initiator: User ID of the inviter / kicker,
|
|
1489
|
+
if the user got invited by another user or kicked by an admin.
|
|
1490
|
+
"""
|
|
1491
|
+
self.timestamp: float = timestamp / 1000
|
|
1492
|
+
self.user: User = user
|
|
1493
|
+
self.chat_id: "int | None" = chat_id
|
|
1494
|
+
self.is_channel: bool = is_channel
|
|
1495
|
+
self.initiator: "int | None" = initiator
|
|
1496
|
+
|
|
1497
|
+
@property
|
|
1498
|
+
def user_id(self):
|
|
1499
|
+
return self.user.user_id
|
|
1500
|
+
|
|
1501
|
+
@staticmethod
|
|
1502
|
+
def from_json(data: dict) -> "UserMembershipPayload | None":
|
|
1503
|
+
if data is None:
|
|
1504
|
+
return None
|
|
1505
|
+
|
|
1506
|
+
return UserMembershipPayload(
|
|
1507
|
+
data["timestamp"],
|
|
1508
|
+
User.from_json(data["user"]),
|
|
1509
|
+
data.get("chat_id"),
|
|
1510
|
+
data.get("is_channel", False),
|
|
1511
|
+
data.get("inviter_id", data.get("admin_id")),
|
|
1512
|
+
)
|