@@ -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 | |||
using Newtonsoft.Json; | |||
namespace Discord.API.Rpc | |||
{ | |||
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>>(); | |||
//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 | |||
{ | |||
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 | |||
{ | |||
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 | |||
public event Func<RpcMessage, Task> MessageReceived | |||
@@ -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<VoiceStateEvent>(_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<VoiceStateEvent>(_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<VoiceStateEvent>(_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<SpeakingEvent>(_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<SpeakingEvent>(_serializer); | |||
await _speakingStopped.InvokeAsync(data.UserId).ConfigureAwait(false); | |||
await _speakingStoppedEvent.InvokeAsync(data.UserId).ConfigureAwait(false); | |||
} | |||
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(); } } | |||
} | |||
} |