Queer European MD passionate about IT

2 Commits 4a79535321 ... 6cf63716ff

Autore SHA1 Messaggio Data
  Davte 6cf63716ff Compliance with bot API 9.0 1 mese fa
  Davte 9c61b564b1 Working on compliance with bot API 9.0 1 mese fa
4 ha cambiato i file con 567 aggiunte e 11 eliminazioni
  1. 1 1
      davtelepot/__init__.py
  2. 560 6
      davtelepot/api.py
  3. 5 3
      davtelepot/api_helper.py
  4. 1 1
      setup.py

+ 1 - 1
davtelepot/__init__.py

@@ -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"
 

+ 560 - 6
davtelepot/api.py

@@ -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()
+        )

+ 5 - 3
davtelepot/api_helper.py

@@ -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"

+ 1 - 1
setup.py

@@ -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=[