discord.py 2.1.0机器人语音频道接入问题及代码现代化咨询
Discord.py 2.1.0 语音频道功能问题及现代写法指南
问题描述
我为自己的Discord服务器开发了一款机器人,除语音频道接入功能外,其余功能(如发送图片)均正常运行。但我无法实现机器人加入语音频道的功能,查阅资料时发现discord.py的使用方式众说纷纭。当前我使用的是discord.py 2.1.0版本,代码写法与示例差异较大,怀疑使用了过时方法。现咨询:
- 当前开发此类机器人的标准写法是什么?
- 如何将代码升级为现代写法?
- 是否需要升级到discord.py rewrite?
附当前代码:
import os import discord import nacl from dotenv import load_dotenv load_dotenv() TOKEN = os.getenv('DISCORD_TOKEN') GUILD = os.getenv('DISCORD_GUILD') intents = discord.Intents.all() client = discord.Client(intents=intents) user = client.get_user(X) voiceChannel = client.get_channel(X) intents.message_content = True # Sends info in the terminal on connection @client.event async def on_ready(): for guild in client.guilds: if guild.name == GUILD: break print( f'{client.user} is connected to the following guild:\n' f'{guild.name}(id: {guild.id})' ) @client.event async def on_message(message): if message.author == client.user: return # Sends a picture of a Tarkov map when a command is sent in chat if message.content.startswith('.customs'): await message.channel.send(file=discord.File('/home/container/Bot/tarkovmaps/customs.png')) # Sends a picture of Tarkov ammo stats when a command is sent in chat if message.content.startswith('.4.6x30'): await message.channel.send(file=discord.File('/home/container/Bot/tarkovammo/4.6x30.png')) if message.content.startswith('.5.7x28'): await message.channel.send(file=discord.File('/home/container/Bot/tarkovammo/5.7x28.png')) #There are more commands like the ones above and they all work #Makes the bot join a voice channel and say stuff when a command is sent; doesn't work if message.content.startswith('.join'): #await client.join_voice_channel(voiceChannel) await voiceChannel.connect() print('Bot joined the channel.') #This format didn't work either if message.contents.startswith('.leave'): await client.leave_voice_channel(voiceChannel) print('Bot left the channel.') client.run(TOKEN)
解答
1. 关于discord.py rewrite的疑问
discord.py rewrite就是官方的v2.x分支,你当前使用的2.1.0版本已经属于rewrite版本,不需要额外升级。旧版的v1.x写法才是过时的,你的核心问题是代码中的错误和过时API的使用。
2. 当前代码的核心问题
- 提前调用客户端方法:在
client.run(TOKEN)之前调用client.get_user(X)和client.get_channel(X),此时客户端还未连接到Discord,这些方法会返回None,导致后续voiceChannel.connect()调用失败。 - 拼写错误:
message.contents.startswith('.leave')中的contents应为content。 - 过时的语音API:
client.join_voice_channel()和client.leave_voice_channel()是v1.x的方法,v2.x中已移除,需使用VoiceChannel.connect()和VoiceClient.disconnect()。
3. 现代写法改造方案
方案一:基于原有on_message事件修复
import os import discord import nacl from dotenv import load_dotenv load_dotenv() TOKEN = os.getenv('DISCORD_TOKEN') GUILD = os.getenv('DISCORD_GUILD') # 提前定义频道ID,不要提前调用get_channel VOICE_CHANNEL_ID = X # 替换成你的语音频道ID intents = discord.Intents.all() intents.message_content = True # 启用消息内容意图,要放在client初始化前 client = discord.Client(intents=intents) @client.event async def on_ready(): for guild in client.guilds: if guild.name == GUILD: break print( f'{client.user} is connected to the following guild:\n' f'{guild.name}(id: {guild.id})' ) @client.event async def on_message(message): if message.author == client.user: return # 地图和弹药命令保留 if message.content.startswith('.customs'): await message.channel.send(file=discord.File('/home/container/Bot/tarkovmaps/customs.png')) if message.content.startswith('.4.6x30'): await message.channel.send(file=discord.File('/home/container/Bot/tarkovammo/4.6x30.png')) if message.content.startswith('.5.7x28'): await message.channel.send(file=discord.File('/home/container/Bot/tarkovammo/5.7x28.png')) # 修复后的.join命令 if message.content.startswith('.join'): # 客户端连接后再获取频道 voice_channel = client.get_channel(VOICE_CHANNEL_ID) if voice_channel and isinstance(voice_channel, discord.VoiceChannel): # 检查是否已经在语音频道中 if message.guild.voice_client is None: await voice_channel.connect() print('Bot joined the channel.') else: await message.channel.send("Bot已经在语音频道中了") else: await message.channel.send("指定的语音频道不存在") # 修复后的.leave命令 if message.content.startswith('.leave'): voice_client = message.guild.voice_client if voice_client and voice_client.is_connected(): await voice_client.disconnect() print('Bot left the channel.') else: await message.channel.send("Bot不在任何语音频道中") client.run(TOKEN)
方案二:使用discord.ext.commands框架(推荐)
这是discord.py官方推荐的现代命令开发方式,更简洁易维护:
import os import discord from discord.ext import commands import nacl from dotenv import load_dotenv load_dotenv() TOKEN = os.getenv('DISCORD_TOKEN') GUILD = os.getenv('DISCORD_GUILD') VOICE_CHANNEL_ID = X # 替换成你的语音频道ID # 定义命令前缀,启用必要的意图 intents = discord.Intents.all() intents.message_content = True bot = commands.Bot(command_prefix='.', intents=intents) @bot.event async def on_ready(): for guild in bot.guilds: if guild.name == GUILD: break print( f'{bot.user} is connected to the following guild:\n' f'{guild.name}(id: {guild.id})' ) # 地图命令 @bot.command() async def customs(ctx): await ctx.send(file=discord.File('/home/container/Bot/tarkovmaps/customs.png')) # 弹药命令 @bot.command() async def _46x30(ctx): await ctx.send(file=discord.File('/home/container/Bot/tarkovammo/4.6x30.png')) @bot.command() async def _57x28(ctx): await ctx.send(file=discord.File('/home/container/Bot/tarkovammo/5.7x28.png')) # 语音频道加入命令 @bot.command() async def join(ctx): voice_channel = bot.get_channel(VOICE_CHANNEL_ID) if not voice_channel or not isinstance(voice_channel, discord.VoiceChannel): await ctx.send("指定的语音频道不存在") return if ctx.voice_client is None: await voice_channel.connect() await ctx.send("Bot已加入语音频道") else: await ctx.send("Bot已经在语音频道中了") # 语音频道离开命令 @bot.command() async def leave(ctx): if ctx.voice_client and ctx.voice_client.is_connected(): await ctx.voice_client.disconnect() await ctx.send("Bot已离开语音频道") else: await ctx.send("Bot不在任何语音频道中") bot.run(TOKEN)
关键注意事项
- 意图设置:确保在Discord开发者门户中启用了
Message Content Intent,否则机器人无法读取消息内容。 - 依赖安装:确保安装了
nacl(pip install pynacl),这是语音功能必需的依赖。 - 频道ID获取:在Discord中开启开发者模式后,右键点击频道即可复制ID。
内容的提问来源于stack exchange,提问作者Jordan Roark
相关产品推荐
相关产品推荐

