botapi-py 1.5.3__tar.gz → 1.5.4__tar.gz
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.
- {botapi_py-1.5.3 → botapi_py-1.5.4}/PKG-INFO +1 -2
- {botapi_py-1.5.3 → botapi_py-1.5.4}/pyproject.toml +2 -2
- botapi_py-1.5.3/src/botapi/dispatchers/__init__.py → botapi_py-1.5.4/src/botapi/dispatchers/__init__.pyi +1 -5
- {botapi_py-1.5.3 → botapi_py-1.5.4}/src/botapi/dispatchers/redis_pubsub_handler.py +15 -11
- {botapi_py-1.5.3 → botapi_py-1.5.4}/src/botapi/enums.py +3 -0
- {botapi_py-1.5.3 → botapi_py-1.5.4}/src/botapi/methods.py +60 -5
- {botapi_py-1.5.3 → botapi_py-1.5.4}/src/botapi/types.py +67 -9
- {botapi_py-1.5.3 → botapi_py-1.5.4}/src/botapi/__init__.py +0 -0
- {botapi_py-1.5.3 → botapi_py-1.5.4}/src/botapi/__init__.pyi +0 -0
- {botapi_py-1.5.3 → botapi_py-1.5.4}/src/botapi/bot.py +0 -0
- {botapi_py-1.5.3 → botapi_py-1.5.4}/src/botapi/core.py +0 -0
- /botapi_py-1.5.3/src/botapi/dispatchers/__init__.pyi → /botapi_py-1.5.4/src/botapi/dispatchers/__init__.py +0 -0
- {botapi_py-1.5.3 → botapi_py-1.5.4}/src/botapi/dispatchers/common.py +0 -0
- {botapi_py-1.5.3 → botapi_py-1.5.4}/src/botapi/dispatchers/get_updates_handler.py +0 -0
- {botapi_py-1.5.3 → botapi_py-1.5.4}/src/botapi/errors.py +0 -0
- {botapi_py-1.5.3 → botapi_py-1.5.4}/src/botapi/internals.py +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: botapi-py
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.4
|
|
4
4
|
Summary: A simple and flexible Python library to conveniently build fast Telegram bots using the Bot API.
|
|
5
5
|
Requires-Dist: httpx>=0.28.1
|
|
6
6
|
Requires-Dist: pydantic>=2.12.0
|
|
7
|
-
Requires-Dist: orjson>=3.11.3 ; extra == 'redis'
|
|
8
7
|
Requires-Dist: redis>=6.4.0 ; extra == 'redis'
|
|
9
8
|
Requires-Python: >=3.12
|
|
10
9
|
Project-URL: Telegram BotAPI Docs, https://core.telegram.org/bots/api
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "botapi-py"
|
|
3
|
-
version = "1.5.
|
|
3
|
+
version = "1.5.4"
|
|
4
4
|
description = "A simple and flexible Python library to conveniently build fast Telegram bots using the Bot API."
|
|
5
5
|
# readme = "README.md"
|
|
6
6
|
# authors = [
|
|
@@ -17,7 +17,6 @@ dependencies = [
|
|
|
17
17
|
|
|
18
18
|
[project.optional-dependencies]
|
|
19
19
|
redis = [
|
|
20
|
-
"orjson>=3.11.3",
|
|
21
20
|
"redis>=6.4.0",
|
|
22
21
|
]
|
|
23
22
|
|
|
@@ -28,6 +27,7 @@ build-backend = "uv_build"
|
|
|
28
27
|
[dependency-groups]
|
|
29
28
|
dev = [
|
|
30
29
|
"bs4>=0.0.2",
|
|
30
|
+
"redis>=6.4.0",
|
|
31
31
|
]
|
|
32
32
|
|
|
33
33
|
[tool.uv.build-backend]
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
from .common import UpdateHandler
|
|
2
2
|
|
|
3
3
|
from .get_updates_handler import GetUpdatesAsyncDispatcher
|
|
4
|
-
|
|
5
|
-
# Fails at runtime if redis-py is not installed
|
|
6
|
-
from .redis_pubsub_handler import RedisPubSubAsyncDispatcher
|
|
7
|
-
except ImportError:
|
|
8
|
-
pass
|
|
4
|
+
from .redis_pubsub_handler import RedisPubSubAsyncDispatcher
|
|
9
5
|
|
|
10
6
|
__all__ = [
|
|
11
7
|
'UpdateHandler',
|
|
@@ -5,23 +5,27 @@ import inspect
|
|
|
5
5
|
import json
|
|
6
6
|
import logging
|
|
7
7
|
import signal
|
|
8
|
-
from typing import override
|
|
8
|
+
from typing import TYPE_CHECKING, override
|
|
9
9
|
|
|
10
|
-
import orjson
|
|
11
10
|
import pydantic
|
|
12
11
|
|
|
13
|
-
from botapi.types import Update
|
|
14
|
-
|
|
15
|
-
from .common import BaseAsyncDispatcher, UpdateHandler
|
|
16
|
-
|
|
17
12
|
try:
|
|
18
13
|
import redis.asyncio as redis
|
|
19
14
|
from redis.asyncio.client import PubSub
|
|
20
15
|
_has_redis = True
|
|
21
16
|
except ImportError as e:
|
|
22
17
|
_has_redis = False
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
# Fix type checker errors,
|
|
19
|
+
# but do not stop the library (with confusing errors) from being used without redis-py installed.
|
|
20
|
+
# Just raise a MEANINGFUL error when the dev tries to create an instance of `RedisPubSubAsyncDispatcher`.
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
raise e
|
|
23
|
+
|
|
24
|
+
from botapi.types import Update
|
|
25
|
+
|
|
26
|
+
from .common import BaseAsyncDispatcher, UpdateHandler
|
|
27
|
+
|
|
28
|
+
|
|
25
29
|
logger = logging.getLogger(__name__)
|
|
26
30
|
|
|
27
31
|
|
|
@@ -29,7 +33,7 @@ class RedisPubSubAsyncDispatcher(BaseAsyncDispatcher):
|
|
|
29
33
|
update_handler: UpdateHandler
|
|
30
34
|
update_handler_argcount: int
|
|
31
35
|
tasks_shutdown_timeout: float
|
|
32
|
-
tasks: set[asyncio.Task] # prevent gc from collecting tasks before they finish
|
|
36
|
+
tasks: set[asyncio.Task[None]] # prevent gc from collecting tasks before they finish
|
|
33
37
|
redis_client: redis.Redis
|
|
34
38
|
redis_reconnect_delay: float
|
|
35
39
|
pubsub: PubSub
|
|
@@ -85,7 +89,7 @@ class RedisPubSubAsyncDispatcher(BaseAsyncDispatcher):
|
|
|
85
89
|
self.pubsub = self.redis_client.pubsub(ignore_subscribe_messages=True)
|
|
86
90
|
self.update_handler_argcount = len(inspect.getfullargspec(update_handler).args)
|
|
87
91
|
|
|
88
|
-
def _task_done_callback(self, task: asyncio.Task) -> None:
|
|
92
|
+
def _task_done_callback(self, task: asyncio.Task[None]) -> None:
|
|
89
93
|
'''
|
|
90
94
|
Callback that is called by the event loop when an update_handler task is done.
|
|
91
95
|
|
|
@@ -106,7 +110,7 @@ class RedisPubSubAsyncDispatcher(BaseAsyncDispatcher):
|
|
|
106
110
|
- extracts the bot token from the specified `self.bot_token_json_key` key
|
|
107
111
|
- validates the update into an `Update` object.
|
|
108
112
|
'''
|
|
109
|
-
data: dict =
|
|
113
|
+
data: dict = json.loads(message)
|
|
110
114
|
|
|
111
115
|
if self.bot_token_json_key not in data:
|
|
112
116
|
err = f'Missing bot token key "{self.bot_token_json_key}" in the update JSON payload'
|
|
@@ -71,6 +71,8 @@ class ServiceMessage(enum.StrEnum):
|
|
|
71
71
|
'''
|
|
72
72
|
NEW_CHAT_MEMBERS = 'new_chat_members'
|
|
73
73
|
LEFT_CHAT_MEMBER = 'left_chat_member'
|
|
74
|
+
CHAT_OWNER_LEFT = 'chat_owner_left'
|
|
75
|
+
CHAT_OWNER_CHANGED = 'chat_owner_changed'
|
|
74
76
|
NEW_CHAT_TITLE = 'new_chat_title'
|
|
75
77
|
NEW_CHAT_PHOTO = 'new_chat_photo'
|
|
76
78
|
DELETE_CHAT_PHOTO = 'delete_chat_photo'
|
|
@@ -119,6 +121,7 @@ class ServiceMessage(enum.StrEnum):
|
|
|
119
121
|
WEB_APP_DATA = 'web_app_data'
|
|
120
122
|
SUGGESTED_POST = 'suggested_post_info'
|
|
121
123
|
|
|
124
|
+
|
|
122
125
|
class OtherMessageType(enum.StrEnum):
|
|
123
126
|
'''
|
|
124
127
|
Subset of `MessageType`.
|
|
@@ -38,6 +38,7 @@ from .types import (
|
|
|
38
38
|
Audio,
|
|
39
39
|
Document,
|
|
40
40
|
Story,
|
|
41
|
+
VideoQuality,
|
|
41
42
|
Video,
|
|
42
43
|
VideoNote,
|
|
43
44
|
Voice,
|
|
@@ -111,6 +112,7 @@ from .types import (
|
|
|
111
112
|
SuggestedPostParameters,
|
|
112
113
|
DirectMessagesTopic,
|
|
113
114
|
UserProfilePhotos,
|
|
115
|
+
UserProfileAudios,
|
|
114
116
|
File,
|
|
115
117
|
WebAppInfo,
|
|
116
118
|
ReplyKeyboardMarkup,
|
|
@@ -217,6 +219,8 @@ from .types import (
|
|
|
217
219
|
ChatBoost,
|
|
218
220
|
ChatBoostUpdated,
|
|
219
221
|
ChatBoostRemoved,
|
|
222
|
+
ChatOwnerLeft,
|
|
223
|
+
ChatOwnerChanged,
|
|
220
224
|
UserChatBoosts,
|
|
221
225
|
BusinessBotRights,
|
|
222
226
|
BusinessConnection,
|
|
@@ -1541,6 +1545,27 @@ class Methods(core.BaseBot):
|
|
|
1541
1545
|
})
|
|
1542
1546
|
return UserProfilePhotos.model_validate(response)
|
|
1543
1547
|
|
|
1548
|
+
async def get_user_profile_audios(
|
|
1549
|
+
self,
|
|
1550
|
+
user_id: int,
|
|
1551
|
+
offset: Optional[int] = None,
|
|
1552
|
+
limit: Optional[int] = None,
|
|
1553
|
+
) -> UserProfileAudios:
|
|
1554
|
+
'''
|
|
1555
|
+
Use this method to get a list of
|
|
1556
|
+
profile audios for a user. Returns a UserProfileAudios
|
|
1557
|
+
object.
|
|
1558
|
+
|
|
1559
|
+
Reference: https://core.telegram.org/bots/api#getuserprofileaudios
|
|
1560
|
+
'''
|
|
1561
|
+
|
|
1562
|
+
response = await self._send_request('getUserProfileAudios', {
|
|
1563
|
+
'user_id': user_id,
|
|
1564
|
+
'offset': offset,
|
|
1565
|
+
'limit': limit,
|
|
1566
|
+
})
|
|
1567
|
+
return UserProfileAudios.model_validate(response)
|
|
1568
|
+
|
|
1544
1569
|
async def set_user_emoji_status(
|
|
1545
1570
|
self,
|
|
1546
1571
|
user_id: int,
|
|
@@ -2326,11 +2351,12 @@ class Methods(core.BaseBot):
|
|
|
2326
2351
|
) -> ForumTopic:
|
|
2327
2352
|
'''
|
|
2328
2353
|
Use this method to create a topic in
|
|
2329
|
-
a forum supergroup chat
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2354
|
+
a forum supergroup chat or a private chat
|
|
2355
|
+
with a user. In the case of a
|
|
2356
|
+
supergroup chat the bot must be an administrator
|
|
2357
|
+
in the chat for this to work and
|
|
2358
|
+
must have the can_manage_topics administrator right. Returns information
|
|
2359
|
+
about the created topic as a ForumTopic object.
|
|
2334
2360
|
|
|
2335
2361
|
Reference: https://core.telegram.org/bots/api#createforumtopic
|
|
2336
2362
|
'''
|
|
@@ -2818,6 +2844,35 @@ class Methods(core.BaseBot):
|
|
|
2818
2844
|
})
|
|
2819
2845
|
return BotShortDescription.model_validate(response)
|
|
2820
2846
|
|
|
2847
|
+
async def set_my_profile_photo(
|
|
2848
|
+
self,
|
|
2849
|
+
photo: _InputProfilePhoto,
|
|
2850
|
+
) -> bool:
|
|
2851
|
+
'''
|
|
2852
|
+
Changes the profile photo of the bot. Returns
|
|
2853
|
+
True on success.
|
|
2854
|
+
|
|
2855
|
+
Reference: https://core.telegram.org/bots/api#setmyprofilephoto
|
|
2856
|
+
'''
|
|
2857
|
+
|
|
2858
|
+
response = await self._send_request('setMyProfilePhoto', {
|
|
2859
|
+
'photo': photo,
|
|
2860
|
+
})
|
|
2861
|
+
return BoolAdapter.validate_python(response)
|
|
2862
|
+
|
|
2863
|
+
async def remove_my_profile_photo(
|
|
2864
|
+
self,
|
|
2865
|
+
) -> bool:
|
|
2866
|
+
'''
|
|
2867
|
+
Removes the profile photo of the bot. Requires
|
|
2868
|
+
no parameters. Returns True on success.
|
|
2869
|
+
|
|
2870
|
+
Reference: https://core.telegram.org/bots/api#removemyprofilephoto
|
|
2871
|
+
'''
|
|
2872
|
+
|
|
2873
|
+
response = await self._send_request('removeMyProfilePhoto', {})
|
|
2874
|
+
return BoolAdapter.validate_python(response)
|
|
2875
|
+
|
|
2821
2876
|
async def set_chat_menu_button(
|
|
2822
2877
|
self,
|
|
2823
2878
|
chat_id: Optional[int] = None,
|
|
@@ -81,6 +81,8 @@ class Message(MaybeInaccessibleMessage):
|
|
|
81
81
|
location: Optional[Location] = Field(default=None)
|
|
82
82
|
new_chat_members: Optional[List[User]] = Field(default=None)
|
|
83
83
|
left_chat_member: Optional[User] = Field(default=None)
|
|
84
|
+
chat_owner_left: Optional[ChatOwnerLeft] = Field(default=None)
|
|
85
|
+
chat_owner_changed: Optional[ChatOwnerChanged] = Field(default=None)
|
|
84
86
|
new_chat_title: Optional[str] = Field(default=None)
|
|
85
87
|
new_chat_photo: Optional[List[PhotoSize]] = Field(default=None)
|
|
86
88
|
delete_chat_photo: Optional[bool] = Field(default=None)
|
|
@@ -2275,7 +2277,8 @@ class User(BaseModel):
|
|
|
2275
2277
|
supports_inline_queries: Optional[bool] = Field(default=None)
|
|
2276
2278
|
can_connect_to_business: Optional[bool] = Field(default=None)
|
|
2277
2279
|
has_main_web_app: Optional[bool] = Field(default=None)
|
|
2278
|
-
has_topics_enabled: Optional[bool] = Field(default=None)
|
|
2280
|
+
has_topics_enabled: Optional[bool] = Field(default=None)
|
|
2281
|
+
allows_users_to_create_topics: Optional[bool] = Field(default=None)
|
|
2279
2282
|
|
|
2280
2283
|
class Chat(BaseModel):
|
|
2281
2284
|
'''
|
|
@@ -2348,6 +2351,7 @@ class ChatFullInfo(BaseModel):
|
|
|
2348
2351
|
linked_chat_id: Optional[int] = Field(default=None)
|
|
2349
2352
|
location: Optional[ChatLocation] = Field(default=None)
|
|
2350
2353
|
rating: Optional[UserRating] = Field(default=None)
|
|
2354
|
+
first_profile_audio: Optional[Audio] = Field(default=None)
|
|
2351
2355
|
unique_gift_colors: Optional[UniqueGiftColors] = Field(default=None)
|
|
2352
2356
|
paid_message_star_count: Optional[int] = Field(default=None)
|
|
2353
2357
|
|
|
@@ -2536,6 +2540,21 @@ class Story(BaseModel):
|
|
|
2536
2540
|
chat: Chat
|
|
2537
2541
|
id: int
|
|
2538
2542
|
|
|
2543
|
+
class VideoQuality(BaseModel):
|
|
2544
|
+
'''
|
|
2545
|
+
This object represents a video file of a
|
|
2546
|
+
specific quality.
|
|
2547
|
+
|
|
2548
|
+
Reference: https://core.telegram.org/bots/api#videoquality
|
|
2549
|
+
'''
|
|
2550
|
+
|
|
2551
|
+
file_id: str
|
|
2552
|
+
file_unique_id: str
|
|
2553
|
+
width: int
|
|
2554
|
+
height: int
|
|
2555
|
+
codec: str
|
|
2556
|
+
file_size: Optional[int] = Field(default=None)
|
|
2557
|
+
|
|
2539
2558
|
class Video(BaseModel):
|
|
2540
2559
|
'''
|
|
2541
2560
|
This object represents a video file.
|
|
@@ -2551,6 +2570,7 @@ class Video(BaseModel):
|
|
|
2551
2570
|
thumbnail: Optional[PhotoSize] = Field(default=None)
|
|
2552
2571
|
cover: Optional[List[PhotoSize]] = Field(default=None)
|
|
2553
2572
|
start_timestamp: Optional[int] = Field(default=None)
|
|
2573
|
+
qualities: Optional[List[VideoQuality]] = Field(default=None)
|
|
2554
2574
|
file_name: Optional[str] = Field(default=None)
|
|
2555
2575
|
mime_type: Optional[str] = Field(default=None)
|
|
2556
2576
|
file_size: Optional[int] = Field(default=None)
|
|
@@ -3214,6 +3234,17 @@ class UserProfilePhotos(BaseModel):
|
|
|
3214
3234
|
total_count: int
|
|
3215
3235
|
photos: List[List[PhotoSize]]
|
|
3216
3236
|
|
|
3237
|
+
class UserProfileAudios(BaseModel):
|
|
3238
|
+
'''
|
|
3239
|
+
This object represents the audios displayed on a
|
|
3240
|
+
user's profile.
|
|
3241
|
+
|
|
3242
|
+
Reference: https://core.telegram.org/bots/api#userprofileaudios
|
|
3243
|
+
'''
|
|
3244
|
+
|
|
3245
|
+
total_count: int
|
|
3246
|
+
audios: List[Audio]
|
|
3247
|
+
|
|
3217
3248
|
class File(BaseModel):
|
|
3218
3249
|
'''
|
|
3219
3250
|
This object represents a file ready to be
|
|
@@ -3304,16 +3335,18 @@ class ReplyKeyboardMarkup(BaseModel):
|
|
|
3304
3335
|
class KeyboardButton(BaseModel):
|
|
3305
3336
|
'''
|
|
3306
3337
|
This object represents one button of the reply
|
|
3307
|
-
keyboard. At most one of the
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
button text.
|
|
3338
|
+
keyboard. At most one of the fields other
|
|
3339
|
+
than text, icon_custom_emoji_id, and style must be used
|
|
3340
|
+
to specify the type of the button. For
|
|
3341
|
+
simple text buttons, String can be used instead
|
|
3342
|
+
of this object to specify the button text.
|
|
3312
3343
|
|
|
3313
3344
|
Reference: https://core.telegram.org/bots/api#keyboardbutton
|
|
3314
3345
|
'''
|
|
3315
3346
|
|
|
3316
3347
|
text: str
|
|
3348
|
+
icon_custom_emoji_id: Optional[str] = Field(default=None)
|
|
3349
|
+
style: Optional[str] = Field(default=None)
|
|
3317
3350
|
request_users: Optional[KeyboardButtonRequestUsers] = Field(default=None)
|
|
3318
3351
|
request_chat: Optional[KeyboardButtonRequestChat] = Field(default=None)
|
|
3319
3352
|
request_contact: Optional[bool] = Field(default=None)
|
|
@@ -3420,13 +3453,16 @@ class InlineKeyboardMarkup(BaseModel):
|
|
|
3420
3453
|
class InlineKeyboardButton(BaseModel):
|
|
3421
3454
|
'''
|
|
3422
3455
|
This object represents one button of an inline
|
|
3423
|
-
keyboard. Exactly one of the
|
|
3424
|
-
|
|
3456
|
+
keyboard. Exactly one of the fields other than
|
|
3457
|
+
text, icon_custom_emoji_id, and style must be used to
|
|
3458
|
+
specify the type of the button.
|
|
3425
3459
|
|
|
3426
3460
|
Reference: https://core.telegram.org/bots/api#inlinekeyboardbutton
|
|
3427
3461
|
'''
|
|
3428
3462
|
|
|
3429
3463
|
text: str
|
|
3464
|
+
icon_custom_emoji_id: Optional[str] = Field(default=None)
|
|
3465
|
+
style: Optional[str] = Field(default=None)
|
|
3430
3466
|
url: Optional[str] = Field(default=None)
|
|
3431
3467
|
callback_data: Optional[str] = Field(default=None)
|
|
3432
3468
|
web_app: Optional[WebAppInfo] = Field(default=None)
|
|
@@ -3878,7 +3914,8 @@ class UniqueGiftModel(BaseModel):
|
|
|
3878
3914
|
|
|
3879
3915
|
name: str
|
|
3880
3916
|
sticker: Sticker
|
|
3881
|
-
rarity_per_mille: int
|
|
3917
|
+
rarity_per_mille: int
|
|
3918
|
+
rarity: Optional[str] = Field(default=None)
|
|
3882
3919
|
|
|
3883
3920
|
class UniqueGiftSymbol(BaseModel):
|
|
3884
3921
|
'''
|
|
@@ -3949,6 +3986,7 @@ class UniqueGift(BaseModel):
|
|
|
3949
3986
|
symbol: UniqueGiftSymbol
|
|
3950
3987
|
backdrop: UniqueGiftBackdrop
|
|
3951
3988
|
is_premium: Optional[bool] = Field(default=None)
|
|
3989
|
+
is_burned: Optional[bool] = Field(default=None)
|
|
3952
3990
|
is_from_blockchain: Optional[bool] = Field(default=None)
|
|
3953
3991
|
colors: Optional[UniqueGiftColors] = Field(default=None)
|
|
3954
3992
|
publisher_chat: Optional[Chat] = Field(default=None)
|
|
@@ -4098,6 +4136,26 @@ class ChatBoostRemoved(BaseModel):
|
|
|
4098
4136
|
remove_date: int
|
|
4099
4137
|
source: _ChatBoostSource
|
|
4100
4138
|
|
|
4139
|
+
class ChatOwnerLeft(BaseModel):
|
|
4140
|
+
'''
|
|
4141
|
+
Describes a service message about the chat owner
|
|
4142
|
+
leaving the chat.
|
|
4143
|
+
|
|
4144
|
+
Reference: https://core.telegram.org/bots/api#chatownerleft
|
|
4145
|
+
'''
|
|
4146
|
+
|
|
4147
|
+
new_owner: Optional[User] = Field(default=None)
|
|
4148
|
+
|
|
4149
|
+
class ChatOwnerChanged(BaseModel):
|
|
4150
|
+
'''
|
|
4151
|
+
Describes a service message about an ownership change
|
|
4152
|
+
in the chat.
|
|
4153
|
+
|
|
4154
|
+
Reference: https://core.telegram.org/bots/api#chatownerchanged
|
|
4155
|
+
'''
|
|
4156
|
+
|
|
4157
|
+
new_owner: User
|
|
4158
|
+
|
|
4101
4159
|
class UserChatBoosts(BaseModel):
|
|
4102
4160
|
'''
|
|
4103
4161
|
This object represents a list of boosts added
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|