Merge branch 'master' into docs

# Conflicts:
#	docs/source/pyrogram/index.rst
This commit is contained in:
Dan 2018-02-07 03:46:24 +01:00
commit 43292be336
8 changed files with 187 additions and 20 deletions

View File

@ -733,7 +733,7 @@ payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_inf
inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials; inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials;
inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials; inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials;
inputPaymentCredentialsApplePay#aa1c39f payment_data:DataJSON = InputPaymentCredentials; inputPaymentCredentialsApplePay#aa1c39f payment_data:DataJSON = InputPaymentCredentials;
inputPaymentCredentialsAndroidPay#795667a6 payment_token:DataJSON = InputPaymentCredentials; inputPaymentCredentialsAndroidPay#ca05d50e payment_token:DataJSON google_transaction_id:string = InputPaymentCredentials;
account.tmpPassword#db64fd34 tmp_password:bytes valid_until:int = account.TmpPassword; account.tmpPassword#db64fd34 tmp_password:bytes valid_until:int = account.TmpPassword;
@ -813,7 +813,7 @@ recentMeUrlStickerSet#bc0a57dc url:string set:StickerSetCovered = RecentMeUrl;
help.recentMeUrls#e0310d7 urls:Vector<RecentMeUrl> chats:Vector<Chat> users:Vector<User> = help.RecentMeUrls; help.recentMeUrls#e0310d7 urls:Vector<RecentMeUrl> chats:Vector<Chat> users:Vector<User> = help.RecentMeUrls;
inputSingleMedia#31bc3d25 media:InputMedia flags:# random_id:long message:string entities:flags.0?Vector<MessageEntity> = InputSingleMedia; inputSingleMedia#1cc6e91f flags:# media:InputMedia random_id:long message:string entities:flags.0?Vector<MessageEntity> = InputSingleMedia;
---functions--- ---functions---
@ -841,8 +841,8 @@ auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentC
auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool; auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool;
auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector<long> = Bool; auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector<long> = Bool;
account.registerDevice#637ea878 token_type:int token:string = Bool; account.registerDevice#1389cc token_type:int token:string app_sandbox:Bool other_uids:Vector<int> = Bool;
account.unregisterDevice#65c55b40 token_type:int token:string = Bool; account.unregisterDevice#3076c4bf token_type:int token:string other_uids:Vector<int> = Bool;
account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool; account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool;
account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings; account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings;
account.resetNotifySettings#db7e1747 = Bool; account.resetNotifySettings#db7e1747 = Bool;
@ -910,7 +910,6 @@ messages.editChatPhoto#ca4c79d8 chat_id:int photo:InputChatPhoto = Updates;
messages.addChatUser#f9a0aa09 chat_id:int user_id:InputUser fwd_limit:int = Updates; messages.addChatUser#f9a0aa09 chat_id:int user_id:InputUser fwd_limit:int = Updates;
messages.deleteChatUser#e0611f16 chat_id:int user_id:InputUser = Updates; messages.deleteChatUser#e0611f16 chat_id:int user_id:InputUser = Updates;
messages.createChat#9cb126e users:Vector<InputUser> title:string = Updates; messages.createChat#9cb126e users:Vector<InputUser> title:string = Updates;
messages.forwardMessage#33963bf9 peer:InputPeer id:int random_id:long = Updates;
messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig; messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig;
messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat; messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat;
messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat; messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat;
@ -923,6 +922,7 @@ messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long da
messages.receivedQueue#55a5bb66 max_qts:int = Vector<long>; messages.receivedQueue#55a5bb66 max_qts:int = Vector<long>;
messages.reportEncryptedSpam#4b0c8c0f peer:InputEncryptedChat = Bool; messages.reportEncryptedSpam#4b0c8c0f peer:InputEncryptedChat = Bool;
messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages; messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages;
messages.getStickers#ae22e045 emoticon:string hash:string = messages.Stickers;
messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers;
messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;
messages.exportChatInvite#7d885289 chat_id:int = ExportedChatInvite; messages.exportChatInvite#7d885289 chat_id:int = ExportedChatInvite;
@ -1034,7 +1034,7 @@ channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> =
channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite; channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite;
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates; channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.toggleInvites#49609307 channel:InputChannel enabled:Bool = Updates; channels.toggleInvites#49609307 channel:InputChannel enabled:Bool = Updates;
channels.exportMessageLink#c846d22d channel:InputChannel id:int = ExportedMessageLink; channels.exportMessageLink#ceb77163 channel:InputChannel id:int grouped:Bool = ExportedMessageLink;
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates; channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates; channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates;
channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats; channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats;

View File

