discord.py非异步上下文无法访问loop属性的问题修复求助
问题描述
运行Discord机器人代码时,机器人能正常启动,但执行/profiles命令时触发以下错误:
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: loop attribute cannot be accessed in non-async contexts. Consider using either an asynchronous main function and passing it to asyncio.run or using asynchronous initialisation hooks such as Client.setup_hook
其他命令运行正常,多次修改(如将get_uuid设为异步函数)仍未解决。
错误原因
- 异步方法调用遗漏
await:client.wait_for是异步方法,调用时未加await,导致在非异步上下文尝试访问事件循环属性。 - 异步函数调用未加
await:get_uuid是异步函数,调用时未加await,触发上下文冲突。 - 冗余客户端实例:代码同时创建
discord.Client和commands.Bot实例,造成上下文混乱。 - 同步请求阻塞事件循环:使用同步
requests.get在异步环境中运行,虽不是直接触发当前错误,但会影响机器人性能。
修复方案
关键修改点
- 移除冗余的
discord.Client实例,统一使用commands.Bot - 所有异步方法/函数调用必须添加
await关键字 - 将同步HTTP请求替换为异步
aiohttp请求,适配Discord.py异步环境
修复后的完整代码
import json import os import random from pprint import pprint import aiohttp import discord from discord.ext import commands from dotenv import load_dotenv from mojang import api # 异步Get请求方法 async def get_info(call): async with aiohttp.ClientSession() as session: async with session.get(call) as r: return await r.json() # 计算 bazaar 中所有收购单的总金币 def get_bazaar_buy_order_value(bazaar_data): sum_coins = 0 price_increase_threshold = 2 buy_order_values = [] for item_name, item_data in bazaar_data.get("products", {}).items(): item_sum_coins = 0 for idx, buy_order in enumerate(item_data.get("buy_summary", [])): if idx == 0: item_expected_value = buy_order.get("pricePerUnit", 0) item_sum_coins += buy_order.get("amount", 0) * buy_order.get("pricePerUnit", 0) else: if buy_order.get("pricePerUnit", 0) < (item_expected_value * price_increase_threshold): item_sum_coins += buy_order.get("amount", 0) * buy_order.get("pricePerUnit", 0) buy_order_values.append((item_name, item_sum_coins)) sum_coins += item_sum_coins sort_bazaar_buy_orders_by_value(buy_order_values) return sum_coins # 按总价值排序并打印收购单 def sort_bazaar_buy_orders_by_value(buy_order_values): buy_order_values.sort(key=lambda x: -x[1]) for (item_name, item_sum_coins) in buy_order_values: print(f"{item_name.ljust(30, ' ')} | {round(item_sum_coins):,}") # 获取 bazaar 数据 async def get_bazaar_data(): return await get_info("https://api.hypixel.net/skyblock/bazaar") # 通过UUID获取玩家拍卖信息 async def get_auctions_from_player(uuid): return await get_info(f"https://api.hypixel.net/skyblock/auction?key={API_KEY}&player={uuid}") # 获取当前镇长/选举数据 async def get_election_data(): return await get_info(f"https://api.hypixel.net/resources/skyblock/election") # 通过UUID获取玩家档案数据 async def get_profiles_data(uuid): return await get_info(f"https://sky.shiiyu.moe/api/v2/profile/{uuid}") # 通过用户名获取玩家UUID async def get_uuid(name): return await get_info(f"https://sky.shiiyu.moe/api/v2/profile/{name}") # Discord 配置 load_dotenv() TOKEN = os.getenv('DISCORD_TOKEN') GUILD = os.getenv('DISCORD_GUILD') intents = discord.Intents.all() bot = commands.Bot(command_prefix='/', intents=intents) # Hypixel 配置 Item = "Diamond" API_FILE = open("API_KEY.json","r") example_name = "4748" example_uuid = "147ab344d3e54952b74a8b0fedee5534" uuid_dashed = "147ab344-d3e5-4952-b74a-8b0fedee5534" API_KEY = json.loads(API_FILE.read())["API_KEY"] example_player_uuid = "147ab344d3e54952b74a8b0fedee5534" auctions_player_url = f"https://api.hypixel.net/skyblock/auction?key={API_KEY}&player={example_player_uuid}" # 命令定义 @bot.command(name='bazaar', description="获取 bazaar 中指定物品的详细数据", brief="查询 bazaar 物品数据") async def bazaar(ctx): data = await get_bazaar_data() await ctx.send(json.dumps(data, indent=2)) await ctx.send(API_KEY) @bot.command(name="bazaartotal", description="显示 bazaar 中所有收购单的总金币数量", brief="查询 bazaar 总金币") async def baztot(ctx): bazaar_data = await get_bazaar_data() await ctx.send(get_bazaar_buy_order_value(bazaar_data)) @bot.command(name="apikey", description="获取4748的API密钥,公开部署前请移除该命令", brief="获取API密钥") async def key(ctx): await ctx.send(API_KEY) @bot.command(name="profiles", description="获取指定玩家的档案列表及数据", brief="查询玩家档案") async def prof(ctx): await ctx.send("请输入要查询的用户名:") # 使用bot实例的wait_for,添加await并修正校验条件 message = await bot.wait_for('message', check=lambda m: m.author == ctx.author and m.channel == ctx.channel) username = message.content.strip() uuid_data = await get_uuid(username) pprint(uuid_data) await ctx.send(json.dumps(uuid_data, indent=2)) bot.run(TOKEN)
额外说明
- 所有原同步HTTP请求改为异步,避免阻塞机器人事件循环
wait_for增加了频道校验,确保只接收当前频道的用户输入,避免跨频道干扰- 发送JSON数据时使用
json.dumps格式化,提升消息可读性
内容的提问来源于stack exchange,提问作者coolguy76
相关产品推荐
相关产品推荐

