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

Discord机器人启动报KeyError: 'Masters',求解决方案

Discord机器人启动报错KeyError: 'Masters'解决办法

问题描述

我是编程新手,正在开发一款Discord机器人,但启动时持续遇到问题。报错信息显示main.py第19行读取allData['Masters']时触发KeyError: 'Masters'。我尝试修改部分参数但未解决问题,希望能得到帮助,或了解是否有其他方式替换Masters相关逻辑来运行机器人。

机器人代码(main.py)

#cython: language_level=3

import data
import config
import discord
import userCommands
import adminCommands

from discordHelper import newEmbed, errorMessage, RED, BLUE, GREEN, YELLOW

PREFIX = '+'


class DiscordBot(discord.Client):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        allData = data.loadJSON(data.DATABASE_FILENAME)
        self.masterIDs = allData['Masters']
        self.staffIDs = allData['Staff']

    async def on_ready(self):
        '''
            Called when the discord bot logs in
        '''
        print(f'{self.user.name} Logged In!')
        print('--------------------\n')

    async def on_reaction_add(self, reaction: discord.Reaction, user: discord.User):
        # Unsubscribe from vouch notifications
        description = reaction.message.embeds[0].description
        if 'Received a' in description and reaction.emoji == '❌':
            noNotifs = data.loadJSON(data.DATABASE_FILENAME)[
                'NoNotificationIDs']
            if user.id not in noNotifs:
                embed = newEmbed(
                    description='Unsubscribed from notifications!')
                embed.set_footer(
                    text='To resubscribe, react with ✅')
                noNotifs.append(user.id)
                data.updateJson(data.DATABASE_FILENAME, {
                                'NoNotificationIDs': noNotifs})
                await user.send(embed=embed)

        # Resubscribe to vouch notifications
        elif 'Unsubscribed' in description and reaction.emoji == '✅':
            noNotifs = data.loadJSON(data.DATABASE_FILENAME)[
                'NoNotificationIDs']
            if user.id in noNotifs:
                embed = newEmbed(description='Resubscribed to notifications!')
                embed.set_footer(text='To unsubscribe, react with ❌')
                noNotifs.remove(user.id)
                data.updateJson(data.DATABASE_FILENAME, {
                                'NoNotificationIDs': noNotifs})
                await user.send(embed=embed)

    async def on_message(self, message: discord.Message):
        '''
            Handles all the discord commands
        '''
        # Make sure we don't respond to ourselves
        if message.author == self.user:
            return

        isMaster = message.author.id in self.masterIDs
        isStaff = message.author.id in self.staffIDs or isMaster
        loweredMsg = message.content.lower()
        words = message.content.split()

        # =====================================================

        if loweredMsg.startswith('+vouch') or loweredMsg.startswith('-vouch'):
            if len(message.mentions) == 0 or len(words) < 3:
                await errorMessage('Please follow this format: [+ or -]vouch [@user] [message]',
                                   message.channel)
                return

            if message.author.id == message.mentions[0].id:
                await errorMessage('You cannot vouch for yourself.', message.channel)
                return

            vouchMessage = ' '.join(words[2:])
            isPositive = loweredMsg[0] == '+'
            pendingChannel = self.get_channel(config.PENDING_VOUCHES_CHANNELID)
            await userCommands.vouch(message.author,
                                     message.mentions[0],
                                     vouchMessage,
                                     isPositive,
                                     message.channel,
                                     pendingChannel)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}dwc') and isMaster:
            if 'dwc1' in loweredMsg:
                level = 1
            elif 'dwc2' in loweredMsg:
                level = 2
            elif 'dwc3' in loweredMsg:
                level = 3
            else:
                level = 0

            if len(message.mentions) == 0 or (level != 0 and len(words) < 3):
                await errorMessage(f'Please follow this format: {PREFIX}dwc [@user] [reason]',
                                   message.channel)
                return

            reason = ' '.join(words[2:]) if level != 0 else ''
            await adminCommands.dwc(message.mentions[0], level, reason, message.channel)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}scammer') and isMaster:
            if len(message.mentions) == 0:
                await errorMessage(f'Please follow this format: {PREFIX}scammer [@user]',
                                   message.channel)
                return

            await adminCommands.scammer(message.mentions[0], message.channel)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}pending') and isStaff:
            await adminCommands.pending(message.channel, self.get_user)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}leaderboard'):
            await userCommands.leaderboard(message.channel, self.get_user, self.user.avatar_url)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}reply') and isStaff:
            if len(words) < 3 or not words[1].isdigit():
                await errorMessage(f'Please follow this format: {PREFIX}reply [vouch id] [message]',
                                   message.channel)
                return

            targetUser = None
            try:
                vouchID = int(words[1])
                pending = data.loadJSON(data.DATABASE_FILENAME)['PendingVouches']
                for i in pending:
                    if i['ID'] == vouchID:
                        targetUser = self.get_user(i['Giver'])
                        break
                else:
                    raise Exception('User not found')

            except Exception as e:
                print(e)
                await errorMessage(f'Could not find user with ID {vouchID}',
                                   message.channel)
                return

            replyMsg = ' '.join(words[2:])
            await adminCommands.reply(targetUser, replyMsg, message.channel)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}remove') and isMaster:
            if len(message.mentions) == 0 or len(words) < 2:
                await errorMessage(f'Please follow this format:\n{PREFIX}remove [@user]\n**or**\n{PREFIX}remove [@user] [vouch ID]',
                                   message.channel)
                return

            vouchNum = -1
            if len(words) >= 3 and words[2].isdigit():
                vouchNum = int(words[2])
                if vouchNum < 0:
                    await errorMessage('Vouch ID cannot be negative!', message.channel)
                    return

            await adminCommands.remove(message.mentions[0], message.channel, vouchNum)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}verify') and isMaster:
            if len(message.mentions) == 0:
                await errorMessage(f'Please follow this format: {PREFIX}verify [@user]',
                                   message.channel)
                return

            await adminCommands.verify(message.mentions[0], message.channel)

        # =====================================================

        elif (loweredMsg.startswith('+add') or loweredMsg.startswith('-add')) and isMaster:
            if len(message.mentions) == 0 or len(words) < 3:
                await errorMessage(f'Please follow this format: [+ or -]add [@user] [giverID (optional)] [message]',
                                   message.channel)
                return

            # Check if they specify the giver ID
            hasGiverID = False
            giverID = 0
            if words[2].isdigit():
                hasGiverID = True
                giverID = int(words[2])

            vouchMessage = ' '.join(
                words[3:]) if hasGiverID else ' '.join(words[2:])
            isPositive = loweredMsg[0] == '+'
            logChannel = self.get_channel(config.LOG_CHANNEL_ID)
            await adminCommands.add(message.author,
                                    message.mentions[0],
                                    vouchMessage,
                                    isPositive,
                                    message.channel,
                                    logChannel,
                                    giverID)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}token'):
            await userCommands.token(message.author, message.channel)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}profile'):
            if len(message.mentions) == 0:
                user = message.author
            else:
                user = message.mentions[0]

            await userCommands.profile(
                targetUser=user,
                bcGuild=self.get_guild(583416004974084107),
                channel=message.channel
            )

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}link'):
            if len(words) < 2:
                await errorMessage(
                    f'Please follow this format: {PREFIX}link [https://nulled.to/ link]',
                    message.channel)
                return

            if 'https://nulled.to/' not in words[1]:
                await errorMessage(
                    'Please provide a proper nulled.to link!',
                    message.channel)
                return

            await userCommands.link(message.author, words[1], message.channel)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}redeem'):
            if len(words) <= 1:
                await errorMessage(f'Please follow this format: {PREFIX}redeem [token]',
                                   message.channel)
                return

            await userCommands.redeem(message.author, words[1],
                                      message.channel, self.logChannel)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}admin') and isMaster:
            if len(message.mentions) == 0:
                await errorMessage(f'Please follow this format: {PREFIX}admin [@user]',
                                   message.channel)
                return

            await adminCommands.admin(message.mentions[0], message.channel)
            if message.mentions[0].id in self.masterIDs:
                self.masterIDs.remove(message.mentions[0].id)
            else:
                self.masterIDs.append(message.mentions[0].id)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}staff') and isMaster:
            if len(message.mentions) == 0:
                await errorMessage(f'Please follow this format: {PREFIX}staff [@user]',
                                   message.channel)
                return

            await adminCommands.staff(message.mentions[0], message.channel)
            if message.mentions[0].id in self.staffIDs:
                self.staffIDs.remove(message.mentions[0].id)
            else:
                self.staffIDs.append(message.mentions[0].id)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}blacklist') and isMaster:
            if len(message.mentions) == 0:
                if len(words) >= 2 and not words[1].isdigit():
                    await errorMessage(f'Please follow this format: {PREFIX}blacklist [@user]',
                                       message.channel)
                    return

            if len(message.mentions) != 0:
                id = message.mentions[0].id
            else:
                id = int(words[1])

            await adminCommands.blacklist(id, message.channel)

        # =====================================================

        elif (loweredMsg.startswith(f'{PREFIX}approve') or
              loweredMsg.startswith(f'{PREFIX}accept')) and isStaff:
            if len(words) < 2 or not words[1].isdigit():
                await errorMessage(f'Please follow this format: {PREFIX}approve [vouch ID]',
                                   message.channel)
                return

            ids = [int(i) for i in words[1:] if i.isdigit()]
            logChannel = self.get_channel(config.LOG_CHANNEL_ID)
            for i in ids:
                await adminCommands.approve(i, message.channel,
                                            logChannel, self.get_user)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}deny') and isStaff:
            if len(words) < 2 or not words[1].isdigit():
                await errorMessage(f'Please follow this format: {PREFIX}deny [vouch ID]',
                                   message.channel)
                return

            ids = [int(i) for i in words[1:] if i.isdigit()]
            for i in ids:
                await adminCommands.deny(i, message.channel)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}help'):
            await userCommands.help(PREFIX,
                                    message.channel,
                                    isMaster)

        # =====================================================

        elif loweredMsg.startswith(f'{PREFIX}about'):
            await userCommands.about(message.channel, self.user.avatar_url)

        # =====================================================


