How to use telethon in a thread - python

I want to run a function in background. so I use Threading in my code.
but return error ValueError: signal only works in main thread and don't know about two things:
what is the main thread
how to solve this problem :)
views.py
def callback(update):
print('I received', update)
def message_poll_start():
try:
client = TelegramClient('phone', api_id, api_hash,
update_workers=1, spawn_read_thread=False)
client.connect()
client.add_update_handler(callback)
client.idle()
except TypeNotFoundError:
pass
def message_poll_start_thread(request):
t = threading.Thread(target=message_poll_start, args=(), kwargs={})
t.setDaemon(True)
t.start()
return HttpResponse("message polling started")
urls.py
urlpatterns = [
path('message_poll_start', messagemanager_views.message_poll_start_thread, name="message_poll_start"),
]
trace
[12/Jan/2018 11:24:38] "GET /messages/message_poll_start HTTP/1.1" 200 23
Exception in thread Thread-3:
Traceback (most recent call last):
File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
self.run()
File "/usr/lib/python3.5/threading.py", line 862, in run
self._target(*self._args, **self._kwargs)
File "/home/teletogram/telethogram/messagemanager/views.py", line 123, in message_poll_start
client0.idle()
File "/home/teletogram/.env/lib/python3.5/site-packages/telethon/telegram_bare_client.py", line 825, in idle
signal(sig, self._signal_handler)
File "/usr/lib/python3.5/signal.py", line 47, in signal
handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread

1) A python script runs in the main thread by default. If you spawn a new thread using threading.Thread, that will create a new thread which runs separately from the main one. When I began learning about threading I spent a lot of time reading before it started to click. The official threading docs are decent for basic functionality, and I like this tutorial for a deeper dive.
2) The internals of Telethon rely on asyncio. In asyncio each thread needs its own asynchronous event loop, and thus spawned threads need an explicitly created event loop. Like threading, asyncio is a large topic, some of which is covered in the Telethon docs.
Something like this should work:
import asyncio
def message_poll_start():
try:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
client = TelegramClient('phone', api_id, api_hash, loop=loop)
client.connect()
client.add_update_handler(callback)
client.idle()
except TypeNotFoundError:
pass

Related

discord.py no current event loop

I have this code which, in theory, will check for how many messages have been sent in a channel, and will do this at a certain time each day. Here is the code:
def checkTime():
# This function runs periodically every 1 second
threading.Timer(1, checkTime).start()
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
print("Current Time =", current_time)
if(current_time == '21:35:20'): # check if matches with the desired time
print("starting")
channel = bot.get_channel(this is where I put the channel ID, just hiding it in here)
counter = 0
for message in channel.history():
counter += 1
print(counter)
checkTime()
However, once the time gets to 21:35:20, this error message appears:
starting
Exception in thread Thread-9:
Traceback (most recent call last):
File "C:\Users\jackt\AppData\Local\Programs\Python\Python39\lib\threading.py", line 954, in _bootstrap_inner
self.run()
File "C:\Users\jackt\AppData\Local\Programs\Python\Python39\lib\threading.py", line 1266, in run
self.function(*self.args, **self.kwargs)
File "C:\Users\jackt\Desktop\bot.py", line 51, in checkTime
for message in channel.history():
File "C:\Users\jackt\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\abc.py", line 1087, in history
return HistoryIterator(self, limit=limit, before=before, after=after, around=around, oldest_first=oldest_first)
File "C:\Users\jackt\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\iterators.py", line 256, in __init__
self.messages = asyncio.Queue()
File "C:\Users\jackt\AppData\Local\Programs\Python\Python39\lib\asyncio\queues.py", line 35, in __init__
self._loop = events.get_event_loop()
File "C:\Users\jackt\AppData\Local\Programs\Python\Python39\lib\asyncio\events.py", line 642, in get_event_loop
raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Thread-9'.
I'm using "from datetime import datetime" and "import threading"
Please can you let me know what my code should look like to prevent this error. Thank you!
In General, with discord.py you should refrain from using threads at all possible as there is almost always an asyncio friendly way to accomplish the task.
As far as the reason for your problem is because asyncio is single-threaded the event loop that discord.py runs on is only on 1 thread, so when you try to access the event loop from another thread it will error because it assumes the event loop is on the same thread as you are calling from.
Now given this, asyncio does have threadsafe methods such as call_soon_threadsafe
But for this question specifically, discord.py has an extension dedicated to doing periodic tasks in the background: called the tasks extension: https://discordpy.readthedocs.io/en/latest/ext/tasks/index.html

Discordbot using threading raise "RuntimeError: set_wakeup_fd only works in main thread" only on linux

