Merge branch 'master' of github.com:lonamiwebs/Telethon

This commit is contained in:
Tanuj 2017-09-19 00:02:03 +01:00
commit 9c990330fd
6 changed files with 83 additions and 19 deletions

View File

@ -8,9 +8,12 @@ https://github.com/pypa/sampleproject
Extra supported commands are:
* gen_tl, to generate the classes required for Telethon to run
* clean_tl, to clean these generated classes
* pypi, to generate sdist, bdist_wheel, and push to PyPi
"""
# To use a consistent encoding
from subprocess import run
from shutil import rmtree
from codecs import open
from sys import argv
from os import path
@ -44,6 +47,15 @@ if __name__ == '__main__':
TLGenerator('telethon/tl').clean_tlobjects()
print('Done.')
elif len(argv) >= 2 and argv[1] == 'pypi':
for x in ('build', 'dist', 'Telethon.egg-info'):
rmtree(x, ignore_errors=True)
run('python3 setup.py sdist', shell=True)
run('python3 setup.py bdist_wheel', shell=True)
run('twine upload dist/*', shell=True)
for x in ('build', 'dist', 'Telethon.egg-info'):
rmtree(x, ignore_errors=True)
else:
if not TelegramClient:
print('Run `python3', argv[0], 'gen_tl` first.')

View File

@ -22,7 +22,9 @@ class ChannelInvalidError(BadRequestError):
def __init__(self, **kwargs):
super(Exception, self).__init__(
self,
'Invalid channel object. Make sure to pass the right types.'
'Invalid channel object. Make sure to pass the right types,'
' for instance making sure that the request is designed for '
'channels or otherwise look for a different one more suited.'
)
@ -40,7 +42,12 @@ class ChatIdInvalidError(BadRequestError):
def __init__(self, **kwargs):
super(Exception, self).__init__(
self,
'Invalid object ID for a chat. Make sure to pass the right types.'
'Invalid object ID for a chat. Make sure to pass the right types,'
' for instance making sure that the request is designed for chats'
' (not channels/megagroups) or otherwise look for a different one'
' more suited.\nAn example working with a megagroup and'
' AddChatUserRequest, it will fail because megagroups are channels'
'. Use InviteToChannelRequest instead.'
)
@ -48,7 +55,8 @@ class ConnectionLangPackInvalid(BadRequestError):
def __init__(self, **kwargs):
super(Exception, self).__init__(
self,
'The specified language pack is not valid.'
'The specified language pack is not valid. This is meant to be '
'used by official applications only so far, leave it empty.'
)
@ -284,7 +292,7 @@ class UsernameInvalidError(BadRequestError):
def __init__(self, **kwargs):
super(Exception, self).__init__(
self,
'Unacceptable username. Must match r"[a-zA-Z][\w\d]{4,31}"'
'Unacceptable username. Must match r"[a-zA-Z][\w\d]{4,31}".'
)
@ -292,7 +300,7 @@ class UsernameNotModifiedError(BadRequestError):
def __init__(self, **kwargs):
super(Exception, self).__init__(
self,
'The username is not different from the current username'
'The username is not different from the current username.'
)
@ -300,7 +308,7 @@ class UsernameNotOccupiedError(BadRequestError):
def __init__(self, **kwargs):
super(Exception, self).__init__(
self,
'See issue #96 for Telethon - try upgrading the library.'
'The username is not in use by anyone else yet.'
)
@ -333,7 +341,9 @@ class UserIdInvalidError(BadRequestError):
def __init__(self, **kwargs):
super(Exception, self).__init__(
self,
'Invalid object ID for an user. Make sure to pass the right types.'
'Invalid object ID for an user. Make sure to pass the right types,'
'for instance making sure that the request is designed for users'
'or otherwise look for a different one more suited.'
)

View File

@ -23,6 +23,7 @@ class BinaryReader:
'Either bytes or a stream must be provided')
self.reader = BufferedReader(self.stream)
self._last = None # Should come in handy to spot -404 errors
# region Reading
@ -57,8 +58,12 @@ class BinaryReader:
"""Read the given amount of bytes"""
result = self.reader.read(length)
if len(result) != length:
raise BufferError('No more data left to read')
raise BufferError(
'No more data left to read (need {}, got {}: {}); last read {}'
.format(length, len(result), repr(result), repr(self._last))
)
self._last = result
return result
def get_bytes(self):

View File

@ -52,7 +52,7 @@ class TelegramBareClient:
"""
# Current TelegramClient version
__version__ = '0.13.3'
__version__ = '0.13.4'
# TODO Make this thread-safe, all connections share the same DC
_dc_options = None

