diff --git a/pyrogram/dispatcher.py b/pyrogram/dispatcher.py index 6e503ceb..ff89445d 100644 --- a/pyrogram/dispatcher.py +++ b/pyrogram/dispatcher.py @@ -26,7 +26,7 @@ from pyrogram import utils from pyrogram.handlers import ( CallbackQueryHandler, MessageHandler, EditedMessageHandler, DeletedMessagesHandler, UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler, - ChosenInlineResultHandler, ChatMemberUpdatedHandler, ChatJoinRequestHandler + ChosenInlineResultHandler, ChatMemberUpdatedHandler, ChatJoinRequestHandler, StoryHandler ) from pyrogram.raw.types import ( UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage, @@ -35,7 +35,7 @@ from pyrogram.raw.types import ( UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery, UpdateUserStatus, UpdateBotInlineQuery, UpdateMessagePoll, UpdateBotInlineSend, UpdateChatParticipant, UpdateChannelParticipant, - UpdateBotChatInviteRequester + UpdateBotChatInviteRequester, UpdateStory ) log = logging.getLogger(__name__) @@ -52,6 +52,7 @@ class Dispatcher: POLL_UPDATES = (UpdateMessagePoll,) CHOSEN_INLINE_RESULT_UPDATES = (UpdateBotInlineSend,) CHAT_JOIN_REQUEST_UPDATES = (UpdateBotChatInviteRequester,) + NEW_STORY_UPDATES = (UpdateStory,) def __init__(self, client: "pyrogram.Client"): self.client = client @@ -127,6 +128,12 @@ class Dispatcher: ChatJoinRequestHandler ) + async def story_parser(update, _, __): + return ( + await pyrogram.types.Story._parse(self.client, update.story, update.peer), + StoryHandler + ) + self.update_parsers = { Dispatcher.NEW_MESSAGE_UPDATES: message_parser, Dispatcher.EDIT_MESSAGE_UPDATES: edited_message_parser, @@ -137,7 +144,8 @@ class Dispatcher: Dispatcher.POLL_UPDATES: poll_parser, Dispatcher.CHOSEN_INLINE_RESULT_UPDATES: chosen_inline_result_parser, Dispatcher.CHAT_MEMBER_UPDATES: chat_member_updated_parser, - Dispatcher.CHAT_JOIN_REQUEST_UPDATES: chat_join_request_parser + Dispatcher.CHAT_JOIN_REQUEST_UPDATES: chat_join_request_parser, + Dispatcher.NEW_STORY_UPDATES: story_parser } self.update_parsers = {key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple} diff --git a/pyrogram/enums/__init__.py b/pyrogram/enums/__init__.py index d19c7044..9d574dd6 100644 --- a/pyrogram/enums/__init__.py +++ b/pyrogram/enums/__init__.py @@ -29,21 +29,25 @@ from .next_code_type import NextCodeType from .parse_mode import ParseMode from .poll_type import PollType from .sent_code_type import SentCodeType +from .stories_privacy import StoriesPrivacy +from .stories_privacy_rules import StoriesPrivacyRules from .user_status import UserStatus __all__ = [ - 'ChatAction', - 'ChatEventAction', - 'ChatMemberStatus', - 'ChatMembersFilter', - 'ChatType', - 'MessageEntityType', - 'MessageMediaType', - 'MessageServiceType', - 'MessagesFilter', - 'NextCodeType', - 'ParseMode', - 'PollType', - 'SentCodeType', + 'ChatAction', + 'ChatEventAction', + 'ChatMemberStatus', + 'ChatMembersFilter', + 'ChatType', + 'MessageEntityType', + 'MessageMediaType', + 'MessageServiceType', + 'MessagesFilter', + 'NextCodeType', + 'ParseMode', + 'PollType', + 'SentCodeType', + 'StoriesPrivacy', + 'StoriesPrivacyRules', 'UserStatus' ] diff --git a/pyrogram/enums/message_media_type.py b/pyrogram/enums/message_media_type.py index 58878114..2f06ccb2 100644 --- a/pyrogram/enums/message_media_type.py +++ b/pyrogram/enums/message_media_type.py @@ -68,3 +68,6 @@ class MessageMediaType(AutoName): GAME = auto() "Game media" + + STORY = auto() + "Story media" diff --git a/pyrogram/enums/stories_privacy.py b/pyrogram/enums/stories_privacy.py new file mode 100644 index 00000000..b88d7e0b --- /dev/null +++ b/pyrogram/enums/stories_privacy.py @@ -0,0 +1,39 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from enum import auto +from .auto_name import AutoName + + +class StoriesPrivacy(AutoName): + """Stories privacy type enumeration used in :obj:`~pyrogram.method.SendStory`.""" + + PUBLIC = auto() + "Public stories" + + CLOSE_FRIENDS = auto() + "Close friends stories" + + CONTACTS = auto() + "Contacts only stories" + + PRIVATE = auto() + "Private stories" + + NO_CONTACTS = auto() + "Hide stories from contacts" diff --git a/pyrogram/enums/stories_privacy_rules.py b/pyrogram/enums/stories_privacy_rules.py new file mode 100644 index 00000000..48cf5c24 --- /dev/null +++ b/pyrogram/enums/stories_privacy_rules.py @@ -0,0 +1,39 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from enum import auto +from .auto_name import AutoName + + +class StoriesPrivacyRules(AutoName): + """Stories privacy rules type enumeration used in :obj:`~pyrogram.method.SendStory`.""" + + PUBLIC = auto() + "Public stories" + + CLOSE_FRIENDS = auto() + "Close friends stories" + + CONTACTS = auto() + "Contacts only stories" + + PRIVATE = auto() + "Private stories" + + NO_CONTACTS = auto() + "Hide stories from contacts" diff --git a/pyrogram/handlers/__init__.py b/pyrogram/handlers/__init__.py index 1c762958..2d9ca60d 100644 --- a/pyrogram/handlers/__init__.py +++ b/pyrogram/handlers/__init__.py @@ -27,4 +27,5 @@ from .inline_query_handler import InlineQueryHandler from .message_handler import MessageHandler from .poll_handler import PollHandler from .raw_update_handler import RawUpdateHandler +from .story_handler import StoryHandler from .user_status_handler import UserStatusHandler diff --git a/pyrogram/handlers/story_handler.py b/pyrogram/handlers/story_handler.py new file mode 100644 index 00000000..b6a07d1c --- /dev/null +++ b/pyrogram/handlers/story_handler.py @@ -0,0 +1,49 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Callable + +from .handler import Handler + + +class StoryHandler(Handler): + """The Story handler class. Used to handle new stories. + It is intended to be used with :meth:`~pyrogram.Client.add_handler` + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_story` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new Stories arrives. It takes *(client, story)* + as positional arguments (look at the section below for a detailed description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of stories to be passed + in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the story handler. + + story (:obj:`~pyrogram.types.Story`): + The received story. + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/methods/__init__.py b/pyrogram/methods/__init__.py index ea71f6b1..18fc8391 100644 --- a/pyrogram/methods/__init__.py +++ b/pyrogram/methods/__init__.py @@ -26,6 +26,7 @@ from .invite_links import InviteLinks from .messages import Messages from .password import Password from .users import Users +from .stories import Stories from .utilities import Utilities @@ -37,6 +38,7 @@ class Methods( Password, Chats, Users, + Stories, Messages, Decorators, Utilities, diff --git a/pyrogram/methods/decorators/__init__.py b/pyrogram/methods/decorators/__init__.py index 1fc61185..80c5f9c4 100644 --- a/pyrogram/methods/decorators/__init__.py +++ b/pyrogram/methods/decorators/__init__.py @@ -28,6 +28,7 @@ from .on_message import OnMessage from .on_poll import OnPoll from .on_raw_update import OnRawUpdate from .on_user_status import OnUserStatus +from .on_story import OnStory class Decorators( @@ -42,6 +43,7 @@ class Decorators( OnPoll, OnChosenInlineResult, OnChatMemberUpdated, - OnChatJoinRequest + OnChatJoinRequest, + OnStory ): pass diff --git a/pyrogram/methods/decorators/on_story.py b/pyrogram/methods/decorators/on_story.py new file mode 100644 index 00000000..a26ba8a0 --- /dev/null +++ b/pyrogram/methods/decorators/on_story.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Callable + +import pyrogram +from pyrogram.filters import Filter + + +class OnStory: + def on_story( + self=None, + filters=None, + group: int = 0 + ) -> Callable: + """Decorator for handling new stories. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.StoryHandler`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of stories to be passed + in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.StoryHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.StoryHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/messages/send_animation.py b/pyrogram/methods/messages/send_animation.py index a29d597a..57e8d5e7 100644 --- a/pyrogram/methods/messages/send_animation.py +++ b/pyrogram/methods/messages/send_animation.py @@ -48,6 +48,7 @@ class SendAnimation: disable_notification: bool = None, message_thread_id: int = None, reply_to_message_id: int = None, + reply_to_story_id: int = None, schedule_date: datetime = None, protect_content: bool = None, reply_markup: Union[ @@ -123,6 +124,9 @@ class SendAnimation: reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message. + reply_to_story_id (``int``, *optional*): + Unique identifier for the target story. + schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. @@ -230,12 +234,13 @@ class SendAnimation: while True: try: + peer = await self.resolve_peer(chat_id) r = await self.invoke( raw.functions.messages.SendMedia( - peer=await self.resolve_peer(chat_id), + peer=peer, media=media, silent=disable_notification or None, - reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id), + reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id, peer, reply_to_story_id), random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, diff --git a/pyrogram/methods/messages/send_audio.py b/pyrogram/methods/messages/send_audio.py index 7861a496..ab814ef4 100644 --- a/pyrogram/methods/messages/send_audio.py +++ b/pyrogram/methods/messages/send_audio.py @@ -46,6 +46,7 @@ class SendAudio: disable_notification: bool = None, message_thread_id: int = None, reply_to_message_id: int = None, + reply_to_story_id: int = None, schedule_date: datetime = None, protect_content: bool = None, reply_markup: Union[ @@ -116,6 +117,9 @@ class SendAudio: reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message. + reply_to_story_id (``int``, *optional*): + Unique identifier for the target story. + schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. @@ -217,12 +221,13 @@ class SendAudio: while True: try: + peer = await self.resolve_peer(chat_id) r = await self.invoke( raw.functions.messages.SendMedia( - peer=await self.resolve_peer(chat_id), + peer=peer, media=media, silent=disable_notification or None, - reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id), + reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id, peer, reply_to_story_id), random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, diff --git a/pyrogram/methods/messages/send_cached_media.py b/pyrogram/methods/messages/send_cached_media.py index 36d0b9d2..5d579fb4 100644 --- a/pyrogram/methods/messages/send_cached_media.py +++ b/pyrogram/methods/messages/send_cached_media.py @@ -36,6 +36,7 @@ class SendCachedMedia: disable_notification: bool = None, message_thread_id: int = None, reply_to_message_id: int = None, + reply_to_story_id: int = None, schedule_date: datetime = None, protect_content: bool = None, reply_markup: Union[ @@ -84,6 +85,9 @@ class SendCachedMedia: reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message. + reply_to_story_id (``int``, *optional*): + Unique identifier for the target story. + schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. @@ -102,12 +106,13 @@ class SendCachedMedia: await app.send_cached_media("me", file_id) """ + peer = await self.resolve_peer(chat_id) r = await self.invoke( raw.functions.messages.SendMedia( - peer=await self.resolve_peer(chat_id), + peer=peer, media=utils.get_input_media_from_file_id(file_id), silent=disable_notification or None, - reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id), + reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id, peer, reply_to_story_id), random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, diff --git a/pyrogram/methods/messages/send_dice.py b/pyrogram/methods/messages/send_dice.py index 0cb573a2..63bf8c6d 100644 --- a/pyrogram/methods/messages/send_dice.py +++ b/pyrogram/methods/messages/send_dice.py @@ -32,6 +32,7 @@ class SendDice: disable_notification: bool = None, message_thread_id: int = None, reply_to_message_id: int = None, + reply_to_story_id: int = None, schedule_date: datetime = None, protect_content: bool = None, reply_markup: Union[ @@ -69,6 +70,9 @@ class SendDice: reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message. + reply_to_story_id (``int``, *optional*): + Unique identifier for the target story. + schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. @@ -94,12 +98,13 @@ class SendDice: # Send a basketball await app.send_dice(chat_id, "🏀") """ + peer = await self.resolve_peer(chat_id) r = await self.invoke( raw.functions.messages.SendMedia( - peer=await self.resolve_peer(chat_id), + peer=peer, media=raw.types.InputMediaDice(emoticon=emoji), silent=disable_notification or None, - reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id), + reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id, peer, reply_to_story_id), random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, diff --git a/pyrogram/methods/messages/send_document.py b/pyrogram/methods/messages/send_document.py index 3388f7d8..8685d75e 100644 --- a/pyrogram/methods/messages/send_document.py +++ b/pyrogram/methods/messages/send_document.py @@ -44,6 +44,7 @@ class SendDocument: disable_notification: bool = None, message_thread_id: int = None, reply_to_message_id: int = None, + reply_to_story_id: int = None, schedule_date: datetime = None, protect_content: bool = None, reply_markup: Union[ @@ -108,6 +109,9 @@ class SendDocument: reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message. + reply_to_story_id (``int``, *optional*): + Unique identifier for the target story. + schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. @@ -195,12 +199,13 @@ class SendDocument: while True: try: + peer = await self.resolve_peer(chat_id) r = await self.invoke( raw.functions.messages.SendMedia( - peer=await self.resolve_peer(chat_id), + peer=peer, media=media, silent=disable_notification or None, - reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id), + reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id, peer, reply_to_story_id), random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, diff --git a/pyrogram/methods/messages/send_media_group.py b/pyrogram/methods/messages/send_media_group.py index a6fcc598..4fc64b4a 100644 --- a/pyrogram/methods/messages/send_media_group.py +++ b/pyrogram/methods/messages/send_media_group.py @@ -45,6 +45,7 @@ class SendMediaGroup: disable_notification: bool = None, message_thread_id: int = None, reply_to_message_id: int = None, + reply_to_story_id: int = None, schedule_date: datetime = None, protect_content: bool = None, ) -> List["types.Message"]: @@ -72,6 +73,9 @@ class SendMediaGroup: reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message. + reply_to_story_id (``int``, *optional*): + Unique identifier for the target story. + schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. @@ -395,12 +399,13 @@ class SendMediaGroup: ) ) + peer = await self.resolve_peer(chat_id) r = await self.invoke( raw.functions.messages.SendMultiMedia( - peer=await self.resolve_peer(chat_id), + peer=peer, multi_media=multi_media, silent=disable_notification or None, - reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id), + reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id, peer, reply_to_story_id), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content ), diff --git a/pyrogram/methods/messages/send_message.py b/pyrogram/methods/messages/send_message.py index 8eb84f86..0bdb73f9 100644 --- a/pyrogram/methods/messages/send_message.py +++ b/pyrogram/methods/messages/send_message.py @@ -35,6 +35,7 @@ class SendMessage: disable_notification: bool = None, message_thread_id: int = None, reply_to_message_id: int = None, + reply_to_story_id: int = None, schedule_date: datetime = None, protect_content: bool = None, reply_markup: Union[ @@ -78,6 +79,9 @@ class SendMessage: reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message. + reply_to_story_id (``int``, *optional*): + Unique identifier for the target story. + schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. @@ -128,12 +132,13 @@ class SendMessage: message, entities = (await utils.parse_text_entities(self, text, parse_mode, entities)).values() + peer = await self.resolve_peer(chat_id) r = await self.invoke( raw.functions.messages.SendMessage( - peer=await self.resolve_peer(chat_id), + peer=peer, no_webpage=disable_web_page_preview or None, silent=disable_notification or None, - reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id), + reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id, peer, reply_to_story_id), random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), reply_markup=await reply_markup.write(self) if reply_markup else None, diff --git a/pyrogram/methods/messages/send_photo.py b/pyrogram/methods/messages/send_photo.py index 3d84dcbc..9688d54d 100644 --- a/pyrogram/methods/messages/send_photo.py +++ b/pyrogram/methods/messages/send_photo.py @@ -42,6 +42,7 @@ class SendPhoto: disable_notification: bool = None, message_thread_id: int = None, reply_to_message_id: int = None, + reply_to_story_id: int = None, schedule_date: datetime = None, protect_content: bool = None, reply_markup: Union[ @@ -99,6 +100,9 @@ class SendPhoto: reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message. + reply_to_story_id (``int``, *optional*): + Unique identifier for the target story. + schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. @@ -179,12 +183,13 @@ class SendPhoto: while True: try: + peer = await self.resolve_peer(chat_id) r = await self.invoke( raw.functions.messages.SendMedia( - peer=await self.resolve_peer(chat_id), + peer=peer, media=media, silent=disable_notification or None, - reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id), + reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id, peer, reply_to_story_id), random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, diff --git a/pyrogram/methods/messages/send_sticker.py b/pyrogram/methods/messages/send_sticker.py index 26e295b9..fe83085c 100644 --- a/pyrogram/methods/messages/send_sticker.py +++ b/pyrogram/methods/messages/send_sticker.py @@ -38,6 +38,7 @@ class SendSticker: disable_notification: bool = None, message_thread_id: int = None, reply_to_message_id: int = None, + reply_to_story_id: int = None, schedule_date: datetime = None, protect_content: bool = None, reply_markup: Union[ @@ -77,6 +78,9 @@ class SendSticker: reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message. + reply_to_story_id (``int``, *optional*): + Unique identifier for the target story. + schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. @@ -154,12 +158,13 @@ class SendSticker: while True: try: + peer = await self.resolve_peer(chat_id) r = await self.invoke( raw.functions.messages.SendMedia( - peer=await self.resolve_peer(chat_id), + peer=peer, media=media, silent=disable_notification or None, - reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id), + reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id, peer, reply_to_story_id), random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, diff --git a/pyrogram/methods/messages/send_video.py b/pyrogram/methods/messages/send_video.py index 8b75f46d..041e0d70 100644 --- a/pyrogram/methods/messages/send_video.py +++ b/pyrogram/methods/messages/send_video.py @@ -49,6 +49,7 @@ class SendVideo: disable_notification: bool = None, message_thread_id: int = None, reply_to_message_id: int = None, + reply_to_story_id: int = None, schedule_date: datetime = None, protect_content: bool = None, reply_markup: Union[ @@ -129,6 +130,9 @@ class SendVideo: reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message. + reply_to_story_id (``int``, *optional*): + Unique identifier for the target story. + schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. @@ -236,12 +240,13 @@ class SendVideo: while True: try: + peer = await self.resolve_peer(chat_id) r = await self.invoke( raw.functions.messages.SendMedia( - peer=await self.resolve_peer(chat_id), + peer=peer, media=media, silent=disable_notification or None, - reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id), + reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id, peer, reply_to_story_id), random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, diff --git a/pyrogram/methods/messages/send_video_note.py b/pyrogram/methods/messages/send_video_note.py index 3ffda5b1..54c924af 100644 --- a/pyrogram/methods/messages/send_video_note.py +++ b/pyrogram/methods/messages/send_video_note.py @@ -40,6 +40,7 @@ class SendVideoNote: disable_notification: bool = None, message_thread_id: int = None, reply_to_message_id: int = None, + reply_to_story_id: int = None, schedule_date: datetime = None, protect_content: bool = None, reply_markup: Union[ @@ -91,6 +92,9 @@ class SendVideoNote: reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message + reply_to_story_id (``int``, *optional*): + Unique identifier for the target story. + schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. @@ -178,12 +182,13 @@ class SendVideoNote: while True: try: + peer = await self.resolve_peer(chat_id) r = await self.invoke( raw.functions.messages.SendMedia( - peer=await self.resolve_peer(chat_id), + peer=peer, media=media, silent=disable_notification or None, - reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id), + reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id, peer, reply_to_story_id), random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, diff --git a/pyrogram/methods/messages/send_voice.py b/pyrogram/methods/messages/send_voice.py index 8905d41b..c9dfde7c 100644 --- a/pyrogram/methods/messages/send_voice.py +++ b/pyrogram/methods/messages/send_voice.py @@ -42,6 +42,7 @@ class SendVoice: disable_notification: bool = None, message_thread_id: int = None, reply_to_message_id: int = None, + reply_to_story_id: int = None, schedule_date: datetime = None, protect_content: bool = None, reply_markup: Union[ @@ -94,6 +95,9 @@ class SendVoice: reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message + reply_to_story_id (``int``, *optional*): + Unique identifier for the target story. + schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. @@ -179,12 +183,13 @@ class SendVoice: while True: try: + peer = await self.resolve_peer(chat_id) r = await self.invoke( raw.functions.messages.SendMedia( - peer=await self.resolve_peer(chat_id), + peer=peer, media=media, silent=disable_notification or None, - reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id), + reply_to=utils.get_reply_to(reply_to_message_id, message_thread_id, peer, reply_to_story_id), random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, diff --git a/pyrogram/methods/stories/__init__.py b/pyrogram/methods/stories/__init__.py new file mode 100644 index 00000000..cfccde76 --- /dev/null +++ b/pyrogram/methods/stories/__init__.py @@ -0,0 +1,46 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from .apply_boost import ApplyBoost +from .delete_stories import DeleteStories +from .edit_story import EditStory +from .export_story_link import ExportStoryLink +from .get_all_stories import GetAllStories +from .get_peer_stories import GetPeerStories +from .get_stories_archive import GetStoriesArchive +from .get_stories import GetStories +from .increment_story_views import IncrementStoryViews +from .read_stories import ReadStories +from .send_story import SendStory +from .toggle_stories_pinned import ToggleStoriesPinned + +class Stories( + ApplyBoost, + DeleteStories, + EditStory, + ExportStoryLink, + GetAllStories, + GetPeerStories, + GetStories, + GetStoriesArchive, + IncrementStoryViews, + ReadStories, + SendStory, + ToggleStoriesPinned +): + pass diff --git a/pyrogram/methods/stories/apply_boost.py b/pyrogram/methods/stories/apply_boost.py new file mode 100644 index 00000000..725157f6 --- /dev/null +++ b/pyrogram/methods/stories/apply_boost.py @@ -0,0 +1,53 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Union + +import pyrogram +from pyrogram import raw + + +class ApplyBoost: + async def apply_boost( + self: "pyrogram.Client", + chat_id: Union[int, str], + ) -> bool: + """Apply boost + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + Returns: + ``str``: On success, a bool is returned. + + Example: + .. code-block:: python + + # Apply boost to chat id + app.apply_boost(chat_id) + """ + r = await self.invoke( + raw.functions.stories.ApplyBoost( + peer=await self.resolve_peer(chat_id), + ) + ) + + return r diff --git a/pyrogram/methods/stories/delete_stories.py b/pyrogram/methods/stories/delete_stories.py new file mode 100644 index 00000000..c37892c7 --- /dev/null +++ b/pyrogram/methods/stories/delete_stories.py @@ -0,0 +1,67 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import List, Union, Iterable + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class DeleteStories: + async def delete_stories( + self: "pyrogram.Client", + chat_id: Union[int, str], + story_ids: Union[int, Iterable[int]], + ) -> List[int]: + """Delete stories. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + story_ids (``int`` | ``list``): + Unique identifier (int) or list of unique identifiers (list of int) for the target stories. + + Returns: + List of ``int``: List of deleted stories IDs + + Example: + .. code-block:: python + + # Delete a single story + app.delete_stories(chat_id, 1) + + # Delete multiple stories + app.delete_stories(chat_id, [1, 2]) + """ + is_iterable = not isinstance(story_ids, int) + ids = list(story_ids) if is_iterable else [story_ids] + + r = await self.invoke( + raw.functions.stories.DeleteStories( + peer=await self.resolve_peer(chat_id), + id=ids + ) + ) + + return types.List(r) diff --git a/pyrogram/methods/stories/edit_story.py b/pyrogram/methods/stories/edit_story.py new file mode 100644 index 00000000..982d1372 --- /dev/null +++ b/pyrogram/methods/stories/edit_story.py @@ -0,0 +1,233 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +import os +import re +from typing import List, Union, BinaryIO, Callable + +import pyrogram +from pyrogram import enums, raw, types, utils, StopTransmission +from pyrogram.errors import FilePartMissing + +class EditStory: + async def edit_story( + self: "pyrogram.Client", + chat_id: Union[int, str], + story_id: int, + media: Union[str, BinaryIO] = None, + caption: str = None, + duration: int = 0, + width: int = 0, + height: int = 0, + thumb: Union[str, BinaryIO] = None, + supports_streaming: bool = True, + file_name: str = None, + privacy: "enums.StoriesPrivacyRules" = None, + allowed_users: List[int] = None, + denied_users: List[int] = None, + allowed_chats: List[int] = None, + denied_chats: List[int] = None, + parse_mode: "enums.ParseMode" = None, + caption_entities: List["types.MessageEntity"] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> "types.Story": + """Edit story. + + .. include:: /_includes/usable-by/users.rst + + Note: You must pass one of following paramater *animation*, *photo*, *video* + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + media (``str`` | ``BinaryIO``, *optional*): + Video or photo to send. + Pass a file_id as string to send a animation that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a animation from the Internet, + pass a file path as string to upload a new animation that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + caption (``str``, *optional*): + Story caption, 0-1024 characters. + + duration (``int``, *optional*): + Duration of sent video in seconds. + + width (``int``, *optional*): + Video width. + + height (``int``, *optional*): + Video height. + + thumb (``str`` | ``BinaryIO``, *optional*): + Thumbnail of the video sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 320 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + privacy (:obj:`~pyrogram.enums.StoriesPrivacyRules`, *optional*): + Story privacy. + Defaults to :obj:`~pyrogram.enums.StoriesPrivacyRules.PUBLIC` + + allowed_chats (List of ``int``, *optional*): + List of chat_id which participant allowed to view the story. + + denied_chats (List of ``int``, *optional*): + List of chat_id which participant denied to view the story. + + allowed_users (List of ``int``, *optional*): + List of user_id whos allowed to view the story. + + denied_users (List of ``int``, *optional*): + List of user_id whos denied to view the story. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Returns: + :obj:`~pyrogram.types.Story` a single story is returned. + + Example: + .. code-block:: python + + # Send new photo story + photo_id = "abcd12345" + await app.send_story(photo=photo_id, caption='Hello guys.') + + Raises: + ValueError: In case of invalid arguments. + """ + # TODO: media_areas + + if privacy: + privacy_rules = [types.StoriesPrivacyRules(type=privacy)] + + message, entities = (await utils.parse_text_entities(self, caption, parse_mode, caption_entities)).values() + + try: + if isinstance(media, str): + if os.path.isfile(media): + thumb = await self.save_file(thumb) + file = await self.save_file(media, progress=progress, progress_args=progress_args) + mime_type = self.guess_mime_type(file.name) + if mime_type == "video/mp4": + media = raw.types.InputMediaUploadedDocument( + mime_type=mime_type, + file=file, + thumb=thumb, + attributes=[ + raw.types.DocumentAttributeVideo( + duration=duration, + w=width, + h=height, + ), + raw.types.DocumentAttributeFilename(file_name=file_name or os.path.basename(media)) + ] + ) + else: + media = raw.types.InputMediaUploadedPhoto( + file=file, + ) + elif re.match("^https?://", media): + mime_type = self.guess_mime_type(media) + if mime_type == "video/mp4": + media = raw.types.InputMediaDocumentExternal( + url=media, + ) + else: + media = raw.types.InputMediaPhotoExternal( + url=media, + ) + else: + media = utils.get_input_media_from_file_id(media) + else: + thumb = await self.save_file(thumb) + file = await self.save_file(media, progress=progress, progress_args=progress_args) + mime_type = self.guess_mime_type(file.name) + if mime_type == "video/mp4": + media = raw.types.InputMediaUploadedDocument( + mime_type=mime_type, + file=file, + thumb=thumb, + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=supports_streaming or None, + duration=duration, + w=width, + h=height, + ), + raw.types.DocumentAttributeFilename(file_name=file_name or media.name) + ] + ) + else: + media = raw.types.InputMediaUploadedPhoto( + file=file, + ) + + if allowed_chats: + chats = [await self.resolve_peer(chat_id) for chat_id in allowed_chats] + privacy_rules.append(raw.types.InputPrivacyValueAllowChatParticipants(chats=chats)) + if denied_chats: + chats = [await self.resolve_peer(chat_id) for chat_id in denied_chats] + privacy_rules.append(raw.types.InputPrivacyValueDisallowChatParticipants(chats=chats)) + if allowed_users: + users = [await self.resolve_peer(user_id) for user_id in allowed_users] + privacy_rules.append(raw.types.InputPrivacyValueAllowUsers(users=users)) + if denied_users: + users = [await self.resolve_peer(user_id) for user_id in denied_users] + privacy_rules.append(raw.types.InputPrivacyValueDisallowUsers(users=users)) + + while True: + try: + r = await self.invoke( + raw.functions.stories.EditStory( + peer=await self.resolve_peer(chat_id), + id=story_id, + media=media, + caption=message, + entities=entities, + privacy_rules=privacy_rules, + ) + ) + except FilePartMissing as e: + await self.save_file(media, file_id=file.id, file_part=e.value) + else: + for i in r.updates: + if isinstance(i, raw.types.UpdateStory): + return await types.Story._parse(self, i.story, i.peer) + except StopTransmission: + return None diff --git a/pyrogram/methods/stories/export_story_link.py b/pyrogram/methods/stories/export_story_link.py new file mode 100644 index 00000000..ac36c470 --- /dev/null +++ b/pyrogram/methods/stories/export_story_link.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class ExportStoryLink: + async def export_story_link( + self: "pyrogram.Client", + chat_id: Union[int, str], + story_id: int, + ) -> "types.ExportedStoryLink": + """Export a story link. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + story_id (``int``): + Unique identifier of the target story. + + Returns: + ``str``: On success, a link to the exported story is returned. + + Example: + .. code-block:: python + + # Export a story link + link = app.export_story_link(chat_id, 1) + """ + r = await self.invoke( + raw.functions.stories.ExportStoryLink( + peer=await self.resolve_peer(chat_id), + id=story_id + ) + ) + + return r.link diff --git a/pyrogram/methods/stories/get_all_stories.py b/pyrogram/methods/stories/get_all_stories.py new file mode 100644 index 00000000..3ab00132 --- /dev/null +++ b/pyrogram/methods/stories/get_all_stories.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import AsyncGenerator, Union, Optional + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetAllStories: + async def get_all_stories( + self: "pyrogram.Client", + next: Optional[bool] = None, + hidden: Optional[bool] = None, + state: Optional[str] = None, + ) -> Optional[AsyncGenerator["types.Story", None]]: + """Get all active stories. + + .. include:: /_includes/usable-by/users.rst + + Returns: + ``Generator``: On success, a generator yielding :obj:`~pyrogram.types.Story` objects is returned. + + Example: + .. code-block:: python + + # Get all active story + async for story in app.get_all_stories(): + print(story) + + Raises: + ValueError: In case of invalid arguments. + """ + + r = await self.invoke( + raw.functions.stories.GetAllStories( + next=next, + hidden=hidden, + state=state + ) + ) + + for peer_story in r.peer_stories: + for story in peer_story.stories: + yield await types.Story._parse(self, story, peer_story.peer) diff --git a/pyrogram/methods/stories/get_peer_stories.py b/pyrogram/methods/stories/get_peer_stories.py new file mode 100644 index 00000000..f321a6d2 --- /dev/null +++ b/pyrogram/methods/stories/get_peer_stories.py @@ -0,0 +1,63 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import AsyncGenerator, Union, Optional + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetPeerStories: + async def get_peer_stories( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> Optional[AsyncGenerator["types.Story", None]]: + """Get all active stories from an user by using user identifiers. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user. + For your personal story you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + Returns: + ``Generator``: On success, a generator yielding :obj:`~pyrogram.types.Story` objects is returned. + + Example: + .. code-block:: python + + # Get all active story from spesific user + async for story in app.get_peer_stories(chat_id): + print(story) + + Raises: + ValueError: In case of invalid arguments. + """ + peer = await self.resolve_peer(chat_id) + + r = await self.invoke( + raw.functions.stories.GetPeerStories( + peer=peer + ) + ) + + for story in r.stories.stories: + yield await types.Story._parse(self, story, peer) diff --git a/pyrogram/methods/stories/get_stories.py b/pyrogram/methods/stories/get_stories.py new file mode 100644 index 00000000..7266d287 --- /dev/null +++ b/pyrogram/methods/stories/get_stories.py @@ -0,0 +1,76 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Union, Iterable + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetStories: + async def get_stories( + self: "pyrogram.Client", + chat_id: Union[int, str], + story_ids: Union[int, Iterable[int]], + ) -> "types.Stories": + """Get stories by id. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user. + For your personal story you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + story_ids (List of ``int`` ``32-bit``): + Pass a single story identifier or an iterable of story ids (as integers) to get the content of the + story themselves. + + Returns: + :obj:`~pyrogram.types.Story` | List of :obj:`~pyrogram.types.Story`: In case *story_ids* was not + a list, a single story is returned, otherwise a list of stories is returned. + + Example: + .. code-block:: python + + # Get stories by id + stories = await app.get_stories_by_id(chat_id, [1, 2, 3]) + + for story in stories: + print(story) + """ + is_iterable = not isinstance(story_ids, int) + ids = list(story_ids) if is_iterable else [story_ids] + + peer = await self.resolve_peer(chat_id) + r = await self.invoke( + raw.functions.stories.GetStoriesByID( + peer=peer, + id=ids + ) + ) + + stories = [] + + for i in r.updates: + if isinstance(i, raw.types.stories.PeerStories): + stories = [await types.Story._parse(self, story, peer) for story in r.stories] + + return types.List(stories) if is_iterable else stories[0] diff --git a/pyrogram/methods/stories/get_stories_archive.py b/pyrogram/methods/stories/get_stories_archive.py new file mode 100644 index 00000000..c91ac551 --- /dev/null +++ b/pyrogram/methods/stories/get_stories_archive.py @@ -0,0 +1,74 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import AsyncGenerator, Union, Optional + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetStoriesArchive: + async def get_stories_archive( + self: "pyrogram.Client", + chat_id: Union[int, str], + limit: int = 0, + offset_id: int = 0 + ) -> Optional[AsyncGenerator["types.Story", None]]: + """Get stories archive. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + limit (``int``, *optional*): + Limits the number of stories to be retrieved. + By default, no limit is applied and all stories are returned. + + offset_id (``int``, *optional*): + Identifier of the first story to be returned. + + Returns: + ``Generator``: On success, a generator yielding :obj:`~pyrogram.types.Story` objects is returned. + + Example: + .. code-block:: python + + # Get story archive + async for story in app.get_stories_archive(chat_id): + print(story) + + Raises: + ValueError: In case of invalid arguments. + """ + peer = await self.resolve_peer(chat_id) + + r = await self.invoke( + raw.functions.stories.GetStoriesArchive( + peer=peer, + offset_id=offset_id, + limit=limit + ) + ) + + for story in r.stories: + yield await types.Story._parse(self, story, peer) diff --git a/pyrogram/methods/stories/increment_story_views.py b/pyrogram/methods/stories/increment_story_views.py new file mode 100644 index 00000000..ad4a4a19 --- /dev/null +++ b/pyrogram/methods/stories/increment_story_views.py @@ -0,0 +1,60 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Union + +import pyrogram +from pyrogram import raw + + +class IncrementStoryViews: + async def increment_story_views( + self: "pyrogram.Client", + chat_id: Union[int, str], + story_id: int, + ) -> bool: + """Increment story views. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + story_id (``int``): + Unique identifier of the target story. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Increment story views + await app.increment_story_views(chat_id, 1) + """ + r = await self.invoke( + raw.functions.stories.IncrementStoryViews( + peer=await self.resolve_peer(chat_id), + id=story_id + ) + ) + + return r diff --git a/pyrogram/methods/stories/read_stories.py b/pyrogram/methods/stories/read_stories.py new file mode 100644 index 00000000..eec4c1a0 --- /dev/null +++ b/pyrogram/methods/stories/read_stories.py @@ -0,0 +1,60 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import List, Union + +import pyrogram +from pyrogram import raw, types + + +class ReadStories: + async def read_stories( + self: "pyrogram.Client", + chat_id: Union[int, str], + max_id: int = 0, + ) -> List[int]: + """Read stories. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + max_id (``int``, *optional*): + Maximum identifier of the target story to read. + + Returns: + List of ``int``: On success, a list of read stories is returned. + + Example: + .. code-block:: python + + # Read stories + await app.read_stories(chat_id) + """ + r = await self.invoke( + raw.functions.stories.ReadStories( + peer=await self.resolve_peer(chat_id), + max_id=max_id + ) + ) + + return types.List(r) diff --git a/pyrogram/methods/stories/send_story.py b/pyrogram/methods/stories/send_story.py new file mode 100644 index 00000000..7b977aee --- /dev/null +++ b/pyrogram/methods/stories/send_story.py @@ -0,0 +1,252 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +import os +import re +from typing import List, Union, BinaryIO, Callable + +import pyrogram +from pyrogram import enums, raw, types, utils, StopTransmission +from pyrogram.errors import FilePartMissing + +class SendStory: + async def send_story( + self: "pyrogram.Client", + chat_id: Union[int, str], + media: Union[str, BinaryIO], + caption: str = None, + period: int = None, + duration: int = 0, + width: int = 0, + height: int = 0, + thumb: Union[str, BinaryIO] = None, + supports_streaming: bool = True, + file_name: str = None, + privacy: "enums.StoriesPrivacyRules" = None, + allowed_users: List[int] = None, + denied_users: List[int] = None, + allowed_chats: List[int] = None, + denied_chats: List[int] = None, + pinned: bool = None, + protect_content: bool = None, + parse_mode: "enums.ParseMode" = None, + caption_entities: List["types.MessageEntity"] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> "types.Story": + """Send new story. + + .. include:: /_includes/usable-by/users.rst + + Note: You must pass one of following paramater *animation*, *photo*, *video* + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + media (``str`` | ``BinaryIO``): + Video or photo to send. + Pass a file_id as string to send a animation that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a animation from the Internet, + pass a file path as string to upload a new animation that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + caption (``str``, *optional*): + Story caption, 0-1024 characters. + + period (``int``, *optional*): + How long the story will posted, in secs. + only for premium users. + + duration (``int``, *optional*): + Duration of sent video in seconds. + + width (``int``, *optional*): + Video width. + + height (``int``, *optional*): + Video height. + + thumb (``str`` | ``BinaryIO``, *optional*): + Thumbnail of the video sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 320 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + privacy (:obj:`~pyrogram.enums.StoriesPrivacyRules`, *optional*): + Story privacy. + Defaults to :obj:`~pyrogram.enums.StoriesPrivacyRules.PUBLIC` + + allowed_chats (List of ``int``, *optional*): + List of chat_id which participant allowed to view the story. + + denied_chats (List of ``int``, *optional*): + List of chat_id which participant denied to view the story. + + allowed_users (List of ``int``, *optional*): + List of user_id whos allowed to view the story. + + denied_users (List of ``int``, *optional*): + List of user_id whos denied to view the story. + + pinned (``bool``, *optional*): + if True, the story will be pinned. + default to False. + + protect_content (``bool``, *optional*): + Protects the contents of the sent story from forwarding and saving. + default to False. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Returns: + :obj:`~pyrogram.types.Story` a single story is returned. + + Example: + .. code-block:: python + + # Send new photo story + photo_id = "abcd12345" + await app.send_story(photo=photo_id, caption='Hello guys.') + + Raises: + ValueError: In case of invalid arguments. + """ + # TODO: media_areas + + if privacy: + privacy_rules = [types.StoriesPrivacyRules(type=privacy)] + else: + privacy_rules = [types.StoriesPrivacyRules(type=enums.StoriesPrivacyRules.PUBLIC)] + + message, entities = (await utils.parse_text_entities(self, caption, parse_mode, caption_entities)).values() + + try: + if isinstance(media, str): + if os.path.isfile(media): + thumb = await self.save_file(thumb) + file = await self.save_file(media, progress=progress, progress_args=progress_args) + mime_type = self.guess_mime_type(file.name) + if mime_type == "video/mp4": + media = raw.types.InputMediaUploadedDocument( + mime_type=mime_type, + file=file, + thumb=thumb, + attributes=[ + raw.types.DocumentAttributeVideo( + duration=duration, + w=width, + h=height, + ), + raw.types.DocumentAttributeFilename(file_name=file_name or os.path.basename(media)) + ] + ) + else: + media = raw.types.InputMediaUploadedPhoto( + file=file, + ) + elif re.match("^https?://", media): + mime_type = self.guess_mime_type(media) + if mime_type == "video/mp4": + media = raw.types.InputMediaDocumentExternal( + url=media, + ) + else: + media = raw.types.InputMediaPhotoExternal( + url=media, + ) + else: + media = utils.get_input_media_from_file_id(media) + else: + thumb = await self.save_file(thumb) + file = await self.save_file(media, progress=progress, progress_args=progress_args) + mime_type = self.guess_mime_type(file.name) + if mime_type == "video/mp4": + media = raw.types.InputMediaUploadedDocument( + mime_type=mime_type, + file=file, + thumb=thumb, + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=supports_streaming or None, + duration=duration, + w=width, + h=height, + ), + raw.types.DocumentAttributeFilename(file_name=file_name or media.name) + ] + ) + else: + media = raw.types.InputMediaUploadedPhoto( + file=file, + ) + + if allowed_chats: + chats = [await self.resolve_peer(chat_id) for chat_id in allowed_chats] + privacy_rules.append(raw.types.InputPrivacyValueAllowChatParticipants(chats=chats)) + if denied_chats: + chats = [await self.resolve_peer(chat_id) for chat_id in denied_chats] + privacy_rules.append(raw.types.InputPrivacyValueDisallowChatParticipants(chats=chats)) + if allowed_users: + users = [await self.resolve_peer(user_id) for user_id in allowed_users] + privacy_rules.append(raw.types.InputPrivacyValueAllowUsers(users=users)) + if denied_users: + users = [await self.resolve_peer(user_id) for user_id in denied_users] + privacy_rules.append(raw.types.InputPrivacyValueDisallowUsers(users=users)) + + while True: + try: + r = await self.invoke( + raw.functions.stories.SendStory( + peer=await self.resolve_peer(chat_id), + media=media, + privacy_rules=privacy_rules, + random_id=self.rnd_id(), + pinned=pinned, + noforwards=protect_content, + caption=message, + entities=entities, + period=period, + ) + ) + except FilePartMissing as e: + await self.save_file(media, file_id=file.id, file_part=e.value) + else: + for i in r.updates: + if isinstance(i, raw.types.UpdateStory): + return await types.Story._parse(self, i.story, i.peer) + except StopTransmission: + return None diff --git a/pyrogram/methods/stories/toggle_stories_pinned.py b/pyrogram/methods/stories/toggle_stories_pinned.py new file mode 100644 index 00000000..eadc49c9 --- /dev/null +++ b/pyrogram/methods/stories/toggle_stories_pinned.py @@ -0,0 +1,68 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import List, Union, Iterable + +import pyrogram +from pyrogram import raw + + +class ToggleStoriesPinned: + async def toggle_stories_pinned( + self: "pyrogram.Client", + chat_id: Union[int, str], + stories_ids: Union[int, Iterable[int]], + pinned: bool, + ) -> List[int]: + """Toggle stories pinned. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + + stories_ids (List of ``int`` ``32-bit``): + List of unique identifiers of the target stories. + + pinned (``bool``): + If set to ``True``, the stories will be pinned. + + Returns: + List of ``int``: List of pinned stories IDs + + Example: + .. code-block:: python + + # Pin a single story + await app.toggle_stories_pinned(chat_id, 123456789, True) + + """ + is_iterable = not isinstance(stories_ids, int) + stories_ids = list(stories_ids) if is_iterable else [stories_ids] + + r = await self.invoke( + raw.functions.stories.TogglePinned( + peer=await self.resolve_peer(chat_id), + id=stories_ids, + pinned=pinned + ) + ) + + return types.List(r) diff --git a/pyrogram/types/input_message_content/__init__.py b/pyrogram/types/input_message_content/__init__.py index 91b5dfbb..4936aaff 100644 --- a/pyrogram/types/input_message_content/__init__.py +++ b/pyrogram/types/input_message_content/__init__.py @@ -18,8 +18,9 @@ from .input_message_content import InputMessageContent from .input_reply_to_message import InputReplyToMessage +from .input_reply_to_story import InputReplyToStory from .input_text_message_content import InputTextMessageContent __all__ = [ - "InputMessageContent", "InputReplyToMessage", "InputTextMessageContent" + "InputMessageContent", "InputReplyToMessage", "InputReplyToStory", "InputTextMessageContent" ] diff --git a/pyrogram/types/input_message_content/input_reply_to_story.py b/pyrogram/types/input_message_content/input_reply_to_story.py new file mode 100644 index 00000000..fe00af25 --- /dev/null +++ b/pyrogram/types/input_message_content/input_reply_to_story.py @@ -0,0 +1,48 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from pyrogram import raw +from ..object import Object + + +class InputReplyToStory(Object): + """Contains information about a target replied story. + + Parameters: + user_id (:obj:`~pyrogram.raw.types.InputUser`): + An InputUser. + + story_id (``int``): + Unique identifier for the target story. + """ + + def __init__( + self, *, + user_id: "raw.types.InputUser" = None, + story_id: int = None + ): + super().__init__() + + self.user_id = user_id + self.story_id = story_id + + def write(self): + return raw.types.InputReplyToStory( + user_id=self.user_id, + story_id=self.story_id + ).write() diff --git a/pyrogram/types/messages_and_media/__init__.py b/pyrogram/types/messages_and_media/__init__.py index 0212c957..b6dcb5d5 100644 --- a/pyrogram/types/messages_and_media/__init__.py +++ b/pyrogram/types/messages_and_media/__init__.py @@ -38,6 +38,11 @@ from .poll_option import PollOption from .reaction import Reaction from .sticker import Sticker from .stripped_thumbnail import StrippedThumbnail +from .stories_privacy_rules import StoriesPrivacyRules +from .story import Story +from .story_deleted import StoryDeleted +from .story_skipped import StorySkipped +from .story_views import StoryViews from .thumbnail import Thumbnail from .venue import Venue from .video import Video @@ -46,11 +51,13 @@ from .voice import Voice from .web_app_data import WebAppData from .web_page import WebPage from .message_reactions import MessageReactions +from .message_story import MessageStory __all__ = [ "Animation", "Audio", "Contact", "Document", "ForumTopic", "ForumTopicCreated", "ForumTopicClosed", "ForumTopicReopened", "ForumTopicEdited", "GeneralTopicHidden", "GeneralTopicUnhidden", "Game", "Location", "Message", "MessageEntity", "Photo", "Thumbnail", - "StrippedThumbnail", "Poll", "PollOption", "Sticker", "Venue", "Video", "VideoNote", "Voice", - "WebPage", "Dice", "Reaction", "WebAppData", "MessageReactions" + "StrippedThumbnail", "Story", "StoryDeleted", "StorySkipped", "StoryViews", "StoriesPrivacyRules", "Poll", "PollOption", "Sticker", + "Venue", "Video", "VideoNote", "Voice", "WebPage", "Dice", "Reaction", "WebAppData", + "MessageReactions", "MessageStory" ] diff --git a/pyrogram/types/messages_and_media/message.py b/pyrogram/types/messages_and_media/message.py index c8c5fe8e..b6f3b4c8 100644 --- a/pyrogram/types/messages_and_media/message.py +++ b/pyrogram/types/messages_and_media/message.py @@ -180,6 +180,9 @@ class Message(Object, Update): game (:obj:`~pyrogram.types.Game`, *optional*): Message is a game, information about the game. + story (:obj:`~pyrogram.types.MessageStory`): + Message is a story, information about the story. + video (:obj:`~pyrogram.types.Video`, *optional*): Message is a video, information about the video. @@ -376,6 +379,7 @@ class Message(Object, Update): sticker: "types.Sticker" = None, animation: "types.Animation" = None, game: "types.Game" = None, + story: "types.MessageStory" = None, video: "types.Video" = None, voice: "types.Voice" = None, video_note: "types.VideoNote" = None, @@ -462,6 +466,7 @@ class Message(Object, Update): self.sticker = sticker self.animation = animation self.game = game + self.story = story self.video = video self.voice = voice self.video_note = video_note @@ -744,6 +749,7 @@ class Message(Object, Update): contact = None venue = None game = None + story = None audio = None voice = None animation = None @@ -776,6 +782,9 @@ class Message(Object, Update): elif isinstance(media, raw.types.MessageMediaGame): game = types.Game._parse(client, message) media_type = enums.MessageMediaType.GAME + elif isinstance(media, raw.types.MessageMediaStory): + story = types.MessageStory._parse(media) + media_type = enums.MessageMediaType.STORY elif isinstance(media, raw.types.MessageMediaDocument): doc = media.document @@ -904,6 +913,7 @@ class Message(Object, Update): voice=voice, animation=animation, game=game, + story=story, video=video, video_note=video_note, sticker=sticker, diff --git a/pyrogram/types/messages_and_media/message_story.py b/pyrogram/types/messages_and_media/message_story.py new file mode 100644 index 00000000..c31735f3 --- /dev/null +++ b/pyrogram/types/messages_and_media/message_story.py @@ -0,0 +1,56 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from pyrogram import raw +from ..object import Object + + +class MessageStory(Object): + """Contains information about a forwarded story. + + Parameters: + chat_id (``int``): + Unique user identifier of story sender. + + story_id (``int``): + Unique story identifier. + + """ + + def __init__( + self, + *, + chat_id: int, + story_id: int + ): + super().__init__() + + self.chat_id = chat_id + self.story_id = story_id + + @staticmethod + def _parse(message_story: "raw.types.MessageMediaStory") -> "MessageStory": + if isinstance(message_story.peer, raw.types.PeerChannel): + chat_id = message_story.peer.channel_id + else: + chat_id = message_story.peer.user_id + + return MessageStory( + chat_id=chat_id, + story_id=message_story.id + ) diff --git a/pyrogram/types/messages_and_media/stories_privacy_rules.py b/pyrogram/types/messages_and_media/stories_privacy_rules.py new file mode 100644 index 00000000..3e816114 --- /dev/null +++ b/pyrogram/types/messages_and_media/stories_privacy_rules.py @@ -0,0 +1,47 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from pyrogram import enums, raw +from ..object import Object + +class StoriesPrivacyRules(Object): + """A story privacy rules. + + Parameters: + type (:obj:`~pyrogram.enums.StoriesPrivacyRules`): + Story privacy type. + """ + + def __init__( + self, *, + type: "enums.StoriesPrivacyRules" + ): + super().__init__() + self.type = type + + def write(self): + if self.type == enums.StoriesPrivacyRules.PUBLIC: + return raw.types.InputPrivacyValueAllowAll().write() + if self.type == enums.StoriesPrivacyRules.CLOSE_FRIENDS: + return raw.types.InputPrivacyValueAllowCloseFriends().write() + if self.type == enums.StoriesPrivacyRules.CONTACTS: + return raw.types.InputPrivacyValueAllowContacts().write() + if self.type == enums.StoriesPrivacyRules.NO_CONTACTS: + return raw.types.InputPrivacyValueDisallowContacts().write() + if self.type == enums.StoriesPrivacyRules.PRIVATE: + return raw.types.InputPrivacyValueDisallowAll().write() diff --git a/pyrogram/types/messages_and_media/story.py b/pyrogram/types/messages_and_media/story.py new file mode 100644 index 00000000..343e360a --- /dev/null +++ b/pyrogram/types/messages_and_media/story.py @@ -0,0 +1,1498 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +import pyrogram + +from datetime import datetime +from pyrogram import enums, raw, types, utils +from typing import BinaryIO, Callable, List, Optional, Union +from ..object import Object +from ..update import Update + +class Story(Object, Update): + """A story. + + Parameters: + id (``int``): + Unique story identifier. + + from_user (:obj:`~pyrogram.types.User`, *optional*): + Sender of the story. + + date (:py:obj:`~datetime.datetime`, *optional*): + Date the story was sent. + + expire_date (:py:obj:`~datetime.datetime`, *optional*): + Date the story will be expired. + + media (:obj:`~pyrogram.enums.MessageMediaType`, *optional*): + The media type of the Story. + This field will contain the enumeration type of the media message. + You can use ``media = getattr(message, message.media.value)`` to access the media message. + + has_protected_content (``bool``, *optional*): + True, if the story can't be forwarded. + + animation (:obj:`~pyrogram.types.Animation`, *optional*): + Story is an animation, information about the animation. + + photo (:obj:`~pyrogram.types.Photo`, *optional*): + Story is a photo, information about the photo. + + video (:obj:`~pyrogram.types.Video`, *optional*): + Story is a video, information about the video. + + edited (``bool``, *optional*): + True, if the Story has been edited. + + pinned (``bool``, *optional*): + True, if the Story is pinned. + + public (``bool``, *optional*): + True, if the Story is shared with public. + + close_friends (``bool``, *optional*): + True, if the Story is shared with close_friends only. + + contacts (``bool``, *optional*): + True, if the Story is shared with contacts only. + + selected_contacts (``bool``, *optional*): + True, if the Story is shared with selected contacts only. + + caption (``str``, *optional*): + Caption for the Story, 0-1024 characters. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*): + For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the caption. + + views (:obj:`~pyrogram.types.StoryViews`, *optional*): + Stories views. + + privacy (:obj:`~pyrogram.enums.StoryPrivacy`, *optional*): + Story privacy. + + allowed_chats (List of ``int``, *optional*): + List of chat_ids which participant allowed to view the story. + + denied_chats (List of ``int``, *optional*): + List of chat_ids which participant denied to view the story. + + allowed_users (List of ``int``, *optional*): + List of user_ids whos allowed to view the story. + + denied_users (List of ``int``, *optional*): + List of user_ids whos denied to view the story. + """ + + # TODO: Add Media Areas + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int, + from_user: "types.User", + date: datetime = None, + expire_date: datetime = None, + media: "enums.MessageMediaType", + has_protected_content: bool = None, + animation: "types.Animation" = None, + photo: "types.Photo" = None, + video: "types.Video" = None, + edited: bool = None, + pinned: bool = None, + public: bool = None, + close_friends: bool = None, + contacts: bool = None, + selected_contacts: bool = None, + caption: str = None, + caption_entities: List["types.MessageEntity"] = None, + views: "types.StoryViews" = None, + privacy: "enums.StoryPrivacy" = None, + allowed_users: List[int] = None, + denied_users: List[int] = None, + allowed_chats: List[int] = None, + denied_chats: List[int] = None + ): + super().__init__(client) + + self.id = id + self.from_user = from_user + self.date = date + self.expire_date = expire_date + self.media = media + self.has_protected_content = has_protected_content + self.animation = animation + self.photo = photo + self.video = video + self.edited = edited + self.pinned = pinned + self.public = public + self.close_friends = close_friends + self.contacts = contacts + self.selected_contacts = selected_contacts + self.caption = caption + self.caption_entities = caption_entities + self.views = views + self.privacy = privacy + self.allowed_users = allowed_users + self.denied_users = denied_users + self.allowed_chats = allowed_chats + self.denied_chats = denied_chats + + @staticmethod + async def _parse( + client: "pyrogram.Client", + stories: raw.base.StoryItem, + peer: Union["raw.types.PeerChannel", "raw.types.PeerUser"] + ) -> "Story": + if isinstance(stories, raw.types.StoryItemSkipped): + return await types.StorySkipped._parse(client, stories, peer) + if isinstance(stories, raw.types.StoryItemDeleted): + return await types.StoryDeleted._parse(client, stories, peer) + + entities = [types.MessageEntity._parse(client, entity, {}) for entity in stories.entities] + entities = types.List(filter(lambda x: x is not None, entities)) + animation = None + photo = None + video = None + from_user = None + sender_chat = None + if stories.media: + if isinstance(stories.media, raw.types.MessageMediaPhoto): + photo = types.Photo._parse(client, stories.media.photo, stories.media.ttl_seconds) + media_type = enums.MessageMediaType.PHOTO + elif isinstance(stories.media, raw.types.MessageMediaDocument): + doc = stories.media.document + + if isinstance(doc, raw.types.Document): + attributes = {type(i): i for i in doc.attributes} + + if raw.types.DocumentAttributeAnimated in attributes: + video_attributes = attributes.get(raw.types.DocumentAttributeVideo, None) + animation = types.Animation._parse(client, doc, video_attributes, None) + media_type = enums.MessageMediaType.ANIMATION + elif raw.types.DocumentAttributeVideo in attributes: + video_attributes = attributes.get(raw.types.DocumentAttributeVideo, None) + video = types.Video._parse(client, doc, video_attributes, None, stories.media.ttl_seconds) + media_type = enums.MessageMediaType.VIDEO + else: + media_type = None + else: + media_type = None + + from_user = await client.get_users(peer.user_id) if isinstance(peer, raw.types.PeerUser) else await client.get_chat(peer.channel_id) + + return Story( + id=stories.id, + from_user=from_user, + date=utils.timestamp_to_datetime(stories.date), + expire_date=utils.timestamp_to_datetime(stories.expire_date), + media=media_type, + has_protected_content=stories.noforwards, + animation=animation, + photo=photo, + video=video, + edited=stories.edited, + pinned=stories.pinned, + public=stories.public, + close_friends=stories.close_friends, + contacts=stories.contacts, + selected_contacts=stories.selected_contacts, + caption=stories.caption, + caption_entities=entities or None, + views=types.StoryViews._parse(stories.views), + client=client + ) + + async def reply_text( + self, + text: str, + parse_mode: Optional["enums.ParseMode"] = None, + entities: List["types.MessageEntity"] = None, + disable_web_page_preview: bool = None, + disable_notification: bool = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup=None + ) -> "types.Message": + """Bound method *reply_text* of :obj:`~pyrogram.types.Story`. + + An alias exists as *reply*. + + Use as a shortcut for: + + .. code-block:: python + + await client.send_message( + chat_id=message.chat.id, + text="hello", + reply_to_story_id=story.id + ) + + Example: + .. code-block:: python + + await story.reply_text("hello", quote=True) + + Parameters: + text (``str``): + Text of the message to be sent. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in message text, which can be specified instead of *parse_mode*. + + disable_web_page_preview (``bool``, *optional*): + Disables link previews for links in this message. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + Returns: + On success, the sent Message is returned. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + + return await self._client.send_message( + chat_id=self.from_user.id, + text=text, + parse_mode=parse_mode, + entities=entities, + disable_web_page_preview=disable_web_page_preview, + disable_notification=disable_notification, + reply_to_story_id=self.id, + schedule_date=schedule_date, + protect_content=protect_content, + reply_markup=reply_markup + ) + + reply = reply_text + + async def reply_animation( + self, + animation: Union[str, BinaryIO], + caption: str = "", + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + has_spoiler: bool = None, + duration: int = 0, + width: int = 0, + height: int = 0, + thumb: Union[str, BinaryIO] = None, + file_name: str = None, + disable_notification: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> "types.Message": + """Bound method *reply_animation* :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.send_animation( + chat_id=story.from_user.id, + animation=animation, + reply_to_story_id=story.id + ) + + Example: + .. code-block:: python + + await story.reply_animation(animation) + + Parameters: + animation (``str``): + Animation to send. + Pass a file_id as string to send an animation that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get an animation from the Internet, or + pass a file path as string to upload a new animation that exists on your local machine. + + caption (``str``, *optional*): + Animation caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + has_spoiler (``bool``, *optional*): + Pass True if the animation needs to be covered with a spoiler animation. + + duration (``int``, *optional*): + Duration of sent animation in seconds. + + width (``int``, *optional*): + Animation width. + + height (``int``, *optional*): + Animation height. + + thumb (``str`` | ``BinaryIO``, *optional*): + Thumbnail of the animation file sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 320 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + file_name (``str``, *optional*): + File name of the animation sent. + Defaults to file's path basename. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`~pyrogram.types.Message` is returned. + In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned + instead. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + + return await self._client.send_animation( + chat_id=self.from_user.id, + animation=animation, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + has_spoiler=has_spoiler, + duration=duration, + width=width, + height=height, + thumb=thumb, + file_name=file_name, + disable_notification=disable_notification, + reply_to_story_id=self.id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + async def reply_audio( + self, + audio: Union[str, BinaryIO], + caption: str = "", + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + duration: int = 0, + performer: str = None, + title: str = None, + thumb: Union[str, BinaryIO] = None, + file_name: str = None, + disable_notification: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> "types.Message": + """Bound method *reply_audio* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.send_audio( + chat_id=story.from_user.id, + audio=audio, + reply_to_story_id=story.id + ) + + Example: + .. code-block:: python + + await story.reply_audio(audio) + + Parameters: + audio (``str``): + Audio file to send. + Pass a file_id as string to send an audio file that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get an audio file from the Internet, or + pass a file path as string to upload a new audio file that exists on your local machine. + + caption (``str``, *optional*): + Audio caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + duration (``int``, *optional*): + Duration of the audio in seconds. + + performer (``str``, *optional*): + Performer. + + title (``str``, *optional*): + Track name. + + thumb (``str`` | ``BinaryIO``, *optional*): + Thumbnail of the music file album cover. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 320 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + file_name (``str``, *optional*): + File name of the audio sent. + Defaults to file's path basename. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`~pyrogram.types.Message` is returned. + In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned + instead. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + + return await self._client.send_audio( + chat_id=self.from_user.id, + audio=audio, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + duration=duration, + performer=performer, + title=title, + thumb=thumb, + file_name=file_name, + disable_notification=disable_notification, + reply_to_story_id=self.id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + async def reply_cached_media( + self, + file_id: str, + caption: str = "", + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + disable_notification: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None + ) -> "types.Message": + """Bound method *reply_cached_media* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.send_cached_media( + chat_id=story.from_user.id, + file_id=file_id, + reply_to_story_id=story.id + ) + + Example: + .. code-block:: python + + await story.reply_cached_media(file_id) + + Parameters: + file_id (``str``): + Media to send. + Pass a file_id as string to send a media that exists on the Telegram servers. + + caption (``bool``, *optional*): + Media caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + Returns: + On success, the sent :obj:`~pyrogram.types.Message` is returned. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + + return await self._client.send_cached_media( + chat_id=self.from_user.id, + file_id=file_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + disable_notification=disable_notification, + reply_to_story_id=self.id, + reply_markup=reply_markup + ) + + async def reply_media_group( + self, + media: List[Union[ + "types.InputMediaPhoto", + "types.InputMediaVideo", + "types.InputMediaAudio", + "types.InputMediaDocument" + ]], + disable_notification: bool = None, + ) -> List["types.Message"]: + """Bound method *reply_media_group* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.send_media_group( + chat_id=story.from_user.id, + media=list_of_media, + reply_to_story_id=story.id + ) + + Example: + .. code-block:: python + + await story.reply_media_group(list_of_media) + + Parameters: + media (``list``): + A list containing either :obj:`~pyrogram.types.InputMediaPhoto` or + :obj:`~pyrogram.types.InputMediaVideo` objects + describing photos and videos to be sent, must include 2–10 items. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + Returns: + On success, a :obj:`~pyrogram.types.Messages` object is returned containing all the + single messages sent. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + return await self._client.send_media_group( + chat_id=self.chat.id, + media=media, + disable_notification=disable_notification, + reply_to_story_id=self.id + ) + + async def reply_photo( + self, + photo: Union[str, BinaryIO], + caption: str = "", + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + has_spoiler: bool = None, + ttl_seconds: int = None, + disable_notification: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> "types.Message": + """Bound method *reply_photo* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.send_photo( + chat_id=story.from_user.id, + photo=photo, + reply_to_story_id=story.id + ) + + Example: + .. code-block:: python + + await story.reply_photo(photo) + + Parameters: + photo (``str``): + Photo to send. + Pass a file_id as string to send a photo that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a photo from the Internet, or + pass a file path as string to upload a new photo that exists on your local machine. + + caption (``str``, *optional*): + Photo caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + has_spoiler (``bool``, *optional*): + Pass True if the photo needs to be covered with a spoiler animation. + + ttl_seconds (``int``, *optional*): + Self-Destruct Timer. + If you set a timer, the photo will self-destruct in *ttl_seconds* + seconds after it was viewed. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`~pyrogram.types.Message` is returned. + In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned + instead. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + return await self._client.send_photo( + chat_id=self.chat.id, + photo=photo, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + has_spoiler=has_spoiler, + ttl_seconds=ttl_seconds, + disable_notification=disable_notification, + reply_to_story_id=self.id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + async def reply_sticker( + self, + sticker: Union[str, BinaryIO], + disable_notification: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> "types.Message": + """Bound method *reply_sticker* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.send_sticker( + chat_id=story.from_user.id, + sticker=sticker, + reply_to_story_id=story.id + ) + + Example: + .. code-block:: python + + await story.reply_sticker(sticker) + + Parameters: + sticker (``str``): + Sticker to send. + Pass a file_id as string to send a sticker that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a .webp sticker file from the Internet, or + pass a file path as string to upload a new sticker that exists on your local machine. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_story_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`~pyrogram.types.Message` is returned. + In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned + instead. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + return await self._client.send_sticker( + chat_id=self.chat.id, + sticker=sticker, + disable_notification=disable_notification, + reply_to_story_id=self.id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + async def reply_video( + self, + video: Union[str, BinaryIO], + caption: str = "", + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + has_spoiler: bool = None, + ttl_seconds: int = None, + duration: int = 0, + width: int = 0, + height: int = 0, + thumb: Union[str, BinaryIO] = None, + file_name: str = None, + supports_streaming: bool = True, + disable_notification: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> "types.Message": + """Bound method *reply_video* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.send_video( + chat_id=story.from_user.id, + video=video, + reply_to_story_id=story.id + ) + + Example: + .. code-block:: python + + await story.reply_video(video) + + Parameters: + video (``str``): + Video to send. + Pass a file_id as string to send a video that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a video from the Internet, or + pass a file path as string to upload a new video that exists on your local machine. + + caption (``str``, *optional*): + Video caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + has_spoiler (``bool``, *optional*): + Pass True if the video needs to be covered with a spoiler animation. + + ttl_seconds (``int``, *optional*): + Self-Destruct Timer. + If you set a timer, the video will self-destruct in *ttl_seconds* + seconds after it was viewed. + + duration (``int``, *optional*): + Duration of sent video in seconds. + + width (``int``, *optional*): + Video width. + + height (``int``, *optional*): + Video height. + + thumb (``str`` | ``BinaryIO``, *optional*): + Thumbnail of the video sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 320 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + file_name (``str``, *optional*): + File name of the video sent. + Defaults to file's path basename. + + supports_streaming (``bool``, *optional*): + Pass True, if the uploaded video is suitable for streaming. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`~pyrogram.types.Message` is returned. + In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned + instead. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + return await self._client.send_video( + chat_id=self.chat.id, + video=video, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + has_spoiler=has_spoiler, + ttl_seconds=ttl_seconds, + duration=duration, + width=width, + height=height, + thumb=thumb, + file_name=file_name, + supports_streaming=supports_streaming, + disable_notification=disable_notification, + reply_to_story_id=self.id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + async def reply_video_note( + self, + video_note: Union[str, BinaryIO], + duration: int = 0, + length: int = 1, + thumb: Union[str, BinaryIO] = None, + disable_notification: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> "types.Message": + """Bound method *reply_video_note* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.send_video_note( + chat_id=story.from_user.id, + video_note=video_note, + reply_to_story_id=story.id + ) + + Example: + .. code-block:: python + + await story.reply_video_note(video_note) + + Parameters: + video_note (``str``): + Video note to send. + Pass a file_id as string to send a video note that exists on the Telegram servers, or + pass a file path as string to upload a new video note that exists on your local machine. + Sending video notes by a URL is currently unsupported. + + duration (``int``, *optional*): + Duration of sent video in seconds. + + length (``int``, *optional*): + Video width and height. + + thumb (``str`` | ``BinaryIO``, *optional*): + Thumbnail of the video sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 320 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`~pyrogram.types.Message` is returned. + In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned + instead. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + return await self._client.send_video_note( + chat_id=self.chat.id, + video_note=video_note, + duration=duration, + length=length, + thumb=thumb, + disable_notification=disable_notification, + reply_to_story_id=self.id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + async def reply_voice( + self, + voice: Union[str, BinaryIO], + caption: str = "", + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + duration: int = 0, + disable_notification: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> "types.Message": + """Bound method *reply_voice* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.send_voice( + chat_id=story.from_user.id, + voice=voice, + reply_to_story_id=story.id + ) + + Example: + .. code-block:: python + + await message.reply_voice(voice) + + Parameters: + voice (``str``): + Audio file to send. + Pass a file_id as string to send an audio that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get an audio from the Internet, or + pass a file path as string to upload a new audio that exists on your local machine. + + caption (``str``, *optional*): + Voice message caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + duration (``int``, *optional*): + Duration of the voice message in seconds. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`~pyrogram.types.Message` is returned. + In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned + instead. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + return await self._client.send_voice( + chat_id=self.chat.id, + voice=voice, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + duration=duration, + disable_notification=disable_notification, + reply_to_story_id=self.id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + async def delete(self): + """Bound method *delete* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.delete_stories( + story_ids=story.id + ) + + Example: + .. code-block:: python + + await story.delete() + + Returns: + True on success, False otherwise. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + return await self._client.delete_stories(chat_id=self.from_user.id ,story_ids=self.id) + + async def edit( + self, + media: Union[str, BinaryIO] = None, + privacy: "enums.StoriesPrivacyRules" = None, + allowed_users: List[int] = None, + denied_users: List[int] = None, + allowed_chats: List[int] = None, + denied_chats: List[int] = None, + caption: str = None, + parse_mode: "enums.ParseMode" = None, + caption_entities: List["types.MessageEntity"] = None + ) -> "types.Story": + """Bound method *edit* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.edit_story( + story_id=story.id, + caption="hello" + ) + + Example: + .. code-block:: python + + await story.edit_caption("hello") + + Parameters: + story_id (``int``): + Unique identifier (int) of the target story. + + animation (``str`` | ``BinaryIO``, *optional*): + New story Animation. + Pass a file_id as string to send a animation that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a animation from the Internet, + pass a file path as string to upload a new animation that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + photo (``str`` | ``BinaryIO``, *optional*): + New story photo. + Pass a file_id as string to send a photo that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a photo from the Internet, + pass a file path as string to upload a new photo that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + video (``str`` | ``BinaryIO``, *optional*): + New story video. + Pass a file_id as string to send a video that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a video from the Internet, + pass a file path as string to upload a new video that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + privacy (:obj:`~pyrogram.enums.StoriesPrivacyRules`, *optional*): + Story privacy. + + allowed_chats (List of ``int``, *optional*): + List of chat_id which participant allowed to view the story. + + denied_chats (List of ``int``, *optional*): + List of chat_id which participant denied to view the story. + + allowed_users (List of ``int``, *optional*): + List of user_id whos allowed to view the story. + + denied_users (List of ``int``, *optional*): + List of user_id whos denied to view the story. + + caption (``str``, *optional*): + Story caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + Returns: + On success, the edited :obj:`~pyrogram.types.Story` is returned. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + return await self._client.edit_story( + chat_id=self.from_user.id, + story_id=self.id, + media=media, + privacy=privacy, + allowed_chats=allowed_chats, + denied_chats=denied_chats, + allowed_users=allowed_users, + denied_users=denied_users, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities + ) + + async def edit_caption( + self, + caption: str, + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None + ) -> "types.Story": + """Bound method *edit_caption* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.edit_story( + story_id=story.id, + caption="hello" + ) + + Example: + .. code-block:: python + + await story.edit_caption("hello") + + Parameters: + caption (``str``): + New caption of the story. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + Returns: + On success, the edited :obj:`~pyrogram.types.Story` is returned. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + return await self._client.edit_story( + chat_id=self.from_user.id, + story_id=self.id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities + ) + + async def edit_privacy( + self, + privacy: "enums.StoriesPrivacyRules" = None, + allowed_users: List[int] = None, + denied_users: List[int] = None, + allowed_chats: List[int] = None, + denied_chats: List[int] = None + ) -> "types.Story": + """Bound method *edit_privacy* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.edit_story( + story_id=story.id, + privacy=enums.StoriesPrivacyRules.PUBLIC + ) + + Example: + .. code-block:: python + + await story.edit_privacy(enums.StoriesPrivacyRules.PUBLIC) + + Parameters: + privacy (:obj:`~pyrogram.enums.StoriesPrivacyRules`, *optional*): + Story privacy. + + allowed_chats (List of ``int``, *optional*): + List of chat_id which participant allowed to view the story. + + denied_chats (List of ``int``, *optional*): + List of chat_id which participant denied to view the story. + + allowed_users (List of ``int``, *optional*): + List of user_id whos allowed to view the story. + + denied_users (List of ``int``, *optional*): + List of user_id whos denied to view the story. + + Returns: + On success, the edited :obj:`~pyrogram.types.Story` is returned. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + return await self._client.edit_story( + story_id=self.id, + privacy=privacy, + allowed_chats=allowed_chats, + denied_chats=denied_chats, + allowed_users=allowed_users, + denied_users=denied_users + ) + + async def export_link(self) -> "types.ExportedStoryLink": + """Bound method *export_link* of :obj:`~pyrogram.types.Story`. + + Use as a shortcut for: + + .. code-block:: python + + await client.export_story_link( + user_id=story.from_user.id, + story_id=story.id + ) + + Example: + .. code-block:: python + + await story.export_link() + + Returns: + :obj:`~pyrogram.types.ExportedStoryLink`: a single story link is returned. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + return await self._client.export_story_link(user_id=self.from_user.id, story_id=self.id) diff --git a/pyrogram/types/messages_and_media/story_deleted.py b/pyrogram/types/messages_and_media/story_deleted.py new file mode 100644 index 00000000..e5f2a052 --- /dev/null +++ b/pyrogram/types/messages_and_media/story_deleted.py @@ -0,0 +1,63 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +import pyrogram + +from pyrogram import raw, types +from typing import Union +from ..object import Object +from ..update import Update + +class StoryDeleted(Object, Update): + """A deleted story. + + Parameters: + id (``int``): + Unique story identifier. + + from_user (:obj:`~pyrogram.types.User`, *optional*): + Sender of the story. + + sender_chat (:obj:`~pyrogram.types.Chat`, *optional*): + Sender of the story. If the story is from channel. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int, + from_user: Union["types.User", "types.Chat"] = None + ): + super().__init__(client) + + self.id = id + self.from_user = from_user + + async def _parse( + client: "pyrogram.Client", + stories: raw.base.StoryItem, + peer: Union["raw.types.PeerChannel", "raw.types.PeerUser"] + ) -> "StoryDeleted": + from_user = await client.get_users(peer.user_id) if isinstance(peer, raw.types.PeerUser) else await client.get_chat(peer.channel_id) + + return StoryDeleted( + id=stories.id, + from_user=from_user, + client=client + ) diff --git a/pyrogram/types/messages_and_media/story_skipped.py b/pyrogram/types/messages_and_media/story_skipped.py new file mode 100644 index 00000000..2703b165 --- /dev/null +++ b/pyrogram/types/messages_and_media/story_skipped.py @@ -0,0 +1,84 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +import pyrogram + +from datetime import datetime +from pyrogram import raw, types, utils +from typing import Union +from ..object import Object +from ..update import Update + +class StorySkipped(Object, Update): + """A skipped story. + + Parameters: + id (``int``): + Unique story identifier. + + from_user (:obj:`~pyrogram.types.User`, *optional*): + Sender of the story. + + sender_chat (:obj:`~pyrogram.types.Chat`, *optional*): + Sender of the story. If the story is from channel. + + date (:py:obj:`~datetime.datetime`, *optional*): + Date the story was sent. + + expire_date (:py:obj:`~datetime.datetime`, *optional*): + Date the story will be expired. + + close_friends (``bool``, *optional*): + True, if the Story is shared with close_friends only. + """ + + # TODO: Add Privacy + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int, + from_user: Union["types.User", "types.Chat"] = None, + date: datetime, + expire_date: datetime, + close_friends: bool = None + ): + super().__init__(client) + + self.id = id + self.from_user = from_user + self.date = date + self.expire_date = expire_date + self.close_friends = close_friends + + async def _parse( + client: "pyrogram.Client", + stories: raw.base.StoryItem, + peer: Union["raw.types.PeerChannel", "raw.types.PeerUser"] + ) -> "StorySkipped": + from_user = await client.get_users(peer.user_id) if isinstance(peer, raw.types.PeerUser) else await client.get_chat(peer.channel_id) + + return StorySkipped( + id=stories.id, + from_user=from_user, + date=utils.timestamp_to_datetime(stories.date), + expire_date=utils.timestamp_to_datetime(stories.expire_date), + close_friends=stories.close_friends, + client=client + ) diff --git a/pyrogram/types/messages_and_media/story_views.py b/pyrogram/types/messages_and_media/story_views.py new file mode 100644 index 00000000..fc652407 --- /dev/null +++ b/pyrogram/types/messages_and_media/story_views.py @@ -0,0 +1,74 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from pyrogram import raw, types +from typing import List +from ..object import Object + +class StoryViews(Object): + """Contains information about a story viewers. + + Parameters: + views_count (``int`` ``32-bit``): + Views count. + + has_viewers (``bool``, *optional*): + Has viewers. + + forwards_count (``int`` ``32-bit``, *optional*): + Forwards count. + + reactions (List of :obj:`~pyrogram.types.Reaction`, *optional*): + Reactions list. + + reactions_count (``int`` ``32-bit``, *optional*): + Reactions count. + + recent_viewers (List of ``int`` ``64-bit``, *optional*): + Viewers list. + """ + + def __init__( + self, *, + views_count: int, + has_viewers: bool = None, + forwards_count: int = None, + reactions: List["types.Reaction"] = None, + reactions_count: int = None, + recent_viewers: List[int] = None + ): + super().__init__() + + self.views_count = views_count + self.has_viewers = has_viewers + self.forwards_count = forwards_count + self.reactions = reactions + self.reactions_count = reactions_count + self.recent_viewers = recent_viewers + + @staticmethod + def _parse(client, storyviews: "raw.types.StoryViews") -> "StoryViews": + return StoryViews( + views_count=getattr(storyviews, "views_count", None), + has_viewers=getattr(storyviews, "has_viewers", None), + forwards_count=getattr(storyviews, "forwards_count", None), + reactions=[ + types.Reaction._parse_count(client, reaction) for reaction in storyviews.reactions + ] or None, + recent_viewers=getattr(storyviews, "recent_viewers", None) + ) diff --git a/pyrogram/utils.py b/pyrogram/utils.py index 91f61e6d..b98e1365 100644 --- a/pyrogram/utils.py +++ b/pyrogram/utils.py @@ -248,16 +248,21 @@ def get_peer_type(peer_id: int) -> str: raise ValueError(f"Peer id invalid: {peer_id}") def get_reply_to( - reply_to_message_id: Optional[int], - message_thread_id: Optional[int], -) -> Optional[raw.types.InputReplyToMessage]: - if not any((reply_to_message_id, message_thread_id)): - return None + reply_to_message_id: Optional[int] = None, + message_thread_id: Optional[int] = None, + user_id: Optional[raw.types.InputUser] = None, + reply_to_story_id: Optional[int] = None +) -> Optional[Union[raw.types.InputReplyToMessage, raw.types.InputReplyToStory]]: + if all((user_id, reply_to_story_id)): + return raw.types.InputReplyToStory(user_id=user_id, story_id=reply_to_story_id) # type: ignore[arg-type] - return raw.types.InputReplyToMessage( - reply_to_msg_id=reply_to_message_id or message_thread_id, # type: ignore[arg-type] - top_msg_id=message_thread_id if reply_to_message_id else None, - ) + if any((reply_to_message_id, message_thread_id)): + return raw.types.InputReplyToMessage( + reply_to_msg_id=reply_to_message_id or message_thread_id, # type: ignore[arg-type] + top_msg_id=message_thread_id if reply_to_message_id else None, + ) + + return None def get_channel_id(peer_id: int) -> int: return MAX_CHANNEL_ID - peer_id diff --git a/update_bot_precheckout_query.py b/update_bot_precheckout_query.py new file mode 100644 index 00000000..cd842ac5 --- /dev/null +++ b/update_bot_precheckout_query.py @@ -0,0 +1,125 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from io import BytesIO + +from pyrogram.raw.core.primitives import Int, Long, Int128, Int256, Bool, Bytes, String, Double, Vector +from pyrogram.raw.core import TLObject +from pyrogram import raw +from typing import List, Optional, Any + +# # # # # # # # # # # # # # # # # # # # # # # # +# !!! WARNING !!! # +# This is a generated file! # +# All changes made in this file will be lost! # +# # # # # # # # # # # # # # # # # # # # # # # # + + +class UpdateBotPrecheckoutQuery(TLObject): # type: ignore + """Telegram API type. + + Constructor of :obj:`~pyrogram.raw.base.Update`. + + Details: + - Layer: ``165`` + - ID: ``8CAA9A96`` + + Parameters: + query_id (``int`` ``64-bit``): + N/A + + user_id (``int`` ``64-bit``): + N/A + + payload (``bytes``): + N/A + + currency (``str``): + N/A + + total_amount (``int`` ``64-bit``): + N/A + + info (:obj:`PaymentRequestedInfo `, *optional*): + N/A + + shipping_option_id (``str``, *optional*): + N/A + + """ + + __slots__: List[str] = ["query_id", "user_id", "payload", "currency", "total_amount", "info", "shipping_option_id"] + + ID = 0x8caa9a96 + QUALNAME = "types.UpdateBotPrecheckoutQuery" + + def __init__(self, *, query_id: int, user_id: int, payload: bytes, currency: str, total_amount: int, info: "raw.base.PaymentRequestedInfo" = None, shipping_option_id: Optional[str] = None) -> None: + self.query_id = query_id # long + self.user_id = user_id # long + self.payload = payload # bytes + self.currency = currency # string + self.total_amount = total_amount # long + self.info = info # flags.0?PaymentRequestedInfo + self.shipping_option_id = shipping_option_id # flags.1?string + + @staticmethod + def read(b: BytesIO, *args: Any) -> "UpdateBotPrecheckoutQuery": + + flags = Int.read(b) + + query_id = Long.read(b) + + user_id = Long.read(b) + + payload = Bytes.read(b) + + info = TLObject.read(b) if flags & (1 << 0) else None + + shipping_option_id = String.read(b) if flags & (1 << 1) else None + currency = String.read(b) + + total_amount = Long.read(b) + + return UpdateBotPrecheckoutQuery(query_id=query_id, user_id=user_id, payload=payload, currency=currency, total_amount=total_amount, info=info, shipping_option_id=shipping_option_id) + + def write(self, *args) -> bytes: + b = BytesIO() + b.write(Int(self.ID, False)) + + flags = 0 + flags |= (1 << 0) if self.info is not None else 0 + flags |= (1 << 1) if self.shipping_option_id is not None else 0 + b.write(Int(flags)) + + b.write(Long(self.query_id)) + + b.write(Long(self.user_id)) + + b.write(Bytes(self.payload)) + + if self.info is not None: + b.write(self.info.write()) + + if self.shipping_option_id is not None: + b.write(String(self.shipping_option_id)) + + b.write(String(self.currency)) + + b.write(Long(self.total_amount)) + + return b.getvalue()