diff --git a/readthedocs/basic/quick-start.rst b/readthedocs/basic/quick-start.rst index 8dbf928d..bd36b048 100644 --- a/readthedocs/basic/quick-start.rst +++ b/readthedocs/basic/quick-start.rst @@ -8,70 +8,70 @@ use these if possible. .. code-block:: python + import asyncio from telethon import TelegramClient # Remember to use your own values from my.telegram.org! api_id = 12345 api_hash = '0123456789abcdef0123456789abcdef' - client = TelegramClient('anon', api_id, api_hash) async def main(): - # Getting information about yourself - me = await client.get_me() + async with TelegramClient('anon', api_id, api_hash).start() as client: + # Getting information about yourself + me = await client.get_me() - # "me" is a user object. You can pretty-print - # any Telegram object with the "stringify" method: - print(me.stringify()) + # "me" is a user object. You can pretty-print + # any Telegram object with the "stringify" method: + print(me.stringify()) - # When you print something, you see a representation of it. - # You can access all attributes of Telegram objects with - # the dot operator. For example, to get the username: - username = me.username - print(username) - print(me.phone) + # When you print something, you see a representation of it. + # You can access all attributes of Telegram objects with + # the dot operator. For example, to get the username: + username = me.username + print(username) + print(me.phone) - # You can print all the dialogs/conversations that you are part of: - async for dialog in client.iter_dialogs(): - print(dialog.name, 'has ID', dialog.id) + # You can print all the dialogs/conversations that you are part of: + async for dialog in client.iter_dialogs(): + print(dialog.name, 'has ID', dialog.id) - # You can send messages to yourself... - await client.send_message('me', 'Hello, myself!') - # ...to some chat ID - await client.send_message(-100123456, 'Hello, group!') - # ...to your contacts - await client.send_message('+34600123123', 'Hello, friend!') - # ...or even to any username - await client.send_message('username', 'Testing Telethon!') + # You can send messages to yourself... + await client.send_message('me', 'Hello, myself!') + # ...to some chat ID + await client.send_message(-100123456, 'Hello, group!') + # ...to your contacts + await client.send_message('+34600123123', 'Hello, friend!') + # ...or even to any username + await client.send_message('username', 'Testing Telethon!') - # You can, of course, use markdown in your messages: - message = await client.send_message( - 'me', - 'This message has **bold**, `code`, __italics__ and ' - 'a [nice website](https://example.com)!', - link_preview=False - ) + # You can, of course, use markdown in your messages: + message = await client.send_message( + 'me', + 'This message has **bold**, `code`, __italics__ and ' + 'a [nice website](https://example.com)!', + link_preview=False + ) - # Sending a message returns the sent message object, which you can use - print(message.raw_text) + # Sending a message returns the sent message object, which you can use + print(message.raw_text) - # You can reply to messages directly if you have a message object - await message.reply('Cool!') + # You can reply to messages directly if you have a message object + await message.reply('Cool!') - # Or send files, songs, documents, albums... - await client.send_file('me', '/home/me/Pictures/holidays.jpg') + # Or send files, songs, documents, albums... + await client.send_file('me', '/home/me/Pictures/holidays.jpg') - # You can print the message history of any chat: - async for message in client.iter_messages('me'): - print(message.id, message.text) + # You can print the message history of any chat: + async for message in client.iter_messages('me'): + print(message.id, message.text) - # You can download media from messages, too! - # The method will return the path where the file was saved. - if message.photo: - path = await message.download_media() - print('File saved to', path) # printed after download is done + # You can download media from messages, too! + # The method will return the path where the file was saved. + if message.photo: + path = await message.download_media() + print('File saved to', path) # printed after download is done - with client: - client.loop.run_until_complete(main()) + asyncio.run(main()) Here, we show how to sign in, get information about yourself, send @@ -100,8 +100,8 @@ proceeding. We will see all the available methods later on. # Most of your code should go here. # You can of course make and use your own async def (do_something). # They only need to be async if they need to await things. - async with client: + async with client.start(): me = await client.get_me() await do_something(me) - client.loop.run_until_complete(main()) + asyncio.run(main()) diff --git a/readthedocs/basic/signing-in.rst b/readthedocs/basic/signing-in.rst index c7b60507..05183f5c 100644 --- a/readthedocs/basic/signing-in.rst +++ b/readthedocs/basic/signing-in.rst @@ -49,6 +49,7 @@ We can finally write some code to log into our account! .. code-block:: python + import asyncio from telethon import TelegramClient # Use your own values from my.telegram.org @@ -57,10 +58,10 @@ We can finally write some code to log into our account! async def main(): # The first parameter is the .session file name (absolute paths allowed) - async with TelegramClient('anon', api_id, api_hash) as client: + async with TelegramClient('anon', api_id, api_hash).start() as client: await client.send_message('me', 'Hello, myself!') - client.loop.run_until_complete(main()) + asyncio.run(main()) In the first line, we import the class name so we can create an instance @@ -98,21 +99,19 @@ You will still need an API ID and hash, but the process is very similar: .. code-block:: python + import asyncio from telethon import TelegramClient api_id = 12345 api_hash = '0123456789abcdef0123456789abcdef' bot_token = '12345:0123456789abcdef0123456789abcdef' - # We have to manually call "start" if we want an explicit bot token - bot = TelegramClient('bot', api_id, api_hash).start(bot_token=bot_token) - async def main(): # But then we can use the client instance as usual - async with bot: - ... + async with TelegramClient('bot', api_id, api_hash).start(bot_token=bot_token) as bot: + ... # bot is your client - client.loop.run_until_complete(main()) + asyncio.run(main()) To get a bot account, you need to talk diff --git a/readthedocs/concepts/full-api.rst b/readthedocs/concepts/full-api.rst index cce026f1..ac38e3d4 100644 --- a/readthedocs/concepts/full-api.rst +++ b/readthedocs/concepts/full-api.rst @@ -74,7 +74,7 @@ Or we call `client.get_input_entity() async def main(): peer = await client.get_input_entity('someone') - client.loop.run_until_complete(main()) + asyncio.run(main()) .. note:: diff --git a/readthedocs/concepts/sessions.rst b/readthedocs/concepts/sessions.rst index 8ba75938..028366b1 100644 --- a/readthedocs/concepts/sessions.rst +++ b/readthedocs/concepts/sessions.rst @@ -156,8 +156,8 @@ you can save it in a variable directly: .. code-block:: python string = '1aaNk8EX-YRfwoRsebUkugFvht6DUPi_Q25UOCzOAqzc...' - with TelegramClient(StringSession(string), api_id, api_hash) as client: - client.loop.run_until_complete(client.send_message('me', 'Hi')) + async with TelegramClient(StringSession(string), api_id, api_hash).start() as client: + await client.send_message('me', 'Hi') These strings are really convenient for using in places like Heroku since diff --git a/readthedocs/misc/v2-migration-guide.rst b/readthedocs/misc/v2-migration-guide.rst index 10556cc0..33a515d8 100644 --- a/readthedocs/misc/v2-migration-guide.rst +++ b/readthedocs/misc/v2-migration-guide.rst @@ -759,3 +759,5 @@ they are meant to work on lists. also mark read only supports single now. a list would just be max anyway. removed max id since it's not really of much use. + +client loop has been removed. embrace implicit loop as asyncio does now diff --git a/readthedocs/modules/client.rst b/readthedocs/modules/client.rst index de5502c9..4fc076d8 100644 --- a/readthedocs/modules/client.rst +++ b/readthedocs/modules/client.rst @@ -20,10 +20,10 @@ Each mixin has its own methods, which you all can use. async def main(): # Now you can use all client methods listed below, like for example... - await client.send_message('me', 'Hello to myself!') + async with client.start(): + await client.send_message('me', 'Hello to myself!') - with client: - client.loop.run_until_complete(main()) + asyncio.run(main()) You **don't** need to import these `AuthMethods`, `MessageMethods`, etc. diff --git a/telethon/_client/chats.py b/telethon/_client/chats.py index f900ce6d..0759acc2 100644 --- a/telethon/_client/chats.py +++ b/telethon/_client/chats.py @@ -40,7 +40,7 @@ class _ChatAction: self._chat, self._action) self._running = True - self._task = self._client.loop.create_task(self._update()) + self._task = asyncio.create_task(self._update()) return self async def __aexit__(self, *args): diff --git a/telethon/_client/telegrambaseclient.py b/telethon/_client/telegrambaseclient.py index 10cd2d7f..aeecedd6 100644 --- a/telethon/_client/telegrambaseclient.py +++ b/telethon/_client/telegrambaseclient.py @@ -88,7 +88,6 @@ def init( app_version: str = None, lang_code: str = 'en', system_lang_code: str = 'en', - loop: asyncio.AbstractEventLoop = None, base_logger: typing.Union[str, logging.Logger] = None, receive_updates: bool = True ): @@ -153,24 +152,6 @@ def init( self.api_id = int(api_id) self.api_hash = api_hash - # Current proxy implementation requires `sock_connect`, and some - # event loops lack this method. If the current loop is missing it, - # bail out early and suggest an alternative. - # - # TODO A better fix is obviously avoiding the use of `sock_connect` - # - # See https://github.com/LonamiWebs/Telethon/issues/1337 for details. - if not callable(getattr(self.loop, 'sock_connect', None)): - raise TypeError( - 'Event loop of type {} lacks `sock_connect`, which is needed to use proxies.\n\n' - 'Change the event loop in use to use proxies:\n' - '# https://github.com/LonamiWebs/Telethon/issues/1337\n' - 'import asyncio\n' - 'asyncio.set_event_loop(asyncio.SelectorEventLoop())'.format( - self.loop.__class__.__name__ - ) - ) - if local_addr is not None: if use_ipv6 is False and ':' in local_addr: raise TypeError( @@ -283,10 +264,6 @@ def init( # A place to store if channels are a megagroup or not (see `edit_admin`) self._megagroup_cache = {} - -def get_loop(self: 'TelegramClient') -> asyncio.AbstractEventLoop: - return asyncio.get_event_loop() - def get_flood_sleep_threshold(self): return self._flood_sleep_threshold @@ -382,7 +359,7 @@ async def connect(self: 'TelegramClient') -> None: await self.session.save() - self._updates_handle = self.loop.create_task(self._update_loop()) + self._updates_handle = asyncio.create_task(self._update_loop()) def is_connected(self: 'TelegramClient') -> bool: sender = getattr(self, '_sender', None) diff --git a/telethon/_client/telegramclient.py b/telethon/_client/telegramclient.py index d5a2fab7..c87f6e84 100644 --- a/telethon/_client/telegramclient.py +++ b/telethon/_client/telegramclient.py @@ -148,10 +148,6 @@ class TelegramClient: "System lang code" to be sent when creating the initial connection. Defaults to `lang_code`. - loop (`asyncio.AbstractEventLoop`, optional): - Asyncio event loop to use. Defaults to `asyncio.get_event_loop()`. - This argument is ignored. - base_logger (`str` | `logging.Logger`, optional): Base logger name or instance to use. If a `str` is given, it'll be passed to `logging.getLogger()`. If a @@ -2666,31 +2662,11 @@ class TelegramClient: app_version: str = None, lang_code: str = 'en', system_lang_code: str = 'en', - loop: asyncio.AbstractEventLoop = None, base_logger: typing.Union[str, logging.Logger] = None, receive_updates: bool = True ): telegrambaseclient.init(**locals()) - @property - def loop(self: 'TelegramClient') -> asyncio.AbstractEventLoop: - """ - Property with the ``asyncio`` event loop used by this client. - - Example - .. code-block:: python - - # Download media in the background - task = client.loop.create_task(message.download_media()) - - # Do some work - ... - - # Join the task (wait for it to complete) - await task - """ - return telegrambaseclient.get_loop(**locals()) - @property def flood_sleep_threshold(self): return telegrambaseclient.get_flood_sleep_threshold(**locals()) diff --git a/telethon/_client/updates.py b/telethon/_client/updates.py index 5f30fbd8..d1178304 100644 --- a/telethon/_client/updates.py +++ b/telethon/_client/updates.py @@ -159,14 +159,14 @@ def _process_update(self: 'TelegramClient', update, entities, others): channel_id = self._state_cache.get_channel_id(update) args = (update, entities, others, channel_id, self._state_cache[channel_id]) if self._dispatching_updates_queue is None: - task = self.loop.create_task(_dispatch_update(self, *args)) + task = asyncio.create_task(_dispatch_update(self, *args)) self._updates_queue.add(task) task.add_done_callback(lambda _: self._updates_queue.discard(task)) else: self._updates_queue.put_nowait(args) if not self._dispatching_updates_queue.is_set(): self._dispatching_updates_queue.set() - self.loop.create_task(_dispatch_queue_updates(self)) + asyncio.create_task(_dispatch_queue_updates(self)) self._state_cache.update(update) diff --git a/telethon/_events/album.py b/telethon/_events/album.py index 19ea4234..f290519d 100644 --- a/telethon/_events/album.py +++ b/telethon/_events/album.py @@ -37,15 +37,15 @@ class AlbumHack: # very short-lived but might as well try to do "the right thing". self._client = weakref.ref(client) self._event = event # parent event - self._due = client.loop.time() + _HACK_DELAY + self._due = asyncio.get_running_loop().time() + _HACK_DELAY - client.loop.create_task(self.deliver_event()) + asyncio.create_task(self.deliver_event()) def extend(self, messages): client = self._client() if client: # weakref may be dead self._event.messages.extend(messages) - self._due = client.loop.time() + _HACK_DELAY + self._due = asyncio.get_running_loop().time() + _HACK_DELAY async def deliver_event(self): while True: @@ -53,7 +53,7 @@ class AlbumHack: if client is None: return # weakref is dead, nothing to deliver - diff = self._due - client.loop.time() + diff = self._due - asyncio.get_running_loop().time() if diff <= 0: # We've hit our due time, deliver event. It won't respect # sequential updates but fixing that would just worsen this. diff --git a/telethon/_events/callbackquery.py b/telethon/_events/callbackquery.py index 15f8d12f..dd4f54aa 100644 --- a/telethon/_events/callbackquery.py +++ b/telethon/_events/callbackquery.py @@ -1,5 +1,7 @@ import re import struct +import asyncio +import functools from .common import EventBuilder, EventCommon, name_inner_event from .._misc import utils @@ -7,6 +9,20 @@ from .. import _tl from ..types import _custom +def auto_answer(func): + @functools.wraps(func) + async def wrapped(self, *args, **kwargs): + if self._answered: + return await func(*args, **kwargs) + else: + return (await asyncio.gather( + self._answer(), + func(*args, **kwargs), + ))[1] + + return wrapped + + @name_inner_event class CallbackQuery(EventBuilder): """ @@ -240,16 +256,15 @@ class CallbackQuery(EventBuilder): if self._answered: return + res = await self._client(_tl.fn.messages.SetBotCallbackAnswer( + query_id=self.query.query_id, + cache_time=cache_time, + alert=alert, + message=message, + url=url, + )) self._answered = True - return await self._client( - _tl.fn.messages.SetBotCallbackAnswer( - query_id=self.query.query_id, - cache_time=cache_time, - alert=alert, - message=message, - url=url - ) - ) + return res @property def via_inline(self): @@ -266,35 +281,36 @@ class CallbackQuery(EventBuilder): """ return isinstance(self.query, _tl.UpdateInlineBotCallbackQuery) + @auto_answer async def respond(self, *args, **kwargs): """ Responds to the message (not as a reply). Shorthand for `telethon.client.messages.MessageMethods.send_message` with ``entity`` already set. - This method also creates a task to `answer` the callback. + This method will also `answer` the callback if necessary. This method will likely fail if `via_inline` is `True`. """ - self._client.loop.create_task(self.answer()) return await self._client.send_message( await self.get_input_chat(), *args, **kwargs) + @auto_answer async def reply(self, *args, **kwargs): """ Replies to the message (as a reply). Shorthand for `telethon.client.messages.MessageMethods.send_message` with both ``entity`` and ``reply_to`` already set. - This method also creates a task to `answer` the callback. + This method will also `answer` the callback if necessary. This method will likely fail if `via_inline` is `True`. """ - self._client.loop.create_task(self.answer()) kwargs['reply_to'] = self.query.msg_id return await self._client.send_message( await self.get_input_chat(), *args, **kwargs) + @auto_answer async def edit(self, *args, **kwargs): """ Edits the message. Shorthand for @@ -303,7 +319,7 @@ class CallbackQuery(EventBuilder): Returns `True` if the edit was successful. - This method also creates a task to `answer` the callback. + This method will also `answer` the callback if necessary. .. note:: @@ -311,7 +327,6 @@ class CallbackQuery(EventBuilder): `Message.edit `, since the message object is normally not present. """ - self._client.loop.create_task(self.answer()) if isinstance(self.query.msg_id, _tl.InputBotInlineMessageID): return await self._client.edit_message( None, self.query.msg_id, *args, **kwargs @@ -322,6 +337,7 @@ class CallbackQuery(EventBuilder): *args, **kwargs ) + @auto_answer async def delete(self, *args, **kwargs): """ Deletes the message. Shorthand for @@ -332,11 +348,10 @@ class CallbackQuery(EventBuilder): this `delete` method. Use a `telethon.client.telegramclient.TelegramClient` instance directly. - This method also creates a task to `answer` the callback. + This method will also `answer` the callback if necessary. This method will likely fail if `via_inline` is `True`. """ - self._client.loop.create_task(self.answer()) return await self._client.delete_messages( await self.get_input_chat(), [self.query.msg_id], *args, **kwargs diff --git a/telethon/_events/inlinequery.py b/telethon/_events/inlinequery.py index d3cd6822..2c718e11 100644 --- a/telethon/_events/inlinequery.py +++ b/telethon/_events/inlinequery.py @@ -242,6 +242,6 @@ class InlineQuery(EventBuilder): if inspect.isawaitable(obj): return asyncio.ensure_future(obj) - f = asyncio.get_event_loop().create_future() + f = asyncio.get_running_loop().create_future() f.set_result(obj) return f diff --git a/telethon/_network/connection.py b/telethon/_network/connection.py index 26674aa2..e256b89a 100644 --- a/telethon/_network/connection.py +++ b/telethon/_network/connection.py @@ -23,13 +23,15 @@ class Connection: """ Establishes a connection with the server. """ - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setblocking(False) if self._local_addr: sock.bind(self._local_addr) + # TODO https://github.com/LonamiWebs/Telethon/issues/1337 may be an issue again + # perhaps we just need to ignore async connect on windows and block? await asyncio.wait_for(loop.sock_connect(sock, (self._ip, self._port)), timeout) self._sock = sock @@ -41,14 +43,14 @@ class Connection: if not self._sock: raise ConnectionError('not connected') - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() await loop.sock_sendall(self._sock, self._transport.pack(data)) async def recv(self): if not self._sock: raise ConnectionError('not connected') - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() while True: try: length, body = self._transport.unpack(self._in_buffer) diff --git a/telethon/_network/mtprotosender.py b/telethon/_network/mtprotosender.py index 29371bf6..42dd69a0 100644 --- a/telethon/_network/mtprotosender.py +++ b/telethon/_network/mtprotosender.py @@ -58,7 +58,7 @@ class MTProtoSender: # pending futures should be cancelled. self._user_connected = False self._reconnecting = False - self._disconnected = asyncio.get_event_loop().create_future() + self._disconnected = asyncio.get_running_loop().create_future() self._disconnected.set_result(None) # We need to join the loops upon disconnection @@ -248,18 +248,17 @@ class MTProtoSender: await self._disconnect(error=e) raise e - loop = asyncio.get_event_loop() self._log.debug('Starting send loop') - self._send_loop_handle = loop.create_task(self._send_loop()) + self._send_loop_handle = asyncio.create_task(self._send_loop()) self._log.debug('Starting receive loop') - self._recv_loop_handle = loop.create_task(self._recv_loop()) + self._recv_loop_handle = asyncio.create_task(self._recv_loop()) # _disconnected only completes after manual disconnection # or errors after which the sender cannot continue such # as failing to reconnect or any unexpected error. if self._disconnected.done(): - self._disconnected = loop.create_future() + self._disconnected = asyncio.get_running_loop().create_future() self._log.info('Connection to %s complete!', self._connection) @@ -381,7 +380,7 @@ class MTProtoSender: self._pending_state.clear() if self._auto_reconnect_callback: - asyncio.get_event_loop().create_task(self._auto_reconnect_callback()) + asyncio.create_task(self._auto_reconnect_callback()) break else: @@ -406,7 +405,7 @@ class MTProtoSender: # gets stuck. # TODO It still gets stuck? Investigate where and why. self._reconnecting = True - asyncio.get_event_loop().create_task(self._reconnect(error)) + asyncio.create_task(self._reconnect(error)) def _keepalive_ping(self, rnd_id): """ diff --git a/telethon_examples/gui.py b/telethon_examples/gui.py index bd241f60..61027d52 100644 --- a/telethon_examples/gui.py +++ b/telethon_examples/gui.py @@ -132,7 +132,7 @@ class App(tkinter.Tk): command=self.send_message).grid(row=3, column=2) # Post-init (async, connect client) - self.cl.loop.create_task(self.post_init()) + asyncio.create_task(self.post_init()) async def post_init(self): """ @@ -369,10 +369,4 @@ async def main(interval=0.05): if __name__ == "__main__": - # Some boilerplate code to set up the main method - aio_loop = asyncio.get_event_loop() - try: - aio_loop.run_until_complete(main()) - finally: - if not aio_loop.is_closed(): - aio_loop.close() + asyncio.run(main()) diff --git a/telethon_examples/interactive_telegram_client.py b/telethon_examples/interactive_telegram_client.py index 88f491de..10ca71a1 100644 --- a/telethon_examples/interactive_telegram_client.py +++ b/telethon_examples/interactive_telegram_client.py @@ -9,9 +9,6 @@ from telethon.errors import SessionPasswordNeededError from telethon.network import ConnectionTcpAbridged from telethon.utils import get_display_name -# Create a global variable to hold the loop we will be using -loop = asyncio.get_event_loop() - def sprint(string, *args, **kwargs): """Safe Print (handle UnicodeEncodeErrors on some terminals)""" @@ -50,7 +47,7 @@ async def async_input(prompt): let the loop run while we wait for input. """ print(prompt, end='', flush=True) - return (await loop.run_in_executor(None, sys.stdin.readline)).rstrip() + return (await asyncio.get_running_loop().run_in_executor(None, sys.stdin.readline)).rstrip() def get_env(name, message, cast=str): @@ -109,34 +106,34 @@ class InteractiveTelegramClient(TelegramClient): # media known the message ID, for every message having media. self.found_media = {} + async def init(self): # Calling .connect() may raise a connection error False, so you need # to except those before continuing. Otherwise you may want to retry # as done here. print('Connecting to Telegram servers...') try: - loop.run_until_complete(self.connect()) + await self.connect() except IOError: # We handle IOError and not ConnectionError because # PySocks' errors do not subclass ConnectionError # (so this will work with and without proxies). print('Initial connection failed. Retrying...') - loop.run_until_complete(self.connect()) + await self.connect() # If the user hasn't called .sign_in() or .sign_up() yet, they won't # be authorized. The first thing you must do is authorize. Calling # .sign_in() should only be done once as the information is saved on # the *.session file so you don't need to enter the code every time. - if not loop.run_until_complete(self.is_user_authorized()): + if not await self.is_user_authorized(): print('First run. Sending code request...') user_phone = input('Enter your phone: ') - loop.run_until_complete(self.sign_in(user_phone)) + await self.sign_in(user_phone) self_user = None while self_user is None: code = input('Enter the code you just received: ') try: - self_user =\ - loop.run_until_complete(self.sign_in(code=code)) + self_user = await self.sign_in(code=code) # Two-step verification may be enabled, and .sign_in will # raise this error. If that's the case ask for the password. @@ -146,8 +143,7 @@ class InteractiveTelegramClient(TelegramClient): pw = getpass('Two step verification is enabled. ' 'Please enter your password: ') - self_user =\ - loop.run_until_complete(self.sign_in(password=pw)) + self_user = await self.sign_in(password=pw) async def run(self): """Main loop of the TelegramClient, will wait for user action""" @@ -397,9 +393,13 @@ class InteractiveTelegramClient(TelegramClient): )) -if __name__ == '__main__': +async def main(): SESSION = os.environ.get('TG_SESSION', 'interactive') API_ID = get_env('TG_API_ID', 'Enter your API ID: ', int) API_HASH = get_env('TG_API_HASH', 'Enter your API hash: ') - client = InteractiveTelegramClient(SESSION, API_ID, API_HASH) - loop.run_until_complete(client.run()) + client = await InteractiveTelegramClient(SESSION, API_ID, API_HASH).init() + await client.run() + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/telethon_examples/payment.py b/telethon_examples/payment.py index 0e9f9733..3eab88f3 100644 --- a/telethon_examples/payment.py +++ b/telethon_examples/payment.py @@ -7,8 +7,6 @@ import os import time import sys -loop = asyncio.get_event_loop() - """ Provider token can be obtained via @BotFather. more info at https://core.telegram.org/bots/payments#getting-a-token @@ -180,4 +178,4 @@ if __name__ == '__main__': if not provider_token: logger.error("No provider token supplied.") exit(1) - loop.run_until_complete(main()) + asyncio.run(main()) diff --git a/telethon_examples/quart_login.py b/telethon_examples/quart_login.py index 98fb35de..20eae383 100644 --- a/telethon_examples/quart_login.py +++ b/telethon_examples/quart_login.py @@ -134,12 +134,13 @@ async def main(): # By default, `Quart.run` uses `asyncio.run()`, which creates a new asyncio -# event loop. If we create the `TelegramClient` before, `telethon` will -# use `asyncio.get_event_loop()`, which is the implicit loop in the main -# thread. These two loops are different, and it won't work. +# event loop. Instead, we use `asyncio.run()` manually in order to make this +# explicit, as the client cannot be "transferred" between loops while +# connected due to the need to schedule work within an event loop. # -# So, we have to manually pass the same `loop` to both applications to -# make 100% sure it works and to avoid headaches. +# In essence one needs to be careful to avoid mixing event loops, but this is +# simple, as `asyncio.run` is generally only used in the entry-point of the +# program. # # To run Quart inside `async def`, we must use `hypercorn.asyncio.serve()` # directly. @@ -149,4 +150,4 @@ async def main(): # won't have to worry about any of this, but it's still good to be # explicit about the event loop. if __name__ == '__main__': - client.loop.run_until_complete(main()) + asyncio.run(main())