I am using the threading module to host a web server and a Discord bot at the same time. Everything runs fine on Windows but as soon as I load it onto my Linux server I get the following error:
Starting Bot
Exception in thread Bot:
Traceback (most recent call last):
File "/usr/lib/python3.8/asyncio/unix_events.py", line 95, in add_signal_handler
signal.set_wakeup_fd(self._csock.fileno())
ValueError: set_wakeup_fd only works in main thread
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
self.run()
File "/home/webadmin/discordbot/bot/moduls/m_threadingmaker.py", line 15, in run
self.client.run(self.args[0])
File "/home/webadmin/discordbot/bot/venv/lib/python3.8/site-packages/discord/client.py", line 614, in run
loop.add_signal_handler(signal.SIGINT, lambda: loop.stop())
File "/usr/lib/python3.8/asyncio/unix_events.py", line 97, in add_signal_handler
raise RuntimeError(str(exc))
RuntimeError: set_wakeup_fd only works in main thread
I have upgraded from python 3.7 up to python 3.8 but I still have the same error.
Here is my code:
main.py (webserver worked)
dcbot = m_threadingmaker.myThread("Bot", client, secrets.token)
webserver = m_threadingmaker.myThread("Flask", app, 'localhost', '7010')
#webserver.start()
dcbot.start()
M_threadingmaker.py
from threading import Thread
class myThread (Thread):
def __init__(self, name, client, *args):
Thread.__init__(self)
self.name = name
self.client = client
self.args = args
def run(self):
print("Starting " + self.name)
if self.name == "Flask":
self.client.run(host=self.args[0], port=self.args[1])
else:
self.client.run(self.args[0])
print("Exiting " + self.name)
webserver.start()
dcbot.run()
Ok now it start the webserver and the bot. But when I try to do something after the bot start nothing happens. However, I am interested in why this is so. If someone knows a good and extensive contribution like books on threading, please send it
I'd suggest you to use client.start() in async coroutine instead of client.run() in separate thread.
More detailed example here

My Discord Bot doesn't start inside a thread (Discord.py)

So, I have coded a Discord Bot, but I want to run it in a thread, because I need pyramid for a webhook with GitHub. I will run it side by side, because the bot should write a message, when I push a update for my website hosted by GitHub Pages.
I got the pyramid server running but the bot wont start and when I try to run the bot normally, It runs quite nice.
The errors I got are:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
self.run()
File "/usr/lib/python3.8/threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "/home/nikurasu/PycharmProjects/pyramidtest/test2.py", line 35, in discordthread
client = MyClient()
File "/home/nikurasu/PycharmProjects/pyramidtest/venv/lib/python3.8/site-packages/discord/client.py", line 206, in __init__
self.loop = asyncio.get_event_loop() if loop is None else loop
File "/usr/lib/python3.8/asyncio/events.py", line 639, in get_event_loop
raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Thread-1'.
I simplified the code to exclude other shitcode written by me, but even when I use that simple code, the bot do not want to run:
import threading
import discord
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
def hello_word(request):
return Response('Hello World')
def hello_jonas(request):
return Response('<b>Hello, Jonas</b>')
def pyramidthread():
with Configurator() as config:
config.add_route('hello', '/')
config.add_view(hello_word, route_name='hello')
config.add_route('jonas', '/jonas')
config.add_view(hello_jonas, route_name='jonas')
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 6543, app)
server.serve_forever()
def discordthread():
token = 'nothing'
class MyClient(discord.Client):
async def on_ready(self):
print('Eingeloggt als', self.user)
print("Izuna versucht zu starten")
client = MyClient()
client.run(token)
t1 = threading.Thread(target=discordthread)
t2 = threading.Thread(target=pyramidthread)
t1.start()
t2.start()
I hope someone here can help me^^

Having a Telepot Bot run in its own Thread

I wrote a telegram bot in python using the telepot library. The bot itself is working. Since I also have some other stuff I need to do periodically while running the bot (namely writing stuff in logs), I want the bot to run in its own thread.
The telegram bot's code is a file called telebot.py while the threading code is in one called plantbot.py
I first tried simply creating a new thread that calls my run_bot() function, which didn't work and gave me the following RuntimeError:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
self.run()
File "/usr/lib/python3.5/threading.py", line 862, in run
self._target(*self._args, **self._kwargs)
File "plantbot.py", line 12, in start_bot
telebot.run_bot()
File "/srv/plantbot/telebot.py", line 108, in run_bot
loop = asyncio.get_event_loop()
File "/usr/lib/python3.5/asyncio/events.py", line 671, in get_event_loop
return get_event_loop_policy().get_event_loop()
File "/usr/lib/python3.5/asyncio/events.py", line 583, in get_event_loop
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'Thread-1'.
Here is the code I used for that:
plantbot.py
def start_bot():
telebot.run_bot()
bot = threading.Thread(target=start_bot, daemon=True)
bot.start()
print("Bot started...")
bot.join()
telebot.py
[lots of unrelated code]
bot = telepot.aio.Bot(TOKEN)
def run_bot():
loop = asyncio.get_event_loop()
loop.create_task(MessageLoop(bot, {'chat': on_chat_message, 'callback_query': on_callback_query}).run_forever())
print('Listening ...')
loop.run_forever()
I then tried around a little with the event loops until I ended up creating the event loop in my main file (plantbot.py) which looked like this:
plantbot.py
def start_bot(loop):
telebot.run_bot(loop)
loop = asyncio.new_event_loop()
bot = threading.Thread(target=start_bot, args=(loop,), daemon=True)
bot.start()
print("Bot started...")
bot.join()
telebot.py
[...]
bot = telepot.aio.Bot(TOKEN)
def run_bot(loop):
loop.create_task(MessageLoop(bot, {'chat': on_chat_message, 'callback_query': on_callback_query}).run_forever())
print('Listening ...')
loop.run_forever()
I also tried another variation:
plantbot.py
def start_bot(loop):
asyncio.set_event_loop(loop)
loop.create_task(telebot.task.run_forever())
loop.run_forever()
loop = asyncio.new_event_loop()
bot = threading.Thread(target=start_bot, args=(loop,), daemon=True)
bot.start()
print("Bot started...")
bot.join()
telebot.py
[...]
bot = telepot.aio.Bot(TOKEN)
task = MessageLoop(bot, {'chat': on_chat_message, 'callback_query': on_callback_query})
Unfortunately, none of these worked.
My question is thus, how do I properly run an event loop in its own designated thread? Specifically: How do I run a telegram bot in its own thread?
I did end up finding a solution, though it's not a satisfying one:
I am now running the bot in the main thread and put everything else into other threads. It does work that way around.

