@@ -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(); } } | |||||
} | |||||
} |