Queer European MD passionate about IT

2 Achegas 4a79535321 ... 6cf63716ff

Autor SHA1 Mensaxe Data
  Davte 6cf63716ff Compliance with bot API 9.0 hai 11 meses
  Davte 9c61b564b1 Working on compliance with bot API 9.0 hai 11 meses
Modificáronse 4 ficheiros con 567 adicións e 11 borrados
  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=[