Compare commits

...

2 Commits

Author SHA1 Message Date
6cf63716ff
Compliance with bot API 9.0
Also, the repository is not hosted on GiTea
2025-04-15 21:27:19 +02:00
9c61b564b1
Working on compliance with bot API 9.0 2025-04-13 00:00:51 +02:00
4 changed files with 567 additions and 11 deletions

View File

@ -11,7 +11,7 @@ __author__ = "Davide Testa"
__email__ = "davide@davte.it"
__credits__ = ["Marco Origlia", "Nick Lee @Nickoala"]
__license__ = "GNU General Public License v3.0"
__version__ = "2.10.9"
__version__ = "2.10.10"
__maintainer__ = "Davide Testa"
__contact__ = "t.me/davte"

View File

@ -318,7 +318,8 @@ class InputSticker(dict):
or pass attach://<file_attach_name> to upload a new one using
multipart/form-data under <file_attach_name> name.
Animated and video stickers can't be uploaded via HTTP URL.
More information on Sending Files: https://core.telegram.org/bots/api#sending-files
More information on Sending Files:
https://core.telegram.org/bots/api#sending-files
@param format_: Format of the added sticker, must be one of static
for a .WEBP or .PNG image, animated for a .TGS animation,
video for a WEBM video
@ -403,9 +404,9 @@ class ReactionType(DictToDump):
emoji: str = None,
custom_emoji_id: str = None):
super().__init__(self)
if type_ not in ('emoji', 'custom_emoji'):
if type_ not in ('emoji', 'custom_emoji', 'paid'):
raise TypeError(
f"ReactionType must be `emoji` or `custom_emoji`.\n"
f"ReactionType must be `emoji`, `custom_emoji` or `paid`.\n"
f"Unknown type {type_}"
)
self['type'] = type_
@ -418,7 +419,7 @@ class ReactionType(DictToDump):
self['emoji'] = emoji
elif custom_emoji_id:
self['custom_emoji_id'] = custom_emoji_id
else:
elif type_ != 'paid':
raise TypeError(
"At least one of the two fields `emoji` or `custom_emoji` "
"must be provided and not None."
@ -491,6 +492,276 @@ class PreparedInlineMessage(DictToDump):
self['id'] = id
self['expiration_date'] = expiration_date
class StoryAreaPosition(DictToDump):
"""Describes the position of a clickable area within a story.
@param x_percentage: The abscissa of the area's center, as a percentage of
the media width
@param y_percentage: The ordinate of the area's center, as a percentage of
the media height
@param width_percentage: The width of the area's rectangle, as a percentage
of the media width
@param height_percentage: The height of the area's rectangle, as a
percentage of the media height
@param rotation_angle: The clockwise rotation angle of the rectangle, in
degrees; 0-360
@param corner_radius_percentage: The radius of the rectangle corner
rounding, as a percentage of the media width
"""
def __init__(self,x_percentage: float = None,
y_percentage: float = None,
width_percentage: float = None,
height_percentage: float = None,
rotation_angle: float = None,
corner_radius_percentage: float = None):
super().__init__()
for parameter, value in locals().items():
if value:
self[parameter] = value
class StoryAreaType(DictToDump):
"""Describes the type of a clickable area on a story.
Currently, it can be one of:
- StoryAreaTypeLocation
- StoryAreaTypeSuggestedReaction
- StoryAreaTypeLink
- StoryAreaTypeWeather
- StoryAreaTypeUniqueGift
"""
def __init__(self, type_):
assert type_ in ('location',
'suggested_reaction',
'link',
'weather',
'unique_gift'), (
f"Invalid StoryAreaType: {type_}"
)
self['type'] = type_
class LocationAddress(DictToDump):
"""Describes the physical address of a location.
@param country_code: the two-letter ISO 3166-1 alpha-2 country code of the
country where the location is located
@param state (optional): state of the location
@param city (optional): city of the location
@param street(optional): street address of the location
"""
def __init__(self, country_code: str,
state: str = None,
city: str = None, street: str = None):
assert len(f"{country_code}") == 2, (
f"Invalid country code: {country_code}"
)
super().__init__()
for parameter, value in locals().items():
if value:
self[parameter] = value
class StoryAreaTypeLocation(StoryAreaType):
"""Describes a story area pointing to a location.
Currently, a story can have up to 10 location areas.
@param latitude: location latitude in degrees
@param longitude: location longitude in degrees
@param address: Optional. Address of the location
"""
def __init__(self, latitude: float, longitude: float,
address: 'LocationAddress' = None):
super().__init__(type_='location')
for parameter, value in locals().items():
if value:
self[parameter] = value
class StoryAreaTypeSuggestedReaction(StoryAreaType):
"""Describes a story area pointing to a suggested reaction.
Currently, a story can have up to 5 suggested reaction areas.
@param reaction_type: type of the reaction
@param is_dark: pass True if the reaction area has a dark background
@param is_flipped: pass True if reaction area corner is flipped
"""
def __init__(self, reaction_type: 'ReactionType',
is_dark: bool = None,
is_flipped: bool = None):
super().__init__(type_='suggested_reaction')
for parameter, value in locals().items():
if value is not None:
self[parameter] = value
class StoryAreaTypeLink(StoryAreaType):
"""Describes a story area pointing to an HTTP or tg:// link.
Currently, a story can have up to 3 link areas.
@param url: HTTP or tg:// URL to be opened when the area is clicked
"""
def __init__(self, url: str):
super().__init__(type_='link')
self['url'] = url
class StoryAreaTypeWeather(StoryAreaType):
"""Describes a story area containing weather information.
Currently, a story can have up to 3 weather areas.
Parameters:
@param temperature: temperature, in degree Celsius
@param emoji: emoji representing the weather
@param background_color: a color of the area background in the ARGB format
"""
def __init__(self, temperature: float, emoji: str, background_color: int):
super().__init__(type_='weather')
for parameter, value in locals().items():
if value:
self[parameter] = value
class StoryAreaTypeUniqueGift(StoryAreaType):
"""Describes a story area pointing to a unique gift.
Currently, a story can have at most 1 unique gift area.
@param name: unique name of the gift
"""
def __init__(self, name):
super().__init__(type_='unique_gift')
for parameter, value in locals().items():
if value:
self[parameter] = value
class StoryArea(DictToDump):
"""Describes a clickable area on a story media.
@param position: Position of the area
@param type: Type of the area
"""
def __init__(self,
position: 'StoryAreaPosition',
type_: 'StoryAreaType'):
super().__init__()
self['position'] = position
self['type'] = type_
class InputStoryContent(DictToDump):
"""This object describes the content of a story to post.
Currently, it can be one of
- InputStoryContentPhoto
- InputStoryContentVideo
"""
def __init__(self, type_):
assert type_ in ('photo',
'video',), (
f"Invalid InputStoryContent type: {type_}"
)
self['type'] = type_
class InputStoryContentPhoto(InputStoryContent):
"""Describes a photo to post as a story.
@param photo: the photo to post as a story. The photo must be of the size
1080x1920 and must not exceed 10 MB. The photo can't be reused and can
only be uploaded as a new file, so you can pass
attach://<file_attach_name> if the photo was uploaded using
multipart/form-data under <file_attach_name>.
More information: https://core.telegram.org/bots/api#sending-files
"""
def __init__(self, photo: str):
super().__init__(type_='photo')
for parameter, value in locals().items():
if value:
self[parameter] = value
class InputStoryContentVideo(InputStoryContent):
"""Describes a video to post as a story.
@param video: The video to post as a story. The video must be of the size
720x1280, streamable, encoded with H.265 codec, with key frames added
each second in the MPEG4 format, and must not exceed 30 MB. The video
can't be reused and can only be uploaded as a new file, so you can pass
attach://<file_attach_name> if the video was uploaded using
multipart/form-data under <file_attach_name>.
More information: https://core.telegram.org/bots/api#sending-files
@param duration: Optional. Precise duration of the video in seconds; 0-60
@param cover_frame_timestamp: Optional. Timestamp in seconds of the frame
that will be used as the static cover for the story. Defaults to 0.0.
@param is_animation: Optional. Pass True if the video has no sound
More information: https://core.telegram.org/bots/api#sending-files
"""
def __init__(self, video: str, duration: float = None,
cover_frame_timestamp: float = None,
is_animation: bool = None):
super().__init__(type_='photo')
for parameter, value in locals().items():
if value is not None:
self[parameter] = value
class InputProfilePhoto(DictToDump):
"""This object describes a profile photo to set.
Currently, it can be one of
- InputProfilePhotoStatic
- InputProfilePhotoAnimated
"""
def __init__(self, type_):
assert type_ in ('InputProfilePhotoStatic',
'InputProfilePhotoAnimated',), (
f"Invalid InputProfilePhoto type: {type_}"
)
self['type'] = type_
class InputProfilePhotoStatic(InputProfilePhoto):
"""A static profile photo in the .JPG format.
@param photo: the static profile photo. Profile photos can't be reused and
can only be uploaded as a new file, so you can pass
"attach://<file_attach_name>" if the photo was uploaded using
multipart/form-data under <file_attach_name>.
More information on Sending Files:
https://core.telegram.org/bots/api#sending-files
"""
def __init__(self, photo: str):
super().__init__(type_='static')
for parameter, value in locals().items():
if value:
self[parameter] = value
class InputProfilePhotoAnimated(InputProfilePhoto):
"""A static profile photo in the MPEG4 format.
@param animation: The animated profile photo. Profile photos can't be reused
and can only be uploaded as a new file, so you can pass
"attach://<file_attach_name>" if the photo was uploaded using
multipart/form-data under <file_attach_name>.
More information on Sending Files:
https://core.telegram.org/bots/api#sending-files
@param main_frame_timestamp: Optional. Timestamp in seconds of the frame
that will be used as the static profile photo. Defaults to 0.0.
"""
def __init__(self, animation: str,
main_frame_timestamp: float = None):
super().__init__(type_='animated')
for parameter, value in locals().items():
if value:
self[parameter] = value
def handle_deprecated_disable_web_page_preview(parameters: dict,
kwargs: dict):
if 'disable_web_page_preview' in kwargs:
@ -975,7 +1246,8 @@ class TelegramBot:
message_id: int,
message_thread_id: int = None,
protect_content: bool = None,
disable_notification: bool = None):
disable_notification: bool = None,
video_start_timestamp: int = None):
"""Forward a message.
See https://core.telegram.org/bots/api#forwardmessage for details.
@ -1100,6 +1372,8 @@ class TelegramBot:
reply_parameters: ReplyParameters = None,
reply_markup=None,
allow_paid_broadcast: bool = None,
start_timestamp: int = None,
cover=None,
**kwargs):
"""Send a video from file_id, HTTP url or file.
@ -2448,6 +2722,7 @@ class TelegramBot:
reply_parameters: ReplyParameters = None,
reply_markup=None,
allow_paid_broadcast: bool = None,
video_start_timestamp: int = None,
**kwargs):
"""Use this method to copy messages of any kind.
@ -3288,7 +3563,8 @@ class TelegramBot:
parameters=locals()
)
async def sendGift(self, user_id: int, gift_id: str, pay_for_upgrade: bool,
async def sendGift(self, user_id: int, chat_id: Union[int, str],
gift_id: str, pay_for_upgrade: bool,
text: str, text_parse_mode: str,
text_entities: List['MessageEntity']):
"""Sends a gift to the given user.
@ -3401,3 +3677,281 @@ class TelegramBot:
'editUserStarSubscription',
parameters=locals()
)
async def giftPremiumSubscription(
self, user_id: int,
month_count: int, star_count: int,
text: str = None,
text_parse_mode: str = None,
text_entities: List['MessageEntity'] = None
):
"""Gifts a Telegram Premium subscription to the given user.
Returns True on success.
See https://core.telegram.org/bots/api#giftpremiumsubscription for details.
"""
if star_count not in (1000, 1500, 2500):
logging.warning("Star count should be 1000 for three months, 1500 "
"for 6 months or 2000 for 12 months")
return await self.api_request(
'giftPremiumSubscription',
parameters=locals()
)
async def readBusinessMessage(self,
business_connection_id: str,
chat_id: int,
message_id: int):
"""Marks incoming message as read on behalf of a business account.
Requires the can_read_messages business bot right.
Returns True on success.
See https://core.telegram.org/bots/api#readbusinessmessage for details.
"""
return await self.api_request(
'readBusinessMessage',
parameters=locals()
)
async def deleteBusinessMessages(self,
business_connection_id: str,
message_ids: List[int]):
"""Delete messages on behalf of a business account.
Requires the can_delete_outgoing_messages business bot right to delete
messages sent by the bot itself, or the can_delete_all_messages
business bot right to delete any message.
Returns True on success.
See https://core.telegram.org/bots/api#deletebusinessmessages for details.
"""
return await self.api_request(
'deleteBusinessMessages',
parameters=locals()
)
async def setBusinessAccountName(self,
business_connection_id: str,
first_name: str,
last_name: str = None):
"""Changes the first and last name of a managed business account.
Requires the can_change_name business bot right.
Returns True on success.
See https://core.telegram.org/bots/api#setbusinessaccountname for details.
"""
return await self.api_request(
'setBusinessAccountName',
parameters=locals()
)
async def setBusinessAccountUsername(self,
business_connection_id: str,
username: str = None):
"""Changes the username of a managed business account.
Requires the can_change_username business bot right.
Returns True on success.
See https://core.telegram.org/bots/api#setbusinessaccountusername for details.
"""
return await self.api_request(
'setBusinessAccountUsername',
parameters=locals()
)
async def setBusinessAccountBio(self,
business_connection_id: str,
bio: str = None):
"""Changes the bio of a managed business account.
Requires the can_change_bio business bot right.
Returns True on success.
See https://core.telegram.org/bots/api#setbusinessaccountbio for details.
"""
return await self.api_request(
'setBusinessAccountBio',
parameters=locals()
)
async def setBusinessAccountProfilePhoto(self,
business_connection_id: str,
photo: 'InputProfilePhoto',
is_public: bool = None):
"""Changes the profile photo of a managed business account.
Requires the can_edit_profile_photo business bot right.
Returns True on success.
See https://core.telegram.org/bots/api#setbusinessaccountprofilephoto for details.
"""
return await self.api_request(
'setBusinessAccountProfilePhoto',
parameters=locals()
)
async def removeBusinessAccountProfilePhoto(self, business_connection_id: str, is_public: bool):
"""Removes the current profile photo of a managed business account.
Requires the can_edit_profile_photo business bot right.
Returns True on success.
See https://core.telegram.org/bots/api#removebusinessaccountprofilephoto for details.
"""
return await self.api_request(
'removeBusinessAccountProfilePhoto',
parameters=locals()
)
async def setBusinessAccountGiftSettings(self, business_connection_id: str, show_gift_button: bool, accepted_gift_types: 'AcceptedGiftTypes'):
"""Changes the privacy settings pertaining to incoming gifts in a managed
business account.
Requires the can_change_gift_settings business bot right.
Returns True on success.
See https://core.telegram.org/bots/api#setbusinessaccountgiftsettings for details.
"""
return await self.api_request(
'setBusinessAccountGiftSettings',
parameters=locals()
)
async def getBusinessAccountStarBalance(self, business_connection_id: str):
"""Returns the amount of Telegram Stars owned by a managed business
account.
Requires the can_view_gifts_and_stars business bot right.
Returns StarAmount on success.
See https://core.telegram.org/bots/api#getbusinessaccountstarbalance for details.
"""
return await self.api_request(
'getBusinessAccountStarBalance',
parameters=locals()
)
async def transferBusinessAccountStars(self, business_connection_id: str, star_count: int):
"""Transfers Telegram Stars from the business account balance to the bot's
balance.
Requires the can_transfer_stars business bot right.
Returns True on success.
See https://core.telegram.org/bots/api#transferbusinessaccountstars for details.
"""
return await self.api_request(
'transferBusinessAccountStars',
parameters=locals()
)
async def getBusinessAccountGifts(self,
business_connection_id: str,
exclude_unsaved: bool = None,
exclude_saved: bool = None,
exclude_unlimited: bool = None,
exclude_limited: bool = None,
exclude_unique: bool = None,
sort_by_price: bool = None,
offset: str = None,
limit: int = None):
"""Returns the gifts received and owned by a managed business account.
Requires the can_view_gifts_and_stars business bot right.
Returns OwnedGifts on success.
See https://core.telegram.org/bots/api#getbusinessaccountgifts for details.
"""
return await self.api_request(
'getBusinessAccountGifts',
parameters=locals()
)
async def convertGiftToStars(self, business_connection_id: str, owned_gift_id: str):
"""Converts a given regular gift to Telegram Stars.
Requires the can_convert_gifts_to_stars business bot right.
Returns True on success.
See https://core.telegram.org/bots/api#convertgifttostars for details.
"""
return await self.api_request(
'convertGiftToStars',
parameters=locals()
)
async def upgradeGift(self, business_connection_id: str,
owned_gift_id: str,
keep_original_details: bool = None,
star_count: int = None):
"""Upgrades a given regular gift to a unique gift.
Requires the can_transfer_and_upgrade_gifts business bot right.
Additionally requires the can_transfer_stars business bot right if the
upgrade is paid.
Returns True on success.
See https://core.telegram.org/bots/api#upgradegift for details.
"""
return await self.api_request(
'upgradeGift',
parameters=locals()
)
async def transferGift(self, business_connection_id: str,
owned_gift_id: str,
new_owner_chat_id: int,
star_count: int = None):
"""Transfers an owned unique gift to another user.
Requires the can_transfer_and_upgrade_gifts business bot right.
Requires can_transfer_stars business bot right if the transfer is paid.
Returns True on success.
See https://core.telegram.org/bots/api#transfergift for details.
"""
return await self.api_request(
'transferGift',
parameters=locals()
)
async def postStory(self, business_connection_id: str,
content: 'InputStoryContent',
active_period: int,
caption: str = None,
parse_mode: str = None,
caption_entities: List['MessageEntity'] = None,
areas: List['StoryArea'] = None,
post_to_chat_page: bool = None,
protect_content: bool = None):
"""Posts a story on behalf of a managed business account.
Requires the can_manage_stories business bot right.
Returns Story on success.
See https://core.telegram.org/bots/api#poststory for details.
"""
return await self.api_request(
'postStory',
parameters=locals()
)
async def editStory(self, business_connection_id: str,
story_id: int,
content: 'InputStoryContent',
caption: str = None,
parse_mode: str = None,
caption_entities: List[dict] = None,
areas: List['StoryArea'] = None):
"""Edits a story previously posted by the bot on behalf of a managed
business account.
Requires the can_manage_stories business bot right.
Returns Story on success.
See https://core.telegram.org/bots/api#editstory for details.
"""
return await self.api_request(
'editStory',
parameters=locals()
)
async def deleteStory(self, business_connection_id: str, story_id: int):
"""Deletes a story previously posted by the bot on behalf of a managed
business account.
Requires the can_manage_stories business bot right.
Returns True on success.
See https://core.telegram.org/bots/api#deletestory for details.
"""
return await self.api_request(
'deleteStory',
parameters=locals()
)

