如果client.run()返回

时间:2018-06-20 21:06:43

标签: python python-3.x discord.py

问题

我的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>

1 个答案:

答案 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是服务器端的致命错误,最终用户通常无法避免。