@ -42,4 +42,5 @@ CDN_METHOD_INVALID The method can't be used on CDN DCs
VOLUME_LOC_NOT_FOUND The volume location can't be found VOLUME_LOC_NOT_FOUND The volume location can't be found
FILE_ID_INVALID The file id is invalid FILE_ID_INVALID The file id is invalid
LOCATION_INVALID The file location is invalid LOCATION_INVALID The file location is invalid
CHAT_ADMIN_REQUIRED The method requires admin privileges CHAT_ADMIN_REQUIRED The method requires admin privileges
PHONE_NUMBER_BANNED The phone number is banned

1 id message
42 VOLUME_LOC_NOT_FOUND The volume location can't be found
43 FILE_ID_INVALID The file id is invalid
44 LOCATION_INVALID The file location is invalid
45 CHAT_ADMIN_REQUIRED The method requires admin privileges
46 PHONE_NUMBER_BANNED The phone number is banned

View File

@ -0,0 +1,6 @@
InputMedia
==========
.. autoclass:: pyrogram.InputMedia
:members:
:undoc-members:

View File

@ -9,8 +9,9 @@ the same parameters as well, thus offering a familiar look to Bot developers.
.. toctree:: .. toctree::
Client Client
Error
ChatAction ChatAction
ParseMode ParseMode
InputMedia
Error
.. _Telegram Bot API: https://core.telegram.org/bots/api#available-methods .. _Telegram Bot API: https://core.telegram.org/bots/api#available-methods

View File

@ -23,9 +23,10 @@ __copyright__ = "Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance
"e" if sys.getfilesystemencoding() == "ascii" else "\xe8" "e" if sys.getfilesystemencoding() == "ascii" else "\xe8"
) )
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)" __license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
__version__ = "0.4.2" __version__ = "0.5.0"
from .api.errors import Error from .api.errors import Error
from .client import ChatAction from .client import ChatAction
from .client import Client from .client import Client
from .client import ParseMode from .client import ParseMode
from .client.input_media import InputMedia

View File

@ -37,7 +37,7 @@ from pyrogram.api.errors import (
PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty, PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty,
PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded, PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded,
PasswordHashInvalid, FloodWait, PeerIdInvalid, FilePartMissing, PasswordHashInvalid, FloodWait, PeerIdInvalid, FilePartMissing,
ChatAdminRequired, FirstnameInvalid ChatAdminRequired, FirstnameInvalid, PhoneNumberBanned
) )
from pyrogram.api.types import ( from pyrogram.api.types import (
User, Chat, Channel, User, Chat, Channel,
@ -48,6 +48,7 @@ from pyrogram.api.types import (
) )
from pyrogram.crypto import CTR from pyrogram.crypto import CTR
from pyrogram.session import Auth, Session from pyrogram.session import Auth, Session
from .input_media import InputMedia
from .style import Markdown, HTML from .style import Markdown, HTML
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -274,7 +275,7 @@ class Client:
) )
) )
break break
except PhoneNumberInvalid as e: except (PhoneNumberInvalid, PhoneNumberBanned) as e:
if phone_number_invalid_raises: if phone_number_invalid_raises:
raise raise
else: else:
@ -494,12 +495,17 @@ class Client:
log.info("Dialogs count: {}".format(len(peers))) log.info("Dialogs count: {}".format(len(peers)))
while len(dialogs.dialogs) == self.DIALOGS_AT_ONCE: while len(dialogs.dialogs) == self.DIALOGS_AT_ONCE:
dialogs = self.send( try:
functions.messages.GetDialogs( dialogs = self.send(
offset_date, 0, types.InputPeerEmpty(), functions.messages.GetDialogs(
self.DIALOGS_AT_ONCE, True offset_date, 0, types.InputPeerEmpty(),
self.DIALOGS_AT_ONCE, True
)
) )
) except FloodWait as e:
log.info("Get dialogs flood wait: {}".format(e.x))
time.sleep(e.x)
continue
offset_date = parse_dialogs(dialogs) offset_date = parse_dialogs(dialogs)
log.info("Dialogs count: {}".format(len(peers))) log.info("Dialogs count: {}".format(len(peers)))
@ -604,7 +610,7 @@ class Client:
parse_mode: str = "", parse_mode: str = "",
disable_web_page_preview: bool = None, disable_web_page_preview: bool = None,
disable_notification: bool = None, disable_notification: bool = None,
reply_to_msg_id: int = None): reply_to_message_id: int = None):
"""Use this method to send text messages. """Use this method to send text messages.
Args: Args:
@ -628,7 +634,7 @@ class Client:
Sends the message silently. Sends the message silently.
Users will receive a notification with no sound. Users will receive a notification with no sound.
reply_to_msg_id (:obj:`bool`, optional): reply_to_message_id (:obj:`bool`, optional):
If the message is a reply, ID of the original message. If the message is a reply, ID of the original message.
Returns: Returns:
@ -644,7 +650,7 @@ class Client:
peer=self.resolve_peer(chat_id), peer=self.resolve_peer(chat_id),
no_webpage=disable_web_page_preview or None, no_webpage=disable_web_page_preview or None,
silent=disable_notification or None, silent=disable_notification or None,
reply_to_msg_id=reply_to_msg_id, reply_to_msg_id=reply_to_message_id,
random_id=self.rnd_id(), random_id=self.rnd_id(),
**style.parse(text) **style.parse(text)
) )
@ -1941,3 +1947,99 @@ class Client:
) )
else: else:
return False return False
def send_media_group(self,
chat_id: int or str,
media: list,
disable_notification: bool = None,
reply_to_message_id: int = None):
"""Use this method to send a group of photos or videos as an album.
On success, an Update containing the sent Messages is returned.
Args:
chat_id (:obj:`int` | :obj:`str`):
Unique identifier for the target chat or username of the target channel/supergroup
(in the format @username). For your personal cloud storage (Saved Messages) you can
simply use "me" or "self".
media (:obj:`list`):
A list containing either :obj:`pyrogram.InputMedia.Photo` or :obj:`pyrogram.InputMedia.Video` objects
describing photos and videos to be sent, must include 210 items.
disable_notification (:obj:`bool`, optional):
Sends the message silently.
Users will receive a notification with no sound.
reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message.
"""
multi_media = []
for i in media:
if isinstance(i, InputMedia.Photo):
style = self.html if i.parse_mode.lower() == "html" else self.markdown
media = self.save_file(i.media)
media = self.send(
functions.messages.UploadMedia(
peer=self.resolve_peer(chat_id),
media=types.InputMediaUploadedPhoto(
file=media
)
)
)
single_media = types.InputSingleMedia(
media=types.InputMediaPhoto(
id=types.InputPhoto(
id=media.photo.id,
access_hash=media.photo.access_hash
)
),
random_id=self.rnd_id(),
**style.parse(i.caption)
)
multi_media.append(single_media)
elif isinstance(i, InputMedia.Video):
style = self.html if i.parse_mode.lower() == "html" else self.markdown
media = self.save_file(i.media)
media = self.send(
functions.messages.UploadMedia(
peer=self.resolve_peer(chat_id),
media=types.InputMediaUploadedDocument(
file=media,
mime_type=mimetypes.types_map[".mp4"],
attributes=[
types.DocumentAttributeVideo(
duration=i.duration,
w=i.width,
h=i.height
)
]
)
)
)
single_media = types.InputSingleMedia(
media=types.InputMediaDocument(
id=types.InputDocument(
id=media.document.id,
access_hash=media.document.access_hash
)
),
random_id=self.rnd_id(),
**style.parse(i.caption)
)
multi_media.append(single_media)
return self.send(
functions.messages.SendMultiMedia(
peer=self.resolve_peer(chat_id),
multi_media=multi_media,
silent=disable_notification or None,
reply_to_msg_id=reply_to_message_id
)
)

