botapi-py 1.4.14__tar.gz → 1.5.1__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,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: botapi-py
3
- Version: 1.4.14
3
+ Version: 1.5.1
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "botapi-py"
3
- version = "1.4.14"
3
+ version = "1.5.1"
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 = [
@@ -0,0 +1,4 @@
1
+ from . import enums, errors, types, dispatchers
2
+ from .bot import Bot
3
+
4
+ __all__ = ['Bot', 'types', 'enums', 'errors', 'dispatchers']
@@ -0,0 +1,4 @@
1
+ from . import enums, errors, types, dispatchers
2
+ from .bot import Bot
3
+
4
+ __all__ = ['Bot', 'types', 'enums', 'errors', 'dispatchers']
@@ -1,17 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Optional, Union, List, cast
3
+ from typing import Optional, Union, List
4
4
 
5
5
  from . import enums
6
6
  from . import types
7
-
8
7
  from . import methods
9
8
 
10
9
 
11
- class BotShortcuts:
10
+ class Bot(methods.Methods):
12
11
  '''
13
- Mixin class containing shortcut bound methods for the `Bot` class.
14
- These methods provide convenient wrappers around standard API methods.
12
+ A (very lightweight) instance of a Telegram Bot.
13
+ Use this to call API methods and shortcut bound methods.
14
+
15
+ _Remember to pass an existing `httpx.AsyncClient` instance
16
+ to avoid significant performance degradation in production._
15
17
  '''
16
18
 
17
19
  async def send_media(
@@ -20,7 +22,7 @@ class BotShortcuts:
20
22
  media: Union[types.InputFile, str, None],
21
23
  file_type: Optional[enums.MediaType | str],
22
24
  caption: Optional[str] = None,
23
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
25
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
24
26
  caption_entities: Optional[List[types.MessageEntity]] = None,
25
27
  show_caption_above_media: Optional[bool] = None,
26
28
  has_spoiler: Optional[bool] = None,
@@ -35,8 +37,8 @@ class BotShortcuts:
35
37
  disable_content_type_detection: Optional[bool] = None, # Documents only
36
38
  ) -> types.Message:
37
39
  '''
38
- Method that sends common types of media by automatically routing to the correct `send_<type>` method.
39
- Also supports send_message with text only for convenience.
40
+ Shortcut method that sends common types of media by automatically routing to the correct `send_<type>` method.
41
+ Also supports `send_message` for plaintext-only messages for convenience.
40
42
 
41
43
  :param chat_id: Unique identifier for the target chat or username of the target channel.
42
44
  :param media: File to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data.
@@ -57,7 +59,6 @@ class BotShortcuts:
57
59
  :param disable_content_type_detection: Disables automatic server-side content type detection.
58
60
  :return: On success, the sent Message is returned.
59
61
  '''
60
- self = cast(methods.Bot, self)
61
62
 
62
63
  # Plain text message
63
64
  if not media or not file_type:
@@ -66,7 +67,7 @@ class BotShortcuts:
66
67
 
67
68
  return await self.send_message(
68
69
  chat_id=chat_id,
69
- # =media,
70
+ # media=media,
70
71
  text=caption,
71
72
  parse_mode=parse_mode,
72
73
  entities=caption_entities,
@@ -237,7 +238,7 @@ class BotShortcuts:
237
238
  # show_caption_above_media=show_caption_above_media
238
239
  )
239
240
 
240
- case enums.MediaType.STORY, enums.MediaType.PAID_MEDIA:
241
+ case enums.MediaType.STORY | enums.MediaType.PAID_MEDIA:
241
242
  raise ValueError(f'Unsupported media type: {file_type}')
242
243
 
243
244
  case _:
@@ -0,0 +1,105 @@
1
+ from __future__ import annotations
2
+
3
+ from . import errors
4
+ from . import internals
5
+
6
+ from typing import Any, Optional, Dict
7
+ from pydantic import TypeAdapter
8
+
9
+ import logging
10
+ import httpx
11
+ import json
12
+
13
+ log = logging.getLogger(__name__)
14
+
15
+ class BaseBot:
16
+ def __init__(
17
+ self,
18
+ token: str,
19
+ api_url: str = 'https://api.telegram.org',
20
+ session: Optional[httpx.AsyncClient] = None,
21
+ test_server: bool = False,
22
+ ):
23
+ self.token: str = token.removeprefix('bot')
24
+ self.api_url: str = api_url
25
+ self.session: httpx.AsyncClient = session or httpx.AsyncClient(timeout=120)
26
+ self.test_server: bool = test_server
27
+
28
+ if not session:
29
+ log.warning('No HTTP session provided, creating a new one.')
30
+
31
+ @property
32
+ def bot_id(self) -> int:
33
+ '''
34
+ Returns the bot's user ID by extracting it from the token.
35
+ '''
36
+ return int(self.token.split(':')[0])
37
+
38
+ def _compose_api_url(self, method: str) -> str:
39
+ url = f'{self.api_url}/bot{self.token}/'
40
+ if self.test_server:
41
+ url += 'test/'
42
+ url += method
43
+ return url
44
+
45
+ def _compose_api_file_url(self, file_path: str) -> str:
46
+ '''
47
+ Composes the URL for file downloads from the Telegram Bot API.
48
+ '''
49
+ # https://api.telegram.org/file/bot<token>/<file_path>
50
+ # https://api.telegram.org/file/bot<token>/test/<file_path>
51
+ url = f'{self.api_url}/file/bot{self.token}/'
52
+ if self.test_server:
53
+ url += 'test/'
54
+ url += file_path
55
+ return url
56
+
57
+ def _convert_data(self, data: dict[str, Any]) -> tuple[dict[str, Any], internals.MultipartFiles]:
58
+ '''
59
+ Converts the dictionary of class instances and raw types into a format
60
+ suitable to be sent to the Telegram Bot API through an HTTP request, with POST data and multipart files.
61
+ '''
62
+ files: internals.MultipartFiles = {} # (filename, file_content, mime_type)
63
+
64
+ # We use a TypeAdapter to dump the entire dict tree.
65
+ # Pydantic `mode='json'` automatically converts sub-models to dicts and primitives.
66
+ # The `context` is passed down to every node, and it allows, for example,
67
+ # the custom `InputFile` serializer to give us access to the multipart data,
68
+ # and just serialize as the appropriate `attach://attach_name` string instead of using weird hacks.
69
+ serialized_data: dict[str, Any] = TypeAdapter(dict[str, Any]).dump_python(
70
+ data,
71
+ mode='json',
72
+ exclude_none=True,
73
+ context={
74
+ 'files': files
75
+ }
76
+ )
77
+
78
+ # Telegram requires root-level lists/dicts to be JSON encoded strings.
79
+ for key, value in serialized_data.items():
80
+ if isinstance(value, (dict, list)):
81
+ serialized_data[key] = json.dumps(value)
82
+
83
+ return serialized_data, files
84
+
85
+ async def _send_request(self, method: str, data: Dict[str, Any]) -> Any:
86
+ '''
87
+ Sends a POST request to the Telegram Bot API.
88
+ '''
89
+ converted_data, files = self._convert_data(data)
90
+
91
+ request = await self.session.post(
92
+ url=self._compose_api_url(method),
93
+ data=converted_data,
94
+ files=files or None
95
+ )
96
+ result: dict[str, Any] = request.json()
97
+
98
+ if not result['ok']:
99
+ raise errors.BotAPIError(
100
+ method=method,
101
+ response=result
102
+ )
103
+
104
+ return result['result']
105
+
@@ -20,7 +20,7 @@ class GetUpdatesAsyncDispatcher(BaseAsyncDispatcher):
20
20
  update_handler_argcount: int
21
21
  tasks_shutdown_timeout: float
22
22
  tasks: set[asyncio.Task] # prevent gc from collecting tasks before they finish
23
- bot: botapi.methods.Bot
23
+ bot: botapi.Bot
24
24
  reconnect_delay: float
25
25
  allowed_updates: list[botapi.enums.UpdateType]
26
26
  offset: int
@@ -28,7 +28,7 @@ class GetUpdatesAsyncDispatcher(BaseAsyncDispatcher):
28
28
  def __init__(
29
29
  self,
30
30
  update_handler: UpdateHandler,
31
- bot: botapi.methods.Bot,
31
+ bot: botapi.Bot,
32
32
  allowed_updates: list[botapi.enums.UpdateType],
33
33
  reconnect_delay: float = 5,
34
34
  shutdown_wait_timeout: float = 3
@@ -138,7 +138,7 @@ class GetUpdatesAsyncDispatcher(BaseAsyncDispatcher):
138
138
 
139
139
  except httpx.RequestError as e:
140
140
  # logger.exception('Request error:')
141
- logger.error('Request error, retrying soon: %s', e)
141
+ logger.error('Request error, retrying soon: %r', e)
142
142
  await asyncio.sleep(delay=self.reconnect_delay)
143
143
 
144
144
  except asyncio.CancelledError:
@@ -52,13 +52,13 @@ class MediaType(enum.StrEnum):
52
52
  ANIMATION = 'animation'
53
53
  AUDIO = 'audio'
54
54
  DOCUMENT = 'document'
55
- PHOTO = 'photo'
55
+ PHOTO = 'photo' # this is a list of `PhotoSize` objects
56
56
  STICKER = 'sticker'
57
57
  VIDEO = 'video'
58
58
  VIDEO_NOTE = 'video_note'
59
59
  VOICE = 'voice'
60
60
  STORY = 'story' # does not have `file_id`, only `id`
61
- PAID_MEDIA = 'paid_media'
61
+ PAID_MEDIA = 'paid_media' # this contains a `photo` attribute that is a list of `PhotoSize` objects
62
62
 
63
63
  class ServiceMessage(enum.StrEnum):
64
64
  '''
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional, TypeAlias
4
+
5
+ MultipartFile: TypeAlias = tuple[
6
+ str | None,
7
+ bytes,
8
+ Optional[str]
9
+ ] # (filename, file_content, mime_type)
10
+
11
+ MultipartFiles: TypeAlias = dict[str, MultipartFile]
@@ -10,8 +10,6 @@ from . import core
10
10
 
11
11
  from . import enums
12
12
 
13
- from .shortcuts import BotShortcuts
14
-
15
13
  from .types import (
16
14
  Update,
17
15
  WebhookInfo,
@@ -321,20 +319,13 @@ from .types import (
321
319
  BoolAdapter,
322
320
  )
323
321
 
324
- class Bot(core.BaseBot, BotShortcuts):
325
- '''
326
- Creates an instance of a new Bot API client.
327
-
328
- _Remember to pass an existing `httpx.AsyncClient` instance
329
- to avoid significant performance degradation in production._
330
- '''
331
-
322
+ class Methods(core.BaseBot):
332
323
  async def get_updates(
333
324
  self,
334
325
  offset: Optional[int] = None,
335
326
  limit: Optional[int] = None,
336
327
  timeout: Optional[int] = None,
337
- allowed_updates: Optional[List[enums.UpdateType]] = None,
328
+ allowed_updates: Optional[Union[List[str], List[enums.UpdateType]]] = None,
338
329
  ) -> List[Update]:
339
330
  '''
340
331
  Use this method to receive incoming updates using
@@ -358,7 +349,7 @@ class Bot(core.BaseBot, BotShortcuts):
358
349
  certificate: Optional[InputFile] = None,
359
350
  ip_address: Optional[str] = None,
360
351
  max_connections: Optional[int] = None,
361
- allowed_updates: Optional[List[enums.UpdateType]] = None,
352
+ allowed_updates: Optional[Union[List[str], List[enums.UpdateType]]] = None,
362
353
  drop_pending_updates: Optional[bool] = None,
363
354
  secret_token: Optional[str] = None,
364
355
  ) -> bool:
@@ -480,7 +471,7 @@ class Bot(core.BaseBot, BotShortcuts):
480
471
  business_connection_id: Optional[str] = None,
481
472
  message_thread_id: Optional[int] = None,
482
473
  direct_messages_topic_id: Optional[int] = None,
483
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
474
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
484
475
  entities: Optional[List[MessageEntity]] = None,
485
476
  link_preview_options: Optional[LinkPreviewOptions] = None,
486
477
  disable_notification: Optional[bool] = None,
@@ -504,7 +495,7 @@ class Bot(core.BaseBot, BotShortcuts):
504
495
  'message_thread_id': message_thread_id,
505
496
  'direct_messages_topic_id': direct_messages_topic_id,
506
497
  'text': text,
507
- 'parse_mode': parse_mode if not entities else None, # FIX,
498
+ 'parse_mode': parse_mode,
508
499
  'entities': entities,
509
500
  'link_preview_options': link_preview_options,
510
501
  'disable_notification': disable_notification,
@@ -595,7 +586,7 @@ class Bot(core.BaseBot, BotShortcuts):
595
586
  direct_messages_topic_id: Optional[int] = None,
596
587
  video_start_timestamp: Optional[int] = None,
597
588
  caption: Optional[str] = None,
598
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
589
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
599
590
  caption_entities: Optional[List[MessageEntity]] = None,
600
591
  show_caption_above_media: Optional[bool] = None,
601
592
  disable_notification: Optional[bool] = None,
@@ -629,7 +620,7 @@ class Bot(core.BaseBot, BotShortcuts):
629
620
  'message_id': message_id,
630
621
  'video_start_timestamp': video_start_timestamp,
631
622
  'caption': caption,
632
- 'parse_mode': parse_mode if not caption_entities else None, # FIX,
623
+ 'parse_mode': parse_mode,
633
624
  'caption_entities': caption_entities,
634
625
  'show_caption_above_media': show_caption_above_media,
635
626
  'disable_notification': disable_notification,
@@ -691,7 +682,7 @@ class Bot(core.BaseBot, BotShortcuts):
691
682
  message_thread_id: Optional[int] = None,
692
683
  direct_messages_topic_id: Optional[int] = None,
693
684
  caption: Optional[str] = None,
694
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
685
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
695
686
  caption_entities: Optional[List[MessageEntity]] = None,
696
687
  show_caption_above_media: Optional[bool] = None,
697
688
  has_spoiler: Optional[bool] = None,
@@ -717,7 +708,7 @@ class Bot(core.BaseBot, BotShortcuts):
717
708
  'direct_messages_topic_id': direct_messages_topic_id,
718
709
  'photo': photo,
719
710
  'caption': caption,
720
- 'parse_mode': parse_mode if not caption_entities else None, # FIX,
711
+ 'parse_mode': parse_mode,
721
712
  'caption_entities': caption_entities,
722
713
  'show_caption_above_media': show_caption_above_media,
723
714
  'has_spoiler': has_spoiler,
@@ -739,7 +730,7 @@ class Bot(core.BaseBot, BotShortcuts):
739
730
  message_thread_id: Optional[int] = None,
740
731
  direct_messages_topic_id: Optional[int] = None,
741
732
  caption: Optional[str] = None,
742
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
733
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
743
734
  caption_entities: Optional[List[MessageEntity]] = None,
744
735
  duration: Optional[int] = None,
745
736
  performer: Optional[str] = None,
@@ -773,7 +764,7 @@ class Bot(core.BaseBot, BotShortcuts):
773
764
  'direct_messages_topic_id': direct_messages_topic_id,
774
765
  'audio': audio,
775
766
  'caption': caption,
776
- 'parse_mode': parse_mode if not caption_entities else None, # FIX,
767
+ 'parse_mode': parse_mode,
777
768
  'caption_entities': caption_entities,
778
769
  'duration': duration,
779
770
  'performer': performer,
@@ -798,7 +789,7 @@ class Bot(core.BaseBot, BotShortcuts):
798
789
  direct_messages_topic_id: Optional[int] = None,
799
790
  thumbnail: Optional[Union[InputFile, str]] = None,
800
791
  caption: Optional[str] = None,
801
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
792
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
802
793
  caption_entities: Optional[List[MessageEntity]] = None,
803
794
  disable_content_type_detection: Optional[bool] = None,
804
795
  disable_notification: Optional[bool] = None,
@@ -827,7 +818,7 @@ class Bot(core.BaseBot, BotShortcuts):
827
818
  'document': document,
828
819
  'thumbnail': thumbnail,
829
820
  'caption': caption,
830
- 'parse_mode': parse_mode if not caption_entities else None, # FIX,
821
+ 'parse_mode': parse_mode,
831
822
  'caption_entities': caption_entities,
832
823
  'disable_content_type_detection': disable_content_type_detection,
833
824
  'disable_notification': disable_notification,
@@ -854,7 +845,7 @@ class Bot(core.BaseBot, BotShortcuts):
854
845
  cover: Optional[Union[InputFile, str]] = None,
855
846
  start_timestamp: Optional[int] = None,
856
847
  caption: Optional[str] = None,
857
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
848
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
858
849
  caption_entities: Optional[List[MessageEntity]] = None,
859
850
  show_caption_above_media: Optional[bool] = None,
860
851
  has_spoiler: Optional[bool] = None,
@@ -891,7 +882,7 @@ class Bot(core.BaseBot, BotShortcuts):
891
882
  'cover': cover,
892
883
  'start_timestamp': start_timestamp,
893
884
  'caption': caption,
894
- 'parse_mode': parse_mode if not caption_entities else None, # FIX,
885
+ 'parse_mode': parse_mode,
895
886
  'caption_entities': caption_entities,
896
887
  'show_caption_above_media': show_caption_above_media,
897
888
  'has_spoiler': has_spoiler,
@@ -918,7 +909,7 @@ class Bot(core.BaseBot, BotShortcuts):
918
909
  height: Optional[int] = None,
919
910
  thumbnail: Optional[Union[InputFile, str]] = None,
920
911
  caption: Optional[str] = None,
921
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
912
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
922
913
  caption_entities: Optional[List[MessageEntity]] = None,
923
914
  show_caption_above_media: Optional[bool] = None,
924
915
  has_spoiler: Optional[bool] = None,
@@ -952,7 +943,7 @@ class Bot(core.BaseBot, BotShortcuts):
952
943
  'height': height,
953
944
  'thumbnail': thumbnail,
954
945
  'caption': caption,
955
- 'parse_mode': parse_mode if not caption_entities else None, # FIX,
946
+ 'parse_mode': parse_mode,
956
947
  'caption_entities': caption_entities,
957
948
  'show_caption_above_media': show_caption_above_media,
958
949
  'has_spoiler': has_spoiler,
@@ -974,7 +965,7 @@ class Bot(core.BaseBot, BotShortcuts):
974
965
  message_thread_id: Optional[int] = None,
975
966
  direct_messages_topic_id: Optional[int] = None,
976
967
  caption: Optional[str] = None,
977
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
968
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
978
969
  caption_entities: Optional[List[MessageEntity]] = None,
979
970
  duration: Optional[int] = None,
980
971
  disable_notification: Optional[bool] = None,
@@ -1008,7 +999,7 @@ class Bot(core.BaseBot, BotShortcuts):
1008
999
  'direct_messages_topic_id': direct_messages_topic_id,
1009
1000
  'voice': voice,
1010
1001
  'caption': caption,
1011
- 'parse_mode': parse_mode if not caption_entities else None, # FIX,
1002
+ 'parse_mode': parse_mode,
1012
1003
  'caption_entities': caption_entities,
1013
1004
  'duration': duration,
1014
1005
  'disable_notification': disable_notification,
@@ -1077,7 +1068,7 @@ class Bot(core.BaseBot, BotShortcuts):
1077
1068
  direct_messages_topic_id: Optional[int] = None,
1078
1069
  payload: Optional[str] = None,
1079
1070
  caption: Optional[str] = None,
1080
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
1071
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
1081
1072
  caption_entities: Optional[List[MessageEntity]] = None,
1082
1073
  show_caption_above_media: Optional[bool] = None,
1083
1074
  disable_notification: Optional[bool] = None,
@@ -1103,7 +1094,7 @@ class Bot(core.BaseBot, BotShortcuts):
1103
1094
  'media': media,
1104
1095
  'payload': payload,
1105
1096
  'caption': caption,
1106
- 'parse_mode': parse_mode if not caption_entities else None, # FIX,
1097
+ 'parse_mode': parse_mode,
1107
1098
  'caption_entities': caption_entities,
1108
1099
  'show_caption_above_media': show_caption_above_media,
1109
1100
  'disable_notification': disable_notification,
@@ -1433,7 +1424,7 @@ class Bot(core.BaseBot, BotShortcuts):
1433
1424
  draft_id: int,
1434
1425
  text: str,
1435
1426
  message_thread_id: Optional[int] = None,
1436
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
1427
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
1437
1428
  entities: Optional[List[MessageEntity]] = None,
1438
1429
  ) -> bool:
