Skip to content
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.</p>
<p align="center">Both synchronous and asynchronous.</p>

## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#january-1-2025"><img src="https://img.shields.io/badge/Bot%20API-8.2-blue?logo=telegram" alt="Supported Bot API version"></a>
## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#february-12-2025"><img src="https://img.shields.io/badge/Bot%20API-8.3-blue?logo=telegram" alt="Supported Bot API version"></a>

<h2><a href='https://pytba.readthedocs.io/en/latest/index.html'>Official documentation</a></h2>
<h2><a href='https://pytba.readthedocs.io/ru/latest/index.html'>Official ru documentation</a></h2>
Expand Down
62 changes: 48 additions & 14 deletions telebot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1810,7 +1810,8 @@ def forward_message(
message_id: int, disable_notification: Optional[bool]=None,
protect_content: Optional[bool]=None,
timeout: Optional[int]=None,
message_thread_id: Optional[int]=None) -> types.Message:
message_thread_id: Optional[int]=None,
video_start_timestamp: Optional[int]=None) -> types.Message:
"""
Use this method to forward messages of any kind.

Expand All @@ -1825,6 +1826,9 @@ def forward_message(
:param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername)
:type from_chat_id: :obj:`int` or :obj:`str`

:param video_start_timestamp: New start timestamp for the forwarded video in the message
:type video_start_timestamp: :obj:`int`

:param message_id: Message identifier in the chat specified in from_chat_id
:type message_id: :obj:`int`

Expand All @@ -1846,7 +1850,8 @@ def forward_message(
return types.Message.de_json(
apihelper.forward_message(
self.token, chat_id, from_chat_id, message_id, disable_notification=disable_notification,
timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id))
timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id,
video_start_timestamp=video_start_timestamp))


def copy_message(
Expand All @@ -1865,7 +1870,8 @@ def copy_message(
message_thread_id: Optional[int]=None,
reply_parameters: Optional[types.ReplyParameters]=None,
show_caption_above_media: Optional[bool]=None,
allow_paid_broadcast: Optional[bool]=None) -> types.MessageID:
allow_paid_broadcast: Optional[bool]=None,
video_start_timestamp: Optional[int]=None) -> types.MessageID:
"""
Use this method to copy messages of any kind.
Service messages, paid media messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied.
Expand All @@ -1883,6 +1889,9 @@ def copy_message(
:param message_id: Message identifier in the chat specified in from_chat_id
:type message_id: :obj:`int`

:param video_start_timestamp: New start timestamp for the copied video in the message
:type video_start_timestamp: :obj:`int`

:param caption: New caption for media, 0-1024 characters after entities parsing. If not specified, the original caption is kept
:type caption: :obj:`str`

Expand Down Expand Up @@ -1956,7 +1965,8 @@ def copy_message(
parse_mode=parse_mode, caption_entities=caption_entities, disable_notification=disable_notification,
reply_markup=reply_markup, timeout=timeout, protect_content=protect_content,
message_thread_id=message_thread_id, reply_parameters=reply_parameters,
show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast))
show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast,
video_start_timestamp=video_start_timestamp))


def delete_message(self, chat_id: Union[int, str], message_id: int,
Expand Down Expand Up @@ -2824,7 +2834,9 @@ def send_video(
business_connection_id: Optional[str]=None,
message_effect_id: Optional[str]=None,
show_caption_above_media: Optional[bool]=None,
allow_paid_broadcast: Optional[bool]=None) -> types.Message:
allow_paid_broadcast: Optional[bool]=None,
cover: Optional[Union[Any, str]]=None,
start_timestamp: Optional[int]=None) -> types.Message:
"""
Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document).

Expand All @@ -2845,8 +2857,18 @@ def send_video(
:param height: Video height
:type height: :obj:`int`

:param thumbnail: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>.
:param thumbnail: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size.
A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file,
so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>.
:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile`

:param cover: Cover for the video in the message. Pass a file_id to send a file that exists on the Telegram servers (recommended),
pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under
<file_attach_name> name. More information on Sending Files »
:type cover: :obj:`str` or :class:`telebot.types.InputFile`

:param start_timestamp: Start timestamp for the video in the message
:type start_timestamp: :obj:`int`

:param caption: Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing
:type caption: :obj:`str`
Expand Down Expand Up @@ -2949,7 +2971,8 @@ def send_video(
thumbnail=thumbnail, height=height, width=width, caption_entities=caption_entities,
protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler,
reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id,
show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)
show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast,
cover=cover, start_timestamp=start_timestamp)
)


Expand Down Expand Up @@ -6250,19 +6273,24 @@ def delete_sticker_set(self, name:str) -> bool:
"""
return apihelper.delete_sticker_set(self.token, name)

