Update to v1.0

This commit is contained in:
Lonami Exo 2018-06-27 13:05:19 +02:00
parent a1799ee74b
commit 72835dfb44
13 changed files with 156 additions and 29 deletions

View File

@ -26,7 +26,7 @@ Installing
.. code:: sh .. code:: sh
pip3 install telethon-aio pip3 install telethon
Creating a client Creating a client

View File

@ -13,9 +13,9 @@ Magic with asyncio
import telethon.sync import telethon.sync
At the beginning of your main script and you will be good. If you At the beginning of your main script and you will be good. If you do use
do use updates or events, keep reading, or use `a simpler version updates or events, keep reading, or install the latest version using
<https://github.com/LonamiWebs/Telethon/tree/sync>`_ (discouraged). threads and Python 3.4 support with ``pip install telethon==0.19.1.6``.
You might also want to check the :ref:`changelog`. You might also want to check the :ref:`changelog`.
@ -120,7 +120,7 @@ way, since your event handlers will be ``async def`` too.
There are two exceptions. Both `client.run_until_disconnected() There are two exceptions. Both `client.run_until_disconnected()
<telethon.client.updates.UpdateMethods.run_until_disconnected>` and <telethon.client.updates.UpdateMethods.run_until_disconnected>` and
`client.start() <telethon.client.updates.UpdateMethods.start>` work in `client.start() <telethon.client.auth.AuthMethods.start>` work in
and outside of ``async def`` for convenience without importing the and outside of ``async def`` for convenience without importing the
magic module. The rest of methods remain ``async`` unless you import it. magic module. The rest of methods remain ``async`` unless you import it.

View File

@ -14,6 +14,131 @@ it can take advantage of new goodies!
.. contents:: List of All Versions .. contents:: List of All Versions
Synchronous magic (v1.0)
========================
.. important::
If you come from Telethon pre-1.0 you **really** want to read
:ref:`asyncio-magic` to port your scripts to the new version.
If you're not ready for this, you can ``pip install telethon==0.19.1.6``.
It's the latest version of the library using threads for Python 3.4+.
If you're interested in maintaining a Telethon version that supports
Python 3.4 and uses threads, please open an issue and let me know.
The library has been around for well over a year. A lot of improvements have
been made, a lot of user complaints have been fixed, and a lot of user desires
have been implemented. It's time to consider the public API as stable, and
remove some of the old methods that were around until now for compatibility
reasons. But there's one more surprise!
There is a new magic ``telethon.sync`` module to let you use **all** the
methods in the :ref:`TelegramClient <telethon-client>` (and the types returned
from its functions) in a synchronous way, while using ``asyncio`` behind
the scenes! This means you're now able to do both of the following:
.. code-block:: python
import asyncio
async def main():
await client.send_message('me', 'Hello!')
asyncio.get_event_loop().run_until_complete(main())
# ...can be rewritten as:
from telethon import sync
client.send_message('me', 'Hello!')
Both ways can coexist (you need to ``await`` if the loop is running).
You can also use the magic ``sync`` module in your own classes, and call
``sync.syncify(cls)`` to convert all their ``async def`` into magic variants.
Breaking Changes
~~~~~~~~~~~~~~~~
- ``message.get_fwd_sender`` is now in `message.forward
<telethon.tl.custom.message.Message.forward>`.
- ``client.add_update_handler`` is now `client.add_event_handler
<telethon.client.updates.UpdateMethods.add_event_handler>`
- ``client.remove_update_handler`` is now `client.remove_event_handler
<telethon.client.updates.UpdateMethods.remove_event_handler>`
- ``client.list_update_handlers`` is now `client.list_event_handlers
<telethon.client.updates.UpdateMethods.list_event_handlers>`
- ``client.get_message_history`` is now `client.get_messages
<telethon.client.messages.MessageMethods.get_messages>`
- ``client.send_voice_note`` is now `client.send_file
<telethon.client.uploads.UploadMethods.send_file>` with ``is_voice=True``.
- ``client.invoke()`` is now ``client(...)``.
- ``report_errors`` has been removed since it's currently not used,
and ``flood_sleep_threshold`` is now part of the client.
- Methods with a lot of arguments can no longer be used without specifying
their argument. Instead you need to use named arguments. This improves
readability and not needing to learn the order of the arguments, which
can also change.
Additions
~~~~~~~~~
- `client.send_file <telethon.client.uploads.UploadMethods.send_file>` now
accepts external ``http://`` and ``https://`` URLs.
- You can use the :ref:`TelegramClient <telethon-client>` inside of ``with``
blocks, which will `client.start() <telethon.client.auth.AuthMethods.start>`
and `disconnect() <telethon.client.telegrambaseclient.TelegramBaseClient.disconnect>`
the client for you:
.. code-block:: python
from telethon import TelegramClient, sync
with TelegramClient(name, api_id, api_hash) as client:
client.send_message('me', 'Hello!')
Convenience at its maximum! You can even chain the `.start()
<telethon.client.auth.AuthMethods.start>` method since
it returns the instance of the client:
.. code-block:: python
with TelegramClient(name, api_id, api_hash).star(bot_token=token) as bot:
bot.send_message(chat, 'Hello!')
Bug fixes
~~~~~~~~~
- There were some ``@property async def`` left, and some ``await property``.
- "User joined" event was being treated as "User was invited".
- SQLite's cursor should not be closed properly after usage.
- ``await`` the updates task upon disconnection.
- Some bug in Python 3.5.2's ``asyncio`` causing 100% CPU load if you
forgot to call `client.disconnect()
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnect>`.
The method is called for you on object destruction, but you still should
disconnect manually or use a ``with`` block.
- Some fixes regarding disconnecting on client deletion and properly
saving the authorization key.
- Passing a class to `message.get_entities_text
<telethon.tl.custom.message.Message.get_entities_text>` now works properly.
- Iterating messages from a specific user in private messages now works.
Enhancements
~~~~~~~~~~~~
- Both `client.start() <telethon.client.auth.AuthMethods.start>` and
`client.run_until_disconnected()
<telethon.client.updates.UpdateMethods.run_until_disconnected>` can
be ran in both a synchronous way (without starting the loop manually)
or from an ``async def`` where they need to have an ``await``.
Core Rewrite in asyncio (v1.0-rc1) Core Rewrite in asyncio (v1.0-rc1)
================================== ==================================