View File

@ -0,0 +1,56 @@
class InputMedia:
class Photo:
"""This object represents a photo to be sent inside an album.
Args:
media (:obj:`str`):
File to send.
Pass a file path as string to send a photo that exists on your local machine.
caption (:obj:`str`):
Caption of the photo to be sent, 0-200 characters
parse_mode (:obj:`str`):
Use :obj:`pyrogram.ParseMode.MARKDOWN` or :obj:`pyrogram.ParseMode.HTML` if you want Telegram apps
to show bold, italic, fixed-width text or inline URLs in your caption.
Defaults to Markdown.
"""
def __init__(self,
media: str,
caption: str = "",
parse_mode: str = ""):
self.media = media
self.caption = caption
self.parse_mode = parse_mode
class Video:
"""This object represents a video to be sent inside an album.
Args:
media (:obj:`str`):
File to send.
Pass a file path as string to send a video that exists on your local machine.
caption (:obj:`str`):
Caption of the video to be sent, 0-200 characters
parse_mode (:obj:`str`):
Use :obj:`pyrogram.ParseMode.MARKDOWN` or :obj:`pyrogram.ParseMode.HTML` if you want Telegram apps
to show bold, italic, fixed-width text or inline URLs in your caption.
Defaults to Markdown.
"""
def __init__(self,
media: str,
caption: str = "",
parse_mode: str = "",
width: int = 0,
height: int = 0,
duration: int = 0):
self.media = media
self.caption = caption
self.parse_mode = parse_mode
self.width = width
self.height = height
self.duration = duration

View File

@ -31,7 +31,7 @@ from . import utils
class HTML: class HTML:
HTML_RE = re.compile(r"<(\w+)(?: href=\"(.*)\")?>(.*)</\1>") HTML_RE = re.compile(r"<(\w+)(?: href=([\"'])(.*)\2)?>(.*)</\1>")
MENTION_RE = re.compile(r"tg://user\?id=(\d+)") MENTION_RE = re.compile(r"tg://user\?id=(\d+)")
def __init__(self, peers_by_id): def __init__(self, peers_by_id):