View File

@ -41,7 +41,7 @@ from .tl.types import (
InputMediaUploadedDocument, InputMediaUploadedPhoto, InputPeerEmpty,
Message, MessageMediaContact, MessageMediaDocument, MessageMediaPhoto,
InputUserSelf, UserProfilePhoto, ChatPhoto, UpdateMessageID,
UpdateNewMessage
UpdateNewMessage, UpdateShortSentMessage
)
from .utils import find_user_or_chat, get_extension
@ -136,18 +136,18 @@ class TelegramClient(TelegramBareClient):
# region Connecting
def connect(self, *args):
def connect(self, exported_auth=None):
"""Connects to the Telegram servers, executing authentication if
required. Note that authenticating to the Telegram servers is
not the same as authenticating the desired user itself, which
may require a call (or several) to 'sign_in' for the first time.
*args will be ignored.
exported_auth is meant for internal purposes and can be ignored.
"""
if self._sender and self._sender.is_connected():
return
ok = super().connect()
ok = super().connect(exported_auth=exported_auth)
# The main TelegramClient is the only one that will have
# constant_read, since it's also the only one who receives
# updates and need to be processed as soon as they occur.
@ -230,6 +230,8 @@ class TelegramClient(TelegramBareClient):
threading.get_ident() == self._recv_thread.ident:
raise AssertionError('Cannot invoke requests from the ReadThread')
self.updates.check_error()
try:
# Users may call this method from within some update handler.
# If this is the case, then the thread invoking the request
@ -292,8 +294,7 @@ class TelegramClient(TelegramBareClient):
If no phone or code is provided, then the sole password will be used.
The password should be used after a normal authorization attempt
has happened and an RPCError with `.password_required = True` was
raised.
has happened and an SessionPasswordNeededError was raised.
To login as a bot, only `bot_token` should be provided.
This should equal to the bot access hash provided by
@ -417,14 +418,26 @@ class TelegramClient(TelegramBareClient):
If 'reply_to' is set to either a message or a message ID,
the sent message will be replying to such message.
"""
entity = self.get_entity(entity)
request = SendMessageRequest(
peer=self.get_entity(entity),
peer=entity,
message=message,
entities=[],
no_webpage=not link_preview,
reply_to_msg_id=self._get_reply_to(reply_to)
)
result = self(request)
if isinstance(request, UpdateShortSentMessage):
return Message(
id=result.id,
to_id=entity,
message=message,
date=result.date,
out=result.out,
media=result.media,
entities=result.entities
)
# Telegram seems to send updateMessageID first, then updateNewMessage,
# however let's not rely on that just in case.
msg_id = None
@ -1026,5 +1039,10 @@ class TelegramClient(TelegramBareClient):
self._recv_thread = None # Not running anymore
self.reconnect()
return
except Exception as e:
# Unknown exception, pass it to the main thread
self.updates.set_error(e)
self._recv_thread = None
return
# endregion

View File

@ -34,7 +34,10 @@ class UpdateState:
if not self._updates:
self._updates_available.clear()
return update
if isinstance(update, Exception):
raise update # Some error was set through .set_error()
return update
def get_polling(self):
return self._polling
@ -47,6 +50,21 @@ class UpdateState:
polling = property(fget=get_polling, fset=set_polling)
def set_error(self, error):
"""Sets an error, so that the next call to .poll() will raise it.
Can be (and is) used to pass exceptions between threads.
"""
with self._updates_lock:
# Insert at the beginning so the very next poll causes an error
# TODO Should this reset the pts and such?
self._updates.insert(0, error)
self._updates_available.set()
def check_error(self):
with self._updates_lock:
if self._updates and isinstance(self._updates[0], Exception):
raise self._updates.pop()
def process(self, update):
"""Processes an update object. This method is normally called by
the library itself.
@ -59,9 +77,10 @@ class UpdateState:
self._state = update
elif not hasattr(update, 'pts') or update.pts > self._state.pts:
self._state.pts = getattr(update, 'pts', self._state.pts)
for handler in self.handlers:
handler(update)
if self._polling:
self._updates.append(update)
self._updates_available.set()
for handler in self.handlers:
handler(update)