diff --git a/src/Discord.Net.Rpc/API/Rpc/Pan.cs b/src/Discord.Net.Rpc/API/Rpc/Pan.cs new file mode 100644 index 000000000..e2a97c369 --- /dev/null +++ b/src/Discord.Net.Rpc/API/Rpc/Pan.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Discord.API.Rpc +{ + public class Pan + { + [JsonProperty("left")] + public float Left { get; set; } + [JsonProperty("right")] + public float Right { get; set; } + } +} diff --git a/src/Discord.Net.Rpc/API/Rpc/VoiceStateEvent.cs b/src/Discord.Net.Rpc/API/Rpc/VoiceStateEvent.cs index 445a02b80..11359fb8f 100644 --- a/src/Discord.Net.Rpc/API/Rpc/VoiceStateEvent.cs +++ b/src/Discord.Net.Rpc/API/Rpc/VoiceStateEvent.cs @@ -1,7 +1,21 @@ #pragma warning disable CS1591 +using Newtonsoft.Json; + namespace Discord.API.Rpc { public class VoiceStateEvent { + [JsonProperty("user")] + public User User { get; set; } + [JsonProperty("voice_state")] + public Optional VoiceState { get; set; } + [JsonProperty("nick")] + public Optional Nickname { get; set; } + [JsonProperty("volume")] + public Optional Volume { get; set; } + [JsonProperty("mute")] + public Optional Mute { get; set; } + [JsonProperty("pan")] + public Optional Pan { get; set; } } } diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs b/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs index 3688bdbda..5639bdbae 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs @@ -34,18 +34,39 @@ namespace Discord.Rpc private readonly AsyncEvent> _guildUpdatedEvent = new AsyncEvent>(); //Voice + public event Func VoiceStateCreated + { + add { _voiceStateCreatedEvent.Add(value); } + remove { _voiceStateCreatedEvent.Remove(value); } + } + private readonly AsyncEvent> _voiceStateCreatedEvent = new AsyncEvent>(); + + public event Func VoiceStateUpdated + { + add { _voiceStateUpdatedEvent.Add(value); } + remove { _voiceStateUpdatedEvent.Remove(value); } + } + private readonly AsyncEvent> _voiceStateUpdatedEvent = new AsyncEvent>(); + + public event Func VoiceStateDeleted + { + add { _voiceStateDeletedEvent.Add(value); } + remove { _voiceStateDeletedEvent.Remove(value); } + } + private readonly AsyncEvent> _voiceStateDeletedEvent = new AsyncEvent>(); + public event Func SpeakingStarted { - add { _speakingStarted.Add(value); } - remove { _speakingStarted.Remove(value); } + add { _speakingStartedEvent.Add(value); } + remove { _speakingStartedEvent.Remove(value); } } - private readonly AsyncEvent> _speakingStarted = new AsyncEvent>(); + private readonly AsyncEvent> _speakingStartedEvent = new AsyncEvent>(); public event Func SpeakingStopped { - add { _speakingStopped.Add(value); } - remove { _speakingStopped.Remove(value); } + add { _speakingStoppedEvent.Add(value); } + remove { _speakingStoppedEvent.Remove(value); } } - private readonly AsyncEvent> _speakingStopped = new AsyncEvent>(); + private readonly AsyncEvent> _speakingStoppedEvent = new AsyncEvent>(); //Messages public event Func MessageReceived diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs index 331b7e786..a5fcd827a 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.cs @@ -331,34 +331,40 @@ namespace Discord.Rpc break; //Voice - /*case "VOICE_STATE_CREATE": + case "VOICE_STATE_CREATE": { await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_CREATE)").ConfigureAwait(false); + var data = (payload.Value as JToken).ToObject(_serializer); + var voiceState = RpcVoiceState.Create(this, data); - await _voiceStateUpdatedEvent.InvokeAsync().ConfigureAwait(false); + await _voiceStateCreatedEvent.InvokeAsync(voiceState).ConfigureAwait(false); } break; case "VOICE_STATE_UPDATE": { await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_UPDATE)").ConfigureAwait(false); + var data = (payload.Value as JToken).ToObject(_serializer); + var voiceState = RpcVoiceState.Create(this, data); - await _voiceStateUpdatedEvent.InvokeAsync().ConfigureAwait(false); + await _voiceStateUpdatedEvent.InvokeAsync(voiceState).ConfigureAwait(false); } break; case "VOICE_STATE_DELETE": { await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_DELETE)").ConfigureAwait(false); + var data = (payload.Value as JToken).ToObject(_serializer); + var voiceState = RpcVoiceState.Create(this, data); - await _voiceStateUpdatedEvent.InvokeAsync().ConfigureAwait(false); + await _voiceStateDeletedEvent.InvokeAsync(voiceState).ConfigureAwait(false); } - break;*/ + break; case "SPEAKING_START": { await _rpcLogger.DebugAsync("Received Dispatch (SPEAKING_START)").ConfigureAwait(false); var data = (payload.Value as JToken).ToObject(_serializer); - await _speakingStarted.InvokeAsync(data.UserId).ConfigureAwait(false); + await _speakingStartedEvent.InvokeAsync(data.UserId).ConfigureAwait(false); } break; case "SPEAKING_STOP": @@ -366,7 +372,7 @@ namespace Discord.Rpc await _rpcLogger.DebugAsync("Received Dispatch (SPEAKING_STOP)").ConfigureAwait(false); var data = (payload.Value as JToken).ToObject(_serializer); - await _speakingStopped.InvokeAsync(data.UserId).ConfigureAwait(false); + await _speakingStoppedEvent.InvokeAsync(data.UserId).ConfigureAwait(false); } break; diff --git a/src/Discord.Net.Rpc/Entities/Users/Pan.cs b/src/Discord.Net.Rpc/Entities/Users/Pan.cs new file mode 100644 index 000000000..8a64bddf3 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Users/Pan.cs @@ -0,0 +1,20 @@ +using Model = Discord.API.Rpc.Pan; + +namespace Discord.Rpc +{ + public struct Pan + { + public float Left { get; } + public float Right { get; } + + public Pan(float left, float right) + { + Left = left; + Right = right; + } + internal static Pan Create(Model model) + { + return new Pan(model.Left, model.Right); + } + } +} diff --git a/src/Discord.Net.Rpc/Entities/Users/RpcVoiceState.cs b/src/Discord.Net.Rpc/Entities/Users/RpcVoiceState.cs new file mode 100644 index 000000000..ee392f5cd --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Users/RpcVoiceState.cs @@ -0,0 +1,79 @@ +using System; +using System.Diagnostics; +using Model = Discord.API.Rpc.VoiceStateEvent; + +namespace Discord.Rpc +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RpcVoiceState : IVoiceState + { + [Flags] + private enum Flags : byte + { + Normal = 0x00, + Suppressed = 0x01, + Muted = 0x02, + Deafened = 0x04, + SelfMuted = 0x08, + SelfDeafened = 0x10, + } + + private Flags _voiceStates; + + public RpcUser User { get; } + public string Nickname { get; private set; } + public int Volume { get; private set; } + public bool IsMuted2 { get; private set; } + public Pan Pan { get; private set; } + + public bool IsMuted => (_voiceStates & Flags.Muted) != 0; + public bool IsDeafened => (_voiceStates & Flags.Deafened) != 0; + public bool IsSuppressed => (_voiceStates & Flags.Suppressed) != 0; + public bool IsSelfMuted => (_voiceStates & Flags.SelfMuted) != 0; + public bool IsSelfDeafened => (_voiceStates & Flags.SelfDeafened) != 0; + + internal RpcVoiceState(DiscordRpcClient discord, ulong userId) + { + User = new RpcUser(discord, userId); + } + internal static RpcVoiceState Create(DiscordRpcClient discord, Model model) + { + var entity = new RpcVoiceState(discord, model.User.Id); + entity.Update(model); + return entity; + } + internal void Update(Model model) + { + if (model.VoiceState.IsSpecified) + { + Flags voiceStates = Flags.Normal; + if (model.VoiceState.Value.Mute) + voiceStates |= Flags.Muted; + if (model.VoiceState.Value.Deaf) + voiceStates |= Flags.Deafened; + if (model.VoiceState.Value.SelfMute) + voiceStates |= Flags.SelfMuted; + if (model.VoiceState.Value.SelfDeaf) + voiceStates |= Flags.SelfDeafened; + if (model.VoiceState.Value.Suppress) + voiceStates |= Flags.Suppressed; + _voiceStates = voiceStates; + } + User.Update(model.User); + if (model.Nickname.IsSpecified) + Nickname = model.Nickname.Value; + if (model.Volume.IsSpecified) + Volume = model.Volume.Value; + if (model.Mute.IsSpecified) + IsMuted2 = model.Mute.Value; + if (model.Pan.IsSpecified) + Pan = Pan.Create(model.Pan.Value); + } + + public override string ToString() => User.ToString(); + internal string DebuggerDisplay => $"{User} ({_voiceStates})"; + + string IVoiceState.VoiceSessionId { get { throw new NotSupportedException(); } } + IVoiceChannel IVoiceState.VoiceChannel { get { throw new NotSupportedException(); } } + } +}