botapi-py 1.5.2__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.
@@ -1,10 +1,9 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: botapi-py
3
- Version: 1.5.2
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.2"
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
- try:
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
- raise e
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 = orjson.loads(message)
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. The bot must be
2330
- an administrator in the chat for this to
2331
- work and must have the can_manage_topics administrator rights.
2332
- Returns information about the created topic as a
2333
- ForumTopic object.
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,
@@ -9,8 +9,9 @@ import uuid
9
9
  from typing import TYPE_CHECKING, Union, Optional, Literal, List, Any, overload
10
10
 
11
11
  from pydantic import BaseModel, Field, TypeAdapter, model_serializer, SerializerFunctionWrapHandler, SerializationInfo
12
+
13
+ from . import enums
12
14
  if TYPE_CHECKING:
13
- from . import enums
14
15
  from . import bot
15
16
  from . import internals
16
17
 
@@ -80,6 +81,8 @@ class Message(MaybeInaccessibleMessage):
80
81
  location: Optional[Location] = Field(default=None)
81
82
  new_chat_members: Optional[List[User]] = Field(default=None)
82
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)
83
86
  new_chat_title: Optional[str] = Field(default=None)
84
87
  new_chat_photo: Optional[List[PhotoSize]] = Field(default=None)
85
88
  delete_chat_photo: Optional[bool] = Field(default=None)
@@ -2274,7 +2277,8 @@ class User(BaseModel):
2274
2277
  supports_inline_queries: Optional[bool] = Field(default=None)
2275
2278
  can_connect_to_business: Optional[bool] = Field(default=None)
2276
2279
  has_main_web_app: Optional[bool] = Field(default=None)
2277
- 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)
2278
2282
 
2279
2283
  class Chat(BaseModel):
2280
2284
  '''
@@ -2347,6 +2351,7 @@ class ChatFullInfo(BaseModel):
2347
2351
  linked_chat_id: Optional[int] = Field(default=None)
2348
2352
  location: Optional[ChatLocation] = Field(default=None)
2349
2353
  rating: Optional[UserRating] = Field(default=None)
2354
+ first_profile_audio: Optional[Audio] = Field(default=None)
2350
2355
  unique_gift_colors: Optional[UniqueGiftColors] = Field(default=None)
2351
2356
  paid_message_star_count: Optional[int] = Field(default=None)
2352
2357
 
@@ -2535,6 +2540,21 @@ class Story(BaseModel):
2535
2540
  chat: Chat
2536
2541
  id: int
2537
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
+
2538
2558
  class Video(BaseModel):
2539
2559
  '''
2540
2560
  This object represents a video file.
@@ -2550,6 +2570,7 @@ class Video(BaseModel):
2550
2570
  thumbnail: Optional[PhotoSize] = Field(default=None)
2551
2571
  cover: Optional[List[PhotoSize]] = Field(default=None)
2552
2572
  start_timestamp: Optional[int] = Field(default=None)
2573
+ qualities: Optional[List[VideoQuality]] = Field(default=None)
2553
2574
  file_name: Optional[str] = Field(default=None)
2554
2575
  mime_type: Optional[str] = Field(default=None)
2555
2576
  file_size: Optional[int] = Field(default=None)
@@ -3213,6 +3234,17 @@ class UserProfilePhotos(BaseModel):
3213
3234
  total_count: int
3214
3235
  photos: List[List[PhotoSize]]
3215
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
+
3216
3248
  class File(BaseModel):
3217
3249
  '''
3218
3250
  This object represents a file ready to be
@@ -3303,16 +3335,18 @@ class ReplyKeyboardMarkup(BaseModel):
3303
3335
  class KeyboardButton(BaseModel):
3304
3336
  '''
3305
3337
  This object represents one button of the reply
3306
- keyboard. At most one of the optional fields
3307
- must be used to specify type of the
3308
- button. For simple text buttons, String can be
3309
- used instead of this object to specify the
3310
- 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.
3311
3343
 
3312
3344
  Reference: https://core.telegram.org/bots/api#keyboardbutton
3313
3345
  '''
3314
3346
 
3315
3347
  text: str
3348
+ icon_custom_emoji_id: Optional[str] = Field(default=None)
3349
+ style: Optional[str] = Field(default=None)
3316
3350
  request_users: Optional[KeyboardButtonRequestUsers] = Field(default=None)
3317
3351
  request_chat: Optional[KeyboardButtonRequestChat] = Field(default=None)
3318
3352
  request_contact: Optional[bool] = Field(default=None)
@@ -3419,13 +3453,16 @@ class InlineKeyboardMarkup(BaseModel):
3419
3453
  class InlineKeyboardButton(BaseModel):
3420
3454
  '''
3421
3455
  This object represents one button of an inline
3422
- keyboard. Exactly one of the optional fields must
3423
- be used to specify type of the button.
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.
3424
3459
 
3425
3460
  Reference: https://core.telegram.org/bots/api#inlinekeyboardbutton
3426
3461
  '''
3427
3462
 
3428
3463
  text: str
3464
+ icon_custom_emoji_id: Optional[str] = Field(default=None)
3465
+ style: Optional[str] = Field(default=None)
3429
3466
  url: Optional[str] = Field(default=None)
3430
3467
  callback_data: Optional[str] = Field(default=None)
3431
3468
  web_app: Optional[WebAppInfo] = Field(default=None)
@@ -3877,7 +3914,8 @@ class UniqueGiftModel(BaseModel):
3877
3914
 
3878
3915
  name: str
3879
3916
  sticker: Sticker
3880
- rarity_per_mille: int
3917
+ rarity_per_mille: int
3918
+ rarity: Optional[str] = Field(default=None)
3881
3919
 
3882
3920
  class UniqueGiftSymbol(BaseModel):
3883
3921
  '''
@@ -3948,6 +3986,7 @@ class UniqueGift(BaseModel):
3948
3986
  symbol: UniqueGiftSymbol
3949
3987
  backdrop: UniqueGiftBackdrop
3950
3988
  is_premium: Optional[bool] = Field(default=None)
3989
+ is_burned: Optional[bool] = Field(default=None)
3951
3990
  is_from_blockchain: Optional[bool] = Field(default=None)
3952
3991
  colors: Optional[UniqueGiftColors] = Field(default=None)
3953
3992
  publisher_chat: Optional[Chat] = Field(default=None)
@@ -4097,6 +4136,26 @@ class ChatBoostRemoved(BaseModel):
4097
4136
  remove_date: int
4098
4137
  source: _ChatBoostSource
4099
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
+
4100
4159
  class UserChatBoosts(BaseModel):
4101
4160
  '''
4102
4161
  This object represents a list of boosts added
File without changes
File without changes