@@ -151,6 +151,9 @@ | |||
<Compile Include="..\Discord.Net\DiscordAPIClient.cs"> | |||
<Link>DiscordAPIClient.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordAPIClientConfig.cs"> | |||
<Link>DiscordAPIClientConfig.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordClient.API.cs"> | |||
<Link>DiscordClient.API.cs</Link> | |||
</Compile> | |||
@@ -11,12 +11,15 @@ namespace Discord | |||
/// <summary> A lightweight wrapper around the Discord API. </summary> | |||
public class DiscordAPIClient | |||
{ | |||
private readonly DiscordAPIClientConfig _config; | |||
internal RestClient RestClient => _rest; | |||
private readonly RestClient _rest; | |||
public DiscordAPIClient(LogMessageSeverity logLevel, string userAgent, int timeout) | |||
public DiscordAPIClient(DiscordAPIClientConfig config = null) | |||
{ | |||
_rest = new RestClient(logLevel, userAgent, timeout); | |||
_config = config ?? new DiscordAPIClientConfig(); | |||
_rest = new RestClient(_config); | |||
} | |||
private string _token; | |||
@@ -0,0 +1,50 @@ | |||
using System; | |||
using System.Net; | |||
using System.Reflection; | |||
namespace Discord | |||
{ | |||
public class DiscordAPIClientConfig | |||
{ | |||
/// <summary> Specifies the minimum log level severity that will be sent to the LogMessage event. Warning: setting this to debug will really hurt performance but should help investigate any internal issues. </summary> | |||
public LogMessageSeverity LogLevel { get { return _logLevel; } set { SetValue(ref _logLevel, value); } } | |||
private LogMessageSeverity _logLevel = LogMessageSeverity.Info; | |||
/// <summary> Max time (in milliseconds) to wait for an API request to complete. </summary> | |||
public int APITimeout { get { return _apiTimeout; } set { SetValue(ref _apiTimeout, value); } } | |||
private int _apiTimeout = 10000; | |||
/// <summary> The proxy to user for API and WebSocket connections. </summary> | |||
public string ProxyUrl { get { return _proxyUrl; } set { SetValue(ref _proxyUrl, value); } } | |||
private string _proxyUrl = null; | |||
/// <summary> The credentials to use for this proxy. </summary> | |||
public NetworkCredential ProxyCredentials { get { return _proxyCredentials; } set { SetValue(ref _proxyCredentials, value); } } | |||
private NetworkCredential _proxyCredentials = null; | |||
internal string UserAgent | |||
{ | |||
get | |||
{ | |||
string version = typeof(DiscordClientConfig).GetTypeInfo().Assembly.GetName().Version.ToString(2); | |||
return $"Discord.Net/{version} (https://github.com/RogueException/Discord.Net)"; | |||
} | |||
} | |||
//Lock | |||
protected bool _isLocked; | |||
internal void Lock() { _isLocked = true; } | |||
protected void SetValue<T>(ref T storage, T value) | |||
{ | |||
if (_isLocked) | |||
throw new InvalidOperationException("Unable to modify a discord client's configuration after it has been created."); | |||
storage = value; | |||
} | |||
public DiscordAPIClientConfig Clone() | |||
{ | |||
var config = MemberwiseClone() as DiscordAPIClientConfig; | |||
config._isLocked = false; | |||
return config; | |||
} | |||
} | |||
} |
@@ -55,7 +55,7 @@ namespace Discord | |||
: base(config ?? new DiscordClientConfig()) | |||
{ | |||
_rand = new Random(); | |||
_api = new DiscordAPIClient(_config.LogLevel, _config.UserAgent, _config.APITimeout); | |||
_api = new DiscordAPIClient(_config); | |||
if (Config.UseMessageQueue) | |||
_pendingMessages = new ConcurrentQueue<Message>(); | |||
if (Config.EnableVoiceMultiserver) | |||
@@ -765,7 +765,6 @@ namespace Discord | |||
{ | |||
var config = _config.Clone(); | |||
config.LogLevel = _config.LogLevel;// (LogMessageSeverity)Math.Min((int)_config.LogLevel, (int)LogMessageSeverity.Warning); | |||
config.EnableVoiceMultiserver = false; | |||
config.VoiceOnly = true; | |||
config.VoiceClientId = unchecked(++_nextVoiceClientId); | |||
return new DiscordWebSocketClient(config, serverId); | |||
@@ -25,7 +25,7 @@ | |||
public new DiscordClientConfig Clone() | |||
{ | |||
var config = this.MemberwiseClone() as DiscordClientConfig; | |||
var config = MemberwiseClone() as DiscordClientConfig; | |||
config._isLocked = false; | |||
return config; | |||
} | |||
@@ -12,12 +12,8 @@ namespace Discord | |||
Both = Outgoing | Incoming | |||
} | |||
public class DiscordWebSocketClientConfig | |||
public class DiscordWebSocketClientConfig : DiscordAPIClientConfig | |||
{ | |||
/// <summary> Specifies the minimum log level severity that will be sent to the LogMessage event. Warning: setting this to debug will really hurt performance but should help investigate any internal issues. </summary> | |||
public LogMessageSeverity LogLevel { get { return _logLevel; } set { SetValue(ref _logLevel, value); } } | |||
private LogMessageSeverity _logLevel = LogMessageSeverity.Info; | |||
/// <summary> Max time in milliseconds to wait for DiscordClient to connect and initialize. </summary> | |||
public int ConnectionTimeout { get { return _connectionTimeout; } set { SetValue(ref _connectionTimeout, value); } } | |||
private int _connectionTimeout = 30000; | |||
@@ -27,9 +23,6 @@ namespace Discord | |||
/// <summary> Gets or sets the time (in milliseconds) to wait after an reconnect fails before retrying. </summary> | |||
public int FailedReconnectDelay { get { return _failedReconnectDelay; } set { SetValue(ref _failedReconnectDelay, value); } } | |||
private int _failedReconnectDelay = 10000; | |||
/// <summary> Max time (in milliseconds) to wait for an API request to complete. </summary> | |||
public int APITimeout { get { return _apiTimeout; } set { SetValue(ref _apiTimeout, value); } } | |||
private int _apiTimeout = 10000; | |||
/// <summary> Gets or sets the time (in milliseconds) to wait when the websocket's message queue is empty before checking again. </summary> | |||
public int WebSocketInterval { get { return _webSocketInterval; } set { SetValue(ref _webSocketInterval, value); } } | |||
@@ -54,28 +47,9 @@ namespace Discord | |||
internal virtual bool EnableVoice => _voiceMode != DiscordVoiceMode.Disabled; | |||
internal string UserAgent | |||
{ | |||
get | |||
{ | |||
string version = typeof(DiscordClientConfig).GetTypeInfo().Assembly.GetName().Version.ToString(2); | |||
return $"Discord.Net/{version} (https://github.com/RogueException/Discord.Net)"; | |||
} | |||
} | |||
//Lock | |||
protected bool _isLocked; | |||
internal void Lock() { _isLocked = true; } | |||
protected void SetValue<T>(ref T storage, T value) | |||
{ | |||
if (_isLocked) | |||
throw new InvalidOperationException("Unable to modify a discord client's configuration after it has been created."); | |||
storage = value; | |||
} | |||
public DiscordClientConfig Clone() | |||
public new DiscordWebSocketClientConfig Clone() | |||
{ | |||
var config = this.MemberwiseClone() as DiscordClientConfig; | |||
var config = MemberwiseClone() as DiscordWebSocketClientConfig; | |||
config._isLocked = false; | |||
return config; | |||
} | |||
@@ -1,6 +1,7 @@ | |||
using Discord.API; | |||
using RestSharp; | |||
using System; | |||
using System.Net; | |||
using System.Net.Http; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
@@ -11,18 +12,19 @@ namespace Discord.Net | |||
{ | |||
private RestSharp.RestClient _client; | |||
partial void Initialize(string userAgent, int timeout) | |||
partial void Initialize() | |||
{ | |||
_client = new RestSharp.RestClient(Endpoints.BaseApi) | |||
{ | |||
PreAuthenticate = false | |||
PreAuthenticate = false, | |||
Proxy = new WebProxy(_config.ProxyUrl, true, new string[0], _config.ProxyCredentials), | |||
ReadWriteTimeout = _config.APITimeout, | |||
UserAgent = _config.UserAgent | |||
}; | |||
_client.RemoveDefaultParameter("Accept"); | |||
_client.AddDefaultHeader("accept", "*/*"); | |||
_client.AddDefaultHeader("accept-encoding", "gzip,deflate"); | |||
_client.UserAgent = userAgent; | |||
_client.ReadWriteTimeout = timeout; | |||
} | |||
} | |||
internal void SetToken(string token) | |||
{ | |||
@@ -10,15 +10,15 @@ namespace Discord.Net | |||
{ | |||
internal partial class RestClient | |||
{ | |||
private readonly LogMessageSeverity _logLevel; | |||
private readonly DiscordAPIClientConfig _config; | |||
private CancellationToken _cancelToken; | |||
public RestClient(LogMessageSeverity logLevel, string userAgent, int timeout) | |||
public RestClient(DiscordAPIClientConfig config) | |||
{ | |||
_logLevel = logLevel; | |||
Initialize(userAgent, timeout); | |||
_config = config; | |||
Initialize(); | |||
} | |||
partial void Initialize(string userAgent, int timeout); | |||
partial void Initialize(); | |||
//DELETE | |||
private static readonly HttpMethod _delete = HttpMethod.Delete; | |||
@@ -90,7 +90,7 @@ namespace Discord.Net | |||
if (content != null) | |||
requestJson = JsonConvert.SerializeObject(content); | |||
if (_logLevel >= LogMessageSeverity.Verbose) | |||
if (_config.LogLevel >= LogMessageSeverity.Verbose) | |||
stopwatch = Stopwatch.StartNew(); | |||
string responseJson = await SendInternal(method, path, requestJson, _cancelToken).ConfigureAwait(false); | |||
@@ -100,10 +100,10 @@ namespace Discord.Net | |||
throw new Exception("API check failed: Response is not empty."); | |||
#endif | |||
if (_logLevel >= LogMessageSeverity.Verbose) | |||
if (_config.LogLevel >= LogMessageSeverity.Verbose) | |||
{ | |||
stopwatch.Stop(); | |||
if (content != null && _logLevel >= LogMessageSeverity.Debug) | |||
if (content != null && _config.LogLevel >= LogMessageSeverity.Debug) | |||
{ | |||
if (path.StartsWith(Endpoints.Auth)) | |||
RaiseOnRequest(method, path, "[Hidden]", stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond); | |||
@@ -129,7 +129,7 @@ namespace Discord.Net | |||
{ | |||
Stopwatch stopwatch = null; | |||
if (_logLevel >= LogMessageSeverity.Verbose) | |||
if (_config.LogLevel >= LogMessageSeverity.Verbose) | |||
stopwatch = Stopwatch.StartNew(); | |||
string responseJson = await SendFileInternal(method, path, filePath, _cancelToken).ConfigureAwait(false); | |||
@@ -139,10 +139,10 @@ namespace Discord.Net | |||
throw new Exception("API check failed: Response is not empty."); | |||
#endif | |||
if (_logLevel >= LogMessageSeverity.Verbose) | |||
if (_config.LogLevel >= LogMessageSeverity.Verbose) | |||
{ | |||
stopwatch.Stop(); | |||
if (_logLevel >= LogMessageSeverity.Debug) | |||
if (_config.LogLevel >= LogMessageSeverity.Debug) | |||
RaiseOnRequest(method, path, filePath, stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond); | |||
else | |||
RaiseOnRequest(method, path, null, stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond); | |||
@@ -1,6 +1,7 @@ | |||
using System; | |||
using System.Collections.Concurrent; | |||
using System.Collections.Generic; | |||
using System.Net; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using WSSharpNWebSocket = WebSocketSharp.WebSocket; | |||
@@ -9,9 +10,8 @@ namespace Discord.Net | |||
{ | |||
internal class WSSharpWebSocketEngine : IWebSocketEngine | |||
{ | |||
private readonly DiscordWebSocketClientConfig _config; | |||
private readonly ConcurrentQueue<string> _sendQueue; | |||
private readonly int _sendInterval; | |||
private readonly string _userAgent; | |||
private readonly WebSocket _parent; | |||
private WSSharpNWebSocket _webSocket; | |||
@@ -22,11 +22,10 @@ namespace Discord.Net | |||
ProcessMessage(this, new WebSocketMessageEventArgs(msg)); | |||
} | |||
internal WSSharpWebSocketEngine(WebSocket parent, string userAgent, int sendInterval) | |||
internal WSSharpWebSocketEngine(WebSocket parent, DiscordWebSocketClientConfig config) | |||
{ | |||
_parent = parent; | |||
_userAgent = userAgent; | |||
_sendInterval = sendInterval; | |||
_config = config; | |||
_sendQueue = new ConcurrentQueue<string>(); | |||
} | |||
@@ -35,7 +34,8 @@ namespace Discord.Net | |||
_webSocket = new WSSharpNWebSocket(host); | |||
_webSocket.EmitOnPing = false; | |||
_webSocket.EnableRedirection = true; | |||
_webSocket.Compression = WebSocketSharp.CompressionMethod.None; | |||
_webSocket.Compression = WebSocketSharp.CompressionMethod.None; | |||
_webSocket.SetProxy(_config.ProxyUrl, _config.ProxyCredentials.UserName, _config.ProxyCredentials.Password); | |||
_webSocket.OnMessage += (s, e) => RaiseProcessMessage(e.Data); | |||
_webSocket.OnError += async (s, e) => | |||
{ | |||
@@ -77,6 +77,7 @@ namespace Discord.Net | |||
private Task SendAsync(CancellationToken cancelToken) | |||
{ | |||
var sendInterval = _config.WebSocketInterval; | |||
return Task.Run(async () => | |||
{ | |||
try | |||
@@ -86,7 +87,7 @@ namespace Discord.Net | |||
string json; | |||
while (_sendQueue.TryDequeue(out json)) | |||
_webSocket.Send(json); | |||
await Task.Delay(_sendInterval, cancelToken).ConfigureAwait(false); | |||
await Task.Delay(sendInterval, cancelToken).ConfigureAwait(false); | |||
} | |||
} | |||
catch (OperationCanceledException) { } | |||
@@ -65,7 +65,7 @@ namespace Discord.Net | |||
_cancelToken = new CancellationToken(true); | |||
_connectedEvent = new ManualResetEventSlim(false); | |||
_engine = new WSSharpWebSocketEngine(this, client.Config.UserAgent, client.Config.WebSocketInterval); | |||
_engine = new WSSharpWebSocketEngine(this, client.Config); | |||
_engine.ProcessMessage += async (s, e) => | |||
{ | |||
if (_logLevel >= LogMessageSeverity.Debug) | |||