@@ -3,6 +3,6 @@ | |||
"sdk": { | |||
"version": "1.0.0-beta6", | |||
"architecture": "x64", | |||
"runtime": "clr" | |||
"runtime": "coreclr" | |||
} | |||
} |
@@ -8,30 +8,6 @@ using System; | |||
namespace Discord.API.Models | |||
{ | |||
internal class WebSocketMessage | |||
{ | |||
[JsonProperty(PropertyName = "op")] | |||
public int Operation; | |||
[JsonProperty(PropertyName = "t")] | |||
public string Type; | |||
[JsonProperty(PropertyName = "d")] | |||
public object Payload; | |||
} | |||
internal abstract class WebSocketMessage<T> : WebSocketMessage | |||
where T : new() | |||
{ | |||
public WebSocketMessage() { Payload = new T(); } | |||
public WebSocketMessage(int op) { Operation = op; Payload = new T(); } | |||
public WebSocketMessage(int op, T payload) { Operation = op; Payload = payload; } | |||
[JsonIgnore] | |||
public new T Payload | |||
{ | |||
get { if (base.Payload is JToken) { base.Payload = (base.Payload as JToken).ToObject<T>(); } return (T)base.Payload; } | |||
set { base.Payload = value; } | |||
} | |||
} | |||
//Users | |||
internal class UserReference | |||
{ | |||
@@ -83,9 +59,9 @@ namespace Discord.API.Models | |||
[JsonProperty(PropertyName = "session_id")] | |||
public string SessionId; | |||
[JsonProperty(PropertyName = "self_mute")] | |||
public bool IsSelfMuted; | |||
public bool? IsSelfMuted; | |||
[JsonProperty(PropertyName = "self_deaf")] | |||
public bool IsSelfDeafened; | |||
public bool? IsSelfDeafened; | |||
[JsonProperty(PropertyName = "mute")] | |||
public bool IsMuted; | |||
[JsonProperty(PropertyName = "deaf")] | |||
@@ -3,6 +3,7 @@ | |||
#pragma warning disable CS0169 | |||
using Newtonsoft.Json; | |||
using Newtonsoft.Json.Linq; | |||
using System; | |||
using System.Collections.Generic; | |||
@@ -10,6 +11,29 @@ namespace Discord.API.Models | |||
{ | |||
internal static class TextWebSocketCommands | |||
{ | |||
public class WebSocketMessage | |||
{ | |||
[JsonProperty(PropertyName = "op")] | |||
public int Operation; | |||
[JsonProperty(PropertyName = "t")] | |||
public string Type; | |||
[JsonProperty(PropertyName = "d")] | |||
public object Payload; | |||
} | |||
internal abstract class WebSocketMessage<T> : WebSocketMessage | |||
where T : new() | |||
{ | |||
public WebSocketMessage() { Payload = new T(); } | |||
public WebSocketMessage(int op) { Operation = op; Payload = new T(); } | |||
public WebSocketMessage(int op, T payload) { Operation = op; Payload = payload; } | |||
[JsonIgnore] | |||
public new T Payload | |||
{ | |||
get { if (base.Payload is JToken) { base.Payload = (base.Payload as JToken).ToObject<T>(); } return (T)base.Payload; } | |||
set { base.Payload = value; } | |||
} | |||
} | |||
public sealed class KeepAlive : WebSocketMessage<int> | |||
{ | |||
private static DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | |||
@@ -3,13 +3,34 @@ | |||
#pragma warning disable CS0169 | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Collections.Generic; | |||
using Newtonsoft.Json.Linq; | |||
namespace Discord.API.Models | |||
{ | |||
internal static class VoiceWebSocketCommands | |||
{ | |||
public class WebSocketMessage | |||
{ | |||
[JsonProperty(PropertyName = "op")] | |||
public int Operation; | |||
[JsonProperty(PropertyName = "d")] | |||
public object Payload; | |||
} | |||
internal abstract class WebSocketMessage<T> : WebSocketMessage | |||
where T : new() | |||
{ | |||
public WebSocketMessage() { Payload = new T(); } | |||
public WebSocketMessage(int op) { Operation = op; Payload = new T(); } | |||
public WebSocketMessage(int op, T payload) { Operation = op; Payload = payload; } | |||
[JsonIgnore] | |||
public new T Payload | |||
{ | |||
get { if (base.Payload is JToken) { base.Payload = (base.Payload as JToken).ToObject<T>(); } return (T)base.Payload; } | |||
set { base.Payload = value; } | |||
} | |||
} | |||
public sealed class KeepAlive : WebSocketMessage<object> | |||
{ | |||
public KeepAlive() : base(3, null) { } | |||
@@ -29,12 +50,12 @@ namespace Discord.API.Models | |||
public string Token; | |||
} | |||
} | |||
public sealed class Login2 : WebSocketMessage<Login.Data> | |||
public sealed class Login2 : WebSocketMessage<Login2.Data> | |||
{ | |||
public Login2() : base(1) { } | |||
public class Data | |||
{ | |||
public class PCData | |||
public class SocketInfo | |||
{ | |||
[JsonProperty(PropertyName = "address")] | |||
public string Address; | |||
@@ -45,8 +66,8 @@ namespace Discord.API.Models | |||
} | |||
[JsonProperty(PropertyName = "protocol")] | |||
public string Protocol = "udp"; | |||
[JsonProperty(PropertyName = "token")] | |||
public string Token; | |||
[JsonProperty(PropertyName = "data")] | |||
public SocketInfo SocketData = new SocketInfo(); | |||
} | |||
} | |||
} | |||
@@ -8,5 +8,24 @@ namespace Discord.API.Models | |||
{ | |||
internal static class VoiceWebSocketEvents | |||
{ | |||
public sealed class Ready | |||
{ | |||
[JsonProperty(PropertyName = "ssrc")] | |||
public int SSRC; | |||
[JsonProperty(PropertyName = "port")] | |||
public int Port; | |||
[JsonProperty(PropertyName = "modes")] | |||
public string[] Modes; | |||
[JsonProperty(PropertyName = "heartbeat_interval")] | |||
public int HeartbeatInterval; | |||
} | |||
public sealed class JoinServer | |||
{ | |||
[JsonProperty(PropertyName = "secret_key")] | |||
public byte[] SecretKey; | |||
[JsonProperty(PropertyName = "mode")] | |||
public string Mode; | |||
} | |||
} | |||
} |
@@ -249,6 +249,18 @@ namespace Discord | |||
} | |||
//Voice | |||
public event EventHandler VoiceConnected; | |||
private void RaiseVoiceConnected() | |||
{ | |||
if (VoiceConnected != null) | |||
VoiceConnected(this, EventArgs.Empty); | |||
} | |||
public event EventHandler VoiceDisconnected; | |||
private void RaiseVoiceDisconnected() | |||
{ | |||
if (VoiceDisconnected != null) | |||
VoiceDisconnected(this, EventArgs.Empty); | |||
} | |||
public sealed class VoiceServerUpdatedEventArgs : EventArgs | |||
{ | |||
public readonly Server Server; | |||
@@ -19,6 +19,7 @@ namespace Discord | |||
{ | |||
private readonly DiscordClientConfig _config; | |||
private readonly DiscordTextWebSocket _webSocket; | |||
private readonly DiscordVoiceWebSocket _voiceWebSocket; | |||
private readonly ManualResetEventSlim _blockEvent; | |||
private readonly Regex _userRegex, _channelRegex; | |||
private readonly MatchEvaluator _userRegexEvaluator, _channelRegexEvaluator; | |||
@@ -26,12 +27,8 @@ namespace Discord | |||
private readonly Random _rand; | |||
private volatile CancellationTokenSource _disconnectToken; | |||
private volatile Task _tasks; | |||
#if !DNXCORE50 | |||
private readonly DiscordVoiceWebSocket _voiceWebSocket; | |||
private string _currentVoiceServer, _currentVoiceToken; | |||
#endif | |||
private volatile Task _tasks; | |||
private string _currentVoiceServerId, _currentVoiceEndpoint, _currentVoiceToken; | |||
/// <summary> Returns the User object for the current logged in user. </summary> | |||
public User User { get; private set; } | |||
@@ -64,6 +61,9 @@ namespace Discord | |||
/// <remarks> This collection does not guarantee any ordering. </remarks> | |||
public IEnumerable<Role> Roles => _roles; | |||
private readonly AsyncCache<Role, API.Models.Role> _roles; | |||
public string CurrentVoiceServerId { get { return _currentVoiceEndpoint != null ? _currentVoiceToken : null; } } | |||
public Server CurrentVoiceServer => _servers[CurrentVoiceServerId]; | |||
/// <summary> Returns true if the user has successfully logged in and the websocket connection has been established. </summary> | |||
public bool IsConnected => _isConnected; | |||
@@ -306,34 +306,15 @@ namespace Discord | |||
} | |||
}; | |||
_webSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Message); | |||
#if !DNXCORE50 | |||
_voiceWebSocket = new DiscordVoiceWebSocket(_config.WebSocketInterval); | |||
_voiceWebSocket.Connected += (s, e) => RaiseConnected(); | |||
_voiceWebSocket.Disconnected += async (s, e) => | |||
_voiceWebSocket.Connected += (s, e) => RaiseVoiceConnected(); | |||
_voiceWebSocket.Disconnected += (s, e) => | |||
{ | |||
//Reconnect if we didn't cause the disconnect | |||
RaiseDisconnected(); | |||
while (!_disconnectToken.IsCancellationRequested) | |||
{ | |||
try | |||
{ | |||
await Task.Delay(_config.ReconnectDelay); | |||
await _voiceWebSocket.ConnectAsync(Endpoints.WebSocket_Hub); | |||
if (_currentVoiceServer != null) | |||
await _voiceWebSocket.Login(_currentVoiceServer, UserId, SessionId, _currentVoiceToken); | |||
break; | |||
} | |||
catch (Exception ex) | |||
{ | |||
RaiseOnDebugMessage($"Reconnect Failed: {ex.Message}"); | |||
//Net is down? We can keep trying to reconnect until the user runs Disconnect() | |||
await Task.Delay(_config.FailedReconnectDelay); | |||
} | |||
} | |||
//TODO: Reconnect if we didn't cause the disconnect | |||
RaiseVoiceDisconnected(); | |||
}; | |||
_voiceWebSocket.OnDebugMessage += (s, e) => RaiseOnVoiceDebugMessage(e.Message); | |||
#endif | |||
#pragma warning disable CS1998 //Disable unused async keyword warning | |||
_webSocket.GotEvent += async (s, e) => | |||
@@ -587,16 +568,14 @@ namespace Discord | |||
var server = _servers[data.ServerId]; | |||
server.VoiceServer = data.Endpoint; | |||
try { RaiseVoiceServerUpdated(server, data.Endpoint); } catch { } | |||
#if !DNXCORE50 | |||
if (data.ServerId == _currentVoiceServer) | |||
if (data.ServerId == _currentVoiceServerId) | |||
{ | |||
_currentVoiceServer = data.Endpoint; | |||
_currentVoiceEndpoint = data.Endpoint.Split(':')[0]; | |||
_currentVoiceToken = data.Token; | |||
await _voiceWebSocket.ConnectAsync(_currentVoiceServer); | |||
await _voiceWebSocket.Login(_currentVoiceServer, UserId, SessionId, _currentVoiceToken); | |||
await _voiceWebSocket.ConnectAsync("wss://" + _currentVoiceEndpoint); | |||
await _voiceWebSocket.Login(_currentVoiceServerId, UserId, SessionId, _currentVoiceToken); | |||
} | |||
#endif | |||
} | |||
break; | |||
@@ -667,7 +646,6 @@ namespace Discord | |||
catch { } | |||
} | |||
/// <summary> Returns the user with the specified id, or null if none was found. </summary> | |||
public User GetUser(string id) => _users[id]; | |||
/// <summary> Returns the user with the specified name and discriminator, or null if none was found. </summary> | |||
@@ -918,9 +896,7 @@ namespace Discord | |||
_tasks = _tasks.ContinueWith(async x => | |||
{ | |||
await _webSocket.DisconnectAsync(); | |||
#if !DNXCORE50 | |||
await _voiceWebSocket.DisconnectAsync(); | |||
#endif | |||
//Clear send queue | |||
Message ignored; | |||
@@ -1252,6 +1228,29 @@ namespace Discord | |||
//Voice | |||
public Task JoinVoice(Server server, Channel channel) | |||
=> JoinVoice(server.Id, channel.Id); | |||
public Task JoinVoice(Server server, string channelId) | |||
=> JoinVoice(server.Id, channelId); | |||
public Task JoinVoice(string serverId, Channel channel) | |||
=> JoinVoice(serverId, channel.Id); | |||
public async Task JoinVoice(string serverId, string channelId) | |||
{ | |||
await LeaveVoice(); | |||
_currentVoiceServerId = serverId; | |||
_webSocket.JoinVoice(serverId, channelId); | |||
} | |||
public async Task LeaveVoice() | |||
{ | |||
await _voiceWebSocket.DisconnectAsync(); | |||
if (_currentVoiceEndpoint != null) | |||
_webSocket.LeaveVoice(); | |||
_currentVoiceEndpoint = null; | |||
_currentVoiceServerId = null; | |||
_currentVoiceToken = null; | |||
} | |||
/// <summary> Mutes a user on the provided server. </summary> | |||
public Task Mute(Server server, User user) | |||
=> Mute(server.Id, user.Id); | |||
@@ -1,9 +1,11 @@ | |||
using Discord.API.Models; | |||
using Discord.Helpers; | |||
using Newtonsoft.Json; | |||
using Newtonsoft.Json.Linq; | |||
using System; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using WebSocketMessage = Discord.API.Models.TextWebSocketCommands.WebSocketMessage; | |||
namespace Discord | |||
{ | |||
@@ -51,22 +53,25 @@ namespace Discord | |||
SetConnected(); | |||
} | |||
protected override void ProcessMessage(WebSocketMessage msg) | |||
protected override void ProcessMessage(string json) | |||
{ | |||
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(json); | |||
switch (msg.Operation) | |||
{ | |||
case 0: | |||
if (msg.Type == "READY") | |||
{ | |||
var payload = (msg.Payload as JToken).ToObject<TextWebSocketEvents.Ready>(); | |||
_heartbeatInterval = payload.HeartbeatInterval; | |||
QueueMessage(new TextWebSocketCommands.UpdateStatus()); | |||
QueueMessage(new TextWebSocketCommands.KeepAlive()); | |||
_connectWaitOnLogin.Set(); //Pre-Event | |||
if (msg.Type == "READY") | |||
{ | |||
var payload = (msg.Payload as JToken).ToObject<TextWebSocketEvents.Ready>(); | |||
_heartbeatInterval = payload.HeartbeatInterval; | |||
QueueMessage(new TextWebSocketCommands.UpdateStatus()); | |||
QueueMessage(GetKeepAlive()); | |||
_connectWaitOnLogin.Set(); //Pre-Event | |||
} | |||
RaiseGotEvent(msg.Type, msg.Payload as JToken); | |||
if (msg.Type == "READY") | |||
_connectWaitOnLogin2.Set(); //Post-Event | |||
} | |||
RaiseGotEvent(msg.Type, msg.Payload as JToken); | |||
if (msg.Type == "READY") | |||
_connectWaitOnLogin2.Set(); //Post-Event | |||
break; | |||
default: | |||
RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation); | |||
@@ -74,7 +79,7 @@ namespace Discord | |||
} | |||
} | |||
protected override WebSocketMessage GetKeepAlive() | |||
protected override object GetKeepAlive() | |||
{ | |||
return new TextWebSocketCommands.KeepAlive(); | |||
} | |||
@@ -1,12 +1,16 @@ | |||
#if !DNXCORE50 | |||
//#if !DNXCORE50 | |||
using Discord.API.Models; | |||
using Discord.Helpers; | |||
using Newtonsoft.Json; | |||
using Newtonsoft.Json.Linq; | |||
using System; | |||
using System.Collections.Concurrent; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Net.Sockets; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using WebSocketMessage = Discord.API.Models.VoiceWebSocketCommands.WebSocketMessage; | |||
namespace Discord | |||
{ | |||
@@ -14,20 +18,28 @@ namespace Discord | |||
{ | |||
private const int ReadyTimeout = 2500; //Max time in milliseconds between connecting to Discord and receiving a READY event | |||
private ManualResetEventSlim _connectWaitOnLogin, _connectWaitOnLogin2; | |||
private ManualResetEventSlim _connectWaitOnLogin; | |||
private UdpClient _udp; | |||
private ConcurrentQueue<byte[]> _sendQueue; | |||
private string _ip; | |||
public DiscordVoiceWebSocket(int interval) | |||
: base(interval) | |||
{ | |||
_connectWaitOnLogin = new ManualResetEventSlim(false); | |||
_connectWaitOnLogin2 = new ManualResetEventSlim(false); | |||
_udp = new UdpClient(); | |||
_sendQueue = new ConcurrentQueue<byte[]>(); | |||
} | |||
protected override void OnConnect() | |||
{ | |||
_udp = new UdpClient(0); | |||
} | |||
protected override void OnDisconnect() | |||
{ | |||
_udp = null; | |||
} | |||
protected override Task[] CreateTasks(CancellationToken cancelToken) | |||
{ | |||
return new Task[] | |||
@@ -37,14 +49,14 @@ namespace Discord | |||
Task.Factory.StartNew(WatcherAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default).Result | |||
}.Concat(base.CreateTasks(cancelToken)).ToArray(); | |||
} | |||
public async Task Login(string serverId, string userId, string sessionId, string token) | |||
{ | |||
var cancelToken = _disconnectToken.Token; | |||
_connectWaitOnLogin.Reset(); | |||
_connectWaitOnLogin2.Reset(); | |||
string ip = await Http.Get("http://ipinfo.io/ip"); | |||
_ip = (await Http.Get("http://ipinfo.io/ip")).Trim(); | |||
VoiceWebSocketCommands.Login msg = new VoiceWebSocketCommands.Login(); | |||
msg.Payload.ServerId = serverId; | |||
@@ -52,18 +64,17 @@ namespace Discord | |||
msg.Payload.Token = token; | |||
msg.Payload.UserId = userId; | |||
await SendMessage(msg, cancelToken); | |||
System.Diagnostics.Debug.WriteLine("<<< " + JsonConvert.SerializeObject(msg)); | |||
try | |||
{ | |||
if (!_connectWaitOnLogin.Wait(ReadyTimeout, cancelToken)) //Waiting on READY message | |||
if (!_connectWaitOnLogin.Wait(ReadyTimeout, cancelToken)) //Waiting on JoinServer message | |||
throw new Exception("No reply from Discord server"); | |||
} | |||
catch (OperationCanceledException) | |||
{ | |||
throw new InvalidOperationException("Bad Token"); | |||
} | |||
try { _connectWaitOnLogin2.Wait(cancelToken); } //Waiting on READY handler | |||
catch (OperationCanceledException) { return; } | |||
SetConnected(); | |||
} | |||
@@ -105,21 +116,57 @@ namespace Discord | |||
{ | |||
await Task.Delay(-1, _disconnectToken.Token); | |||
} | |||
catch (OperationCanceledException) { } | |||
_udp.Close(); | |||
catch (TaskCanceledException) { } | |||
#if DNXCORE50 | |||
finally { _udp.Dispose(); } | |||
#else | |||
finally { _udp.Close(); } | |||
#endif | |||
} | |||
protected override void ProcessMessage(WebSocketMessage msg) | |||
protected override void ProcessMessage(string json) | |||
{ | |||
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(json); | |||
System.Diagnostics.Debug.WriteLine(">>> " + JsonConvert.SerializeObject(msg)); | |||
switch (msg.Operation) | |||
{ | |||
case 2: | |||
{ | |||
var payload = (msg.Payload as JToken).ToObject<VoiceWebSocketEvents.Ready>(); | |||
_heartbeatInterval = payload.HeartbeatInterval; | |||
var login2 = new VoiceWebSocketCommands.Login2(); | |||
login2.Payload.Protocol = "udp"; | |||
login2.Payload.SocketData.Address = _ip; | |||
login2.Payload.SocketData.Mode = payload.Modes.Last(); | |||
login2.Payload.SocketData.Port = (_udp.Client.LocalEndPoint as IPEndPoint).Port; | |||
QueueMessage(login2); | |||
System.Diagnostics.Debug.WriteLine("<<< " + JsonConvert.SerializeObject(login2)); | |||
} | |||
break; | |||
case 4: | |||
{ | |||
var payload = (msg.Payload as JToken).ToObject<VoiceWebSocketEvents.JoinServer>(); | |||
QueueMessage(GetKeepAlive()); | |||
System.Diagnostics.Debug.WriteLine("<<< " + JsonConvert.SerializeObject(GetKeepAlive())); | |||
_connectWaitOnLogin.Set(); | |||
} | |||
break; | |||
default: | |||
RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation); | |||
break; | |||
} | |||
} | |||
private void ProcessUdpMessage(UdpReceiveResult msg) | |||
{ | |||
} | |||
protected override WebSocketMessage GetKeepAlive() | |||
protected override object GetKeepAlive() | |||
{ | |||
return new VoiceWebSocketCommands.KeepAlive(); | |||
} | |||
} | |||
} | |||
#endif | |||
//#endif |
@@ -43,6 +43,8 @@ namespace Discord | |||
_webSocket.Options.KeepAliveInterval = TimeSpan.Zero; | |||
await _webSocket.ConnectAsync(new Uri(url), cancelToken); | |||
OnConnect(); | |||
_tasks = Task.WhenAll(CreateTasks(cancelToken)) | |||
.ContinueWith(x => | |||
{ | |||
@@ -58,6 +60,8 @@ namespace Discord | |||
byte[] ignored; | |||
while (_sendQueue.TryDequeue(out ignored)) { } | |||
OnDisconnect(); | |||
if (_isConnected) | |||
{ | |||
_isConnected = false; | |||
@@ -75,6 +79,8 @@ namespace Discord | |||
try { await _tasks; } catch (NullReferenceException) { } | |||
} | |||
} | |||
protected virtual void OnConnect() { } | |||
protected virtual void OnDisconnect() { } | |||
protected void SetConnected() | |||
{ | |||
@@ -117,8 +123,7 @@ namespace Discord | |||
} | |||
while (!result.EndOfMessage); | |||
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(builder.ToString()); | |||
ProcessMessage(msg); | |||
ProcessMessage(builder.ToString()); | |||
builder.Clear(); | |||
} | |||
@@ -152,8 +157,8 @@ namespace Discord | |||
finally { _disconnectToken.Cancel(); } | |||
} | |||
protected abstract void ProcessMessage(WebSocketMessage msg); | |||
protected abstract WebSocketMessage GetKeepAlive(); | |||
protected abstract void ProcessMessage(string json); | |||
protected abstract object GetKeepAlive(); | |||
protected void QueueMessage(object message) | |||
{ | |||
@@ -7,6 +7,7 @@ using System.Reflection; | |||
using System.Net; | |||
using System.IO; | |||
using System.Globalization; | |||
using Discord.API; | |||
#if TEST_RESPONSES | |||
using System.Diagnostics; | |||
@@ -23,7 +24,9 @@ namespace Discord.Helpers | |||
#endif | |||
private static readonly HttpClient _client; | |||
private static readonly HttpMethod _patch; | |||
#if TEST_RESPONSES | |||
private static readonly JsonSerializerSettings _settings; | |||
#endif | |||
static Http() | |||
{ | |||
@@ -41,12 +44,12 @@ namespace Discord.Helpers | |||
string version = typeof(Http).GetTypeInfo().Assembly.GetName().Version.ToString(2); | |||
_client.DefaultRequestHeaders.Add("user-agent", $"Discord.Net/{version} (https://github.com/RogueException/Discord.Net)"); | |||
_settings = new JsonSerializerSettings(); | |||
#if TEST_RESPONSES | |||
_settings = new JsonSerializerSettings(); | |||
_settings.CheckAdditionalContent = true; | |||
_settings.MissingMemberHandling = MissingMemberHandling.Error; | |||
#endif | |||
} | |||
} | |||
private static string _token; | |||
public static string Token | |||
@@ -121,14 +124,18 @@ namespace Discord.Helpers | |||
where ResponseT : class | |||
{ | |||
string responseJson = await SendRequest(method, path, content, true); | |||
var response = JsonConvert.DeserializeObject<ResponseT>(responseJson, _settings); | |||
return response; | |||
#if TEST_RESPONSES | |||
if (path.StartsWith(Endpoints.BaseApi)) | |||
return JsonConvert.DeserializeObject<ResponseT>(responseJson, _settings); | |||
#endif | |||
return JsonConvert.DeserializeObject<ResponseT>(responseJson); | |||
} | |||
private static async Task<string> Send(HttpMethod method, string path, HttpContent content) | |||
{ | |||
string responseJson = await SendRequest(method, path, content, _isDebug); | |||
#if TEST_RESPONSES | |||
CheckEmptyResponse(responseJson); | |||
if (path.StartsWith(Endpoints.BaseApi)) | |||
CheckEmptyResponse(responseJson); | |||
#endif | |||
return responseJson; | |||
} | |||
@@ -54,6 +54,10 @@ namespace Discord | |||
/// <summary> Returns a collection of all channels within this server. </summary> | |||
public IEnumerable<Channel> Channels => _client.Channels.Where(x => x.ServerId == Id); | |||
/// <summary> Returns a collection of all channels within this server. </summary> | |||
public IEnumerable<Channel> TextChannels => _client.Channels.Where(x => x.ServerId == Id && x.Type == ChannelTypes.Text); | |||
/// <summary> Returns a collection of all channels within this server. </summary> | |||
public IEnumerable<Channel> VoiceChannels => _client.Channels.Where(x => x.ServerId == Id && x.Type == ChannelTypes.Voice); | |||
/// <summary> Returns a collection of all roles within this server. </summary> | |||
public IEnumerable<Role> Roles => _client.Roles.Where(x => x.ServerId == Id); | |||
@@ -78,8 +82,10 @@ namespace Discord | |||
member.VoiceChannelId = extendedModel.ChannelId; | |||
member.IsDeafened = extendedModel.IsDeafened; | |||
member.IsMuted = extendedModel.IsMuted; | |||
member.IsSelfDeafened = extendedModel.IsSelfDeafened; | |||
member.IsSelfMuted = extendedModel.IsSelfMuted; | |||
if (extendedModel.IsSelfDeafened.HasValue) | |||
member.IsSelfDeafened = extendedModel.IsSelfDeafened.Value; | |||
if (extendedModel.IsSelfMuted.HasValue) | |||
member.IsSelfMuted = extendedModel.IsSelfMuted.Value; | |||
member.IsSuppressed = extendedModel.IsSuppressed; | |||
member.SessionId = extendedModel.SessionId; | |||
member.Token = extendedModel.Token; | |||
@@ -39,6 +39,7 @@ | |||
"System.IO.Compression": "4.0.0", | |||
"System.Linq": "4.0.0", | |||
"System.Net.Requests": "4.0.10", | |||
"System.Net.Sockets": "4.0.10-beta-23019", | |||
"System.Net.WebSockets.Client": "4.0.0-beta-23123", | |||
"System.Runtime": "4.0.20", | |||
"System.Text.RegularExpressions": "4.0.10" | |||