Discord.net/Discord.py录制用户/频道音频流失败的解决方案咨询
问题
尝试录制Discord特定用户的音频,无法实现则录制整个语音频道。最初使用Discord.py未成功,现在用C#开发的机器人会在特定用户加入频道时自动加入并启动录制,但参考旧帖子和GitHub线程的方案已失效,怀疑Discord是否移除了音频流获取支持?
当前C#代码中,var bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length, _cts.Token);一行一直阻塞无返回值,音频流始终没有数据。求C#或Python的可行解决方案。
用户提供的C#代码:
private Task OnVoiceStateUpdated(SocketUser? socketUser, SocketVoiceState before, SocketVoiceState after) { _ = Task.Run(async () => { try { Logger.Output($"Starting to handle user movements", Logger.LogType.Default); var currentChannel = after.VoiceChannel ?? before.VoiceChannel; if (currentChannel == null) return; var botUser = currentChannel.Guild.Users.FirstOrDefault(u => u.IsBot); if (before.VoiceChannel == null && after.VoiceChannel != null) // If member joins a voice channel { var channel = after.VoiceChannel; try { Logger.Output($"Connect bot to channel '{channel.Name}'", Logger.LogType.Default); await Task.Delay(1000); _audioClient = await channel.ConnectAsync(); _cts = new CancellationTokenSource(); if (socketUser is not SocketGuildUser socketGuildUser) return; if (socketGuildUser.AudioStream is not InputStream inputStream) return; _ = Task.Run(() => ProcessAudioAsync(inputStream) ); } catch (Exception e) { Logger.Output($"Failed to connect and start recording: {e}", Logger.LogType.Error); } } } catch (Exception e) { Logger.Output($"Handling user movements failed! {e.Message} ", Logger.LogType.Error); } }); return Task.CompletedTask; } private async Task ProcessAudioAsync(InputStream inputStream) { // Create a new wave file writer to save the audio stream string currentTime = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); string filename = Path.Combine(AudioRecordingsPath, $"output_{currentTime}.wav"); // Create the wave file writer to save the audio stream _writer = new WaveFileWriter(filename, new WaveFormat(48000, 16, 2)); try { var buffer = new byte[81920]; // Buffer size can be adjusted as needed while (!_cts.IsCancellationRequested) { var bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length, _cts.Token); // If bytesRead is zero, end of the stream has been reached if (bytesRead == 0) { Task.Delay(1000).Wait(); continue; } // Write from buffer to wave file await _writer.WriteAsync(buffer, 0, bytesRead); // Flush the writer to ensure data is written to disk await _writer.FlushAsync(); } } catch (OperationCanceledException e) { Logger.Output($"Audio processing was cancelled. {e.Message} {e.StackTrace}", Logger.LogType.Error); } catch (TimeoutException e) { Logger.Output($"Audio processing timed out. {e.Message} {e.StackTrace}", Logger.LogType.Error); } catch (InvalidOperationException e) { Logger.Output($"Audio processing failed due to stream issue. {e.Message} {e.StackTrace}", Logger.LogType.Error); } catch (Exception e) { Logger.Output($"Audio processing failed. {e.Message} {e.StackTrace}", Logger.LogType.Error); } finally { await inputStream.FlushAsync(); } }
解决方案
C# 基于DSharpPlus的可行方案
Discord.NET的旧音频流获取方式已被废弃,目前推荐使用DSharpPlus的VoiceNext扩展实现音频捕获,步骤如下:
- 安装依赖包:
Install-Package DSharpPlus Install-Package DSharpPlus.VoiceNext
- 核心实现代码:
using DSharpPlus; using DSharpPlus.VoiceNext; using NAudio.Wave; using System.IO; private async Task OnVoiceStateUpdated(DiscordClient client, VoiceStateUpdateEventArgs e) { // 替换为目标用户的ID ulong targetUserId = 1234567890; if (e.User.Id != targetUserId) return; // 用户加入语音频道时,机器人自动连接并开始捕获 if (e.Before.Channel == null && e.After.Channel != null) { var voiceNext = client.GetVoiceNext(); var connection = await voiceNext.ConnectAsync(e.After.Channel); // 注册音频接收事件 connection.AudioReceived += async (s, audioArgs) => { // 仅捕获目标用户的音频 if (audioArgs.User.Id != targetUserId) return; // 生成保存路径 string recordingsDir = Path.Combine(AppContext.BaseDirectory, "audio_recordings"); Directory.CreateDirectory(recordingsDir); string filePath = Path.Combine(recordingsDir, $"{e.User.Username}_{DateTime.Now:yyyyMMddHHmmss}.wav"); // 将PCM数据写入WAV文件 using var writer = new WaveFileWriter(filePath, new WaveFormat(48000, 16, 2)); await writer.WriteAsync(audioArgs.PcmData, 0, audioArgs.PcmData.Length); await writer.FlushAsync(); }; } }
关键说明:
- VoiceNext是DSharpPlus官方维护的音频模块,支持最新Discord音频协议
AudioReceived事件会主动推送每个说话用户的PCM音频数据,无需手动读取流,避免阻塞问题- 可通过
audioArgs.User.Id精准过滤目标用户的音频
Python 基于discord.py的可行方案
Discord.py最新版本仍支持音频捕获,此前失败大概率是未正确配置Intents或音频监听逻辑,核心实现:
- 安装依赖:
pip install discord.py[voice]
- 核心代码:
import discord from discord.ext import commands import wave import os from datetime import datetime # 配置机器人 intents = discord.Intents.all() bot = commands.Bot(command_prefix="!", intents=intents) # 目标用户ID和录制目录 TARGET_USER_ID = 1234567890 RECORD_DIR = "discord_recordings" os.makedirs(RECORD_DIR, exist_ok=True) @bot.event async def on_voice_state_update(member, before, after): if member.id != TARGET_USER_ID: return # 用户加入频道时,机器人连接并开始录制 if before.channel is None and after.channel is not None: voice_client = await after.channel.connect() def audio_callback(user, data): # 仅处理目标用户的音频 if user.id != TARGET_USER_ID: return # 生成文件名并写入WAV file_name = f"{member.name}_{datetime.now().strftime('%Y%m%d%H%M%S')}.wav" file_path = os.path.join(RECORD_DIR, file_name) with wave.open(file_path, 'wb') as wf: wf.setnchannels(2) wf.setsampwidth(2) wf.setframerate(48000) wf.writeframes(data) # 注册音频监听回调 voice_client.listen(audio_callback) # 启动机器人,替换为你的Bot Token bot.run("YOUR_DISCORD_BOT_TOKEN")
关键说明:
- 必须启用
Intents.all(),尤其是语音相关的权限,否则无法获取语音状态更新和音频数据 voice_client.listen()会自动监听频道内所有用户的音频,通过回调参数user可过滤目标用户- 直接写入PCM数据到WAV文件,无需处理流阻塞问题
内容的提问来源于stack exchange,提问作者Shazy
相关产品推荐
相关产品推荐

