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

Discord.py+Pymongo:跨服务器用户角色存储及报错解决

问题分析
  1. 存在性判断逻辑失效:cursor.find(...)返回的是MongoDB游标对象,哪怕没有匹配数据也永远不会等于None,导致你的判断逻辑完全错误,本该执行更新的场景却触发了插入操作。
  2. 全局唯一索引冲突:你用用户ID作为文档_id字段值,而MongoDB的_id是全局唯一的,同一用户在不同服务器执行插入时会触发重复键错误,也就是你收到的“用户已存在”提示。
  3. 服务器维度未正确隔离:需要确保每个文档对应一个用户在单个服务器的角色数据,唯一标识必须结合服务器ID和用户ID。
解决方案

方案1:复合唯一索引+Upsert简化操作(推荐)

先给MongoDB集合创建guild_id和_id的复合唯一索引(只需在Bot启动时执行一次):

# 初始化集合时执行
cursor.create_index([("guild_id", 1), ("_id", 1)], unique=True)

然后用update_one配合upsert=True,一步完成“存在则更新,不存在则插入”的逻辑:

# 获取当前服务器、用户信息
guild_id = self.ctx.guild.id
user_id = self.ctx.author.id
user_name = self.ctx.author.name

# 查询条件:服务器ID + 用户ID,确保按服务器维度隔离
query = {"guild_id": guild_id, "_id": user_id}
# 要更新/插入的数据
update_content = {
    "$set": {
        "name": user_name,
        "roles": [str(r.id) for r in self.ctx.author.roles[1:]]
    }
}

# upsert=True:匹配到则更新,未匹配到则插入新文档
cursor.update_one(query, update_content, upsert=True)

方案2:组合ID作为文档唯一标识

如果不想创建复合索引,可以把文档_id设为服务器ID+用户ID的组合字符串,确保每个服务器的用户数据都是独立文档:

guild_id = self.ctx.guild.id
user_id = self.ctx.author.id
# 生成唯一文档ID
doc_unique_id = f"{guild_id}_{user_id}"
user_name = self.ctx.author.name

query = {"_id": doc_unique_id}
update_content = {
    "$set": {
        "guild_id": guild_id,
        "user_id": user_id,
        "name": user_name,
        "roles": [str(r.id) for r in self.ctx.author.roles[1:]]
    }
}

cursor.update_one(query, update_content, upsert=True)

关键说明

  • 获取命令所属服务器:直接调用self.ctx.guild.id即可,ctx.guild就是命令执行所在的服务器对象。
  • self.ctx.author.roles[1:]跳过了@everyone角色,这个逻辑如果符合你的需求可以保留,若需要包含所有角色则去掉[1:]。

内容的提问来源于stack exchange,提问作者Dr. Troll

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 00:55:21