View File

@ -25,6 +25,7 @@ class TelegramApiMethod(object):
'Boolean': "bool",
'Integer': "int",
'Integer or String': "Union[int, str]",
'Array of Integer': "List[str]",
'String': "str",
}
"""Telegram bot API method."""
@ -55,14 +56,15 @@ class TelegramApiMethod(object):
for n, paragraph in enumerate(self.description.replace('.', '.\n').split('\n')):
additional_indentation = 0
if n == 0 and paragraph.startswith(redundant_string):
paragraph = paragraph[len(redundant_string)].upper() + paragraph[len(redundant_string)+1:]
paragraph = (paragraph[len(redundant_string)].upper()
+ paragraph[len(redundant_string)+1:])
for word in paragraph.split(' '):
if len(current_line) + len(word) > 80 - indentation - additional_indentation:
additional_indentation = max(additional_indentation, 4)
result += f"{current_line.strip()}\n{' ' * additional_indentation}"
current_line = ""
current_line += f"{word} "
if len(current_line):
if len(current_line) > 0:
result += f"{current_line.strip()}\n"
current_line = ""
if n == 0:
@ -203,7 +205,7 @@ async def print_api_methods(filename=None,
)
)
if output_file:
with open(output_file, 'w') as file:
with open(output_file, 'w', encoding="utf-8") as file:
if new_methods:
file.write(
"from typing import List, Union\n"

View File

@ -51,7 +51,7 @@ setuptools.setup(
license=find_information("license", "davtelepot", "__init__.py"),
long_description=long_description,
long_description_content_type="text/markdown",
url="https://gogs.davte.it/davte/davtelepot",
url="https://gitea.davte.it/davte/davtelepot",
packages=setuptools.find_packages(),
platforms=['any'],
install_requires=[