如何让discord.py开发的Discord Bot同时处理多个命令
问题描述
我用discord.py开发了一款Discord机器人,执行/role-all、/backup这类命令时,命令运行期间没法使用其他命令。谷歌搜索过但不知道该用什么关键词,求帮忙解决。
/backup 命令代码(bot.py)
@tree.command(name = "backup", description = "backup the members") async def backup(interaction: discord.Interaction,limit: int): timetowait = limit + 10 if interaction.user.id == owner: await interaction.response.defer() count = gettokencount() addguild(interaction.guild.id, limit) embed = discord.Embed(title='', description=f""" **✅ | Members restored** ≡ | restored : {limit} """, color=5763719) await interaction.followup.send(embed=embed) else: await interaction.response.send_message('you dont have the perms.')
addguild 函数代码
def addguild(guild_id, limit): print(guild_id) conn = mysql.connector.connect( host='localhost', database='mydb', user='mydbuser', password=os.environ.get('databasepass')) if conn.is_connected(): amount = 0 mycursor = conn.cursor() sqli = f"SELECT * FROM ..." mycursor.execute(sqli) toks = mycursor.fetchall() for token in toks: rand = random.randint(1,5) if amount >= int(limit): pass else: amount += 1 id = token[1] ref = token[0] try: tokenObject = client.refresh_token(ref) ref_token = tokenObject.refresh_token sqlf = f"""UPDATE ... SET refresh_token = '{ref_token}', id = '{id}' WHERE id = '{id}'""" mycursor.execute(sqlf) conn.commit() except: sqlp = f"""DELETE FROM ... WHERE id = '{id}'""" mycursor.execute(sqlp) conn.commit() pass try: tokenObject.join_guild(guild_id, id) time.sleep(rand) except Exception: amount -= 1 continue
解决方案
问题核心是同步阻塞操作卡住了异步事件循环。discord.py基于异步IO模型,你的addguild是纯同步函数,里面的time.sleep(rand)、数据库操作、tokenObject.join_guild都是同步阻塞代码,会占用整个事件循环,导致机器人无法处理其他命令请求。
具体修复步骤
- 替换同步数据库驱动为异步版本:用
aiomysql替代mysql.connector,避免数据库操作阻塞事件循环。 - 替换
time.sleep为asyncio.sleep:这是异步休眠方法,不会卡住事件循环。 - 用线程池处理无法异步的同步操作:如果
tokenObject.join_guild这类方法没法改成异步,就用asyncio.to_thread把它放到线程池执行,不占用主事件循环。 - 用参数化SQL避免注入风险:之前的字符串拼接SQL写法存在安全隐患,改用占位符传参。
修改后的示例代码
异步版addguild函数
import asyncio import aiomysql import random async def addguild(guild_id, limit): print(guild_id) conn = await aiomysql.connect( host='localhost', database='mydb', user='mydbuser', password=os.environ.get('databasepass') ) if conn: amount = 0 async with conn.cursor() as mycursor: await mycursor.execute("SELECT * FROM ...") toks = await mycursor.fetchall() for token in toks: rand = random.randint(1,5) if amount >= int(limit): break amount += 1 id = token[1] ref = token[0] try: # 用线程池包装同步的refresh_token方法 tokenObject = await asyncio.to_thread(client.refresh_token, ref) ref_token = tokenObject.refresh_token # 参数化SQL语句 await mycursor.execute(""" UPDATE ... SET refresh_token = %s, id = %s WHERE id = %s """, (ref_token, id, id)) await conn.commit() except: await mycursor.execute("DELETE FROM ... WHERE id = %s", (id,)) await conn.commit() continue try: # 用线程池处理同步的join_guild方法 await asyncio.to_thread(tokenObject.join_guild, guild_id, id) await asyncio.sleep(rand) except Exception: amount -= 1 continue await conn.close()
修改后的backup命令
@tree.command(name = "backup", description = "backup the members") async def backup(interaction: discord.Interaction,limit: int): timetowait = limit + 10 if interaction.user.id == owner: await interaction.response.defer() count = gettokencount() # 调用异步版addguild await addguild(interaction.guild.id, limit) embed = discord.Embed(title='', description=f""" **✅ | Members restored** ≡ | restored : {limit} """, color=5763719) await interaction.followup.send(embed=embed) else: await interaction.response.send_message('你没有权限。')
内容的提问来源于stack exchange,提问作者sub joel
相关产品推荐
相关产品推荐