Calling python asyncio loop.run_until_complete() with discord.py not working?

Making a Discord bot using discord.py, this is the first time I work with asyncio, and probably the first time I encountered something this frustrating in Python.
The point of this question isn't to teach me how to use asyncio, but instead to teach me how to avoid using it, even if it's not the right way to do things.
So I needed to run the discord client coroutines from regular def functions. After hours of searching I found this: asyncio.get_event_loop().run_until_complete(...). I set up a small script to test it out:
import asyncio
async def test():
print('Success')
asyncio.get_event_loop().run_until_complete(test())
And it worked perfectly. So I went ahead and tried to use it in a discord bot:
import discord
import asyncio
client = discord.Client()
#client.event
async def on_ready():
test()
def test():
asyncio.get_event_loop().run_until_complete(run())
async def run():
print('Success')
client.run('TOKEN_HERE')
And I got an error... Stacktrace/Output:
Success
Ignoring exception in on_ready
Traceback (most recent call last):
File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 307, in _run_event
yield from getattr(self, event)(*args, **kwargs)
File "C:/Users/OverclockedSanic/PyCharm Projects/asyncio test/test.py", line 8, in on_ready
test()
File "C:/Users/OverclockedSanic/PyCharm Projects/asyncio test/test.py", line 11, in test
asyncio.get_event_loop().run_until_complete(run())
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 454, in run_until_complete
self.run_forever()
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 408, in run_forever
raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
What's weird is that "Success" part at the end... I tried some other tests to see if I could return data from the coroutine or execute more stuff, but it couldn't.
I even tried replacing asyncio.get_event_loop() with client.loop, which didn't work either.
I looked for like 2 days, still no solution. Any ideas?
EDIT: Replacing get_event_loop() with new_event_loop() as suggested in the comments raised this:
Ignoring exception in on_ready
Traceback (most recent call last):
File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 307, in _run_event
yield from getattr(self, event)(*args, **kwargs)
File "C:/Users/USER/PyCharm Projects/asyncio test/test.py", line 8, in on_ready
test()
File "C:/Users/USER/PyCharm Projects/asyncio test/test.py", line 11, in test
asyncio.new_event_loop().run_until_complete(run())
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 454, in run_until_complete
self.run_forever()
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 411, in run_forever
'Cannot run the event loop while another loop is running')
RuntimeError: Cannot run the event loop while another loop is running
Your problem seems to essentially be about mixing synchronous and asynchronous code. There are two possibilities:
1) If your non-async routines don't need to block, just to schedule some async task (e.g. send_message) to be run later, then they can simply call get_event_loop().create_task(). You can even use add_done_callback on the returned task if you want some other (non-async) routine to be called when the asynchronous operation is complete. (If the routine to be run is also non-async, then use get_event_loop().call_soon().)
2) If your non-async routines absolutely must block (which includes possibly awaiting an asynchronous routine), and cannot schedule the blocking operation for later, then you should not run them from the same thread as the main event loop. You can create a thread pool with concurrent.futures.ThreadPoolExecutor, and use asyncio.run_in_executor() to schedule your non-async routines, then await the result. And if they in turn need to call async routines, then run_until_complete() should work because now you're not running in a thread that already has an event loop. (But beware of threadsafety issues. You may need something like run_coroutine_threadsafe if you need to wait for something to run in the main event loop.)
If it helps, the asgiref package contains routines that can simplify this for you. They're designed for a slightly different purpose (web servers), but may also work for you. You can use await asgiref.sync.sync_to_async(func)(args) when you want to call a non-async routine from an async routine, which will run the routine in a thread pool, then use asgiref.sync.async_to_sync(func)(args) when you want to call an async routine from a non-async routine that's running inside that thread pool.

Categories