View File

@ -183,7 +183,7 @@ def main():
version = re.search(r"^__version__\s*=\s*'(.*)'.*$", version = re.search(r"^__version__\s*=\s*'(.*)'.*$",
f.read(), flags=re.MULTILINE).group(1) f.read(), flags=re.MULTILINE).group(1)
setup( setup(
name='Telethon-aio', name='Telethon',
version=version, version=version,
description="Full-featured Telegram client library for Python 3", description="Full-featured Telegram client library for Python 3",
long_description=long_description, long_description=long_description,
@ -206,7 +206,7 @@ def main():
# 3 - Alpha # 3 - Alpha
# 4 - Beta # 4 - Beta
# 5 - Production/Stable # 5 - Production/Stable
'Development Status :: 3 - Alpha', 'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers', 'Intended Audience :: Developers',
'Topic :: Communications :: Chat', 'Topic :: Communications :: Chat',

View File

@ -17,6 +17,7 @@ class AuthMethods(MessageParseMethods, UserMethods):
self, self,
phone=lambda: input('Please enter your phone: '), phone=lambda: input('Please enter your phone: '),
password=lambda: getpass.getpass('Please enter your password: '), password=lambda: getpass.getpass('Please enter your password: '),
*,
bot_token=None, force_sms=False, code_callback=None, bot_token=None, force_sms=False, code_callback=None,
first_name='New User', last_name='', max_attempts=3): first_name='New User', last_name='', max_attempts=3):
""" """
@ -193,7 +194,7 @@ class AuthMethods(MessageParseMethods, UserMethods):
return self return self
async def sign_in( async def sign_in(
self, phone=None, code=None, password=None, self, phone=None, *, code=None, password=None,
bot_token=None, phone_code_hash=None): bot_token=None, phone_code_hash=None):
""" """
Starts or completes the sign in process with the given phone number Starts or completes the sign in process with the given phone number
@ -325,7 +326,7 @@ class AuthMethods(MessageParseMethods, UserMethods):
self._authorized = True self._authorized = True
return result.user return result.user
async def send_code_request(self, phone, force_sms=False): async def send_code_request(self, phone, *, force_sms=False):
""" """
Sends a code request to the specified phone number. Sends a code request to the specified phone number.
@ -382,8 +383,8 @@ class AuthMethods(MessageParseMethods, UserMethods):
return True return True
async def edit_2fa( async def edit_2fa(
self, current_password=None, new_password=None, hint='', self, current_password=None, new_password=None,
email=None): *, hint='', email=None):
""" """
Changes the 2FA settings of the logged in user, according to the Changes the 2FA settings of the logged in user, according to the
passed parameters. Take note of the parameter explanations. passed parameters. Take note of the parameter explanations.

