mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-03-12 15:38:03 +03:00
Get rid of client.loop
Instead, use the asyncio-intended way of implicit loop.
This commit is contained in:
parent
6eadc8aed8
commit
a62627534e
|
@ -8,14 +8,15 @@ use these if possible.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from telethon import TelegramClient
|
from telethon import TelegramClient
|
||||||
|
|
||||||
# Remember to use your own values from my.telegram.org!
|
# Remember to use your own values from my.telegram.org!
|
||||||
api_id = 12345
|
api_id = 12345
|
||||||
api_hash = '0123456789abcdef0123456789abcdef'
|
api_hash = '0123456789abcdef0123456789abcdef'
|
||||||
client = TelegramClient('anon', api_id, api_hash)
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
|
async with TelegramClient('anon', api_id, api_hash).start() as client:
|
||||||
# Getting information about yourself
|
# Getting information about yourself
|
||||||
me = await client.get_me()
|
me = await client.get_me()
|
||||||
|
|
||||||
|
@ -70,8 +71,7 @@ use these if possible.
|
||||||
path = await message.download_media()
|
path = await message.download_media()
|
||||||
print('File saved to', path) # printed after download is done
|
print('File saved to', path) # printed after download is done
|
||||||
|
|
||||||
with client:
|
asyncio.run(main())
|
||||||
client.loop.run_until_complete(main())
|
|
||||||
|
|
||||||
|
|
||||||
Here, we show how to sign in, get information about yourself, send
|
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.
|
# Most of your code should go here.
|
||||||
# You can of course make and use your own async def (do_something).
|
# 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.
|
# They only need to be async if they need to await things.
|
||||||
async with client:
|
async with client.start():
|
||||||
me = await client.get_me()
|
me = await client.get_me()
|
||||||
await do_something(me)
|
await do_something(me)
|
||||||
|
|
||||||
client.loop.run_until_complete(main())
|
asyncio.run(main())
|
||||||
|
|
|
@ -49,6 +49,7 @@ We can finally write some code to log into our account!
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from telethon import TelegramClient
|
from telethon import TelegramClient
|
||||||
|
|
||||||
# Use your own values from my.telegram.org
|
# 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():
|
async def main():
|
||||||
# The first parameter is the .session file name (absolute paths allowed)
|
# 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!')
|
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
|
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
|
.. code-block:: python
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from telethon import TelegramClient
|
from telethon import TelegramClient
|
||||||
|
|
||||||
api_id = 12345
|
api_id = 12345
|
||||||
api_hash = '0123456789abcdef0123456789abcdef'
|
api_hash = '0123456789abcdef0123456789abcdef'
|
||||||
bot_token = '12345: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():
|
async def main():
|
||||||
# But then we can use the client instance as usual
|
# 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
|
To get a bot account, you need to talk
|
||||||
|
|
|
@ -74,7 +74,7 @@ Or we call `client.get_input_entity()
|
||||||
async def main():
|
async def main():
|
||||||
peer = await client.get_input_entity('someone')
|
peer = await client.get_input_entity('someone')
|
||||||
|
|
||||||
client.loop.run_until_complete(main())
|
asyncio.run(main())
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|
|
@ -156,8 +156,8 @@ you can save it in a variable directly:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
string = '1aaNk8EX-YRfwoRsebUkugFvht6DUPi_Q25UOCzOAqzc...'
|
string = '1aaNk8EX-YRfwoRsebUkugFvht6DUPi_Q25UOCzOAqzc...'
|
||||||
with TelegramClient(StringSession(string), api_id, api_hash) as client:
|
async with TelegramClient(StringSession(string), api_id, api_hash).start() as client:
|
||||||
client.loop.run_until_complete(client.send_message('me', 'Hi'))
|
await client.send_message('me', 'Hi')
|
||||||
|
|
||||||
|
|
||||||
These strings are really convenient for using in places like Heroku since
|
These strings are really convenient for using in places like Heroku since
|
||||||
|
|
|
@ -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.
|
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.
|
removed max id since it's not really of much use.
|
||||||
|
|
||||||
|
client loop has been removed. embrace implicit loop as asyncio does now
|
||||||
|
|
|
@ -20,10 +20,10 @@ Each mixin has its own methods, which you all can use.
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
# Now you can use all client methods listed below, like for example...
|
# Now you can use all client methods listed below, like for example...
|
||||||
|
async with client.start():
|
||||||
await client.send_message('me', 'Hello to myself!')
|
await client.send_message('me', 'Hello to myself!')
|
||||||
|
|
||||||
with client:
|
asyncio.run(main())
|
||||||
client.loop.run_until_complete(main())
|
|
||||||
|
|
||||||
|
|
||||||
You **don't** need to import these `AuthMethods`, `MessageMethods`, etc.
|
You **don't** need to import these `AuthMethods`, `MessageMethods`, etc.
|
||||||
|
|
|
@ -40,7 +40,7 @@ class _ChatAction:
|
||||||
self._chat, self._action)
|
self._chat, self._action)
|
||||||
|
|
||||||
self._running = True
|
self._running = True
|
||||||
self._task = self._client.loop.create_task(self._update())
|
self._task = asyncio.create_task(self._update())
|
||||||
return self
|
return self
|
||||||
|
|
||||||
async def __aexit__(self, *args):
|
async def __aexit__(self, *args):
|
||||||
|
|
|
@ -88,7 +88,6 @@ def init(
|
||||||
app_version: str = None,
|
app_version: str = None,
|
||||||
lang_code: str = 'en',
|
lang_code: str = 'en',
|
||||||
system_lang_code: str = 'en',
|
system_lang_code: str = 'en',
|
||||||
loop: asyncio.AbstractEventLoop = None,
|
|
||||||
base_logger: typing.Union[str, logging.Logger] = None,
|
base_logger: typing.Union[str, logging.Logger] = None,
|
||||||
receive_updates: bool = True
|
receive_updates: bool = True
|
||||||
):
|
):
|
||||||
|
@ -153,24 +152,6 @@ def init(
|
||||||
self.api_id = int(api_id)
|
self.api_id = int(api_id)
|
||||||
self.api_hash = api_hash
|
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 local_addr is not None:
|
||||||
if use_ipv6 is False and ':' in local_addr:
|
if use_ipv6 is False and ':' in local_addr:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
|
@ -283,10 +264,6 @@ def init(
|
||||||
# A place to store if channels are a megagroup or not (see `edit_admin`)
|
# A place to store if channels are a megagroup or not (see `edit_admin`)
|
||||||
self._megagroup_cache = {}
|
self._megagroup_cache = {}
|
||||||
|
|
||||||
|
|
||||||
def get_loop(self: 'TelegramClient') -> asyncio.AbstractEventLoop:
|
|
||||||
return asyncio.get_event_loop()
|
|
||||||
|
|
||||||
def get_flood_sleep_threshold(self):
|
def get_flood_sleep_threshold(self):
|
||||||
return self._flood_sleep_threshold
|
return self._flood_sleep_threshold
|
||||||
|
|
||||||
|
@ -382,7 +359,7 @@ async def connect(self: 'TelegramClient') -> None:
|
||||||
|
|
||||||
await self.session.save()
|
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:
|
def is_connected(self: 'TelegramClient') -> bool:
|
||||||
sender = getattr(self, '_sender', None)
|
sender = getattr(self, '_sender', None)
|
||||||
|
|
|
@ -148,10 +148,6 @@ class TelegramClient:
|
||||||
"System lang code" to be sent when creating the initial connection.
|
"System lang code" to be sent when creating the initial connection.
|
||||||
Defaults to `lang_code`.
|
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 (`str` | `logging.Logger`, optional):
|
||||||
Base logger name or instance to use.
|
Base logger name or instance to use.
|
||||||
If a `str` is given, it'll be passed to `logging.getLogger()`. If a
|
If a `str` is given, it'll be passed to `logging.getLogger()`. If a
|
||||||
|
@ -2666,31 +2662,11 @@ class TelegramClient:
|
||||||
app_version: str = None,
|
app_version: str = None,
|
||||||
lang_code: str = 'en',
|
lang_code: str = 'en',
|
||||||
system_lang_code: str = 'en',
|
system_lang_code: str = 'en',
|
||||||
loop: asyncio.AbstractEventLoop = None,
|
|
||||||
base_logger: typing.Union[str, logging.Logger] = None,
|
base_logger: typing.Union[str, logging.Logger] = None,
|
||||||
receive_updates: bool = True
|
receive_updates: bool = True
|
||||||
):
|
):
|
||||||
telegrambaseclient.init(**locals())
|
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
|
@property
|
||||||
def flood_sleep_threshold(self):
|
def flood_sleep_threshold(self):
|
||||||
return telegrambaseclient.get_flood_sleep_threshold(**locals())
|
return telegrambaseclient.get_flood_sleep_threshold(**locals())
|
||||||
|
|
|
@ -159,14 +159,14 @@ def _process_update(self: 'TelegramClient', update, entities, others):
|
||||||
channel_id = self._state_cache.get_channel_id(update)
|
channel_id = self._state_cache.get_channel_id(update)
|
||||||
args = (update, entities, others, channel_id, self._state_cache[channel_id])
|
args = (update, entities, others, channel_id, self._state_cache[channel_id])
|
||||||
if self._dispatching_updates_queue is None:
|
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)
|
self._updates_queue.add(task)
|
||||||
task.add_done_callback(lambda _: self._updates_queue.discard(task))
|
task.add_done_callback(lambda _: self._updates_queue.discard(task))
|
||||||
else:
|
else:
|
||||||
self._updates_queue.put_nowait(args)
|
self._updates_queue.put_nowait(args)
|
||||||
if not self._dispatching_updates_queue.is_set():
|
if not self._dispatching_updates_queue.is_set():
|
||||||
self._dispatching_updates_queue.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)
|
self._state_cache.update(update)
|
||||||
|
|
||||||
|
|
|
@ -37,15 +37,15 @@ class AlbumHack:
|
||||||
# very short-lived but might as well try to do "the right thing".
|
# very short-lived but might as well try to do "the right thing".
|
||||||
self._client = weakref.ref(client)
|
self._client = weakref.ref(client)
|
||||||
self._event = event # parent event
|
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):
|
def extend(self, messages):
|
||||||
client = self._client()
|
client = self._client()
|
||||||
if client: # weakref may be dead
|
if client: # weakref may be dead
|
||||||
self._event.messages.extend(messages)
|
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):
|
async def deliver_event(self):
|
||||||
while True:
|
while True:
|
||||||
|
@ -53,7 +53,7 @@ class AlbumHack:
|
||||||
if client is None:
|
if client is None:
|
||||||
return # weakref is dead, nothing to deliver
|
return # weakref is dead, nothing to deliver
|
||||||
|
|
||||||
diff = self._due - client.loop.time()
|
diff = self._due - asyncio.get_running_loop().time()
|
||||||
if diff <= 0:
|
if diff <= 0:
|
||||||
# We've hit our due time, deliver event. It won't respect
|
# We've hit our due time, deliver event. It won't respect
|
||||||
# sequential updates but fixing that would just worsen this.
|
# sequential updates but fixing that would just worsen this.
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import re
|
import re
|
||||||
import struct
|
import struct
|
||||||
|
import asyncio
|
||||||
|
import functools
|
||||||
|
|
||||||
from .common import EventBuilder, EventCommon, name_inner_event
|
from .common import EventBuilder, EventCommon, name_inner_event
|
||||||
from .._misc import utils
|
from .._misc import utils
|
||||||
|
@ -7,6 +9,20 @@ from .. import _tl
|
||||||
from ..types import _custom
|
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
|
@name_inner_event
|
||||||
class CallbackQuery(EventBuilder):
|
class CallbackQuery(EventBuilder):
|
||||||
"""
|
"""
|
||||||
|
@ -240,16 +256,15 @@ class CallbackQuery(EventBuilder):
|
||||||
if self._answered:
|
if self._answered:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._answered = True
|
res = await self._client(_tl.fn.messages.SetBotCallbackAnswer(
|
||||||
return await self._client(
|
|
||||||
_tl.fn.messages.SetBotCallbackAnswer(
|
|
||||||
query_id=self.query.query_id,
|
query_id=self.query.query_id,
|
||||||
cache_time=cache_time,
|
cache_time=cache_time,
|
||||||
alert=alert,
|
alert=alert,
|
||||||
message=message,
|
message=message,
|
||||||
url=url
|
url=url,
|
||||||
)
|
))
|
||||||
)
|
self._answered = True
|
||||||
|
return res
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def via_inline(self):
|
def via_inline(self):
|
||||||
|
@ -266,35 +281,36 @@ class CallbackQuery(EventBuilder):
|
||||||
"""
|
"""
|
||||||
return isinstance(self.query, _tl.UpdateInlineBotCallbackQuery)
|
return isinstance(self.query, _tl.UpdateInlineBotCallbackQuery)
|
||||||
|
|
||||||
|
@auto_answer
|
||||||
async def respond(self, *args, **kwargs):
|
async def respond(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Responds to the message (not as a reply). Shorthand for
|
Responds to the message (not as a reply). Shorthand for
|
||||||
`telethon.client.messages.MessageMethods.send_message` with
|
`telethon.client.messages.MessageMethods.send_message` with
|
||||||
``entity`` already set.
|
``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`.
|
This method will likely fail if `via_inline` is `True`.
|
||||||
"""
|
"""
|
||||||
self._client.loop.create_task(self.answer())
|
|
||||||
return await self._client.send_message(
|
return await self._client.send_message(
|
||||||
await self.get_input_chat(), *args, **kwargs)
|
await self.get_input_chat(), *args, **kwargs)
|
||||||
|
|
||||||
|
@auto_answer
|
||||||
async def reply(self, *args, **kwargs):
|
async def reply(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Replies to the message (as a reply). Shorthand for
|
Replies to the message (as a reply). Shorthand for
|
||||||
`telethon.client.messages.MessageMethods.send_message` with
|
`telethon.client.messages.MessageMethods.send_message` with
|
||||||
both ``entity`` and ``reply_to`` already set.
|
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`.
|
This method will likely fail if `via_inline` is `True`.
|
||||||
"""
|
"""
|
||||||
self._client.loop.create_task(self.answer())
|
|
||||||
kwargs['reply_to'] = self.query.msg_id
|
kwargs['reply_to'] = self.query.msg_id
|
||||||
return await self._client.send_message(
|
return await self._client.send_message(
|
||||||
await self.get_input_chat(), *args, **kwargs)
|
await self.get_input_chat(), *args, **kwargs)
|
||||||
|
|
||||||
|
@auto_answer
|
||||||
async def edit(self, *args, **kwargs):
|
async def edit(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Edits the message. Shorthand for
|
Edits the message. Shorthand for
|
||||||
|
@ -303,7 +319,7 @@ class CallbackQuery(EventBuilder):
|
||||||
|
|
||||||
Returns `True` if the edit was successful.
|
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::
|
.. note::
|
||||||
|
|
||||||
|
@ -311,7 +327,6 @@ class CallbackQuery(EventBuilder):
|
||||||
`Message.edit <telethon.tl._custom.message.Message.edit>`,
|
`Message.edit <telethon.tl._custom.message.Message.edit>`,
|
||||||
since the message object is normally not present.
|
since the message object is normally not present.
|
||||||
"""
|
"""
|
||||||
self._client.loop.create_task(self.answer())
|
|
||||||
if isinstance(self.query.msg_id, _tl.InputBotInlineMessageID):
|
if isinstance(self.query.msg_id, _tl.InputBotInlineMessageID):
|
||||||
return await self._client.edit_message(
|
return await self._client.edit_message(
|
||||||
None, self.query.msg_id, *args, **kwargs
|
None, self.query.msg_id, *args, **kwargs
|
||||||
|
@ -322,6 +337,7 @@ class CallbackQuery(EventBuilder):
|
||||||
*args, **kwargs
|
*args, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@auto_answer
|
||||||
async def delete(self, *args, **kwargs):
|
async def delete(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Deletes the message. Shorthand for
|
Deletes the message. Shorthand for
|
||||||
|
@ -332,11 +348,10 @@ class CallbackQuery(EventBuilder):
|
||||||
this `delete` method. Use a
|
this `delete` method. Use a
|
||||||
`telethon.client.telegramclient.TelegramClient` instance directly.
|
`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`.
|
This method will likely fail if `via_inline` is `True`.
|
||||||
"""
|
"""
|
||||||
self._client.loop.create_task(self.answer())
|
|
||||||
return await self._client.delete_messages(
|
return await self._client.delete_messages(
|
||||||
await self.get_input_chat(), [self.query.msg_id],
|
await self.get_input_chat(), [self.query.msg_id],
|
||||||
*args, **kwargs
|
*args, **kwargs
|
||||||
|
|
|
@ -242,6 +242,6 @@ class InlineQuery(EventBuilder):
|
||||||
if inspect.isawaitable(obj):
|
if inspect.isawaitable(obj):
|
||||||
return asyncio.ensure_future(obj)
|
return asyncio.ensure_future(obj)
|
||||||
|
|
||||||
f = asyncio.get_event_loop().create_future()
|
f = asyncio.get_running_loop().create_future()
|
||||||
f.set_result(obj)
|
f.set_result(obj)
|
||||||
return f
|
return f
|
||||||
|
|
|
@ -23,13 +23,15 @@ class Connection:
|
||||||
"""
|
"""
|
||||||
Establishes a connection with the server.
|
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 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.setblocking(False)
|
sock.setblocking(False)
|
||||||
if self._local_addr:
|
if self._local_addr:
|
||||||
sock.bind(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)
|
await asyncio.wait_for(loop.sock_connect(sock, (self._ip, self._port)), timeout)
|
||||||
self._sock = sock
|
self._sock = sock
|
||||||
|
|
||||||
|
@ -41,14 +43,14 @@ class Connection:
|
||||||
if not self._sock:
|
if not self._sock:
|
||||||
raise ConnectionError('not connected')
|
raise ConnectionError('not connected')
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_running_loop()
|
||||||
await loop.sock_sendall(self._sock, self._transport.pack(data))
|
await loop.sock_sendall(self._sock, self._transport.pack(data))
|
||||||
|
|
||||||
async def recv(self):
|
async def recv(self):
|
||||||
if not self._sock:
|
if not self._sock:
|
||||||
raise ConnectionError('not connected')
|
raise ConnectionError('not connected')
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_running_loop()
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
length, body = self._transport.unpack(self._in_buffer)
|
length, body = self._transport.unpack(self._in_buffer)
|
||||||
|
|
|
@ -58,7 +58,7 @@ class MTProtoSender:
|
||||||
# pending futures should be cancelled.
|
# pending futures should be cancelled.
|
||||||
self._user_connected = False
|
self._user_connected = False
|
||||||
self._reconnecting = 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)
|
self._disconnected.set_result(None)
|
||||||
|
|
||||||
# We need to join the loops upon disconnection
|
# We need to join the loops upon disconnection
|
||||||
|
@ -248,18 +248,17 @@ class MTProtoSender:
|
||||||
await self._disconnect(error=e)
|
await self._disconnect(error=e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
self._log.debug('Starting send 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._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
|
# _disconnected only completes after manual disconnection
|
||||||
# or errors after which the sender cannot continue such
|
# or errors after which the sender cannot continue such
|
||||||
# as failing to reconnect or any unexpected error.
|
# as failing to reconnect or any unexpected error.
|
||||||
if self._disconnected.done():
|
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)
|
self._log.info('Connection to %s complete!', self._connection)
|
||||||
|
|
||||||
|
@ -381,7 +380,7 @@ class MTProtoSender:
|
||||||
self._pending_state.clear()
|
self._pending_state.clear()
|
||||||
|
|
||||||
if self._auto_reconnect_callback:
|
if self._auto_reconnect_callback:
|
||||||
asyncio.get_event_loop().create_task(self._auto_reconnect_callback())
|
asyncio.create_task(self._auto_reconnect_callback())
|
||||||
|
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
@ -406,7 +405,7 @@ class MTProtoSender:
|
||||||
# gets stuck.
|
# gets stuck.
|
||||||
# TODO It still gets stuck? Investigate where and why.
|
# TODO It still gets stuck? Investigate where and why.
|
||||||
self._reconnecting = True
|
self._reconnecting = True
|
||||||
asyncio.get_event_loop().create_task(self._reconnect(error))
|
asyncio.create_task(self._reconnect(error))
|
||||||
|
|
||||||
def _keepalive_ping(self, rnd_id):
|
def _keepalive_ping(self, rnd_id):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -132,7 +132,7 @@ class App(tkinter.Tk):
|
||||||
command=self.send_message).grid(row=3, column=2)
|
command=self.send_message).grid(row=3, column=2)
|
||||||
|
|
||||||
# Post-init (async, connect client)
|
# Post-init (async, connect client)
|
||||||
self.cl.loop.create_task(self.post_init())
|
asyncio.create_task(self.post_init())
|
||||||
|
|
||||||
async def post_init(self):
|
async def post_init(self):
|
||||||
"""
|
"""
|
||||||
|
@ -369,10 +369,4 @@ async def main(interval=0.05):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Some boilerplate code to set up the main method
|
asyncio.run(main())
|
||||||
aio_loop = asyncio.get_event_loop()
|
|
||||||
try:
|
|
||||||
aio_loop.run_until_complete(main())
|
|
||||||
finally:
|
|
||||||
if not aio_loop.is_closed():
|
|
||||||
aio_loop.close()
|
|
||||||
|
|
|
@ -9,9 +9,6 @@ from telethon.errors import SessionPasswordNeededError
|
||||||
from telethon.network import ConnectionTcpAbridged
|
from telethon.network import ConnectionTcpAbridged
|
||||||
from telethon.utils import get_display_name
|
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):
|
def sprint(string, *args, **kwargs):
|
||||||
"""Safe Print (handle UnicodeEncodeErrors on some terminals)"""
|
"""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.
|
let the loop run while we wait for input.
|
||||||
"""
|
"""
|
||||||
print(prompt, end='', flush=True)
|
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):
|
def get_env(name, message, cast=str):
|
||||||
|
@ -109,34 +106,34 @@ class InteractiveTelegramClient(TelegramClient):
|
||||||
# media known the message ID, for every message having media.
|
# media known the message ID, for every message having media.
|
||||||
self.found_media = {}
|
self.found_media = {}
|
||||||
|
|
||||||
|
async def init(self):
|
||||||
# Calling .connect() may raise a connection error False, so you need
|
# Calling .connect() may raise a connection error False, so you need
|
||||||
# to except those before continuing. Otherwise you may want to retry
|
# to except those before continuing. Otherwise you may want to retry
|
||||||
# as done here.
|
# as done here.
|
||||||
print('Connecting to Telegram servers...')
|
print('Connecting to Telegram servers...')
|
||||||
try:
|
try:
|
||||||
loop.run_until_complete(self.connect())
|
await self.connect()
|
||||||
except IOError:
|
except IOError:
|
||||||
# We handle IOError and not ConnectionError because
|
# We handle IOError and not ConnectionError because
|
||||||
# PySocks' errors do not subclass ConnectionError
|
# PySocks' errors do not subclass ConnectionError
|
||||||
# (so this will work with and without proxies).
|
# (so this will work with and without proxies).
|
||||||
print('Initial connection failed. Retrying...')
|
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
|
# 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
|
# be authorized. The first thing you must do is authorize. Calling
|
||||||
# .sign_in() should only be done once as the information is saved on
|
# .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.
|
# 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...')
|
print('First run. Sending code request...')
|
||||||
user_phone = input('Enter your phone: ')
|
user_phone = input('Enter your phone: ')
|
||||||
loop.run_until_complete(self.sign_in(user_phone))
|
await self.sign_in(user_phone)
|
||||||
|
|
||||||
self_user = None
|
self_user = None
|
||||||
while self_user is None:
|
while self_user is None:
|
||||||
code = input('Enter the code you just received: ')
|
code = input('Enter the code you just received: ')
|
||||||
try:
|
try:
|
||||||
self_user =\
|
self_user = await self.sign_in(code=code)
|
||||||
loop.run_until_complete(self.sign_in(code=code))
|
|
||||||
|
|
||||||
# Two-step verification may be enabled, and .sign_in will
|
# Two-step verification may be enabled, and .sign_in will
|
||||||
# raise this error. If that's the case ask for the password.
|
# 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. '
|
pw = getpass('Two step verification is enabled. '
|
||||||
'Please enter your password: ')
|
'Please enter your password: ')
|
||||||
|
|
||||||
self_user =\
|
self_user = await self.sign_in(password=pw)
|
||||||
loop.run_until_complete(self.sign_in(password=pw))
|
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
"""Main loop of the TelegramClient, will wait for user action"""
|
"""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')
|
SESSION = os.environ.get('TG_SESSION', 'interactive')
|
||||||
API_ID = get_env('TG_API_ID', 'Enter your API ID: ', int)
|
API_ID = get_env('TG_API_ID', 'Enter your API ID: ', int)
|
||||||
API_HASH = get_env('TG_API_HASH', 'Enter your API hash: ')
|
API_HASH = get_env('TG_API_HASH', 'Enter your API hash: ')
|
||||||
client = InteractiveTelegramClient(SESSION, API_ID, API_HASH)
|
client = await InteractiveTelegramClient(SESSION, API_ID, API_HASH).init()
|
||||||
loop.run_until_complete(client.run())
|
await client.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
asyncio.run(main())
|
||||||
|
|
|
@ -7,8 +7,6 @@ import os
|
||||||
import time
|
import time
|
||||||
import sys
|
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
|
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:
|
if not provider_token:
|
||||||
logger.error("No provider token supplied.")
|
logger.error("No provider token supplied.")
|
||||||
exit(1)
|
exit(1)
|
||||||
loop.run_until_complete(main())
|
asyncio.run(main())
|
||||||
|
|
|
@ -134,12 +134,13 @@ async def main():
|
||||||
|
|
||||||
|
|
||||||
# By default, `Quart.run` uses `asyncio.run()`, which creates a new asyncio
|
# By default, `Quart.run` uses `asyncio.run()`, which creates a new asyncio
|
||||||
# event loop. If we create the `TelegramClient` before, `telethon` will
|
# event loop. Instead, we use `asyncio.run()` manually in order to make this
|
||||||
# use `asyncio.get_event_loop()`, which is the implicit loop in the main
|
# explicit, as the client cannot be "transferred" between loops while
|
||||||
# thread. These two loops are different, and it won't work.
|
# 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
|
# In essence one needs to be careful to avoid mixing event loops, but this is
|
||||||
# make 100% sure it works and to avoid headaches.
|
# 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()`
|
# To run Quart inside `async def`, we must use `hypercorn.asyncio.serve()`
|
||||||
# directly.
|
# directly.
|
||||||
|
@ -149,4 +150,4 @@ async def main():
|
||||||
# won't have to worry about any of this, but it's still good to be
|
# won't have to worry about any of this, but it's still good to be
|
||||||
# explicit about the event loop.
|
# explicit about the event loop.
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
client.loop.run_until_complete(main())
|
asyncio.run(main())
|
||||||
|
|
Loading…
Reference in New Issue
Block a user