使用MongoDB(Motor)实现Discord机器人setprefix命令时遭遇TypeError错误求助
解决Discord Bot setprefix命令的TypeError问题
你的错误TypeError: command_prefix must be plain string, iterable of strings, or callable returning either of these, not coroutine,核心原因有两个,我来帮你拆解并修复:
错误原因分析
初始化Bot时传入了协程对象
你在Main.py里写了bot = commands.Bot(command_prefix = Prefixes.setprefix(), ...),但Prefixes.setprefix是一个异步的命令函数(用async def定义的),调用它会返回一个协程对象,而command_prefix需要的是字符串、字符串列表,或者能返回这些类型的可调用函数(可以是异步的),不是协程本身。错误地尝试重新构造Bot实例
在你的Prefixes cog里,self.bot(command_prefix = str(first_value), ...)这行完全错了——Bot实例已经初始化完成,不能像调用函数一样重新构造它来修改前缀。我们需要让Bot在每次处理命令时动态获取当前服务器的前缀,而不是硬编码或者事后修改实例参数。
正确的实现方案
我们需要做这几件事:
- 写一个异步函数,根据服务器ID从MongoDB获取对应的前缀,没有的话返回默认前缀
- 把这个函数传给Bot的
command_prefix参数 - 修正setprefix命令里的查询逻辑(你之前是按Prefix查,应该按GuildId查,因为每个服务器对应一个前缀)
- 移除错误的Bot重构代码
修改后的Main.py
#------导入包 import keep_alive import os import discord from discord.ext import commands import motor.motor_asyncio ### MongoDB变量 ### mongo_url = os.environ['Mongodb_url'] cluster = motor.motor_asyncio.AsyncIOMotorClient(str(mongo_url)) db = cluster['Database'] collection = db['prefixes'] # 异步获取前缀的函数 async def get_prefix(bot, message): # 如果是私信,用默认前缀 if not message.guild: return '-' # 从MongoDB查当前服务器的前缀 prefix_data = await collection.find_one({'GuildId': message.guild.id}) # 有数据就返回对应的前缀,没有就用默认的'-' return prefix_data['Prefix'] if prefix_data else '-' # 把get_prefix传给command_prefix,注意不要加括号(要传函数本身,不是调用后的结果) bot = commands.Bot(command_prefix = get_prefix, case_insensitive=True) bot.remove_command('help') my_token = os.environ['Token'] #------机器人上线时 @bot.event async def on_ready(): await bot.change_presence(activity=discord.Game( name=f'On {len(bot.guilds)} Servers | -help')) print('Bot is Ready') initial_extensions = ['help', 'math1', 'mod', 'anime', 'prefixes'] if __name__ == '__main__': for extension in initial_extensions: bot.load_extension(extension) #ping延迟.... @bot.command() async def ping(ctx): await ctx.send(f'Pong\n{round(bot.latency * 1000)}ms') #------运行机器人 keep_alive.keep_alive() bot.run(my_token)
修改后的Prefixes cog文件
import discord from discord.ext import commands import motor.motor_asyncio import os class Prefixes(commands.Cog): def __init__(self, bot): self.bot = bot # 初始化MongoDB连接,不用每次命令都重新连 mongo_url = os.environ['Mongodb_url'] self.cluster = motor.motor_asyncio.AsyncIOMotorClient(str(mongo_url)) self.db = self.cluster['Database'] self.collection = self.db['prefixes'] #自定义前缀 @commands.command() @commands.has_permissions(administrator = True) async def setprefix(self, ctx, prefix): guild_id = ctx.guild.id server_prefix = prefix # 尝试更新,如果不存在就插入(用update_one的upsert参数更简洁) await self.collection.update_one( {'GuildId': guild_id}, {'$set': {'Prefix': server_prefix}}, upsert=True # 没有匹配文档就自动插入 ) await ctx.send(f'My prefix is now {prefix}') # 不需要重新构造Bot,因为get_prefix会自动从数据库取最新值 def setup(bot): bot.add_cog(Prefixes(bot))
关键知识点说明
- 动态前缀的正确姿势:discord.py支持将异步函数作为
command_prefix,这个函数会在每次处理命令时被调用,传入Bot实例和Message对象,返回当前要使用的前缀。这样每次修改前缀后,下一次命令就会自动用新前缀。 - MongoDB操作优化:用
update_one的upsert=True可以合并“插入新文档”和“更新已有文档”的逻辑,比先判断count再分开处理更简洁高效。 - 避免重复初始化:在Cog的
__init__里初始化MongoDB连接,而不是每次命令都重新创建客户端,减少资源消耗。
内容的提问来源于stack exchange,提问作者Nishil Sheth
相关产品推荐
相关产品推荐