View File

@ -13,7 +13,7 @@ class ChatMethods(UserMethods):
@async_generator @async_generator
async def iter_participants( async def iter_participants(
self, entity, limit=None, search='', self, entity, limit=None, *, search='',
filter=None, aggressive=False, _total=None): filter=None, aggressive=False, _total=None):
""" """
Iterator over the participants belonging to the specified chat. Iterator over the participants belonging to the specified chat.

View File

@ -14,7 +14,7 @@ class DialogMethods(UserMethods):
@async_generator @async_generator
async def iter_dialogs( async def iter_dialogs(
self, limit=None, offset_date=None, offset_id=0, self, limit=None, *, offset_date=None, offset_id=0,
offset_peer=types.InputPeerEmpty(), ignore_migrated=False, offset_peer=types.InputPeerEmpty(), ignore_migrated=False,
_total=None): _total=None):
""" """

View File

@ -16,7 +16,7 @@ class DownloadMethods(UserMethods):
# region Public methods # region Public methods
async def download_profile_photo( async def download_profile_photo(
self, entity, file=None, download_big=True): self, entity, file=None, *, download_big=True):
""" """
Downloads the profile photo of the given entity (user/chat/channel). Downloads the profile photo of the given entity (user/chat/channel).
@ -91,7 +91,8 @@ class DownloadMethods(UserMethods):
# Until there's a report for chats, no need to. # Until there's a report for chats, no need to.
return None return None
async def download_media(self, message, file=None, progress_callback=None): async def download_media(self, message, file=None,
*, progress_callback=None):
""" """
Downloads the given media, or the media from a specified Message. Downloads the given media, or the media from a specified Message.
@ -141,7 +142,7 @@ class DownloadMethods(UserMethods):
) )
async def download_file( async def download_file(
self, input_location, file=None, part_size_kb=None, self, input_location, file=None, *, part_size_kb=None,
file_size=None, progress_callback=None): file_size=None, progress_callback=None):
""" """
Downloads the given input location to a file. Downloads the given input location to a file.

View File

