using System; using System.Threading.Tasks; using Ayu.Discord.Voice; using Ayu.Discord.Voice.Models; using Serilog; namespace NadekoBot.Modules.Music { public sealed class VoiceProxy : IVoiceProxy { public enum VoiceProxyState { Created, Started, Stopped } private const int MAX_ERROR_COUNT = 20; private const int DELAY_ON_ERROR_MILISECONDS = 200; public VoiceProxyState State => _gateway switch { {Started: true, Stopped: false} => VoiceProxyState.Started, {Stopped: false} => VoiceProxyState.Created, _ => VoiceProxyState.Stopped }; private VoiceGateway _gateway; public VoiceProxy(VoiceGateway initial) { _gateway = initial; } public bool SendPcmFrame(VoiceClient vc, Span data, int length) { try { var gw = _gateway; if (gw is null || gw.Stopped || !gw.Started) { return false; } vc.SendPcmFrame(gw, data, 0, length); return true; } catch (Exception) { return false; } } public async Task RunGatewayAction(Func action) { var errorCount = 0; do { if (State == VoiceProxyState.Stopped) { break; } try { var gw = _gateway; if (gw is null || !gw.ConnectingFinished.Task.IsCompleted) { ++errorCount; await Task.Delay(DELAY_ON_ERROR_MILISECONDS); Log.Debug("Gateway is not ready"); continue; } await action(gw); errorCount = 0; } catch (Exception ex) { ++errorCount; await Task.Delay(DELAY_ON_ERROR_MILISECONDS); Log.Debug(ex, "Error performing proxy gateway action"); } } while (errorCount > 0 && errorCount <= MAX_ERROR_COUNT); return State != VoiceProxyState.Stopped && errorCount <= MAX_ERROR_COUNT; } public void SetGateway(VoiceGateway gateway) { _gateway = gateway; } public Task StartSpeakingAsync() { return RunGatewayAction((gw) => gw.SendSpeakingAsync(VoiceSpeaking.State.Microphone)); } public Task StopSpeakingAsync() { return RunGatewayAction((gw) => gw.SendSpeakingAsync(VoiceSpeaking.State.None)); } public async Task StartGateway() { await _gateway.Start(); } public Task StopGateway() { if(_gateway is VoiceGateway gw) return gw.StopAsync(); return Task.CompletedTask; } } }