| @@ -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; } | |||||
| } | |||||
| } | |||||
| @@ -1,7 +1,21 @@ | |||||
| #pragma warning disable CS1591 | #pragma warning disable CS1591 | ||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API.Rpc | namespace Discord.API.Rpc | ||||
| { | { | ||||
| public class VoiceStateEvent | public class VoiceStateEvent | ||||
| { | { | ||||
| [JsonProperty("user")] | |||||
| public User User { get; set; } | |||||
| [JsonProperty("voice_state")] | |||||
| public Optional<VoiceState> VoiceState { get; set; } | |||||
| [JsonProperty("nick")] | |||||
| public Optional<string> Nickname { get; set; } | |||||
| [JsonProperty("volume")] | |||||
| public Optional<int> Volume { get; set; } | |||||
| [JsonProperty("mute")] | |||||
| public Optional<bool> Mute { get; set; } | |||||
| [JsonProperty("pan")] | |||||
| public Optional<Pan> Pan { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -34,18 +34,39 @@ namespace Discord.Rpc | |||||
| private readonly AsyncEvent<Func<Task>> _guildUpdatedEvent = new AsyncEvent<Func<Task>>(); | private readonly AsyncEvent<Func<Task>> _guildUpdatedEvent = new AsyncEvent<Func<Task>>(); | ||||
| //Voice | //Voice | ||||
| public event Func<RpcVoiceState, Task> VoiceStateCreated | |||||
| { | |||||
| add { _voiceStateCreatedEvent.Add(value); } | |||||
| remove { _voiceStateCreatedEvent.Remove(value); } | |||||
| } | |||||
| private readonly AsyncEvent<Func<RpcVoiceState, Task>> _voiceStateCreatedEvent = new AsyncEvent<Func<RpcVoiceState, Task>>(); | |||||
| public event Func<RpcVoiceState, Task> VoiceStateUpdated | |||||
| { | |||||
| add { _voiceStateUpdatedEvent.Add(value); } | |||||
| remove { _voiceStateUpdatedEvent.Remove(value); } | |||||
| } | |||||
| private readonly AsyncEvent<Func<RpcVoiceState, Task>> _voiceStateUpdatedEvent = new AsyncEvent<Func<RpcVoiceState, Task>>(); | |||||
| public event Func<RpcVoiceState, Task> VoiceStateDeleted | |||||
| { | |||||
| add { _voiceStateDeletedEvent.Add(value); } | |||||
| remove { _voiceStateDeletedEvent.Remove(value); } | |||||
| } | |||||
| private readonly AsyncEvent<Func<RpcVoiceState, Task>> _voiceStateDeletedEvent = new AsyncEvent<Func<RpcVoiceState, Task>>(); | |||||
| public event Func<ulong, Task> SpeakingStarted | public event Func<ulong, Task> SpeakingStarted | ||||
| { | { | ||||
| add { _speakingStarted.Add(value); } | |||||
| remove { _speakingStarted.Remove(value); } | |||||
| add { _speakingStartedEvent.Add(value); } | |||||
| remove { _speakingStartedEvent.Remove(value); } | |||||
| } | } | ||||
| private readonly AsyncEvent<Func<ulong, Task>> _speakingStarted = new AsyncEvent<Func<ulong, Task>>(); | |||||
| private readonly AsyncEvent<Func<ulong, Task>> _speakingStartedEvent = new AsyncEvent<Func<ulong, Task>>(); | |||||
| public event Func<ulong, Task> SpeakingStopped | public event Func<ulong, Task> SpeakingStopped | ||||
| { | { | ||||
| add { _speakingStopped.Add(value); } | |||||
| remove { _speakingStopped.Remove(value); } | |||||
| add { _speakingStoppedEvent.Add(value); } | |||||
| remove { _speakingStoppedEvent.Remove(value); } | |||||
| } | } | ||||
| private readonly AsyncEvent<Func<ulong, Task>> _speakingStopped = new AsyncEvent<Func<ulong, Task>>(); | |||||
| private readonly AsyncEvent<Func<ulong, Task>> _speakingStoppedEvent = new AsyncEvent<Func<ulong, Task>>(); | |||||
| //Messages | //Messages | ||||
| public event Func<RpcMessage, Task> MessageReceived | public event Func<RpcMessage, Task> MessageReceived | ||||
| @@ -331,34 +331,40 @@ namespace Discord.Rpc | |||||
| break; | break; | ||||
| //Voice | //Voice | ||||
| /*case "VOICE_STATE_CREATE": | |||||
| case "VOICE_STATE_CREATE": | |||||
| { | { | ||||
| await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_CREATE)").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_CREATE)").ConfigureAwait(false); | ||||
| var data = (payload.Value as JToken).ToObject<VoiceStateEvent>(_serializer); | |||||
| var voiceState = RpcVoiceState.Create(this, data); | |||||
| await _voiceStateUpdatedEvent.InvokeAsync().ConfigureAwait(false); | |||||
| await _voiceStateCreatedEvent.InvokeAsync(voiceState).ConfigureAwait(false); | |||||
| } | } | ||||
| break; | break; | ||||
| case "VOICE_STATE_UPDATE": | case "VOICE_STATE_UPDATE": | ||||
| { | { | ||||
| await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_UPDATE)").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_UPDATE)").ConfigureAwait(false); | ||||
| var data = (payload.Value as JToken).ToObject<VoiceStateEvent>(_serializer); | |||||
| var voiceState = RpcVoiceState.Create(this, data); | |||||
| await _voiceStateUpdatedEvent.InvokeAsync().ConfigureAwait(false); | |||||
| await _voiceStateUpdatedEvent.InvokeAsync(voiceState).ConfigureAwait(false); | |||||
| } | } | ||||
| break; | break; | ||||
| case "VOICE_STATE_DELETE": | case "VOICE_STATE_DELETE": | ||||
| { | { | ||||
| await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_DELETE)").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_DELETE)").ConfigureAwait(false); | ||||
| var data = (payload.Value as JToken).ToObject<VoiceStateEvent>(_serializer); | |||||
| var voiceState = RpcVoiceState.Create(this, data); | |||||
| await _voiceStateUpdatedEvent.InvokeAsync().ConfigureAwait(false); | |||||
| await _voiceStateDeletedEvent.InvokeAsync(voiceState).ConfigureAwait(false); | |||||
| } | } | ||||
| break;*/ | |||||
| break; | |||||
| case "SPEAKING_START": | case "SPEAKING_START": | ||||
| { | { | ||||
| await _rpcLogger.DebugAsync("Received Dispatch (SPEAKING_START)").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Received Dispatch (SPEAKING_START)").ConfigureAwait(false); | ||||
| var data = (payload.Value as JToken).ToObject<SpeakingEvent>(_serializer); | var data = (payload.Value as JToken).ToObject<SpeakingEvent>(_serializer); | ||||
| await _speakingStarted.InvokeAsync(data.UserId).ConfigureAwait(false); | |||||
| await _speakingStartedEvent.InvokeAsync(data.UserId).ConfigureAwait(false); | |||||
| } | } | ||||
| break; | break; | ||||
| case "SPEAKING_STOP": | case "SPEAKING_STOP": | ||||
| @@ -366,7 +372,7 @@ namespace Discord.Rpc | |||||
| await _rpcLogger.DebugAsync("Received Dispatch (SPEAKING_STOP)").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Received Dispatch (SPEAKING_STOP)").ConfigureAwait(false); | ||||
| var data = (payload.Value as JToken).ToObject<SpeakingEvent>(_serializer); | var data = (payload.Value as JToken).ToObject<SpeakingEvent>(_serializer); | ||||
| await _speakingStopped.InvokeAsync(data.UserId).ConfigureAwait(false); | |||||
| await _speakingStoppedEvent.InvokeAsync(data.UserId).ConfigureAwait(false); | |||||
| } | } | ||||
| break; | break; | ||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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(); } } | |||||
| } | |||||
| } | |||||