You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Discord Bot线程中发送DM触发AttributeError问题求助

问题

我开发了一个从GitHub获取数据的Python Discord Bot,为避免GitHub限制,设置了每周自动更新数据。当Bot无法连接GitHub完成更新时,希望通过Discord私信收到通知。

已实现更新失败检测逻辑,但添加私信功能时遇到问题:自动更新器运行在单独线程中,执行guild = await bot.fetch_guild(GUILD_ID)时触发AttributeError: 'NoneType' object has no attribute 'request'错误。通过Discord命令触发notify_on_no_respond函数时可正常运行。

完整错误信息

Exception in thread Thread-1 (start_updater):
Traceback (most recent call last):
  File "C:\Users\[User]\AppData\Local\Programs\Python\Python310\lib\threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "C:\Users\[User]\AppData\Local\Programs\Python\Python310\lib\threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "\\StationDS216\home\Projects\Python\Discord Bots\test lab\Bot_auto_updater.py", line 7, in start_updater
    Thread_Auto_Updater = auto_updater(
  File "\\StationDS216\home\Projects\Python\Discord Bots\test lab\Bot_auto_updater.py", line 22, in __init__
    self.data['Bot'].problem_occured()
  File "\\StationDS216\home\Projects\Python\Discord Bots\test lab\Bot_main.py", line 70, in problem_occured
    asyncio.run(notify_on_no_respond())
  File "C:\Users\[User]\AppData\Local\Programs\Python\Python310\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "C:\Users\[User]\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 646, in run_until_complete
    return future.result()
  File "\\StationDS216\home\Projects\Python\Discord Bots\test lab\Bot_main.py", line 74, in notify_on_no_respond
    guild = await bot.fetch_guild(GUILD_ID)
  File "C:\Users\[User]\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\client.py", line 1188, in fetch_guild
    data = await self.http.get_guild(guild_id)
  File "C:\Users\[User]\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\http.py", line 192, in request
    async with self.__session.request(method, url, **kwargs) as r:
AttributeError: 'NoneType' object has no attribute 'request'

复现代码

Bot_Game_main.py

import Bot_Game_auto_updater
import os
import discord
import threading
import asyncio
from discord.ext import commands
from dotenv import load_dotenv

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
MY_ID = os.getenv('DISCORD_MY_ID')
GUILD_ID = os.getenv('DISCORD_GUILD_ID')
bot = commands.Bot(command_prefix='/')


class maintainer(object):
    def __init__(self):
        pass

    def start_thread(self, Bot_Game):
        threading.Thread(target=Bot_Game_auto_updater.start_updater,
                         args=(Bot_Game,), daemon=True).start()

    def problem_occured(self):
        asyncio.run(notify_on_no_respond())


async def notify_on_no_respond():
    guild = await bot.fetch_guild(GUILD_ID)
    user = await guild.fetch_member(MY_ID)
    await user.send('a problem occured')

Bot_Game = maintainer()
Bot_Game.start_thread(Bot_Game)
bot.run(TOKEN)

Bot_Game_auto_updater.py

def start_updater(Bot_Game):
    Thread_Auto_Updater = auto_updater(Bot_Game)


class auto_updater(object):
    def __init__(self, Bot_Game):
        Bot_Game.problem_occured()

解决方法

错误核心原因有两点:

  1. 线程启动时机过早:在bot.run(TOKEN)前启动更新线程,此时Bot未完成初始化,内部HTTP会话未创建,导致API调用失败。
  2. 跨线程异步调用方式错误:用asyncio.run()会创建新事件循环,而discord.py的Bot实例绑定在自身启动的循环中,跨循环调用会导致会话无法访问。

以下是具体修复步骤:

1. 延迟线程启动,等待Bot初始化完成

通过discord.py的on_ready事件触发更新线程,确保Bot完成登录初始化:
修改Bot_Game_main.py的启动逻辑:

# 移除原有的Bot_Game.start_thread(Bot_Game)
@bot.event
async def on_ready():
    print(f'Logged in as {bot.user}')
    # Bot初始化完成后启动更新线程
    Bot_Game.start_thread(Bot_Game)

2. 正确在非异步线程中调用Bot异步方法

不要用asyncio.run(),而是利用Bot实例的loop属性,将异步任务提交到Bot所在的事件循环执行:
修改maintainer类的problem_occured方法:

def problem_occured(self):
    # 将异步任务提交到Bot的事件循环
    asyncio.run_coroutine_threadsafe(notify_on_no_respond(), bot.loop)

3. 优化私信发送逻辑(可选)

无需先获取公会再取成员,直接用bot.fetch_user()获取用户,减少API调用:

async def notify_on_no_respond():
    user = await bot.fetch_user(MY_ID)
    await user.send('更新出现问题,无法连接GitHub')

修复后的完整代码

Bot_Game_main.py

import Bot_Game_auto_updater
import os
import discord
import threading
import asyncio
from discord.ext import commands
from dotenv import load_dotenv

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
MY_ID = int(os.getenv('DISCORD_MY_ID'))  # 转换为整数类型
GUILD_ID = os.getenv('DISCORD_GUILD_ID')
bot = commands.Bot(command_prefix='/')


class maintainer(object):
    def __init__(self):
        pass

    def start_thread(self, Bot_Game):
        threading.Thread(target=Bot_Game_auto_updater.start_updater,
                         args=(Bot_Game,), daemon=True).start()

    def problem_occured(self):
        # 提交任务到Bot的事件循环
        asyncio.run_coroutine_threadsafe(notify_on_no_respond(), bot.loop)


async def notify_on_no_respond():
    # 直接获取用户,无需经过公会
    user = await bot.fetch_user(MY_ID)
    await user.send('更新出现问题,无法连接GitHub')

Bot_Game = maintainer()

@bot.event
async def on_ready():
    print(f'已登录为 {bot.user}')
    Bot_Game.start_thread(Bot_Game)

bot.run(TOKEN)

Bot_Game_auto_updater.py

def start_updater(Bot_Game):
    Thread_Auto_Updater = auto_updater(Bot_Game)


class auto_updater(object):
    def __init__(self, Bot_Game):
        # 模拟更新失败触发通知
        Bot_Game.problem_occured()

内容的提问来源于stack exchange,提问作者aszubat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 20:24:30