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

使用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,核心原因有两个,我来帮你拆解并修复:

错误原因分析

  1. 初始化Bot时传入了协程对象
    你在Main.py里写了bot = commands.Bot(command_prefix = Prefixes.setprefix(), ...),但Prefixes.setprefix是一个异步的命令函数(用async def定义的),调用它会返回一个协程对象,而command_prefix需要的是字符串、字符串列表,或者能返回这些类型的可调用函数(可以是异步的),不是协程本身。

  2. 错误地尝试重新构造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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 20:47:36