mirror of
https://github.com/pyrogram/pyrogram
synced 2025-09-04 08:15:08 +00:00
Merge branch 'develop' into asyncio
# Conflicts: # pyrogram/__init__.py # pyrogram/client/client.py # pyrogram/client/methods/bots/send_inline_bot_result.py # pyrogram/client/methods/chats/pin_chat_message.py # pyrogram/client/methods/chats/unpin_chat_message.py # pyrogram/client/methods/password/change_cloud_password.py # pyrogram/client/methods/password/enable_cloud_password.py # pyrogram/client/methods/password/remove_cloud_password.py
This commit is contained in:
@@ -39,7 +39,8 @@ from .client.types import (
|
||||
InputMediaVideo, InputMediaDocument, InputMediaAudio, InputMediaAnimation, InputPhoneContact,
|
||||
Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, User, UserStatus,
|
||||
UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply,
|
||||
InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove
|
||||
InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove,
|
||||
Poll, PollOption
|
||||
)
|
||||
from .client import (
|
||||
Client, ChatAction, ParseMode, Emoji,
|
||||
|
@@ -45,8 +45,12 @@ from pyrogram.api.errors import (
|
||||
PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty,
|
||||
PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded,
|
||||
PasswordHashInvalid, FloodWait, PeerIdInvalid, FirstnameInvalid, PhoneNumberBanned,
|
||||
VolumeLocNotFound, UserMigrate, FileIdInvalid, ChannelPrivate, PhoneNumberOccupied)
|
||||
VolumeLocNotFound, UserMigrate, FileIdInvalid, ChannelPrivate, PhoneNumberOccupied,
|
||||
PasswordRecoveryNa, PasswordEmpty
|
||||
)
|
||||
from pyrogram.client.handlers import DisconnectHandler
|
||||
from pyrogram.client.handlers.handler import Handler
|
||||
from pyrogram.client.methods.password.utils import compute_check
|
||||
from pyrogram.crypto import AES
|
||||
from pyrogram.session import Auth, Session
|
||||
from .dispatcher import Dispatcher
|
||||
@@ -578,21 +582,47 @@ class Client(Methods, BaseClient):
|
||||
self.first_name = None
|
||||
except SessionPasswordNeeded as e:
|
||||
print(e.MESSAGE)
|
||||
r = await self.send(functions.account.GetPassword())
|
||||
|
||||
while True:
|
||||
try:
|
||||
r = await self.send(functions.account.GetPassword())
|
||||
|
||||
if self.password is None:
|
||||
print("Hint: {}".format(r.hint))
|
||||
self.password = await ainput("Enter password: ")
|
||||
|
||||
if type(self.password) is str:
|
||||
self.password = r.current_salt + self.password.encode() + r.current_salt
|
||||
self.password = await ainput("Enter password (empty to recover): ")
|
||||
|
||||
password_hash = sha256(self.password).digest()
|
||||
if self.password == "":
|
||||
r = await self.send(functions.auth.RequestPasswordRecovery())
|
||||
|
||||
r = await self.send(functions.auth.CheckPassword(password_hash))
|
||||
print("An e-mail containing the recovery code has been sent to {}".format(
|
||||
r.email_pattern
|
||||
))
|
||||
|
||||
r = await self.send(
|
||||
functions.auth.RecoverPassword(
|
||||
code=await ainput("Enter password recovery code: ")
|
||||
)
|
||||
)
|
||||
else:
|
||||
r = await self.send(
|
||||
functions.auth.CheckPassword(
|
||||
password=compute_check(r, self.password)
|
||||
)
|
||||
)
|
||||
except PasswordEmpty as e:
|
||||
if password_hash_invalid_raises:
|
||||
raise
|
||||
else:
|
||||
print(e.MESSAGE)
|
||||
self.password = None
|
||||
except PasswordRecoveryNa as e:
|
||||
if password_hash_invalid_raises:
|
||||
raise
|
||||
else:
|
||||
print(e.MESSAGE)
|
||||
self.password = None
|
||||
except PasswordHashInvalid as e:
|
||||
if password_hash_invalid_raises:
|
||||
raise
|
||||
@@ -605,6 +635,7 @@ class Client(Methods, BaseClient):
|
||||
else:
|
||||
print(e.MESSAGE.format(x=e.x))
|
||||
time.sleep(e.x)
|
||||
self.password = None
|
||||
except Exception as e:
|
||||
log.error(e, exc_info=True)
|
||||
else:
|
||||
@@ -1263,7 +1294,7 @@ class Client(Methods, BaseClient):
|
||||
volume_id: int = None,
|
||||
local_id: int = None,
|
||||
secret: int = None,
|
||||
version: int = 0,
|
||||
|
||||
size: int = None,
|
||||
progress: callable = None,
|
||||
progress_args: tuple = ()) -> str:
|
||||
@@ -1311,13 +1342,14 @@ class Client(Methods, BaseClient):
|
||||
location = types.InputFileLocation(
|
||||
volume_id=volume_id,
|
||||
local_id=local_id,
|
||||
secret=secret
|
||||
secret=secret,
|
||||
file_reference=b""
|
||||
)
|
||||
else: # Any other file can be more easily accessed by id and access_hash
|
||||
location = types.InputDocumentFileLocation(
|
||||
id=id,
|
||||
access_hash=access_hash,
|
||||
version=version
|
||||
file_reference=b""
|
||||
)
|
||||
|
||||
limit = 1024 * 1024
|
||||
|
@@ -121,6 +121,9 @@ class Filters:
|
||||
web_page = create("WebPage", lambda _, m: m.web_page)
|
||||
"""Filter messages sent with a webpage preview."""
|
||||
|
||||
poll = create("Poll", lambda _, m: m.poll)
|
||||
"""Filter messages that contain :obj:`Poll <pyrogram.Poll>` objects."""
|
||||
|
||||
private = create("Private", lambda _, m: bool(m.chat and m.chat.type == "private"))
|
||||
"""Filter messages sent in private chats."""
|
||||
|
||||
|
@@ -28,7 +28,8 @@ class SendInlineBotResult(BaseClient):
|
||||
query_id: int,
|
||||
result_id: str,
|
||||
disable_notification: bool = None,
|
||||
reply_to_message_id: int = None):
|
||||
reply_to_message_id: int = None,
|
||||
hide_via: bool = None):
|
||||
"""Use this method to send an inline bot result.
|
||||
Bot results can be retrieved using :obj:`get_inline_bot_results <pyrogram.Client.get_inline_bot_results>`
|
||||
|
||||
@@ -51,6 +52,9 @@ class SendInlineBotResult(BaseClient):
|
||||
reply_to_message_id (``bool``, *optional*):
|
||||
If the message is a reply, ID of the original message.
|
||||
|
||||
hide_via (``bool``):
|
||||
Sends the message with *via @bot* hidden.
|
||||
|
||||
Returns:
|
||||
On success, the sent Message is returned.
|
||||
|
||||
@@ -64,6 +68,7 @@ class SendInlineBotResult(BaseClient):
|
||||
id=result_id,
|
||||
random_id=self.rnd_id(),
|
||||
silent=disable_notification or None,
|
||||
reply_to_msg_id=reply_to_message_id
|
||||
reply_to_msg_id=reply_to_message_id,
|
||||
hide_via=hide_via or None
|
||||
)
|
||||
)
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
from typing import Union
|
||||
|
||||
from pyrogram.api import functions, types
|
||||
from pyrogram.api import functions
|
||||
from ...ext import BaseClient
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class PinChatMessage(BaseClient):
|
||||
chat_id: Union[int, str],
|
||||
message_id: int,
|
||||
disable_notification: bool = None) -> bool:
|
||||
"""Use this method to pin a message in a supergroup or a channel.
|
||||
"""Use this method to pin a message in a group, channel or your own chat.
|
||||
You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin right in
|
||||
the supergroup or "can_edit_messages" admin right in the channel.
|
||||
|
||||
@@ -49,19 +49,10 @@ class PinChatMessage(BaseClient):
|
||||
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||
``ValueError`` if a chat_id doesn't belong to a supergroup or a channel.
|
||||
"""
|
||||
peer = await self.resolve_peer(chat_id)
|
||||
|
||||
if isinstance(peer, types.InputPeerChannel):
|
||||
await self.send(
|
||||
functions.channels.UpdatePinnedMessage(
|
||||
channel=peer,
|
||||
id=message_id,
|
||||
silent=disable_notification or None
|
||||
)
|
||||
await self.send(
|
||||
functions.messages.UpdatePinnedMessage(
|
||||
peer=await self.resolve_peer(chat_id),
|
||||
id=message_id,
|
||||
silent=disable_notification or None
|
||||
)
|
||||
elif isinstance(peer, types.InputPeerChat):
|
||||
raise ValueError("The chat_id \"{}\" belongs to a basic group".format(chat_id))
|
||||
else:
|
||||
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))
|
||||
|
||||
return True
|
||||
)
|
||||
|
@@ -61,7 +61,8 @@ class SetChatPhoto(BaseClient):
|
||||
photo = types.InputChatPhoto(
|
||||
id=types.InputPhoto(
|
||||
id=s[0],
|
||||
access_hash=s[1]
|
||||
access_hash=s[1],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
|
@@ -18,14 +18,14 @@
|
||||
|
||||
from typing import Union
|
||||
|
||||
from pyrogram.api import functions, types
|
||||
from pyrogram.api import functions
|
||||
from ...ext import BaseClient
|
||||
|
||||
|
||||
class UnpinChatMessage(BaseClient):
|
||||
async def unpin_chat_message(self,
|
||||
chat_id: Union[int, str]) -> bool:
|
||||
"""Use this method to unpin a message in a supergroup or a channel.
|
||||
"""Use this method to unpin a message in a group, channel or your own chat.
|
||||
You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin
|
||||
right in the supergroup or "can_edit_messages" admin right in the channel.
|
||||
|
||||
@@ -40,18 +40,11 @@ class UnpinChatMessage(BaseClient):
|
||||
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||
``ValueError`` if a chat_id doesn't belong to a supergroup or a channel.
|
||||
"""
|
||||
peer = await self.resolve_peer(chat_id)
|
||||
|
||||
if isinstance(peer, types.InputPeerChannel):
|
||||
await self.send(
|
||||
functions.channels.UpdatePinnedMessage(
|
||||
channel=peer,
|
||||
id=0
|
||||
)
|
||||
await self.send(
|
||||
functions.messages.UpdatePinnedMessage(
|
||||
peer=await self.resolve_peer(chat_id),
|
||||
id=0
|
||||
)
|
||||
elif isinstance(peer, types.InputPeerChat):
|
||||
raise ValueError("The chat_id \"{}\" belongs to a basic group".format(chat_id))
|
||||
else:
|
||||
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))
|
||||
)
|
||||
|
||||
return True
|
||||
|
@@ -24,6 +24,7 @@ from .edit_message_text import EditMessageText
|
||||
from .forward_messages import ForwardMessages
|
||||
from .get_history import GetHistory
|
||||
from .get_messages import GetMessages
|
||||
from .retract_vote import RetractVote
|
||||
from .send_animation import SendAnimation
|
||||
from .send_audio import SendAudio
|
||||
from .send_chat_action import SendChatAction
|
||||
@@ -33,11 +34,13 @@ from .send_location import SendLocation
|
||||
from .send_media_group import SendMediaGroup
|
||||
from .send_message import SendMessage
|
||||
from .send_photo import SendPhoto
|
||||
from .send_poll import SendPoll
|
||||
from .send_sticker import SendSticker
|
||||
from .send_venue import SendVenue
|
||||
from .send_video import SendVideo
|
||||
from .send_video_note import SendVideoNote
|
||||
from .send_voice import SendVoice
|
||||
from .vote_poll import VotePoll
|
||||
|
||||
|
||||
class Messages(
|
||||
@@ -62,6 +65,9 @@ class Messages(
|
||||
SendVenue,
|
||||
SendVideo,
|
||||
SendVideoNote,
|
||||
SendVoice
|
||||
SendVoice,
|
||||
SendPoll,
|
||||
VotePoll,
|
||||
RetractVote
|
||||
):
|
||||
pass
|
||||
|
@@ -84,7 +84,8 @@ class EditMessageMedia(BaseClient):
|
||||
media = types.InputMediaPhoto(
|
||||
id=types.InputPhoto(
|
||||
id=media.photo.id,
|
||||
access_hash=media.photo.access_hash
|
||||
access_hash=media.photo.access_hash,
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
elif media.media.startswith("http"):
|
||||
@@ -110,7 +111,8 @@ class EditMessageMedia(BaseClient):
|
||||
media = types.InputMediaPhoto(
|
||||
id=types.InputPhoto(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -138,7 +140,8 @@ class EditMessageMedia(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=media.document.id,
|
||||
access_hash=media.document.access_hash
|
||||
access_hash=media.document.access_hash,
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
elif media.media.startswith("http"):
|
||||
@@ -164,7 +167,8 @@ class EditMessageMedia(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -191,7 +195,8 @@ class EditMessageMedia(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=media.document.id,
|
||||
access_hash=media.document.access_hash
|
||||
access_hash=media.document.access_hash,
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
elif media.media.startswith("http"):
|
||||
@@ -217,7 +222,8 @@ class EditMessageMedia(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -246,7 +252,8 @@ class EditMessageMedia(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=media.document.id,
|
||||
access_hash=media.document.access_hash
|
||||
access_hash=media.document.access_hash,
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
elif media.media.startswith("http"):
|
||||
@@ -272,7 +279,8 @@ class EditMessageMedia(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -294,7 +302,8 @@ class EditMessageMedia(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=media.document.id,
|
||||
access_hash=media.document.access_hash
|
||||
access_hash=media.document.access_hash,
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
elif media.media.startswith("http"):
|
||||
@@ -320,7 +329,8 @@ class EditMessageMedia(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
|
54
pyrogram/client/methods/messages/retract_vote.py
Normal file
54
pyrogram/client/methods/messages/retract_vote.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Union
|
||||
|
||||
from pyrogram.api import functions
|
||||
from pyrogram.client.ext import BaseClient
|
||||
|
||||
|
||||
class RetractVote(BaseClient):
|
||||
def retract_vote(self,
|
||||
chat_id: Union[int, str],
|
||||
message_id: id) -> bool:
|
||||
"""Use this method to retract your vote in a poll.
|
||||
|
||||
Args:
|
||||
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).
|
||||
|
||||
message_id (``int``):
|
||||
Unique poll message identifier inside this chat.
|
||||
|
||||
Returns:
|
||||
On success, True is returned.
|
||||
|
||||
Raises:
|
||||
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||
"""
|
||||
self.send(
|
||||
functions.messages.SendVote(
|
||||
peer=self.resolve_peer(chat_id),
|
||||
msg_id=message_id,
|
||||
options=[]
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
@@ -167,7 +167,8 @@ class SendAnimation(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
|
@@ -166,7 +166,8 @@ class SendAudio(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
|
@@ -147,7 +147,8 @@ class SendDocument(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
|
@@ -77,7 +77,8 @@ class SendMediaGroup(BaseClient):
|
||||
media = types.InputMediaPhoto(
|
||||
id=types.InputPhoto(
|
||||
id=media.photo.id,
|
||||
access_hash=media.photo.access_hash
|
||||
access_hash=media.photo.access_hash,
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -99,7 +100,8 @@ class SendMediaGroup(BaseClient):
|
||||
media = types.InputMediaPhoto(
|
||||
id=types.InputPhoto(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
elif isinstance(i, pyrogram.InputMediaVideo):
|
||||
@@ -127,7 +129,8 @@ class SendMediaGroup(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=media.document.id,
|
||||
access_hash=media.document.access_hash
|
||||
access_hash=media.document.access_hash,
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -149,7 +152,8 @@ class SendMediaGroup(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
|
@@ -142,7 +142,8 @@ class SendPhoto(BaseClient):
|
||||
media = types.InputMediaPhoto(
|
||||
id=types.InputPhoto(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
),
|
||||
ttl_seconds=ttl_seconds
|
||||
)
|
||||
|
95
pyrogram/client/methods/messages/send_poll.py
Normal file
95
pyrogram/client/methods/messages/send_poll.py
Normal file
@@ -0,0 +1,95 @@
|
||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Union, List
|
||||
|
||||
import pyrogram
|
||||
from pyrogram.api import functions, types
|
||||
from pyrogram.client.ext import BaseClient
|
||||
|
||||
|
||||
class SendPoll(BaseClient):
|
||||
def send_poll(self,
|
||||
chat_id: Union[int, str],
|
||||
question: str,
|
||||
options: List[str],
|
||||
disable_notification: bool = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
|
||||
"pyrogram.ReplyKeyboardMarkup",
|
||||
"pyrogram.ReplyKeyboardRemove",
|
||||
"pyrogram.ForceReply"] = None) -> "pyrogram.Message":
|
||||
"""Use this method to send a new poll.
|
||||
|
||||
Args:
|
||||
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).
|
||||
|
||||
question (``str``):
|
||||
The poll question, as string.
|
||||
|
||||
options (List of ``str``):
|
||||
The poll options, as list of strings (2 to 10 options are allowed).
|
||||
|
||||
disable_notification (``bool``, *optional*):
|
||||
Sends the message silently.
|
||||
Users will receive a notification with no sound.
|
||||
|
||||
reply_to_message_id (``int``, *optional*):
|
||||
If the message is a reply, ID of the original message.
|
||||
|
||||
reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`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:`Message <pyrogram.Message>` is returned.
|
||||
|
||||
Raises:
|
||||
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||
"""
|
||||
r = self.send(
|
||||
functions.messages.SendMedia(
|
||||
peer=self.resolve_peer(chat_id),
|
||||
media=types.InputMediaPoll(
|
||||
poll=types.Poll(
|
||||
id=0,
|
||||
question=question,
|
||||
answers=[
|
||||
types.PollAnswer(text=o, option=bytes([i]))
|
||||
for i, o in enumerate(options)
|
||||
]
|
||||
)
|
||||
),
|
||||
message="",
|
||||
silent=disable_notification or None,
|
||||
reply_to_msg_id=reply_to_message_id,
|
||||
random_id=self.rnd_id(),
|
||||
reply_markup=reply_markup.write() if reply_markup else None
|
||||
)
|
||||
)
|
||||
|
||||
for i in r.updates:
|
||||
if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)):
|
||||
return pyrogram.Message._parse(
|
||||
self, i.message,
|
||||
{i.id: i for i in r.users},
|
||||
{i.id: i for i in r.chats}
|
||||
)
|
@@ -127,7 +127,8 @@ class SendSticker(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
|
@@ -170,7 +170,8 @@ class SendVideo(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
|
@@ -145,7 +145,8 @@ class SendVideoNote(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
|
@@ -146,7 +146,8 @@ class SendVoice(BaseClient):
|
||||
media = types.InputMediaDocument(
|
||||
id=types.InputDocument(
|
||||
id=unpacked[2],
|
||||
access_hash=unpacked[3]
|
||||
access_hash=unpacked[3],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
|
60
pyrogram/client/methods/messages/vote_poll.py
Normal file
60
pyrogram/client/methods/messages/vote_poll.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Union
|
||||
|
||||
from pyrogram.api import functions
|
||||
from pyrogram.client.ext import BaseClient
|
||||
|
||||
|
||||
class VotePoll(BaseClient):
|
||||
def vote_poll(self,
|
||||
chat_id: Union[int, str],
|
||||
message_id: id,
|
||||
option: int) -> bool:
|
||||
"""Use this method to vote a poll.
|
||||
|
||||
Args:
|
||||
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).
|
||||
|
||||
message_id (``int``):
|
||||
Unique poll message identifier inside this chat.
|
||||
|
||||
option (``int``):
|
||||
Index of the poll option you want to vote for (0 to 9).
|
||||
|
||||
Returns:
|
||||
On success, True is returned.
|
||||
|
||||
Raises:
|
||||
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||
"""
|
||||
poll = self.get_messages(chat_id, message_id).poll
|
||||
|
||||
self.send(
|
||||
functions.messages.SendVote(
|
||||
peer=self.resolve_peer(chat_id),
|
||||
msg_id=message_id,
|
||||
options=[poll.options[option].data]
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
@@ -17,17 +17,17 @@
|
||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
from hashlib import sha256
|
||||
|
||||
from pyrogram.api import functions, types
|
||||
from .utils import compute_hash, compute_check, btoi, itob
|
||||
from ...ext import BaseClient
|
||||
|
||||
|
||||
class ChangeCloudPassword(BaseClient):
|
||||
async def change_cloud_password(self,
|
||||
current_password: str,
|
||||
new_password: str,
|
||||
new_hint: str = "") -> bool:
|
||||
current_password: str,
|
||||
new_password: str,
|
||||
new_hint: str = "") -> bool:
|
||||
"""Use this method to change your Two-Step Verification password (Cloud Password) with a new one.
|
||||
|
||||
Args:
|
||||
@@ -41,28 +41,30 @@ class ChangeCloudPassword(BaseClient):
|
||||
A new password hint.
|
||||
|
||||
Returns:
|
||||
True on success, False otherwise.
|
||||
True on success.
|
||||
|
||||
Raises:
|
||||
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||
``ValueError`` in case there is no cloud password to change.
|
||||
"""
|
||||
r = await self.send(functions.account.GetPassword())
|
||||
|
||||
if isinstance(r, types.account.Password):
|
||||
current_password_hash = sha256(r.current_salt + current_password.encode() + r.current_salt).digest()
|
||||
if not r.has_password:
|
||||
raise ValueError("There is no cloud password to change")
|
||||
|
||||
new_salt = r.new_salt + os.urandom(8)
|
||||
new_password_hash = sha256(new_salt + new_password.encode() + new_salt).digest()
|
||||
r.new_algo.salt1 += os.urandom(32)
|
||||
new_hash = btoi(compute_hash(r.new_algo, new_password))
|
||||
new_hash = itob(pow(r.new_algo.g, new_hash, btoi(r.new_algo.p)))
|
||||
|
||||
return await self.send(
|
||||
functions.account.UpdatePasswordSettings(
|
||||
current_password_hash=current_password_hash,
|
||||
new_settings=types.account.PasswordInputSettings(
|
||||
new_salt=new_salt,
|
||||
new_password_hash=new_password_hash,
|
||||
hint=new_hint
|
||||
)
|
||||
await self.send(
|
||||
functions.account.UpdatePasswordSettings(
|
||||
password=compute_check(r, current_password),
|
||||
new_settings=types.account.PasswordInputSettings(
|
||||
new_algo=r.new_algo,
|
||||
new_password_hash=new_hash,
|
||||
hint=new_hint
|
||||
)
|
||||
)
|
||||
else:
|
||||
return False
|
||||
)
|
||||
|
||||
return True
|
||||
|
@@ -17,9 +17,9 @@
|
||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
from hashlib import sha256
|
||||
|
||||
from pyrogram.api import functions, types
|
||||
from .utils import compute_hash, btoi, itob
|
||||
from ...ext import BaseClient
|
||||
|
||||
|
||||
@@ -27,10 +27,10 @@ class EnableCloudPassword(BaseClient):
|
||||
async def enable_cloud_password(self,
|
||||
password: str,
|
||||
hint: str = "",
|
||||
email: str = "") -> bool:
|
||||
email: str = None) -> bool:
|
||||
"""Use this method to enable the Two-Step Verification security feature (Cloud Password) on your account.
|
||||
|
||||
This password will be asked when you log in on a new device in addition to the SMS code.
|
||||
This password will be asked when you log-in on a new device in addition to the SMS code.
|
||||
|
||||
Args:
|
||||
password (``str``):
|
||||
@@ -43,27 +43,31 @@ class EnableCloudPassword(BaseClient):
|
||||
Recovery e-mail.
|
||||
|
||||
Returns:
|
||||
True on success, False otherwise.
|
||||
True on success.
|
||||
|
||||
Raises:
|
||||
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||
``ValueError`` in case there is already a cloud password enabled.
|
||||
"""
|
||||
r = await self.send(functions.account.GetPassword())
|
||||
|
||||
if isinstance(r, types.account.NoPassword):
|
||||
salt = r.new_salt + os.urandom(8)
|
||||
password_hash = sha256(salt + password.encode() + salt).digest()
|
||||
if r.has_password:
|
||||
raise ValueError("There is already a cloud password enabled")
|
||||
|
||||
return await self.send(
|
||||
functions.account.UpdatePasswordSettings(
|
||||
current_password_hash=salt,
|
||||
new_settings=types.account.PasswordInputSettings(
|
||||
new_salt=salt,
|
||||
new_password_hash=password_hash,
|
||||
hint=hint,
|
||||
email=email
|
||||
)
|
||||
r.new_algo.salt1 += os.urandom(32)
|
||||
new_hash = btoi(compute_hash(r.new_algo, password))
|
||||
new_hash = itob(pow(r.new_algo.g, new_hash, btoi(r.new_algo.p)))
|
||||
|
||||
await self.send(
|
||||
functions.account.UpdatePasswordSettings(
|
||||
password=types.InputCheckPasswordEmpty(),
|
||||
new_settings=types.account.PasswordInputSettings(
|
||||
new_algo=r.new_algo,
|
||||
new_password_hash=new_hash,
|
||||
hint=hint,
|
||||
email=email
|
||||
)
|
||||
)
|
||||
else:
|
||||
return False
|
||||
)
|
||||
|
||||
return True
|
||||
|
@@ -16,9 +16,8 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from hashlib import sha256
|
||||
|
||||
from pyrogram.api import functions, types
|
||||
from .utils import compute_check
|
||||
from ...ext import BaseClient
|
||||
|
||||
|
||||
@@ -32,25 +31,26 @@ class RemoveCloudPassword(BaseClient):
|
||||
Your current password.
|
||||
|
||||
Returns:
|
||||
True on success, False otherwise.
|
||||
True on success.
|
||||
|
||||
Raises:
|
||||
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||
``ValueError`` in case there is no cloud password to remove.
|
||||
"""
|
||||
r = await self.send(functions.account.GetPassword())
|
||||
|
||||
if isinstance(r, types.account.Password):
|
||||
password_hash = sha256(r.current_salt + password.encode() + r.current_salt).digest()
|
||||
if not r.has_password:
|
||||
raise ValueError("There is no cloud password to remove")
|
||||
|
||||
return await self.send(
|
||||
functions.account.UpdatePasswordSettings(
|
||||
current_password_hash=password_hash,
|
||||
new_settings=types.account.PasswordInputSettings(
|
||||
new_salt=b"",
|
||||
new_password_hash=b"",
|
||||
hint=""
|
||||
)
|
||||
await self.send(
|
||||
functions.account.UpdatePasswordSettings(
|
||||
password=compute_check(r, password),
|
||||
new_settings=types.account.PasswordInputSettings(
|
||||
new_algo=types.PasswordKdfAlgoUnknown(),
|
||||
new_password_hash=b"",
|
||||
hint=""
|
||||
)
|
||||
)
|
||||
else:
|
||||
return False
|
||||
)
|
||||
|
||||
return True
|
||||
|
104
pyrogram/client/methods/password/utils.py
Normal file
104
pyrogram/client/methods/password/utils.py
Normal file
@@ -0,0 +1,104 @@
|
||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
from pyrogram.api import types
|
||||
|
||||
|
||||
def btoi(b: bytes) -> int:
|
||||
return int.from_bytes(b, "big")
|
||||
|
||||
|
||||
def itob(i: int) -> bytes:
|
||||
return i.to_bytes(256, "big")
|
||||
|
||||
|
||||
def sha256(data: bytes) -> bytes:
|
||||
return hashlib.sha256(data).digest()
|
||||
|
||||
|
||||
def xor(a: bytes, b: bytes) -> bytes:
|
||||
return bytes(i ^ j for i, j in zip(a, b))
|
||||
|
||||
|
||||
def compute_hash(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, password: str) -> bytes:
|
||||
hash1 = sha256(algo.salt1 + password.encode() + algo.salt1)
|
||||
hash2 = sha256(algo.salt2 + hash1 + algo.salt2)
|
||||
hash3 = hashlib.pbkdf2_hmac("sha512", hash2, algo.salt1, 100000)
|
||||
|
||||
return sha256(algo.salt2 + hash3 + algo.salt2)
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
def compute_check(r: types.account.Password, password: str) -> types.InputCheckPasswordSRP:
|
||||
algo = r.current_algo
|
||||
|
||||
p_bytes = algo.p
|
||||
p = btoi(algo.p)
|
||||
|
||||
g_bytes = itob(algo.g)
|
||||
g = algo.g
|
||||
|
||||
B_bytes = r.srp_B
|
||||
B = btoi(B_bytes)
|
||||
|
||||
srp_id = r.srp_id
|
||||
|
||||
x_bytes = compute_hash(algo, password)
|
||||
x = btoi(x_bytes)
|
||||
|
||||
g_x = pow(g, x, p)
|
||||
|
||||
k_bytes = sha256(p_bytes + g_bytes)
|
||||
k = btoi(k_bytes)
|
||||
|
||||
kg_x = (k * g_x) % p
|
||||
|
||||
while True:
|
||||
a_bytes = os.urandom(256)
|
||||
a = btoi(a_bytes)
|
||||
|
||||
A = pow(g, a, p)
|
||||
A_bytes = itob(A)
|
||||
|
||||
u = btoi(sha256(A_bytes + B_bytes))
|
||||
|
||||
if u > 0:
|
||||
break
|
||||
|
||||
g_b = (B - kg_x) % p
|
||||
|
||||
ux = u * x
|
||||
a_ux = a + ux
|
||||
S = pow(g_b, a_ux, p)
|
||||
S_bytes = itob(S)
|
||||
|
||||
K_bytes = sha256(S_bytes)
|
||||
|
||||
M1_bytes = sha256(
|
||||
xor(sha256(p_bytes), sha256(g_bytes))
|
||||
+ sha256(algo.salt1)
|
||||
+ sha256(algo.salt2)
|
||||
+ A_bytes
|
||||
+ B_bytes
|
||||
+ K_bytes
|
||||
)
|
||||
|
||||
return types.InputCheckPasswordSRP(srp_id, A_bytes, M1_bytes)
|
@@ -49,7 +49,8 @@ class DeleteUserProfilePhotos(BaseClient):
|
||||
input_photos.append(
|
||||
types.InputPhoto(
|
||||
id=s[0],
|
||||
access_hash=s[1]
|
||||
access_hash=s[1],
|
||||
file_reference=b""
|
||||
)
|
||||
)
|
||||
|
||||
|
@@ -31,7 +31,7 @@ from .input_media import (
|
||||
from .messages_and_media import (
|
||||
Audio, Contact, Document, Animation, Location, Photo, PhotoSize,
|
||||
Sticker, Venue, Video, VideoNote, Voice, UserProfilePhotos,
|
||||
Message, Messages, MessageEntity
|
||||
Message, Messages, MessageEntity, Poll, PollOption
|
||||
)
|
||||
from .user_and_chats import (
|
||||
Chat, ChatMember, ChatMembers, ChatPhoto,
|
||||
|
@@ -26,6 +26,8 @@ from .message_entity import MessageEntity
|
||||
from .messages import Messages
|
||||
from .photo import Photo
|
||||
from .photo_size import PhotoSize
|
||||
from .poll import Poll
|
||||
from .poll_option import PollOption
|
||||
from .sticker import Sticker
|
||||
from .user_profile_photos import UserProfilePhotos
|
||||
from .venue import Venue
|
||||
|
@@ -262,6 +262,7 @@ class Message(PyrogramType):
|
||||
location: "pyrogram.Location" = None,
|
||||
venue: "pyrogram.Venue" = None,
|
||||
web_page: bool = None,
|
||||
poll: "pyrogram.Poll" = None,
|
||||
new_chat_members: List[User] = None,
|
||||
left_chat_member: User = None,
|
||||
new_chat_title: str = None,
|
||||
@@ -317,6 +318,7 @@ class Message(PyrogramType):
|
||||
self.location = location
|
||||
self.venue = venue
|
||||
self.web_page = web_page
|
||||
self.poll = poll
|
||||
self.new_chat_members = new_chat_members
|
||||
self.left_chat_member = left_chat_member
|
||||
self.new_chat_title = new_chat_title
|
||||
@@ -440,6 +442,7 @@ class Message(PyrogramType):
|
||||
sticker = None
|
||||
document = None
|
||||
web_page = None
|
||||
poll = None
|
||||
|
||||
media = message.media
|
||||
|
||||
@@ -494,6 +497,8 @@ class Message(PyrogramType):
|
||||
elif isinstance(media, types.MessageMediaWebPage):
|
||||
web_page = True
|
||||
media = None
|
||||
elif isinstance(media, types.MessageMediaPoll):
|
||||
poll = pyrogram.Poll._parse(client, media)
|
||||
else:
|
||||
media = None
|
||||
|
||||
@@ -542,6 +547,7 @@ class Message(PyrogramType):
|
||||
sticker=sticker,
|
||||
document=document,
|
||||
web_page=web_page,
|
||||
poll=poll,
|
||||
views=message.views,
|
||||
via_bot=User._parse(client, users.get(message.via_bot_id, None)),
|
||||
outgoing=message.out,
|
||||
|
102
pyrogram/client/types/messages_and_media/poll.py
Normal file
102
pyrogram/client/types/messages_and_media/poll.py
Normal file
@@ -0,0 +1,102 @@
|
||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import List
|
||||
|
||||
import pyrogram
|
||||
from pyrogram.api import types
|
||||
from .poll_option import PollOption
|
||||
from ..pyrogram_type import PyrogramType
|
||||
|
||||
|
||||
class Poll(PyrogramType):
|
||||
"""This object represents a Poll.
|
||||
|
||||
Args:
|
||||
id (``int``):
|
||||
The poll id in this chat.
|
||||
|
||||
closed (``bool``):
|
||||
Whether the poll is closed or not.
|
||||
|
||||
question (``str``):
|
||||
Poll question.
|
||||
|
||||
options (List of :obj:`PollOption`):
|
||||
The available poll options.
|
||||
|
||||
total_voters (``int``):
|
||||
Total amount of voters for this poll.
|
||||
|
||||
option_chosen (``int``, *optional*):
|
||||
The index of your chosen option (in case you voted already), None otherwise.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
*,
|
||||
client: "pyrogram.client.ext.BaseClient",
|
||||
id: int,
|
||||
closed: bool,
|
||||
question: str,
|
||||
options: List[PollOption],
|
||||
total_voters: int,
|
||||
option_chosen: int = None):
|
||||
super().__init__(client)
|
||||
|
||||
self.id = id
|
||||
self.closed = closed
|
||||
self.question = question
|
||||
self.options = options
|
||||
self.total_voters = total_voters
|
||||
self.option_chosen = option_chosen
|
||||
|
||||
@staticmethod
|
||||
def _parse(client, media_poll: types.MessageMediaPoll) -> "Poll":
|
||||
poll = media_poll.poll
|
||||
results = media_poll.results.results
|
||||
total_voters = media_poll.results.total_voters
|
||||
option_chosen = None
|
||||
|
||||
options = []
|
||||
|
||||
for i, answer in enumerate(poll.answers):
|
||||
voters = 0
|
||||
|
||||
if results:
|
||||
result = results[i]
|
||||
voters = result.voters
|
||||
|
||||
if result.chosen:
|
||||
option_chosen = i
|
||||
|
||||
options.append(PollOption(
|
||||
text=answer.text,
|
||||
voters=voters,
|
||||
data=answer.option,
|
||||
client=client
|
||||
))
|
||||
|
||||
return Poll(
|
||||
id=poll.id,
|
||||
closed=poll.closed,
|
||||
question=poll.question,
|
||||
options=options,
|
||||
total_voters=total_voters,
|
||||
option_chosen=option_chosen,
|
||||
client=client
|
||||
)
|
47
pyrogram/client/types/messages_and_media/poll_option.py
Normal file
47
pyrogram/client/types/messages_and_media/poll_option.py
Normal file
@@ -0,0 +1,47 @@
|
||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pyrogram
|
||||
from ..pyrogram_type import PyrogramType
|
||||
|
||||
|
||||
class PollOption(PyrogramType):
|
||||
"""This object represents a Poll Option.
|
||||
|
||||
Args:
|
||||
text (``str``):
|
||||
Text of the poll option.
|
||||
|
||||
voters (``int``):
|
||||
The number of users who voted this option.
|
||||
|
||||
data (``bytes``):
|
||||
Unique data that identifies this option among all the other options in a poll.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
*,
|
||||
client: "pyrogram.client.ext.BaseClient",
|
||||
text: str,
|
||||
voters: int,
|
||||
data: bytes):
|
||||
super().__init__(client)
|
||||
|
||||
self.text = text
|
||||
self.voters = voters
|
||||
self.data = data
|
@@ -199,11 +199,11 @@ class Chat(PyrogramType):
|
||||
parsed_chat.can_set_sticker_set = full_chat.can_set_stickers
|
||||
parsed_chat.sticker_set_name = full_chat.stickerset
|
||||
|
||||
if full_chat.pinned_msg_id:
|
||||
parsed_chat.pinned_message = client.get_messages(
|
||||
parsed_chat.id,
|
||||
message_ids=full_chat.pinned_msg_id
|
||||
)
|
||||
if full_chat.pinned_msg_id:
|
||||
parsed_chat.pinned_message = client.get_messages(
|
||||
parsed_chat.id,
|
||||
message_ids=full_chat.pinned_msg_id
|
||||
)
|
||||
|
||||
if isinstance(full_chat.exported_invite, types.ChatInviteExported):
|
||||
parsed_chat.invite_link = full_chat.exported_invite.link
|
||||
|
Reference in New Issue
Block a user