mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-08-04 12:10:21 +03:00
Merge branch 'master' of github.com:lonamiwebs/Telethon
This commit is contained in:
commit
9c990330fd
12
setup.py
12
setup.py
|
@ -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.')
|
||||
|
|
|
@ -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.'
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -34,6 +34,9 @@ class UpdateState:
|
|||
if not self._updates:
|
||||
self._updates_available.clear()
|
||||
|
||||
if isinstance(update, Exception):
|
||||
raise update # Some error was set through .set_error()
|
||||
|
||||
return update
|
||||
|
||||
def get_polling(self):
|
||||
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user