def main():
    DiscordBot().run(config.DISCORD_TOKEN)


if __name__ == '__main__':
    main()

config.py代码

DISCORD_TOKEN = 'OwOD'
PENDING_VOUCHES_CHANNELID = 105159912
LOG_CHANNEL_ID = 1051599

错误日志

File "c:\Users\ne\Desktop\test bot\main.py", line 348, in main
    DiscordBot().run(config.DISCORD_TOKEN)
  File "c:\Users\ne\Desktop\test bot\main.py", line 19, in __init__
    self.masterIDs = allData['Masters']
KeyError: 'Masters'

问题根源

你的机器人通过data.loadJSON(data.DATABASE_FILENAME)加载JSON数据库文件,但该文件中不存在Masters和Staff键,或者文件本身不存在、内容为空,导致读取时触发KeyError。

解决方法

1. 初始化数据库JSON文件

找到data.DATABASE_FILENAME指向的文件(一般是database.json),如果不存在就新建一个,写入以下基础结构:

{
  "Masters": [],
  "Staff": [],
  "NoNotificationIDs": [],
  "PendingVouches": []
}
  • 可以把你的Discord用户ID添加到Masters数组中(比如"Masters": [123456789012345678]),这样就能使用机器人的管理员命令了。

2. 在代码中添加容错处理

修改DiscordBot类的__init__方法,使用字典的get方法避免KeyError,同时设置默认空列表:

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)

    allData = data.loadJSON(data.DATABASE_FILENAME)
    # 用get方法获取,不存在则返回空列表
    self.masterIDs = allData.get('Masters', [])
    self.staffIDs = allData.get('Staff', [])

这样即使数据库里没有对应键,机器人也能正常启动,后续可以通过+admin命令添加管理员账号。

3. 临时跳过权限逻辑(仅用于测试)

如果只是想先让机器人跑起来测试普通用户功能,可以直接硬编码空列表:

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)

    # 临时替换为空白列表,后续再接入数据库
    self.masterIDs = []
    self.staffIDs = []

注意:这种方式下所有管理员命令都会无法使用,适合先测试+vouch、+profile等普通功能,之后再补全权限逻辑。

额外提示

  • 检查data.py中的loadJSON和updateJson函数,确保它们能正确读取和写入JSON文件,避免因路径错误导致加载失败。
  • 你的config.py中DISCORD_TOKEN是占位符'OwOD',记得替换为真实的Discord机器人令牌,否则机器人无法登录Discord服务器。

内容的提问来源于stack exchange,提问作者coderk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 07:40:33