Discord.Net音频机器人遇System.TimeoutException超时问题求助
Discord.Net开发机器人执行play命令触发TimeoutException求助
执行play命令播放音频时触发以下错误:
System.TimeoutException: The operation has timed out. at Discord.WebSocket.SocketGuild.<ConnectAudioAsync>d__349.MoveNext()
已确认使用最新版本的Discord.Net,搜索相关解决方案但未找到有效办法,以下是核心代码:
public async Task<bool> ExecuteCommand(SocketMessage message, string cmd, string[] args) { // Check if command is on cooldown if (_cooldowns.ContainsKey(cmd)) { var cooldownEnd = _cooldowns[cmd].AddSeconds(_commandCooldowns[cmd]); if (DateTime.UtcNow < cooldownEnd) { await message.Channel.SendMessageAsync($"Command \"{cmd}\" is on cooldown. Please wait {(cooldownEnd - DateTime.UtcNow).TotalSeconds:F1} seconds."); return false; } } // Check if command has reached its limit if (_commandLimits.ContainsKey(cmd)) { if (!_commandUsages.ContainsKey(cmd)) _commandUsages[cmd] = 0; if (_commandUsages[cmd] >= _commandLimits[cmd]) { await message.Channel.SendMessageAsync($"Command \"{cmd}\" has reached its usage limit."); return false; } _commandUsages[cmd]++; } // Execute the command await _commands[cmd](message, args); // Set cooldown if (_commandCooldowns.ContainsKey(cmd)) _cooldowns[cmd] = DateTime.UtcNow; return true; } public async Task OnMessageReceived(SocketMessage message) { // Ignore messages from bots and system messages if (message.Author.IsBot || message.Author.IsWebhook) return; if (message.Content.StartsWith(_prefix)) { // Remove the prefix from the message string command = message.Content.Substring(_prefix.Length); // Split the message into words string[] words = command.Split(' '); // The first word is the command string cmd = words[0]; // The rest of the words are the arguments string[] args = words.Skip(1).ToArray(); // Handle the command await ExecuteCommand(message, cmd, args); } } public void RegisterCommand(string command, Func<SocketMessage, string[], Task> action, int limit = 0, int cooldown = 0) { _commands[command] = action; if (limit > 0) _commandLimits[command] = limit; if (cooldown > 0) _commandCooldowns[command] = cooldown; } RegisterCommand("play", async (message, args) => { if (args.Length < 1) { await bot.SendMessage(message.Channel.Id, "Usage: !play <url>"); return; } var url = args[0]; try { bot._audioClient = await (message.Author as IVoiceState).VoiceChannel.ConnectAsync(); var stream = bot.CreateStream(url); var output = bot._audioClient.CreatePCMStream(AudioApplication.Music); await stream.StandardOutput.BaseStream.CopyToAsync(output); await output.FlushAsync(); } catch (Exception ex) { await bot.SendMessage(message.Channel.Id, "Error: " + ex.ToString()); } }); bot.RegisterCommand("stop", async (message, args) => { if (bot._audioClient != null) { await bot._audioClient.StopAsync(); } });
排查与解决建议
- 验证语音权限与网络:确保机器人在目标语音频道拥有
连接和发言权限,同时检查服务器网络是否允许Discord语音UDP端口(50000-65535)的通信,部分防火墙可能会拦截这类端口。 - 复用现有语音连接:当前代码每次执行
play都会重新调用ConnectAsync,如果机器人已经在语音频道中,重复连接会引发冲突导致超时。可以先判断bot._audioClient是否处于有效连接状态,再决定是否创建新连接:if (bot._audioClient == null || !bot._audioClient.ConnectionState.Equals(ConnectionState.Connected)) { bot._audioClient = await (message.Author as IVoiceState).VoiceChannel.ConnectAsync(); } - 调整连接超时参数:给
ConnectAsync设置更长的超时时间,避免因网络延迟导致超时:bot._audioClient = await (message.Author as IVoiceState).VoiceChannel.ConnectAsync(timeout: TimeSpan.FromSeconds(30)); - 清理失效的语音客户端:在
stop命令中,调用StopAsync后需要将bot._audioClient置为null,避免下次play时尝试使用已失效的客户端:bot.RegisterCommand("stop", async (message, args) => { if (bot._audioClient != null) { await bot._audioClient.StopAsync(); bot._audioClient = null; } }); - 检查音频流创建逻辑:
CreateStream方法如果同步阻塞或耗时过长,可能导致连接后等待流的过程中触发超时,确保该方法是异步实现且高效的,避免阻塞主线程。
内容的提问来源于stack exchange,提问作者ThatFriendlyAsianDev
相关产品推荐
相关产品推荐

