Browse Source

Added connecting to voice chat.

tags/docs-0.9
Brandon Smith 9 years ago
parent
commit
c6af57974b
13 changed files with 231 additions and 109 deletions
  1. +1
    -1
      global.json
  2. +2
    -26
      src/Discord.Net/API/Models/Common.cs
  3. +24
    -0
      src/Discord.Net/API/Models/TextWebSocketCommands.cs
  4. +27
    -6
      src/Discord.Net/API/Models/VoiceWebSocketCommands.cs
  5. +19
    -0
      src/Discord.Net/API/Models/VoiceWebSocketEvents.cs
  6. +12
    -0
      src/Discord.Net/DiscordClient.Events.cs
  7. +39
    -40
      src/Discord.Net/DiscordClient.cs
  8. +16
    -11
      src/Discord.Net/DiscordTextWebSocket.cs
  9. +61
    -14
      src/Discord.Net/DiscordVoiceWebSocket.cs
  10. +9
    -4
      src/Discord.Net/DiscordWebSocket.cs
  11. +12
    -5
      src/Discord.Net/Helpers/Http.cs
  12. +8
    -2
      src/Discord.Net/Server.cs
  13. +1
    -0
      src/Discord.Net/project.json

+ 1
- 1
global.json View File

@@ -3,6 +3,6 @@
"sdk": {
"version": "1.0.0-beta6",
"architecture": "x64",
"runtime": "clr"
"runtime": "coreclr"
}
}

+ 2
- 26
src/Discord.Net/API/Models/Common.cs View File

@@ -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")]


+ 24
- 0
src/Discord.Net/API/Models/TextWebSocketCommands.cs View File

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


+ 27
- 6
src/Discord.Net/API/Models/VoiceWebSocketCommands.cs View File

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


+ 19
- 0
src/Discord.Net/API/Models/VoiceWebSocketEvents.cs View File

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

+ 12
- 0
src/Discord.Net/DiscordClient.Events.cs View File

@@ -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;


+ 39
- 40
src/Discord.Net/DiscordClient.cs View File

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


+ 16
- 11
src/Discord.Net/DiscordTextWebSocket.cs View File

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


+ 61
- 14
src/Discord.Net/DiscordVoiceWebSocket.cs View File

@@ -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

+ 9
- 4
src/Discord.Net/DiscordWebSocket.cs View File

@@ -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)
{


+ 12
- 5
src/Discord.Net/Helpers/Http.cs View File

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


+ 8
- 2
src/Discord.Net/Server.cs View File

@@ -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;


+ 1
- 0
src/Discord.Net/project.json View File

@@ -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"


Loading…
Cancel
Save