1439
1430
  '''
@@ -1450,7 +1441,7 @@ class Bot(core.BaseBot, BotShortcuts):
1450
1441
  'message_thread_id': message_thread_id,
1451
1442
  'draft_id': draft_id,
1452
1443
  'text': text,
1453
- 'parse_mode': parse_mode if not entities else None, # FIX,
1444
+ 'parse_mode': parse_mode,
1454
1445
  'entities': entities,
1455
1446
  })
1456
1447
  return BoolAdapter.validate_python(response)
@@ -3400,7 +3391,7 @@ class Bot(core.BaseBot, BotShortcuts):
3400
3391
  content: InputStoryContent,
3401
3392
  active_period: int,
3402
3393
  caption: Optional[str] = None,
3403
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
3394
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
3404
3395
  caption_entities: Optional[List[MessageEntity]] = None,
3405
3396
  areas: Optional[List[StoryArea]] = None,
3406
3397
  post_to_chat_page: Optional[bool] = None,
@@ -3419,7 +3410,7 @@ class Bot(core.BaseBot, BotShortcuts):
3419
3410
  'content': content,
3420
3411
  'active_period': active_period,
3421
3412
  'caption': caption,
3422
- 'parse_mode': parse_mode if not caption_entities else None, # FIX,
3413
+ 'parse_mode': parse_mode,
3423
3414
  'caption_entities': caption_entities,
3424
3415
  'areas': areas,
3425
3416
  'post_to_chat_page': post_to_chat_page,
@@ -3464,7 +3455,7 @@ class Bot(core.BaseBot, BotShortcuts):
3464
3455
  story_id: int,
3465
3456
  content: InputStoryContent,
3466
3457
  caption: Optional[str] = None,
3467
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
3458
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
3468
3459
  caption_entities: Optional[List[MessageEntity]] = None,
3469
3460
  areas: Optional[List[StoryArea]] = None,
3470
3461
  ) -> Story:
@@ -3482,7 +3473,7 @@ class Bot(core.BaseBot, BotShortcuts):
3482
3473
  'story_id': story_id,
3483
3474
  'content': content,
3484
3475
  'caption': caption,
3485
- 'parse_mode': parse_mode if not caption_entities else None, # FIX,
3476
+ 'parse_mode': parse_mode,
3486
3477
  'caption_entities': caption_entities,
3487
3478
  'areas': areas,
3488
3479
  })
@@ -3515,7 +3506,7 @@ class Bot(core.BaseBot, BotShortcuts):
3515
3506
  chat_id: Optional[Union[int, str]] = None,
3516
3507
  message_id: Optional[int] = None,
3517
3508
  inline_message_id: Optional[str] = None,
3518
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
3509
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
3519
3510
  entities: Optional[List[MessageEntity]] = None,
3520
3511
  link_preview_options: Optional[LinkPreviewOptions] = None,
3521
3512
  reply_markup: Optional[InlineKeyboardMarkup] = None,
@@ -3539,7 +3530,7 @@ class Bot(core.BaseBot, BotShortcuts):
3539
3530
  'message_id': message_id,
3540
3531
  'inline_message_id': inline_message_id,
3541
3532
  'text': text,
3542
- 'parse_mode': parse_mode if not entities else None, # FIX,
3533
+ 'parse_mode': parse_mode,
3543
3534
  'entities': entities,
3544
3535
  'link_preview_options': link_preview_options,
3545
3536
  'reply_markup': reply_markup,
@@ -3556,7 +3547,7 @@ class Bot(core.BaseBot, BotShortcuts):
3556
3547
  message_id: Optional[int] = None,
3557
3548
  inline_message_id: Optional[str] = None,
3558
3549
  caption: Optional[str] = None,
3559
- parse_mode: Optional[enums.ParseMode] = enums.ParseMode.HTML,
3550
+ parse_mode: Optional[Union[str, enums.ParseMode]] = None,
3560
3551
  caption_entities: Optional[List[MessageEntity]] = None,
3561
3552
  show_caption_above_media: Optional[bool] = None,
3562
3553
  reply_markup: Optional[InlineKeyboardMarkup] = None,
@@ -3580,7 +3571,7 @@ class Bot(core.BaseBot, BotShortcuts):
3580
3571
  'message_id': message_id,
3581
3572
  'inline_message_id': inline_message_id,
3582
3573
  'caption': caption,
3583
- 'parse_mode': parse_mode if not caption_entities else None, # FIX,
3574
+ 'parse_mode': parse_mode,
3584
3575
  'caption_entities': caption_entities,
3585
3576
  'show_caption_above_media': show_caption_above_media,
3586
3577
  'reply_markup': reply_markup,