@ -22,7 +22,7 @@ class MessageMethods(UploadMethods, MessageParseMethods):
@async_generator @async_generator
async def iter_messages( async def iter_messages(
self, entity, limit=None, offset_date=None, offset_id=0, self, entity, limit=None, *, offset_date=None, offset_id=0,
max_id=0, min_id=0, add_offset=0, search=None, filter=None, max_id=0, min_id=0, add_offset=0, search=None, filter=None,
from_user=None, batch_size=100, wait_time=None, ids=None, from_user=None, batch_size=100, wait_time=None, ids=None,
_total=None): _total=None):
@ -285,7 +285,7 @@ class MessageMethods(UploadMethods, MessageParseMethods):
# region Message sending/editing/deleting # region Message sending/editing/deleting
async def send_message( async def send_message(
self, entity, message='', reply_to=None, self, entity, message='', *, reply_to=None,
parse_mode=utils.Default, link_preview=True, file=None, parse_mode=utils.Default, link_preview=True, file=None,
force_document=False, clear_draft=False): force_document=False, clear_draft=False):
""" """
@ -403,7 +403,7 @@ class MessageMethods(UploadMethods, MessageParseMethods):
return self._get_response_message(request, result, entity) return self._get_response_message(request, result, entity)
async def forward_messages(self, entity, messages, from_peer=None): async def forward_messages(self, entity, messages, *, from_peer=None):
""" """
Forwards the given message(s) to the specified entity. Forwards the given message(s) to the specified entity.
@ -469,8 +469,8 @@ class MessageMethods(UploadMethods, MessageParseMethods):
return result[0] if single else result return result[0] if single else result
async def edit_message( async def edit_message(
self, entity, message=None, text=None, parse_mode=utils.Default, self, entity, message=None, text=None,
link_preview=True, file=None): *, parse_mode=utils.Default, link_preview=True, file=None):
""" """
Edits the given message ID (to change its contents or disable preview). Edits the given message ID (to change its contents or disable preview).
@ -542,7 +542,7 @@ class MessageMethods(UploadMethods, MessageParseMethods):
self._cache_media(msg, file, file_handle) self._cache_media(msg, file, file_handle)
return msg return msg
async def delete_messages(self, entity, message_ids, revoke=True): async def delete_messages(self, entity, message_ids, *, revoke=True):
""" """
Deletes a message from a chat, optionally "for everyone". Deletes a message from a chat, optionally "for everyone".
@ -586,8 +586,8 @@ class MessageMethods(UploadMethods, MessageParseMethods):
# region Miscellaneous # region Miscellaneous
async def send_read_acknowledge(self, entity, message=None, max_id=None, async def send_read_acknowledge(
clear_mentions=False): self, entity, message=None, *, max_id=None, clear_mentions=False):
""" """
Sends a "read acknowledge" (i.e., notifying the given peer that we've Sends a "read acknowledge" (i.e., notifying the given peer that we've
read their messages, also known as the "double check"). read their messages, also known as the "double check").

View File

@ -27,7 +27,7 @@ class UploadMethods(MessageParseMethods, UserMethods):
# region Public methods # region Public methods
async def send_file( async def send_file(
self, entity, file, caption='', force_document=False, self, entity, file, *, caption='', force_document=False,
progress_callback=None, reply_to=None, attributes=None, progress_callback=None, reply_to=None, attributes=None,
thumb=None, allow_cache=True, parse_mode=utils.Default, thumb=None, allow_cache=True, parse_mode=utils.Default,
voice_note=False, video_note=False, **kwargs): voice_note=False, video_note=False, **kwargs):
@ -221,7 +221,7 @@ class UploadMethods(MessageParseMethods, UserMethods):
] ]
async def upload_file( async def upload_file(
self, file, part_size_kb=None, file_name=None, use_cache=None, self, file, *, part_size_kb=None, file_name=None, use_cache=None,
progress_callback=None): progress_callback=None):
""" """
Uploads the specified file and returns a handle (an instance of Uploads the specified file and returns a handle (an instance of

View File

@ -14,7 +14,7 @@ class MessageRead(EventBuilder):
messages the event will be fired. By default (``False``) only messages the event will be fired. By default (``False``) only
when messages you sent are read by someone else will fire it. when messages you sent are read by someone else will fire it.
""" """
def __init__(self, inbox=False, chats=None, blacklist_chats=None): def __init__(self, chats=None, *, blacklist_chats=None, inbox=False):
super().__init__(chats, blacklist_chats) super().__init__(chats, blacklist_chats)
self.inbox = inbox self.inbox = inbox

View File

@ -82,7 +82,7 @@ class UserUpdate(EventBuilder):
contact (`bool`): contact (`bool`):
``True`` if what's being uploaded (selected) is a contact. ``True`` if what's being uploaded (selected) is a contact.
""" """
def __init__(self, user_id, status=None, typing=None): def __init__(self, user_id, *, status=None, typing=None):
super().__init__(types.PeerUser(user_id)) super().__init__(types.PeerUser(user_id))
self.online = None if status is None else \ self.online = None if status is None else \

View File

@ -1,3 +1,3 @@
# Versions should comply with PEP440. # Versions should comply with PEP440.
# This line is parsed in setup.py: # This line is parsed in setup.py:
__version__ = '1.0-rc1' __version__ = '1.0'