diff --git a/src/Discord.Net.Audio/AudioConfig.cs b/src/Discord.Net.Audio/AudioConfig.cs deleted file mode 100644 index 43db99020..000000000 --- a/src/Discord.Net.Audio/AudioConfig.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Discord.Net.WebSockets; - -namespace Discord.Audio -{ - public class AudioConfig - { - /// Gets or sets the time (in milliseconds) to wait for the websocket to connect and initialize. - public int ConnectionTimeout { get; set; } = 30000; - /// Gets or sets the time (in milliseconds) to wait after an unexpected disconnect before reconnecting. - public int ReconnectDelay { get; set; } = 1000; - /// Gets or sets the time (in milliseconds) to wait after an reconnect fails before retrying. - public int FailedReconnectDelay { get; set; } = 15000; - - /// Gets or sets the provider used to generate new websocket connections. - public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient(); - } -} diff --git a/src/Discord.Net.Audio/Discord.Net.Audio.xproj b/src/Discord.Net.Audio/Discord.Net.Audio.xproj deleted file mode 100644 index 7434ce9ff..000000000 --- a/src/Discord.Net.Audio/Discord.Net.Audio.xproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - 14.0.25123 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - ddfcc44f-934e-478a-978c-69cdda2a1c5b - Discord.Audio - .\obj - .\bin\ - - - 2.0 - - - \ No newline at end of file diff --git a/src/Discord.Net.Audio/Logger.cs b/src/Discord.Net.Audio/Logger.cs deleted file mode 100644 index cdb3b5fa6..000000000 --- a/src/Discord.Net.Audio/Logger.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Discord.Audio -{ - internal class Logger - { - } -} \ No newline at end of file diff --git a/src/Discord.Net.Audio/Utilities/AsyncEvent.cs b/src/Discord.Net.Audio/Utilities/AsyncEvent.cs deleted file mode 100644 index e94b1d892..000000000 --- a/src/Discord.Net.Audio/Utilities/AsyncEvent.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Threading.Tasks; - -namespace Discord -{ - public class AsyncEvent - { - private readonly object _subLock = new object(); - internal ImmutableArray _subscriptions; - - public IReadOnlyList Subscriptions => _subscriptions; - - public AsyncEvent() - { - _subscriptions = ImmutableArray.Create(); - } - - public void Add(T subscriber) - { - lock (_subLock) - _subscriptions = _subscriptions.Add(subscriber); - } - public void Remove(T subscriber) - { - lock (_subLock) - _subscriptions = _subscriptions.Remove(subscriber); - } - } - - public static class EventExtensions - { - public static async Task InvokeAsync(this AsyncEvent> eventHandler) - { - var subscribers = eventHandler.Subscriptions; - if (subscribers.Count > 0) - { - for (int i = 0; i < subscribers.Count; i++) - await subscribers[i].Invoke().ConfigureAwait(false); - } - } - public static async Task InvokeAsync(this AsyncEvent> eventHandler, T arg) - { - var subscribers = eventHandler.Subscriptions; - for (int i = 0; i < subscribers.Count; i++) - await subscribers[i].Invoke(arg).ConfigureAwait(false); - } - public static async Task InvokeAsync(this AsyncEvent> eventHandler, T1 arg1, T2 arg2) - { - var subscribers = eventHandler.Subscriptions; - for (int i = 0; i < subscribers.Count; i++) - await subscribers[i].Invoke(arg1, arg2).ConfigureAwait(false); - } - public static async Task InvokeAsync(this AsyncEvent> eventHandler, T1 arg1, T2 arg2, T3 arg3) - { - var subscribers = eventHandler.Subscriptions; - for (int i = 0; i < subscribers.Count; i++) - await subscribers[i].Invoke(arg1, arg2, arg3).ConfigureAwait(false); - } - public static async Task InvokeAsync(this AsyncEvent> eventHandler, T1 arg1, T2 arg2, T3 arg3, T4 arg4) - { - var subscribers = eventHandler.Subscriptions; - for (int i = 0; i < subscribers.Count; i++) - await subscribers[i].Invoke(arg1, arg2, arg3, arg4).ConfigureAwait(false); - } - public static async Task InvokeAsync(this AsyncEvent> eventHandler, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) - { - var subscribers = eventHandler.Subscriptions; - for (int i = 0; i < subscribers.Count; i++) - await subscribers[i].Invoke(arg1, arg2, arg3, arg4, arg5).ConfigureAwait(false); - } - } -} diff --git a/src/Discord.Net.Audio/project.json b/src/Discord.Net.Audio/project.json deleted file mode 100644 index c7788f942..000000000 --- a/src/Discord.Net.Audio/project.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "version": "1.0.0-dev", - "description": "A Discord.Net extension adding audio support.", - "authors": [ "RogueException" ], - - "packOptions": { - "tags": [ "discord", "discordapp" ], - "licenseUrl": "http://opensource.org/licenses/MIT", - "projectUrl": "https://github.com/RogueException/Discord.Net", - "repository": { - "type": "git", - "url": "git://github.com/RogueException/Discord.Net" - } - }, - - "buildOptions": { - "allowUnsafe": true, - "warningsAsErrors": false - }, - - "dependencies": { - "Discord.Net": "1.0.0-dev", - "System.Runtime.InteropServices": "4.1.0" - }, - - "frameworks": { - "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] - } - } -} diff --git a/src/Discord.Net/API/DiscordAPIClient.cs b/src/Discord.Net/API/DiscordAPIClient.cs index 2a0b4c02e..128c15474 100644 --- a/src/Discord.Net/API/DiscordAPIClient.cs +++ b/src/Discord.Net/API/DiscordAPIClient.cs @@ -398,6 +398,17 @@ namespace Discord.API { await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false); } + public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null) + { + var payload = new VoiceStateUpdateParams + { + GuildId = guildId, + ChannelId = channelId, + SelfDeaf = selfDeaf, + SelfMute = selfMute + }; + await SendGatewayAsync(GatewayOpCode.VoiceStateUpdate, payload, options: options).ConfigureAwait(false); + } //Channels public async Task GetChannelAsync(ulong channelId, RequestOptions options = null) diff --git a/src/Discord.Net.Audio/AudioAPIClient.cs b/src/Discord.Net/API/DiscordVoiceAPIClient.cs similarity index 97% rename from src/Discord.Net.Audio/AudioAPIClient.cs rename to src/Discord.Net/API/DiscordVoiceAPIClient.cs index db3418610..e64d74c10 100644 --- a/src/Discord.Net.Audio/AudioAPIClient.cs +++ b/src/Discord.Net/API/DiscordVoiceAPIClient.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; namespace Discord.Audio { - public class AudioAPIClient + public class DiscordVoiceAPIClient { public const int MaxBitrate = 128; private const string Mode = "xsalsa20_poly1305"; @@ -40,7 +40,7 @@ namespace Discord.Audio public string SessionId { get; } public ConnectionState ConnectionState { get; private set; } - internal AudioAPIClient(ulong guildId, ulong userId, string sessionId, string token, WebSocketProvider webSocketProvider, JsonSerializer serializer = null) + internal DiscordVoiceAPIClient(ulong guildId, ulong userId, string sessionId, string token, WebSocketProvider webSocketProvider, JsonSerializer serializer = null) { GuildId = guildId; _userId = userId; diff --git a/src/Discord.Net/API/Gateway/GuildEmojiUpdateEvent.cs b/src/Discord.Net/API/Gateway/GuildEmojiUpdateEvent.cs new file mode 100644 index 000000000..13f083d40 --- /dev/null +++ b/src/Discord.Net/API/Gateway/GuildEmojiUpdateEvent.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Discord.API.Gateway +{ + public class GuildEmojiUpdateEvent + { + [JsonProperty("guild_id")] + public ulong GuildId; + [JsonProperty("emojis")] + public Emoji[] Emojis; + } +} diff --git a/src/Discord.Net/API/Gateway/RequestMembersParams.cs b/src/Discord.Net/API/Gateway/RequestMembersParams.cs index f11be49b1..a0819c556 100644 --- a/src/Discord.Net/API/Gateway/RequestMembersParams.cs +++ b/src/Discord.Net/API/Gateway/RequestMembersParams.cs @@ -1,15 +1,19 @@ using Newtonsoft.Json; using System.Collections.Generic; +using System.Linq; namespace Discord.API.Gateway { public class RequestMembersParams { - [JsonProperty("guild_id")] - public IEnumerable GuildIds { get; set; } [JsonProperty("query")] public string Query { get; set; } [JsonProperty("limit")] public int Limit { get; set; } + + [JsonProperty("guild_id")] + public IEnumerable GuildIds { get; set; } + [JsonIgnore] + public IEnumerable Guilds { set { GuildIds = value.Select(x => x.Id); } } } } diff --git a/src/Discord.Net/API/Gateway/UpdateVoiceParams.cs b/src/Discord.Net/API/Gateway/UpdateVoiceParams.cs deleted file mode 100644 index d72d63548..000000000 --- a/src/Discord.Net/API/Gateway/UpdateVoiceParams.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Newtonsoft.Json; - -namespace Discord.API.Gateway -{ - public class UpdateVoiceParams - { - [JsonProperty("guild_id")] - public ulong? GuildId { get; set; } - [JsonProperty("channel_id")] - public ulong? ChannelId { get; set; } - [JsonProperty("self_mute")] - public bool IsSelfMuted { get; set; } - [JsonProperty("self_deaf")] - public bool IsSelfDeafened { get; set; } - } -} diff --git a/src/Discord.Net/API/Gateway/VoiceStateUpdateParams.cs b/src/Discord.Net/API/Gateway/VoiceStateUpdateParams.cs new file mode 100644 index 000000000..6eb285cea --- /dev/null +++ b/src/Discord.Net/API/Gateway/VoiceStateUpdateParams.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace Discord.API.Gateway +{ + public class VoiceStateUpdateParams + { + [JsonProperty("self_mute")] + public bool SelfMute { get; set; } + [JsonProperty("self_deaf")] + public bool SelfDeaf { get; set; } + + [JsonProperty("guild_id")] + public ulong GuildId { get; set; } + [JsonIgnore] + public IGuild Guild { set { GuildId = value.Id; } } + [JsonProperty("channel_id")] + public ulong? ChannelId { get; set; } + [JsonIgnore] + public IChannel Channel { set { ChannelId = value?.Id; } } + } +} diff --git a/src/Discord.Net.Audio/AudioClient.cs b/src/Discord.Net/Audio/AudioClient.cs similarity index 91% rename from src/Discord.Net.Audio/AudioClient.cs rename to src/Discord.Net/Audio/AudioClient.cs index 7f9298ed0..854c3219b 100644 --- a/src/Discord.Net.Audio/AudioClient.cs +++ b/src/Discord.Net/Audio/AudioClient.cs @@ -1,15 +1,15 @@ using Discord.API.Voice; using Discord.Logging; using Discord.Net.Converters; +using Discord.Net.WebSockets; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System; using System.Threading; using System.Threading.Tasks; namespace Discord.Audio { - public class AudioClient + internal class AudioClient : IAudioClient { public event Func Connected { @@ -30,12 +30,11 @@ namespace Discord.Audio } private readonly AsyncEvent> _latencyUpdatedEvent = new AsyncEvent>(); - private readonly ILogger _webSocketLogger; + private readonly ILogger _webSocketLogger, _udpLogger; #if BENCHMARK private readonly ILogger _benchmarkLogger; #endif private readonly JsonSerializer _serializer; - private readonly int _connectionTimeout, _reconnectDelay, _failedReconnectDelay; internal readonly SemaphoreSlim _connectionLock; private TaskCompletionSource _connectTask; @@ -45,20 +44,18 @@ namespace Discord.Audio private bool _isReconnecting; private string _url; - public AudioAPIClient ApiClient { get; private set; } - /// Gets the current connection state of this client. + private DiscordSocketClient Discord { get; } + public DiscordVoiceAPIClient ApiClient { get; private set; } public ConnectionState ConnectionState { get; private set; } - /// Gets the estimated round-trip latency, in milliseconds, to the gateway server. public int Latency { get; private set; } /// Creates a new REST/WebSocket discord client. - internal AudioClient(ulong guildId, ulong userId, string sessionId, string token, AudioConfig config, ILogManager logManager) + internal AudioClient(DiscordSocketClient discord, ulong guildId, ulong userId, string sessionId, string token, WebSocketProvider webSocketProvider, ILogManager logManager) { - _connectionTimeout = config.ConnectionTimeout; - _reconnectDelay = config.ReconnectDelay; - _failedReconnectDelay = config.FailedReconnectDelay; + Discord = discord; - _webSocketLogger = logManager.CreateLogger("AudioWS"); + _webSocketLogger = logManager.CreateLogger("Audio"); + _udpLogger = logManager.CreateLogger("AudioUDP"); #if BENCHMARK _benchmarkLogger = logManager.CreateLogger("Benchmark"); #endif @@ -72,8 +69,7 @@ namespace Discord.Audio e.ErrorContext.Handled = true; }; - var webSocketProvider = config.WebSocketProvider; //TODO: Clean this check - ApiClient = new AudioAPIClient(guildId, userId, sessionId, token, config.WebSocketProvider); + ApiClient = new DiscordVoiceAPIClient(guildId, userId, sessionId, token, webSocketProvider); ApiClient.SentGatewayMessage += async opCode => await _webSocketLogger.DebugAsync($"Sent {(VoiceOpCode)opCode}").ConfigureAwait(false); ApiClient.ReceivedEvent += ProcessMessageAsync; diff --git a/src/Discord.Net.Audio/AudioMode.cs b/src/Discord.Net/Audio/AudioMode.cs similarity index 62% rename from src/Discord.Net.Audio/AudioMode.cs rename to src/Discord.Net/Audio/AudioMode.cs index b9acdbf89..7cc5a08c1 100644 --- a/src/Discord.Net.Audio/AudioMode.cs +++ b/src/Discord.Net/Audio/AudioMode.cs @@ -1,7 +1,11 @@ -namespace Discord.Audio +using System; + +namespace Discord.Audio { + [Flags] public enum AudioMode : byte { + Disabled = 0, Outgoing = 1, Incoming = 2, Both = Outgoing | Incoming diff --git a/src/Discord.Net/Audio/IAudioClient.cs b/src/Discord.Net/Audio/IAudioClient.cs new file mode 100644 index 000000000..5f59851ee --- /dev/null +++ b/src/Discord.Net/Audio/IAudioClient.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading.Tasks; + +namespace Discord.Audio +{ + public interface IAudioClient + { + event Func Connected; + event Func Disconnected; + event Func LatencyUpdated; + + DiscordVoiceAPIClient ApiClient { get; } + /// Gets the current connection state of this client. + ConnectionState ConnectionState { get; } + /// Gets the estimated round-trip latency, in milliseconds, to the gateway server. + int Latency { get; } + + Task DisconnectAsync(); + } +} diff --git a/src/Discord.Net.Audio/Opus/Ctl.cs b/src/Discord.Net/Audio/Opus/Ctl.cs similarity index 100% rename from src/Discord.Net.Audio/Opus/Ctl.cs rename to src/Discord.Net/Audio/Opus/Ctl.cs diff --git a/src/Discord.Net.Audio/Opus/OpusApplication.cs b/src/Discord.Net/Audio/Opus/OpusApplication.cs similarity index 100% rename from src/Discord.Net.Audio/Opus/OpusApplication.cs rename to src/Discord.Net/Audio/Opus/OpusApplication.cs diff --git a/src/Discord.Net.Audio/Opus/OpusConverter.cs b/src/Discord.Net/Audio/Opus/OpusConverter.cs similarity index 100% rename from src/Discord.Net.Audio/Opus/OpusConverter.cs rename to src/Discord.Net/Audio/Opus/OpusConverter.cs diff --git a/src/Discord.Net.Audio/Opus/OpusDecoder.cs b/src/Discord.Net/Audio/Opus/OpusDecoder.cs similarity index 100% rename from src/Discord.Net.Audio/Opus/OpusDecoder.cs rename to src/Discord.Net/Audio/Opus/OpusDecoder.cs diff --git a/src/Discord.Net.Audio/Opus/OpusEncoder.cs b/src/Discord.Net/Audio/Opus/OpusEncoder.cs similarity index 97% rename from src/Discord.Net.Audio/Opus/OpusEncoder.cs rename to src/Discord.Net/Audio/Opus/OpusEncoder.cs index 2e1d4d861..e17487f43 100644 --- a/src/Discord.Net.Audio/Opus/OpusEncoder.cs +++ b/src/Discord.Net/Audio/Opus/OpusEncoder.cs @@ -36,7 +36,7 @@ namespace Discord.Audio.Opus { if (channels != 1 && channels != 2) throw new ArgumentOutOfRangeException(nameof(channels)); - if (bitrate != null && (bitrate < 1 || bitrate > AudioAPIClient.MaxBitrate)) + if (bitrate != null && (bitrate < 1 || bitrate > DiscordVoiceAPIClient.MaxBitrate)) throw new ArgumentOutOfRangeException(nameof(bitrate)); OpusError error; diff --git a/src/Discord.Net.Audio/Opus/OpusError.cs b/src/Discord.Net/Audio/Opus/OpusError.cs similarity index 100% rename from src/Discord.Net.Audio/Opus/OpusError.cs rename to src/Discord.Net/Audio/Opus/OpusError.cs diff --git a/src/Discord.Net.Audio/LibSodium.cs b/src/Discord.Net/Audio/Sodium/SecretBox.cs similarity index 93% rename from src/Discord.Net.Audio/LibSodium.cs rename to src/Discord.Net/Audio/Sodium/SecretBox.cs index 3b4129165..727db2711 100644 --- a/src/Discord.Net.Audio/LibSodium.cs +++ b/src/Discord.Net/Audio/Sodium/SecretBox.cs @@ -1,8 +1,8 @@ using System.Runtime.InteropServices; -namespace Discord.Net.Audio +namespace Discord.Net.Audio.Sodium { - public unsafe static class LibSodium + public unsafe static class SecretBox { [DllImport("libsodium", EntryPoint = "crypto_secretbox_easy", CallingConvention = CallingConvention.Cdecl)] private static extern int SecretBoxEasy(byte* output, byte[] input, long inputLength, byte[] nonce, byte[] secret); diff --git a/src/Discord.Net/DiscordSocketClient.Events.cs b/src/Discord.Net/DiscordSocketClient.Events.cs index b4d4db47d..545f3a4fb 100644 --- a/src/Discord.Net/DiscordSocketClient.Events.cs +++ b/src/Discord.Net/DiscordSocketClient.Events.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; namespace Discord { + //TODO: Add event docstrings public partial class DiscordSocketClient { //General diff --git a/src/Discord.Net/DiscordSocketClient.cs b/src/Discord.Net/DiscordSocketClient.cs index 01a9ae88c..d09675e18 100644 --- a/src/Discord.Net/DiscordSocketClient.cs +++ b/src/Discord.Net/DiscordSocketClient.cs @@ -1,7 +1,9 @@ using Discord.API.Gateway; +using Discord.Audio; using Discord.Extensions; using Discord.Logging; using Discord.Net.Converters; +using Discord.Net.WebSockets; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; @@ -14,9 +16,6 @@ using System.Threading.Tasks; namespace Discord { - //TODO: Add event docstrings - //TODO: Add reconnect logic (+ensure the heartbeat task to shut down) - //TODO: Add resume logic public partial class DiscordSocketClient : DiscordClient, IDiscordClient { private readonly ConcurrentQueue _largeGuilds; @@ -25,9 +24,6 @@ namespace Discord private readonly ILogger _benchmarkLogger; #endif private readonly JsonSerializer _serializer; - private readonly int _connectionTimeout, _reconnectDelay, _failedReconnectDelay; - private readonly int _largeThreshold; - private readonly int _totalShards; private string _sessionId; private int _lastSeq; @@ -46,9 +42,17 @@ namespace Discord public ConnectionState ConnectionState { get; private set; } /// Gets the estimated round-trip latency, in milliseconds, to the gateway server. public int Latency { get; private set; } - + + //From DiscordConfig + internal int TotalShards { get; private set; } + internal int ConnectionTimeout { get; private set; } + internal int ReconnectDelay { get; private set; } + internal int FailedReconnectDelay { get; private set; } internal int MessageCacheSize { get; private set; } + internal int LargeThreshold { get; private set; } + internal AudioMode AudioMode { get; private set; } internal DataStore DataStore { get; private set; } + internal WebSocketProvider WebSocketProvider { get; private set; } internal CachedSelfUser CurrentUser => _currentUser as CachedSelfUser; internal IReadOnlyCollection Guilds => DataStore.Guilds; @@ -62,15 +66,15 @@ namespace Discord : base(config) { ShardId = config.ShardId; - _totalShards = config.TotalShards; - - _connectionTimeout = config.ConnectionTimeout; - _reconnectDelay = config.ReconnectDelay; - _failedReconnectDelay = config.FailedReconnectDelay; - + TotalShards = config.TotalShards; + ConnectionTimeout = config.ConnectionTimeout; + ReconnectDelay = config.ReconnectDelay; + FailedReconnectDelay = config.FailedReconnectDelay; MessageCacheSize = config.MessageCacheSize; - _largeThreshold = config.LargeThreshold; - + LargeThreshold = config.LargeThreshold; + AudioMode = config.AudioMode; + WebSocketProvider = config.WebSocketProvider; + _gatewayLogger = _log.CreateLogger("Gateway"); #if BENCHMARK _benchmarkLogger = _log.CreateLogger("Benchmark"); @@ -471,7 +475,7 @@ namespace Discord case GatewayOpCode.Dispatch: switch (type) { - //Global + //Connection case "READY": { await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false); @@ -507,6 +511,11 @@ namespace Discord await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); } break; + case "RESUMED": + await _gatewayLogger.DebugAsync("Received Dispatch (RESUMED)").ConfigureAwait(false); + + await _gatewayLogger.InfoAsync("Resume").ConfigureAwait(false); + return; //Guilds case "GUILD_CREATE": @@ -569,6 +578,28 @@ namespace Discord } } break; + case "GUILD_EMOJI_UPDATE": //TODO: Add + { + await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_EMOJI_UPDATE)").ConfigureAwait(false); + + var data = (payload as JToken).ToObject(_serializer); + var guild = DataStore.GetGuild(data.GuildId); + if (guild != null) + { + var before = guild.Clone(); + guild.Update(data, UpdateSource.WebSocket); + await _guildUpdatedEvent.InvokeAsync(before, guild).ConfigureAwait(false); + } + else + { + await _gatewayLogger.WarningAsync("GUILD_EMOJI_UPDATE referenced an unknown guild.").ConfigureAwait(false); + return; + } + } + return; + case "GUILD_INTEGRATIONS_UPDATE": + await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_INTEGRATIONS_UPDATE)").ConfigureAwait(false); + return; case "GUILD_DELETE": { var data = (payload as JToken).ToObject(_serializer); @@ -1099,26 +1130,17 @@ namespace Discord } } break; + case "VOICE_SERVER_UPDATE": + await _gatewayLogger.DebugAsync("Ignored Dispatch (VOICE_SERVER_UPDATE)").ConfigureAwait(false); + return; - //Ignored + //Ignored (User only) case "USER_SETTINGS_UPDATE": await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false); return; - case "MESSAGE_ACK": //TODO: Add (User only) + case "MESSAGE_ACK": await _gatewayLogger.DebugAsync("Ignored Dispatch (MESSAGE_ACK)").ConfigureAwait(false); return; - case "GUILD_EMOJIS_UPDATE": //TODO: Add - await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_EMOJIS_UPDATE)").ConfigureAwait(false); - return; - case "GUILD_INTEGRATIONS_UPDATE": //TODO: Add - await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_INTEGRATIONS_UPDATE)").ConfigureAwait(false); - return; - case "VOICE_SERVER_UPDATE": //TODO: Add - await _gatewayLogger.DebugAsync("Ignored Dispatch (VOICE_SERVER_UPDATE)").ConfigureAwait(false); - return; - case "RESUMED": //TODO: Add - await _gatewayLogger.DebugAsync("Ignored Dispatch (RESUMED)").ConfigureAwait(false); - return; //Others default: diff --git a/src/Discord.Net/DiscordSocketConfig.cs b/src/Discord.Net/DiscordSocketConfig.cs index a40fba0e7..cd54fcd8d 100644 --- a/src/Discord.Net/DiscordSocketConfig.cs +++ b/src/Discord.Net/DiscordSocketConfig.cs @@ -1,4 +1,5 @@ -using Discord.Net.WebSockets; +using Discord.Audio; +using Discord.Net.WebSockets; namespace Discord { @@ -28,7 +29,10 @@ namespace Discord /// Decreasing this may reduce CPU usage while increasing login time and network usage. /// public int LargeThreshold { get; set; } = 250; - + + /// Gets or sets the type of audio this DiscordClient supports. + public AudioMode AudioMode { get; set; } = AudioMode.Disabled; + /// Gets or sets the provider used to generate new websocket connections. public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient(); } diff --git a/src/Discord.Net/Entities/Channels/IVoiceChannel.cs b/src/Discord.Net/Entities/Channels/IVoiceChannel.cs index fc90b2935..5f6e8c817 100644 --- a/src/Discord.Net/Entities/Channels/IVoiceChannel.cs +++ b/src/Discord.Net/Entities/Channels/IVoiceChannel.cs @@ -1,4 +1,5 @@ using Discord.API.Rest; +using Discord.Audio; using System; using System.Threading.Tasks; @@ -13,5 +14,7 @@ namespace Discord /// Modifies this voice channel. Task ModifyAsync(Action func); + /// Connects to this voice channel. + Task ConnectAsync(); } } \ No newline at end of file diff --git a/src/Discord.Net/Entities/Channels/VoiceChannel.cs b/src/Discord.Net/Entities/Channels/VoiceChannel.cs index 4e2cb6e80..a7e32d5a4 100644 --- a/src/Discord.Net/Entities/Channels/VoiceChannel.cs +++ b/src/Discord.Net/Entities/Channels/VoiceChannel.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; using Model = Discord.API.Channel; +using Discord.Audio; namespace Discord { @@ -49,6 +50,8 @@ namespace Discord throw new NotSupportedException(); } + public virtual Task ConnectAsync() { throw new NotSupportedException(); } + private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; } } diff --git a/src/Discord.Net/Entities/Guilds/Guild.cs b/src/Discord.Net/Entities/Guilds/Guild.cs index d4a47f302..31a0f5916 100644 --- a/src/Discord.Net/Entities/Guilds/Guild.cs +++ b/src/Discord.Net/Entities/Guilds/Guild.cs @@ -1,4 +1,5 @@ using Discord.API.Rest; +using Discord.Audio; using Discord.Extensions; using System; using System.Collections.Concurrent; @@ -311,6 +312,7 @@ namespace Discord IReadOnlyCollection IGuild.Emojis => Emojis; IReadOnlyCollection IGuild.Features => Features; Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); } + IAudioClient IGuild.AudioClient => null; IRole IGuild.GetRole(ulong id) => GetRole(id); } diff --git a/src/Discord.Net/Entities/Guilds/IGuild.cs b/src/Discord.Net/Entities/Guilds/IGuild.cs index 7302e15f8..8979677ac 100644 --- a/src/Discord.Net/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net/Entities/Guilds/IGuild.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Discord.API.Rest; +using Discord.Audio; namespace Discord { @@ -37,6 +38,8 @@ namespace Discord /// Gets the id of the region hosting this guild's voice channels. string VoiceRegionId { get; } + /// Gets the IAudioClient currently associated with this guild. + IAudioClient AudioClient { get; } /// Gets the built-in role containing all users in this guild. IRole EveryoneRole { get; } /// Gets a collection of all custom emojis for this guild. diff --git a/src/Discord.Net/Entities/WebSocket/CachedGuild.cs b/src/Discord.Net/Entities/WebSocket/CachedGuild.cs index 4d0af0783..2f383b4bc 100644 --- a/src/Discord.Net/Entities/WebSocket/CachedGuild.cs +++ b/src/Discord.Net/Entities/WebSocket/CachedGuild.cs @@ -1,4 +1,5 @@ -using Discord.Extensions; +using Discord.Audio; +using Discord.Extensions; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -6,6 +7,7 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using ChannelModel = Discord.API.Channel; +using EmojiUpdateModel = Discord.API.Gateway.GuildEmojiUpdateEvent; using ExtendedModel = Discord.API.Gateway.ExtendedGuild; using MemberModel = Discord.API.GuildMember; using Model = Discord.API.Guild; @@ -25,6 +27,7 @@ namespace Discord public bool Available { get; private set; } public int MemberCount { get; private set; } public int DownloadedMemberCount { get; private set; } + public IAudioClient AudioClient { get; private set; } public bool HasAllMembers => _downloaderPromise.Task.IsCompleted; public Task DownloaderPromise => _downloaderPromise.Task; @@ -102,6 +105,16 @@ namespace Discord _voiceStates = voiceStates; } + public void Update(EmojiUpdateModel model, UpdateSource source) + { + if (source == UpdateSource.Rest && IsAttached) return; + + var emojis = ImmutableArray.CreateBuilder(model.Emojis.Length); + for (int i = 0; i < model.Emojis.Length; i++) + emojis.Add(new Emoji(model.Emojis[i])); + Emojis = emojis.ToImmutableArray(); + } + public override Task GetChannelAsync(ulong id) => Task.FromResult(GetChannel(id)); public override Task> GetChannelsAsync() => Task.FromResult>(Channels); public void AddChannel(ChannelModel model, DataStore dataStore, ConcurrentHashSet channels = null) diff --git a/src/Discord.Net/Entities/WebSocket/CachedVoiceChannel.cs b/src/Discord.Net/Entities/WebSocket/CachedVoiceChannel.cs index 6b00d82b5..358464188 100644 --- a/src/Discord.Net/Entities/WebSocket/CachedVoiceChannel.cs +++ b/src/Discord.Net/Entities/WebSocket/CachedVoiceChannel.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using Discord.Audio; +using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; @@ -33,6 +35,19 @@ namespace Discord return null; } + public override async Task ConnectAsync() + { + var audioMode = Discord.AudioMode; + if (audioMode == AudioMode.Disabled) + throw new InvalidOperationException($"Audio is not enabled on this client, {nameof(DiscordSocketConfig.AudioMode)} in {nameof(DiscordSocketConfig)} must be set."); + + await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, Id, + (audioMode & AudioMode.Incoming) == 0, + (audioMode & AudioMode.Outgoing) == 0).ConfigureAwait(false); + return null; + //TODO: Block and return + } + public CachedVoiceChannel Clone() => MemberwiseClone() as CachedVoiceChannel; ICachedChannel ICachedChannel.Clone() => Clone(); diff --git a/src/Discord.Net/project.json b/src/Discord.Net/project.json index 04969b27b..76133d6d7 100644 --- a/src/Discord.Net/project.json +++ b/src/Discord.Net/project.json @@ -28,6 +28,7 @@ "System.Net.Http": "4.1.0", "System.Net.WebSockets.Client": "4.0.0", "System.Reflection.Extensions": "4.0.1", + "System.Runtime.InteropServices": "4.1.0", "System.Runtime.Serialization.Primitives": "4.1.1", "System.Text.RegularExpressions": "4.1.0" },