Python Discord机器人Blackjack命令报错:'bj'无to_components属性
问题描述
我正在用Python开发Discord机器人的二十一点(Blackjack)命令,采用分步编写、逐步测试优化的方式开发。但执行代码中的await interaction.response.send_message(embed=match, view=bj(match ,game_sum, player_cards))语句时,触发了AttributeError: 'bj' object has no attribute 'to_components'错误,导致命令无法正常运行。相关代码及报错信息如下:
原代码
import discord from discord.ext import commands from discord import app_commands import random, json, datetime class bj(discord.ui.Button): def __init__(self, game ,gamesum, player_cards): super().__init__() self.value = None @discord.ui.button (label="Hit", style=discord.ButtonStyle.blurple) async def confirm(self, button: discord.ui.Button, interaction: discord. Interaction): self.value = True self.stop() value_1=["A", 1,2,3,4,5,6,7,8,9,10,"J","Q","K",] nype_1=["♦️","♠️","♥️","♣️"] card_value_3 = random.randint(0, len(value_1) - 1) nype_card_3 = random.randint(0, len(nype_1) - 1) card_3 = f"{card_value_3[card_value_3]} {card_value_3[nype_card_3]}" await match.edit() match.edit_field(name='Your hand', value=f"{player_cards} {card_value_3}{nype_card_3}") if type(card_value_3)==str: if value_card_3=="A": value_letter_3=11 else: value_letter_3=10 else: value_letter_3=value_card_3 class client(discord.Client): def __init__(self): super().__init__(intents=discord.Intents.default()) self.synced = False async def setup_hook(self) -> None: self.add_view(DropdownView()) async def on_ready(self): await aclient.change_presence(activity = discord.Streaming(name = "/news", url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ")) await self.wait_until_ready() if not self.synced: await tree.sync() self.synced = True print(f"We entered as {self.user}!") print(f"That has the ID {self.user.id}") client = client() tree = app_commands.CommandTree(aclient) @tree.command(name='blackjack', description='Play a game of blackjack') async def blackjack(interaction: discord.Interaction, amount:int): await open_account(interaction.user) money = await add_money(interaction.user) if amount>money[0]: await interaction.response.send_message(f"You don't have all this amount {interaction.user.mention}.") else: if quantity<0: await interaction.response.send_message(f"You must enter a positive value") else: value_1=["A",2,3,4,5,6,7,8,9,10,'J','Q',"K"] nype_1=["♦️","♠️","♥️","♣️"] value_card_a = random.randint(0, len(value_1) - 1) nype_card_a = random.randint(0, len(nype_1) - 1) letter_a = f"{value_1[value_card_a]}{nype_1[nype_letter_a]}" b_card_value = random.randint(0, len(1_value) - 1) nype_card_b = random.randint(0, len(nype_1) - 1) letter_b = f"{value_1[value_card_b]}{nype_1[nype_letter_a]}" player_cards = f"{card_a} {card_b}" if type(value_card_a)==str: if value_card_a=="A": value_letter_a=11 else: value_letter_a=10 else: value_card_a=value_card_a if type(value_card_b)==str: if value_card_b=="A": value_letter_b=11 else: value_letter_b=10 else: value_card_b=value_card_b sum_of_game=value_card_a+value_card_b await add_money(interaction.user, -1*amount) if sum_of_game==21: result='Blackjack' titulo_resultado=f'You won ~~K~~ {1.5*amount}' else: result=sum_of_game titulo_resultado=f'You invested ~~K~~ {amount} in this blackjack' departure=discord.Embed( description=f'{result_title}', colour=discord.Color.random() ) match.add_field(name='Your hand', value=f"{player_cards}\{result}") if result=='Blackjack': await add_money(interaction.user, 1.5*amount) await interaction.response.send_message(embed=game, view=bj(game ,gamesum, player_cards)) async def open_account(user): users = await bank_data() if str(user.id) in users: return False else: users[str(user.id)] = {} users[str(user.id)]['wallet'] = 0 users[str(user.id)]['bank']= 0 with open("money.json", "w") as f: json.dump(users, f) return true async def bank_data(): with open("money.json", "r") as f: users = json.load(f) return users async def add_money(user, change=0, mode = "wallet"): users = await bank_data() users[str(user.id)][mode] +=change with open("money.json", "w") as f: json.dump(users, f) money = [users[str(user.id)]["wallet"], users[str(user.id)]["bank"]] return money aclient.run(Token)
报错信息
2023-02-06 19:14:31 ERROR discord.app_commands.tree Ignoring exception in command 'blackjack' Traceback (most recent call last): File "/home/runner/Kllin/venv/lib/python3.10/site-packages/discord/app_commands/commands.py", line 862, in _do_call return await self._callback(interaction, **params) # type: ignore File "main.py", line 1155, in blackjack await interaction.response.send_message(embed=partida, view=bj(partida ,soma_de_jogo, cartas_jogador)) File "/home/runner/Kllin/venv/lib/python3.10/site-packages/discord/interactions.py", line 754, in send_message params = interaction_message_response_params( File "/home/runner/Kllin/venv/lib/python3.10/site-packages/discord/webhook/async_.py", line 577, in interaction_message_response_params data['components'] = view.to_components() AttributeError: 'bj' object has no attribute 'to_components' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/home/runner/Kllin/venv/lib/python3.10/site-packages/discord/app_commands/tree.py", line 1242, in _call await command._invoke_with_namespace(interaction, namespace) File "/home/runner/Kllin/venv/lib/python3.10/site-packages/discord/app_commands/commands.py", line 887, in _invoke_with_namespace return await self._do_call(interaction, transformed_values) File "/home/runner/Kllin/venv/lib/python3.10/site-packages/discord/app_commands/commands.py", line 880, in _do_call raise CommandInvokeError(self, e) from e discord.app_commands.errors.CommandInvokeError: Command 'blackjack' raised an exception: AttributeError: 'bj' object has no attribute 'to_components'
问题原因与修复方案
核心错误原因
send_message方法的view参数要求传入**discord.ui.View类的实例**,但原代码中bj类继承自discord.ui.Button,单个Button组件不具备to_components()方法,因此触发AttributeError。所有交互组件(如Button、Select)必须嵌套在View容器中才能作为view参数使用。
此外,原代码还存在多处变量名不统一、未定义变量、逻辑错误,需要一并修正:
- 变量名混淆:比如
departure与match、game_sum与sum_of_game、letter_a与card_a等 - 未定义变量:
nype_letter_a、quantity、1_value等 - 逻辑错误:计算卡牌数值时直接使用索引而非对应值,字符串索引错误等
修正后的代码
import discord from discord import app_commands import random, json class BJView(discord.ui.View): def __init__(self, embed, player_cards, current_sum, bet_amount, user): super().__init__(timeout=60) self.embed = embed self.player_cards = player_cards self.current_sum = current_sum self.bet_amount = bet_amount self.user = user @discord.ui.button(label="Hit", style=discord.ButtonStyle.blurple) async def hit(self, button: discord.ui.Button, interaction: discord.Interaction): # 抽新牌 value_list = ["A", 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K"] suit_list = ["♦️", "♠️", "♥️", "♣️"] card_value_idx = random.randint(0, len(value_list)-1) card_suit_idx = random.randint(0, len(suit_list)-1) new_card = f"{value_list[card_value_idx]}{suit_list[card_suit_idx]}" # 计算新牌数值 if isinstance(value_list[card_value_idx], str): add_value = 11 if value_list[card_value_idx] == "A" else 10 else: add_value = value_list[card_value_idx] self.current_sum += add_value self.player_cards += f" {new_card}" # 更新Embed self.embed.set_field_at(0, name='Your hand', value=f"{self.player_cards}\n当前点数: {self.current_sum}") # 检查爆牌或Blackjack if self.current_sum > 21: self.embed.description = f"你爆牌了!输掉了~~K~~{self.bet_amount}" self.stop() elif self.current_sum == 21: self.embed.description = f"Blackjack!赢得了~~K~~{self.bet_amount * 1.5}" await add_money(self.user, self.bet_amount * 1.5) self.stop() await interaction.response.edit_message(embed=self.embed, view=self) @discord.ui.button(label="Stand", style=discord.ButtonStyle.green) async def stand(self, button: discord.ui.Button, interaction: discord.Interaction): # 后续可添加庄家逻辑,这里先简化 self.embed.description = f"你选择停牌,当前点数: {self.current_sum}" self.stop() await interaction.response.edit_message(embed=self.embed, view=self) class MyClient(discord.Client): def __init__(self): super().__init__(intents=discord.Intents.default()) self.synced = False async def on_ready(self): await self.change_presence(activity=discord.Streaming(name="/news", url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")) await self.wait_until_ready() if not self.synced: await tree.sync() self.synced = True print(f"已登录为 {self.user}\n用户ID: {self.user.id}") client = MyClient() tree = app_commands.CommandTree(client) @tree.command(name='blackjack', description='Play a game of blackjack') async def blackjack(interaction: discord.Interaction, amount: int): await open_account(interaction.user) money_data = await add_money(interaction.user) wallet_balance = money_data[0] if amount <= 0: await interaction.response.send_message(f"{interaction.user.mention},你必须输入正数金额!") return if amount > wallet_balance: await interaction.response.send_message(f"{interaction.user.mention},你的余额不足!当前钱包余额: ~~K~~{wallet_balance}") return # 扣除下注金额 await add_money(interaction.user, -amount) # 初始化玩家手牌 value_list = ["A", 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K"] suit_list = ["♦️", "♠️", "♥️", "♣️"] # 第一张牌 card1_idx = random.randint(0, len(value_list)-1) suit1_idx = random.randint(0, len(suit_list)-1) card1 = f"{value_list[card1_idx]}{suit_list[suit1_idx]}" # 计算牌值 if isinstance(value_list[card1_idx], str): val1 = 11 if value_list[card1_idx] == "A" else 10 else: val1 = value_list[card1_idx] # 第二张牌 card2_idx = random.randint(0, len(value_list)-1) suit2_idx = random.randint(0, len(suit_list)-1) card2 = f"{value_list[card2_idx]}{suit_list[suit2_idx]}" if isinstance(value_list[card2_idx], str): val2 = 11 if value_list[card2_idx] == "A" else 10 else: val2 = value_list[card2_idx] player_cards = f"{card1} {card2}" total_sum = val1 + val2 # 创建Embed if total_sum == 21: embed_desc = f"Blackjack!赢得了~~K~~{amount * 1.5}" await add_money(interaction.user, amount * 1.5) view = None else: embed_desc = f"你下注了~~K~~{amount},开始游戏!" view = BJView(embed=None, player_cards=player_cards, current_sum=total_sum, bet_amount=amount, user=interaction.user) game_embed = discord.Embed(description=embed_desc, colour=discord.Color.random()) game_embed.add_field(name='Your hand', value=f"{player_cards}\n当前点数: {total_sum}") if view: view.embed = game_embed await interaction.response.send_message(embed=game_embed, view=view) else: await interaction.response.send_message(embed=game_embed) async def open_account(user): users = await bank_data() if str(user.id) in users: return False users[str(user.id)] = {'wallet': 0, 'bank': 0} with open("money.json", "w") as f: json.dump(users, f) return True async def bank_data(): try: with open("money.json", "r") as f: users = json.load(f) except FileNotFoundError: users = {} return users async def add_money(user, change=0, mode="wallet"): users = await bank_data() users[str(user.id)][mode] += change with open("money.json", "w") as f: json.dump(users, f) return [users[str(user.id)]["wallet"], users[str(user.id)]["bank"]] # 替换为你的机器人Token client.run("YOUR_BOT_TOKEN")
关键修复点说明
- View容器改造:将原
bj类改为继承discord.ui.View的BJView,并在其中定义Hit和Stand按钮,符合Discord组件的使用规范。 - 变量名统一:修正所有变量名混淆问题,确保变量引用一致。
- 逻辑修正:修复卡牌数值计算逻辑,正确使用卡牌列表中的值而非索引;添加爆牌、停牌的基础逻辑。
- 错误处理:增加钱包文件不存在时的默认处理,避免读取文件报错。
内容的提问来源于stack exchange,提问作者Luca
相关产品推荐
相关产品推荐