def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None,
text_entities: Optional[List[types.MessageEntity]]=None, pay_for_upgrade: Optional[bool]=None) -> bool:
def send_gift(self, user_id: Optional[Union[str, int]] = None, gift_id: str=None, text: Optional[str]=None, text_parse_mode: Optional[str]=None,
text_entities: Optional[List[types.MessageEntity]]=None, pay_for_upgrade: Optional[bool]=None,
chat_id: Optional[Union[str, int]] = None) -> bool:
"""
Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success.

Telegram documentation: https://core.telegram.org/bots/api#sendgift

:param user_id: Unique identifier of the target user that will receive the gift
:type user_id: :obj:`int`

:param gift_id: Identifier of the gift
:type gift_id: :obj:`str`

:param user_id: Required if chat_id is not specified. Unique identifier of the target user who will receive the gift.
:type user_id::obj:`int` | :obj:`str`

:param chat_id: Required if user_id is not specified. Unique identifier for the chat or username of the channel
(in the format @channelusername) that will receive the gift.
:type chat_id: :obj:`int` | :obj:`str`

:param pay_for_upgrade: Pass True to pay for the gift upgrade from the bot's balance, thereby making the upgrade free for the receiver
:type pay_for_upgrade: :obj:`bool`

Expand All @@ -6278,8 +6306,14 @@ def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_p
:return: Returns True on success.
:rtype: :obj:`bool`
"""
return apihelper.send_gift(self.token, user_id, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities,
pay_for_upgrade=pay_for_upgrade)
if user_id is None and chat_id is None:
raise ValueError("Either user_id or chat_id must be specified.")

if gift_id is None:
raise ValueError("gift_id must be specified.")

return apihelper.send_gift(self.token, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities,
pay_for_upgrade=pay_for_upgrade, chat_id=chat_id, user_id=user_id)

def verify_user(self, user_id: int, custom_description: Optional[str]=None) -> bool:
"""
Expand Down
30 changes: 26 additions & 4 deletions telebot/apihelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,8 @@ def get_chat_member(token, chat_id, user_id):

def forward_message(
token, chat_id, from_chat_id, message_id,
disable_notification=None, timeout=None, protect_content=None, message_thread_id=None):
disable_notification=None, timeout=None, protect_content=None, message_thread_id=None,
video_start_timestamp=None):
method_url = r'forwardMessage'
payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id}
if disable_notification is not None:
Expand All @@ -435,12 +436,15 @@ def forward_message(
payload['protect_content'] = protect_content
if message_thread_id:
payload['message_thread_id'] = message_thread_id
if video_start_timestamp:
payload['video_start_timestamp'] = video_start_timestamp
return _make_request(token, method_url, params=payload)


def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None,
disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None,
reply_parameters=None, show_caption_above_media=None, allow_paid_broadcast=None):
reply_parameters=None, show_caption_above_media=None, allow_paid_broadcast=None,
video_start_timestamp=None):
method_url = r'copyMessage'
payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id}
if caption is not None:
Expand All @@ -465,6 +469,8 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m
payload['show_caption_above_media'] = show_caption_above_media
if allow_paid_broadcast is not None:
payload['allow_paid_broadcast'] = allow_paid_broadcast
if video_start_timestamp:
payload['video_start_timestamp'] = video_start_timestamp
return _make_request(token, method_url, params=payload)


Expand Down Expand Up @@ -783,7 +789,8 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None,
thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None,
message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None,
message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None):
message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None,
cover=None, start_timestamp=None):
method_url = r'sendVideo'
payload = {'chat_id': chat_id}
files = None
Expand Down Expand Up @@ -835,6 +842,16 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N
payload['show_caption_above_media'] = show_caption_above_media
if allow_paid_broadcast is not None:
payload['allow_paid_broadcast'] = allow_paid_broadcast
if cover:
if not util.is_string(cover):
if files:
files['cover'] = cover
else:
files = {'cover': cover}
else:
payload['cover'] = cover
if start_timestamp:
payload['start_timestamp'] = start_timestamp

return _make_request(token, method_url, params=payload, files=files, method='post')

Expand Down Expand Up @@ -1929,7 +1946,8 @@ def get_available_gifts(token):
return _make_request(token, method_url)


def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None, pay_for_upgrade=None):
def send_gift(token, gift_id, text=None, text_parse_mode=None, text_entities=None, pay_for_upgrade=None,
chat_id=None, user_id=None):
method_url = 'sendGift'
payload = {'user_id': user_id, 'gift_id': gift_id}
if text:
Expand All @@ -1940,6 +1958,10 @@ def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_ent
payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities))
if pay_for_upgrade is not None:
payload['pay_for_upgrade'] = pay_for_upgrade
if chat_id:
payload['chat_id'] = chat_id
if user_id:
payload['user_id'] = user_id
return _make_request(token, method_url, params=payload, method='post')


Expand Down
Loading