我正在制作一个Discord机器人,该机器人在收到Github钩子时会发送PM。
它使用Discord.py和BottlePy,最后一个在专用线程中运行。 因为两个框架都有阻塞的主循环。
在BottlePy回调中,我调用了一些Discord.py异步代码。
我不知道什么是Python异步,当与同步代码混合时,这似乎很复杂...
这是完整的源代码:
import discord
import bottle
import threading
import asyncio
client = discord.Client()
server = bottle.Bottle()
async def dm_on_github_async(userid,request):
print("Fire Discord dm to "+str(userid))
global client
user = client.get_user(userid)
if (user==None):
abort(500, "User lookup failed");
dm_channel = user.dm_channel
if (dm_channel==None):
dm_channel = await user.create_dm()
if (dm_channel==None):
abort(500, "Fail to create DM channel");
print("DM channel is "+str(asyncio.wait(dm_channel.id)))
await dm_channel.send("There's a Github shot !")
await dm_channel.send(str(request.body))
return
@server.post("/dm_on_github/<userid:int>")
def dm_on_github(userid):
return asyncio.run(dm_on_github_async(userid,bottle.request))
@client.event
async def on_ready():
print('We have logged in as {0.user} '.format(client))
#@client.event
#async def on_message(message):
# if message.author == client.user:
# return
#
# if message.content.startswith('$hello'):
# await message.channel.send('Hello!')
# # This sample was working very well
class HTTPThread(threading.Thread):
def run(self):
global server
server.run(port=8080)
server_thread = HTTPThread()
print("Starting HTTP server")
server_thread.start()
print("Starting Discord client")
client.run('super secret key')
print("Client terminated")
server.close()
print("Asked server to terminate")
server_thread.join()
print("Server thread successful join")
我希望我的Python机器人以PM形式发送HTTP请求的正文。
我在RuntimeError: Timeout context manager should be used inside a task
得到了return asyncio.run(dm_on_github_async(userid,bottle.request))
。
我认为我的混合方式不正确...
答案 0 :(得分:0)
一个晚上后,我找到了路。
要在另一个线程中从同步代码中调用异步代码,我们要求loop
(这里是Discord.py的代码)使用asyncio.run_coroutine_threadsafe()
运行回调,这将返回Task()
然后我们用result()
等待他的结果。
回调将在循环线程中运行,在我的情况下,我需要copy()
瓶请求。
这是一个正在运行的程序(只要您不介意停止它...):
import discord
import bottle
import threading
import asyncio
client = discord.Client()
server = bottle.Bottle()
class HTTPThread(threading.Thread):
def run(self):
global server
server.run(port=8080)
async def dm_on_github_async(userid,request):
user = client.get_user(userid)
if (user==None):
abort(500, "User lookup failed");
dm_channel = user.dm_channel
if (dm_channel==None):
dm_channel = await user.create_dm()
if (dm_channel==None):
abort(500, "Fail to create DM channel");
# Handle the request
event = request.get_header("X-GitHub-Event")
await dm_channel.send("Got event "+str(event))
#await dm_channel.send(str(request.body)) # Doesn't work well...
return
@server.post("/dm_on_github/<userid:int>")
def dm_on_github(userid):
request = bottle.request
asyncio.run_coroutine_threadsafe(dm_on_github_async(userid,request.copy()),client.loop).result()
@client.event
async def on_ready():
print('We have logged in as {0.user} '.format(client))
# Wait for the old HTTP server
if hasattr(client,"server_thread"):
server.close()
client.server_thread.join()
client.server_thread = HTTPThread()
client.server_thread.start()
#@client.event
#async def on_message(message):
# if message.author == client.user:
# return
#
# if message.content.startswith('$hello'):
# await message.channel.send('Hello!')
print("Starting Discord client")
client.run('super secret key')
print("Client terminated")
server.close()
print("Asked server to terminate")
server_thread.join()
print("Server thread successful join")