It is a very common issue, but I cannot actually find a proper answer to it. I have the following code to connect to a server through a websocket and I want to keep it alive and keep listening to messages it sends like below:
import asyncio
import websockets
import nest_asyncio
nest_asyncio.apply()
async def listen_for_message(websocket):
while True:
await asyncio.sleep(0)
message = await websocket.recv()
print(message)
async def connect_to_dealer():
websocket = await websockets.connect(websocketadress)
hello_message = await websocket.recv()
print(hello_message)
async def my_app():
websocket = await connect_to_dealer()
asyncio.ensure_future(listen_for_message(websocket))
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(my_app())
loop.run_forever()
And it raises the error:
File "\untitled0.py", line 71, in <module>
loop.run_forever()
File "\Anaconda3\lib\asyncio\base_events.py", line 525, in run_forever
raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
And without the
import nest_asyncio
nest_asyncio.apply()
I get:
File "\untitled0.py", line 70, in <module>
loop.run_until_complete(my_app())
File "\Anaconda3\lib\asyncio\base_events.py", line 570, in run_until_complete
self.run_forever()
File "\Anaconda3\lib\asyncio\base_events.py", line 525, in run_forever
raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
I still do not understand why it does this.
loop.run_until_complete() already runs your loop 'forever'.
Instead of firing of your listen_for_message() awaitable as a task, just await on it. That then runs forever, because listen_for_message() itself never returns:
async def my_app():
websocket = await connect_to_dealer()
await listen_for_message(websocket)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(my_app())
Note that your connect_to_dealer() function doesn't return the websocket; that's probably an oversight you want to correct:
async def connect_to_dealer():
return await websockets.connect(websocketadress)
I removed the hello_message = await websocket.recv() / print(hello_message) lines there because listen_for_message() will already receive messages and print them.
Related
My English is not good, I am still a newcomer, please forgive me if the question is not good.
I'm using PyCord 2.3.2 to make a discord music robot. Currently, I only do the join command. Although I can join the channel, it throws an error.
Ignoring exception in command join:
Traceback (most recent call last):
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\commands\core.py", line 178, in wrapped
ret = await coro(*args, **kwargs)
File "C:\Users\user\Desktop\BOT\Cogs\Music.py", line 52, in join
await channel.connect()
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\abc.py", line 1932, in connect
await voice.connect(timeout=timeout, reconnect=reconnect)
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\voice_client.py", line 404, in connect
self.ws = await self.connect_websocket()
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\voice_client.py", line 375, in connect_websocket
await ws.poll_event()
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\gateway.py", line 944, in poll_event
await self.received_message(utils._from_json(msg.data))
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\gateway.py", line 861, in received_message
await self.initial_connection(data)
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\gateway.py", line 898, in initial_connection
recv = await self.loop.sock_recv(state.socket, 70)
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\asyncio\proactor_events.py", line 696, in sock_recv
return await self._proactor.recv(sock, n)
RuntimeError: Task <Task pending name='pycord: on_message' coro=<Client._run_event() running at C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\client.py:377>> got Future <_OverlappedFuture pending overlapped=<pending, 0x183de095fc0>> attached to a different loop
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\commands\bot.py", line 347, in invoke
await ctx.command.invoke(ctx)
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\commands\core.py", line 950, in invoke
await injected(*ctx.args, **ctx.kwargs)
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\commands\core.py", line 187, in wrapped
raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: RuntimeError: Task <Task pending name='pycord: on_message' coro=<Client._run_event() running at C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\client.py:377>> got Future <_OverlappedFuture pending overlapped=<pending, 0x183de095fc0>> attached to a different loop
I tried to find the problem according to the error, but there is no good solution, hope to find a solution here.
this is my program
Cogs:
import discord
from discord.ext import commands,tasks
import os
import youtube_dl
import asyncio
from definition.Classes import Cog_Extension
class Music(Cog_Extension):
#commands.command(name='join', help='Tells the bot to join the voice channel')
async def join(self,ctx):
if not ctx.message.author.voice:
await ctx.send("{} is not connected to a voice channel".format(ctx.message.author.name))
return
else:
channel = ctx.message.author.voice.channel
await channel.connect()
def setup(bot):
bot.add_cog(Music(bot))
Bot:
import discord
import asyncio
from discord.ext import commands
import os
import json
with open("config.json" , "r" ,encoding="utf8") as configFiles:
config = json.load(configFiles)
intents = discord.Intents.all()
bot = commands.Bot(command_prefix=config["command_prefix"], intents=intents)
#bot.event
async def on_ready():
os.system('clear')
print("-"*15)
print("πbot online")
print("-"*15)
print(bot.user.name)
print(bot.user.id)
print(bot.user)
print("-"*15)
Activity = discord.Activity(type=discord.ActivityType.watching,name="bot")
await bot.change_presence(activity=Activity)
await asyncio.sleep(3)
async def load():
for filename in os.listdir('./Cogs'):
if filename.endswith('.py'):
try:
bot.load_extension(f'Cogs.{filename[:-3]}')
print(f'β
load {filename}')
except Exception as error:
print(f'β {filename} error {error}')
async def main():
await load()
await bot.start(config["Token"])
asyncio.run(main())
I think you could simplify your code a bit and that'll likely help:
def load():
# doesn't need to be async
for filename in os.listdir('./Cogs'):
if filename.endswith('.py'):
try:
bot.load_extension(f'Cogs.{filename[:-3]}')
print(f'β
load {filename}')
except Exception as error:
print(f'β {filename} error {error}')
load()
bot.run(config["Token"])
Or, if you really want to use bot.start then the docs recommend doing it this way:
def load():
# still doesn't need to be async
for filename in os.listdir('./Cogs'):
if filename.endswith('.py'):
try:
bot.load_extension(f'Cogs.{filename[:-3]}')
print(f'β
load {filename}')
except Exception as error:
print(f'β {filename} error {error}')
load()
# for python3.11
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# loop = asyncio.get_event_loop()
try:
loop.run_until_complete(bot.start(config["Token"]))
except KeyboardInterrupt:
loop.run_until_complete(close())
# cancel all tasks lingering
finally:
loop.close()
I used the latter method, and copied your cog and my bot joined the VC I was in without any issues.
I am trying to run multiple discord bots with the same script at once. The way I am doing this is going down a csv file, to get the bot token and bot name. I then start my script:
import time, csv, re, random, string, sys, os, asyncio
from datetime import datetime
from threading import Thread
import discord
from dotenv import load_dotenv
from discord_webhook import DiscordWebhook, DiscordEmbed
def main(bottoken, botname):
TOKEN = (bottoken)
client = discord.Client()
#client.event
async def on_ready():
print(f'{client.user.name} has connected to Discord!')
#client.event
async def on_message(message):
#do stuff
print('Do stuff')
client.run(TOKEN)
def runtask():
if __name__ == '__main__':
with open('botinfo.csv', 'r') as f:
reader = csv.reader(f, delimiter=',')
for i, row in enumerate(reader):
Thread(target = main, args=(row[0], row[1])).start()
if __name__ == '__main__':
runtask()
But then I get this error:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 917, in _bootstrap_inner
self.run()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "/Users/alexforman/Documents/GitHub/bot-price-monitor/frontend_multithread.py", line 14, in main
client = discord.Client()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/discord/client.py", line 216, in __init__
self.loop = asyncio.get_event_loop() if loop is None else loop
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/events.py", line 644, in get_event_loop
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'Thread-1'.
Exception in thread Thread-2:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 917, in _bootstrap_inner
self.run()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "/Users/alexforman/Documents/GitHub/bot-price-monitor/frontend_multithread.py", line 14, in main
client = discord.Client()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/discord/client.py", line 216, in __init__
self.loop = asyncio.get_event_loop() if loop is None else loop
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/events.py", line 644, in get_event_loop
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'Thread-2'.
Does anyone have any ideas on how I could fix it?
You don't need to start a separate thread for each bot, the asyncio event loop is perfectly capable of serving all bots at once. (That's the kind of thing event loops are designed for.)
You just need to start all the bots as coroutines of the same asyncio event loop:
async def run_client(bottoken, botname):
TOKEN = (bottoken)
client = discord.Client()
#client.event
async def on_ready():
print(f'{client.user.name} has connected to Discord!')
#client.event
async def on_message(message):
#do stuff
print('Do stuff')
# can't use client.run() because it would block the event loop
# but we can use client.start(), which is a coroutine that does
# the same thing (and which is internally called by client.run)
await client.start(TOKEN)
async def main():
coros = []
with open('botinfo.csv', 'r') as f:
reader = csv.reader(f, delimiter=',')
for i, row in enumerate(reader):
coros.append(run_client(row[0], row[1]))
await asyncio.gather(*coros)
if __name__ == '__main__':
asyncio.run(main())
I am trying to build a websockets server where the client connects and sends a request of some kind, and then the server adds that request to a queue and returns something back when finished.
to simulate the process of queueing and waiting for tasks to finish, I called asyncio.sleep whenever the process is supposed to be called.
however, whenever I do that I get the following error from the client-side:
Traceback (most recent call last):
File "D:/Python/server/client1.py", line 10, in <module>
asyncio.get_event_loop().run_until_complete(message())
File "C:\ProgramData\Anaconda3\envs\server\lib\asyncio\base_events.py", line 587, in run_until_complete
return future.result()
File "D:/Python/server/client1.py", line 8, in message
print(await socket.recv())
File "C:\ProgramData\Anaconda3\envs\server\lib\site-packages\websockets\protocol.py", line 509, in recv
await self.ensure_open()
File "C:\ProgramData\Anaconda3\envs\server\lib\site-packages\websockets\protocol.py", line 812, in ensure_open
raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedOK: code = 1000 (OK), no reason
and this error from the server-side:
Task exception was never retrieved
future: <Task finished coro=<queue_dispatcher() done, defined at D:/RamNew2020/Python/server/asyncserver.py:39> exception=ConnectionClosedOK('code = 1000 (OK), no reason')>
Traceback (most recent call last):
File "D:/Python/server/asyncserver.py", line 45, in queue_dispatcher
await data_from_q.sendto_websocket("yay")
File "D:/Python/server/asyncserver.py", line 20, in sendto_websocket
await self.mywebsocket.send(message)
File "C:\ProgramData\Anaconda3\envs\server\lib\site-packages\websockets\protocol.py", line 555, in send
await self.ensure_open()
File "C:\ProgramData\Anaconda3\envs\server\lib\site-packages\websockets\protocol.py", line 803, in ensure_open
raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedOK: code = 1000 (OK), no reason
My scripts:
server.py:
import asyncio
import websockets
import datetime
import random
queue = asyncio.Queue()
async def rnd_sleep(t):
# sleep for T seconds on average
await asyncio.sleep(t * random.random() * 2)
#When a client sends a message the information is saved in a Request object,
#including the socket connection
class Request:
def __init__(self, mywebsocket, mydat):
self.mywebsocket = mywebsocket
self.mydat = mydat
self.start_time_of_request = datetime.datetime.now()
self.id = random.randint(0, 128)
async def sendto_websocket(self, message):
await self.mywebsocket.send(message)
def get_dat(self):
return self.mydat
def get_start_time(self):
return self.start_time_of_request
async def add_to_queue(websocket, path):
global queue
dat = await websocket.recv()
base64dat = Request(websocket, dat)
await queue.put(base64dat)
#problem in queue_dispatcher
async def queue_dispatcher(q):
while True:
data_from_q = await q.get()
print(data_from_q.id)
await asyncio.sleep(1) #The problem is here, whenever I remove it, it works
await data_from_q.sendto_websocket("yay")
q.task_done()
async def main():
global queue
asyncio.create_task(queue_dispatcher(queue))
pass
start_server = websockets.serve(add_to_queue, 'localhost', 5000)
asyncio.get_event_loop().run_until_complete(main())
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
client.py:
import asyncio
async def message():
async with websockets.connect("ws://localhost:5000")as socket:
msg = input("What do you want to send: ")
await socket.send(msg)
print(await socket.recv())
asyncio.get_event_loop().run_until_complete(message())
In addition, whenever I try to await any other async function, it works fine no matter how long.
I'm currently exploring async/await in python.
I'd like to start Pyrogram, then run the cronjob function. I created this code:
import asyncio
from pyrogram import Client, Filters, MessageHandler
import time
app = Client("pihome")
async def cronjob():
print("Cronjob started")
while True:
#print(f"time is {time.strftime('%X')}")
print(int(time.time()))
if int(time.strftime('%S')) == 10:
print("SECONDO 10") # Change this with checks in future
await asyncio.sleep(1)
async def main():
print(f"Starting {app.session_name}...\n")
# Pyrogram
await app.start()
print("Pyrogram started")
# Cronjob
loop = asyncio.new_event_loop()
loop.run_until_complete(loop.create_task(await cronjob()))
print("\nDone")
await Client.idle()
await app.stop()
print(f"\nStopping {app.session_name}...")
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
But when i want to stop it with Ctrl+C it gives me this traceback:
^CTraceback (most recent call last):
File "pihome.py", line 39, in <module>
asyncio.get_event_loop().run_until_complete(main())
File "/usr/local/lib/python3.8/asyncio/base_events.py", line 603, in run_until_complete
self.run_forever()
File "/usr/local/lib/python3.8/asyncio/base_events.py", line 570, in run_forever
self._run_once()
File "/usr/local/lib/python3.8/asyncio/base_events.py", line 1823, in _run_once
event_list = self._selector.select(timeout)
File "/usr/local/lib/python3.8/selectors.py", line 468, in select
fd_event_list = self._selector.poll(timeout, max_ev)
KeyboardInterrupt
How can I solve it? Also, try/except blocks seem not working
To run the application, it is better to use asyncio.run(main()). In my opinion, itβs more clear.
Replace loop = asyncio.new_event_loop() by loop = asyncio.get_event_loop(). Because using asyncio.new_event_loop() after asyncio.get_event_loop().run_until_complete(main()) you create a second event loop in main thread which is prohibited. Only one event loop per thread is permitted!
Remove await from this line:
loop.run_until_complete(loop.create_task(await cronjob()))
Because create_task need to Coroutine as the first argument but you pass it None
i have multiple tasks that use websocket. i would like to detect the connection close of websocket and then reconnect the websocket, I'm doing it through a variable called break_task if it is true then in on of the task there was a connection close detection, below the code and the error exception I got (i use python 3.6):
break_task = False
async def value_1():
global break_task
msg_dict = deepcopy(MESG)
async with websockets.connect('wss://api.xxxxx') as ws:
await ws.send(json.dumps(msg_dict))
while break_task == False:
try:
res = await ws.recv()
except ConnectionClosed:
print('CONNECTION CLOSED raised exception in value_1() -> TRYING TO RECONNECT !!!')
break_task = True
break
async def value_2():
global break_task
auth_dict = deepcopy(PAYLOAD)
async with websockets.connect('wss://api.xxxxx') as ws:
await ws.send(json.dumps(auth_dict))
while break_task == False:
await asyncio.sleep(0.05)
try:
res = await ws.recv()
except ConnectionClosed:
print('CONNECTION CLOSED raised exception in value_2() -> TRYING TO RECONNECT !!!')
break_task = True
break
data = json.loads(res)
and...
while True:
if (break_task == False):
tasks = value_1(), value_2()
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(*tasks))
elif (break_task == True):
tasks = None
loop.stop()
loop.close()
break_task = False
I get this result:
Traceback (most recent call last):
File "tija_v0.1.py", line 584, in <module>
loop.run_until_complete(asyncio.gather(*tasks))
File "C:\Dev\Anaconda2\envs\python36\lib\asyncio\tasks.py", line 589, in gather
fut = ensure_future(arg, loop=loop)
File "C:\Dev\Anaconda2\envs\python36\lib\asyncio\tasks.py", line 513, in ensure_future
task = loop.create_task(coro_or_future)
File "C:\Dev\Anaconda2\envs\python36\lib\asyncio\base_events.py", line 282, in create_task
self._check_closed()
File "C:\Dev\Anaconda2\envs\python36\lib\asyncio\base_events.py", line 357, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
sys:1: RuntimeWarning: coroutine 'value_2' was never awaited
sys:1: RuntimeWarning: coroutine 'value_1' was never awaited
sys:1: RuntimeWarning: coroutine 'value_2' was never awaited
sys:1: RuntimeWarning: coroutine 'value_1' was never awaited
might trip your process from reconnecting, it might effect in more severe way than a warning. I have similar problem when attempting to bind async message update_handler function to synchronic third party web socket message handler.
You should find a method to call the handler with await to prevent this warning.