问题
我的discord.py僵尸程序的client.run()每隔几天左右就会意外返回,并出现错误“任务已销毁,但仍在等待中!”。
问题
为什么client.run()完全返回?以及如何更改我的机器人以正确处理此问题并永久运行?
代码(为了便于讨论,摘录了很多代码):
import asyncio
import discord
TOKEN = 'my bot token goes here'
CHANNEL_ID = 'my channel id goes here'
async def DiscordMsgSendTask():
await client.wait_until_ready()
my_channel = discord.Object(id=CHANNEL_ID)
while not client.is_closed:
# wait a bit to prevent busy loop
await asyncio.sleep(2)
# check for and handle new event here
# if an event was handled then send a message to the channel
embed = discord.Embed(description='Event was handled')
await client.send_message(my_channel, embed=embed)
client = discord.Client()
while True:
client.loop.create_task(DiscordMsgSendTask())
try:
client.run(TOKEN)
except Exception as e:
logging.warning('Exception: ' + str(e))
client = discord.Client()
其他信息
该机器人的目的基本上只是检查特定目录中的新文件,然后向特定的不和谐频道发送消息以进行报告。一天仅发生10次左右,因此该机器人的流量非常低。
为了使机器人能够容忍错误/断开连接,我将其放入while True循环中,这可能是错误的方法。
使用Python 3.6.5和Discord.py 0.16.12
编辑-添加了回溯
从当天早些时候的先前故障中添加回溯。
2018-06-20 04:33:08 [ERROR] Task exception was never retrieved
future: <Task finished coro=<WebSocketCommonProtocol.run() done, defined at /usr/local/lib64/python3.6/site-packages/websockets/protocol.py:428> exception=ConnectionResetError(104, 'Connection reset by peer')>
Traceback (most recent call last):
File "/usr/local/lib64/python3.6/site-packages/websockets/protocol.py", line 434, in run
msg = yield from self.read_message()
File "/usr/local/lib64/python3.6/site-packages/websockets/protocol.py", line 456, in read_message
frame = yield from self.read_data_frame(max_size=self.max_size)
File "/usr/local/lib64/python3.6/site-packages/websockets/protocol.py", line 511, in read_data_frame
frame = yield from self.read_frame(max_size)
File "/usr/local/lib64/python3.6/site-packages/websockets/protocol.py", line 546, in read_frame
self.reader.readexactly, is_masked, max_size=max_size)
File "/usr/local/lib64/python3.6/site-packages/websockets/framing.py", line 86, in read_frame
data = yield from reader(2)
File "/usr/lib64/python3.6/asyncio/streams.py", line 674, in readexactly
yield from self._wait_for_data('readexactly')
File "/usr/lib64/python3.6/asyncio/streams.py", line 464, in _wait_for_data
yield from self._waiter
File "/usr/lib64/python3.6/asyncio/selector_events.py", line 723, in _read_ready
data = self._sock.recv(self.max_size)
ConnectionResetError: [Errno 104] Connection reset by peer
2018-06-20 04:33:08 [ERROR] Task was destroyed but it is pending!
task: <Task pending coro=<DiscordMsgSendTask() running at /home/bot.py:119> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7fc99bfd7a68>()]>>
2018-06-20 04:33:08 [ERROR] Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7fc999b59240>
答案 0 :(得分:5)
您不能使用预定义的run
方法,因为该方法只能处理KeyboardInterrupt
。您将需要创建退出策略,以关闭所有任务,同时仍使循环继续运行。
以下示例将在机器人收到消息“死”时引发SystemExit
(以模拟意外的致命错误(RST-ConnectionError错误))。 SystemExit
将被“捕获”并重新启动您的机器人。引发KeyboardInterrupt
后,该漫游器将成功退出而不会出现错误。
import asyncio
import discord
TOKEN = 'TOKEN_HERE'
client = discord.Client()
async def task():
await client.wait_until_ready()
while True:
await asyncio.sleep(1)
print('Running')
def handle_exit():
print("Handling")
client.loop.run_until_complete(client.logout())
for t in asyncio.Task.all_tasks(loop=client.loop):
if t.done():
t.exception()
continue
t.cancel()
try:
client.loop.run_until_complete(asyncio.wait_for(t, 5, loop=client.loop))
t.exception()
except asyncio.InvalidStateError:
pass
except asyncio.TimeoutError:
pass
except asyncio.CancelledError:
pass
while True:
@client.event
async def on_message(m):
if m.content == 'die':
print("Terminating")
raise SystemExit
client.loop.create_task(task())
try:
client.loop.run_until_complete(client.start(TOKEN))
except SystemExit:
handle_exit()
except KeyboardInterrupt:
handle_exit()
client.loop.close()
print("Program ended")
break
print("Bot restarting")
client = discord.Client(loop=client.loop)
在不和谐中(有人键入die
):
die
输入端子(STDOUT):
Running
Running
Running
Terminating
Handling
Bot restarting
Running
Running
Running
Running
Running
<CTRL-C> (KeyboardInterrupt)
Handling
Program ended
旁注:
如果您仍然对错误感到困惑,那不是您的代码应受的责备。 Errno 104是服务器端的致命错误,最终用户通常无法避免。