Browse Source

Merge eeecc8486b into b3203a7a0b

pull/222/merge
Finite Reality GitHub 8 years ago
parent
commit
a6d7822549
23 changed files with 125 additions and 3 deletions
  1. +5
    -0
      src/Discord.Net/Audio/AudioMode.cs
  2. +7
    -0
      src/Discord.Net/Audio/IAudioClient.cs
  3. +7
    -0
      src/Discord.Net/Audio/Opus/OpusApplication.cs
  4. +2
    -0
      src/Discord.Net/Audio/Opus/OpusDecoder.cs
  5. +3
    -0
      src/Discord.Net/Audio/Opus/OpusEncoder.cs
  6. +3
    -1
      src/Discord.Net/Audio/Sodium/SecretBox.cs
  7. +3
    -0
      src/Discord.Net/Audio/Streams/OpusDecodeStream.cs
  8. +6
    -1
      src/Discord.Net/Audio/Streams/OpusEncodeStream.cs
  9. +11
    -0
      src/Discord.Net/Audio/Streams/RTPReadStream.cs
  10. +12
    -0
      src/Discord.Net/Audio/Streams/RTPWriteStream.cs
  11. +5
    -0
      src/Discord.Net/ConnectionState.cs
  12. +10
    -1
      src/Discord.Net/DiscordConfig.cs
  13. +5
    -0
      src/Discord.Net/Entities/Channels/ChannelType.cs
  14. +1
    -0
      src/Discord.Net/Format.cs
  15. +7
    -0
      src/Discord.Net/LogSeverity.cs
  16. +5
    -0
      src/Discord.Net/LoginState.cs
  17. +8
    -0
      src/Discord.Net/Net/Rest/DefaultRestClient.cs
  18. +5
    -0
      src/Discord.Net/Net/Rest/IRestClient.cs
  19. +1
    -0
      src/Discord.Net/Net/Rest/RestClientProvider.cs
  20. +4
    -0
      src/Discord.Net/Net/RpcException.cs
  21. +3
    -0
      src/Discord.Net/RequestOptions.cs
  22. +10
    -0
      src/Discord.Net/Rest/DiscordRestClient.cs
  23. +2
    -0
      src/Discord.Net/Rest/DiscordRestConfig.cs

+ 5
- 0
src/Discord.Net/Audio/AudioMode.cs View File

@@ -2,12 +2,17 @@


namespace Discord.Audio namespace Discord.Audio
{ {
/// <summary> Specifies an audio mode for Discord. </summary>
[Flags] [Flags]
public enum AudioMode : byte public enum AudioMode : byte
{ {
/// <summary> Audio send/receive is disabled. </summary>
Disabled = 0, Disabled = 0,
/// <summary> Audio can only be broadcasted by the client. </summary>
Outgoing = 1, Outgoing = 1,
/// <summary> Audio can only be received by the client. </summary>
Incoming = 2, Incoming = 2,
/// <summary> Audio can be sent and received by the client. </summary>
Both = Outgoing | Incoming Both = Outgoing | Incoming
} }
} }

+ 7
- 0
src/Discord.Net/Audio/IAudioClient.cs View File

@@ -5,19 +5,26 @@ namespace Discord.Audio
{ {
public interface IAudioClient public interface IAudioClient
{ {
/// <summary> Fired when the client connects to Discord. </summary>
event Func<Task> Connected; event Func<Task> Connected;
/// <summary> Fired when the client disconnects from Discord. </summary>
event Func<Exception, Task> Disconnected; event Func<Exception, Task> Disconnected;
/// <summary> Fired in response to a heartbeat, providing the old and new latency. </summary>
event Func<int, int, Task> LatencyUpdated; event Func<int, int, Task> LatencyUpdated;


/// <summary> Gets the API client used for communicating with Discord. </summary>
DiscordVoiceAPIClient ApiClient { get; } DiscordVoiceAPIClient ApiClient { get; }
/// <summary> Gets the current connection state of this client. </summary> /// <summary> Gets the current connection state of this client. </summary>
ConnectionState ConnectionState { get; } ConnectionState ConnectionState { get; }
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary> /// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary>
int Latency { get; } int Latency { get; }


/// <summary> Disconnects the current client from Discord. </summary>
Task DisconnectAsync(); Task DisconnectAsync();


/// <summary> Creates an Opus stream for sending raw Opus-encoded data. </summary>
RTPWriteStream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000); RTPWriteStream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000);
/// <summary> Creates a PCM stream for sending unencoded PCM data. </summary>
OpusEncodeStream CreatePCMStream(int samplesPerFrame, int? bitrate = null, OpusApplication application = OpusApplication.MusicOrMixed, int bufferSize = 4000); OpusEncodeStream CreatePCMStream(int samplesPerFrame, int? bitrate = null, OpusApplication application = OpusApplication.MusicOrMixed, int bufferSize = 4000);
} }
} }

+ 7
- 0
src/Discord.Net/Audio/Opus/OpusApplication.cs View File

@@ -1,9 +1,16 @@
namespace Discord.Audio namespace Discord.Audio
{ {
/// <summary> The types of encoding which Opus supports during encoding. </summary>
public enum OpusApplication : int public enum OpusApplication : int
{ {
/// <summary> Specifies that the <see cref="Discord.Audio.IAudioClient"/> uses
/// encoding to improve the quality of voice communication. </summary>
Voice = 2048, Voice = 2048,
/// <summary> Specifies that the <see cref="Discord.Audio.IAudioClient"/> uses
/// encoding to improve the overall quality of mixed-media audio transmission. </summary>
MusicOrMixed = 2049, MusicOrMixed = 2049,
/// <summary> Specifies that the <see cref="Discord.Audio.IAudioClient"/> uses
/// encoding to reduce overall latency. </summary>
LowLatency = 2051 LowLatency = 2051
} }
} }

+ 2
- 0
src/Discord.Net/Audio/Opus/OpusDecoder.cs View File

@@ -24,7 +24,9 @@ namespace Discord.Audio
/// <summary> Produces PCM samples from Opus-encoded audio. </summary> /// <summary> Produces PCM samples from Opus-encoded audio. </summary>
/// <param name="input">PCM samples to decode.</param> /// <param name="input">PCM samples to decode.</param>
/// <param name="inputOffset">Offset of the frame in input.</param> /// <param name="inputOffset">Offset of the frame in input.</param>
/// <param name="inputCount">Number of bytes of the frame in input.</param>
/// <param name="output">Buffer to store the decoded frame.</param> /// <param name="output">Buffer to store the decoded frame.</param>
/// <param name="outputOffset">Zero-based offset for the output.</param>
public unsafe int DecodeFrame(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset) public unsafe int DecodeFrame(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset)
{ {
int result = 0; int result = 0;


+ 3
- 0
src/Discord.Net/Audio/Opus/OpusEncoder.cs View File

@@ -31,6 +31,9 @@ namespace Discord.Audio
/// <summary> Produces Opus encoded audio from PCM samples. </summary> /// <summary> Produces Opus encoded audio from PCM samples. </summary>
/// <param name="input">PCM samples to encode.</param> /// <param name="input">PCM samples to encode.</param>
/// <param name="output">Buffer to store the encoded frame.</param> /// <param name="output">Buffer to store the encoded frame.</param>
/// <param name="inputOffset">Offset of the frame in input.</param>
/// <param name="inputCount">Number of bytes of the frame in input.</param>
/// <param name="outputOffset">Zero-based offset for the output.</param>
/// <returns>Length of the frame contained in outputBuffer.</returns> /// <returns>Length of the frame contained in outputBuffer.</returns>
public unsafe int EncodeFrame(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset) public unsafe int EncodeFrame(byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset)
{ {


+ 3
- 1
src/Discord.Net/Audio/Sodium/SecretBox.cs View File

@@ -3,13 +3,14 @@ using System.Runtime.InteropServices;


namespace Discord.Audio namespace Discord.Audio
{ {
public unsafe static class SecretBox
public unsafe static class SecretBox // TODO: should this be public?
{ {
[DllImport("libsodium", EntryPoint = "crypto_secretbox_easy", CallingConvention = CallingConvention.Cdecl)] [DllImport("libsodium", EntryPoint = "crypto_secretbox_easy", CallingConvention = CallingConvention.Cdecl)]
private static extern int SecretBoxEasy(byte* output, byte* input, long inputLength, byte[] nonce, byte[] secret); private static extern int SecretBoxEasy(byte* output, byte* input, long inputLength, byte[] nonce, byte[] secret);
[DllImport("libsodium", EntryPoint = "crypto_secretbox_open_easy", CallingConvention = CallingConvention.Cdecl)] [DllImport("libsodium", EntryPoint = "crypto_secretbox_open_easy", CallingConvention = CallingConvention.Cdecl)]
private static extern int SecretBoxOpenEasy(byte* output, byte* input, long inputLength, byte[] nonce, byte[] secret); private static extern int SecretBoxOpenEasy(byte* output, byte* input, long inputLength, byte[] nonce, byte[] secret);


/// <summary> Encrypts a payload with the given nonce and secret. </summary>
public static int Encrypt(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, byte[] nonce, byte[] secret) public static int Encrypt(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, byte[] nonce, byte[] secret)
{ {
fixed (byte* inPtr = input) fixed (byte* inPtr = input)
@@ -21,6 +22,7 @@ namespace Discord.Audio
return inputLength + 16; return inputLength + 16;
} }
} }
/// <summary> Decrypts a payload with the given nonce and secret. </summary>
public static int Decrypt(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, byte[] nonce, byte[] secret) public static int Decrypt(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, byte[] nonce, byte[] secret)
{ {
fixed (byte* inPtr = input) fixed (byte* inPtr = input)


+ 3
- 0
src/Discord.Net/Audio/Streams/OpusDecodeStream.cs View File

@@ -1,5 +1,6 @@
namespace Discord.Audio namespace Discord.Audio
{ {
/// <summary> A stream which decodes Opus frames as they are read. </summary>
public class OpusDecodeStream : RTPReadStream public class OpusDecodeStream : RTPReadStream
{ {
private readonly byte[] _buffer; private readonly byte[] _buffer;
@@ -13,12 +14,14 @@
_decoder = new OpusDecoder(samplingRate, channels); _decoder = new OpusDecoder(samplingRate, channels);
} }


/// <summary> Reads Opus-encoded frame from the stream, filling the buffer with PCM data </summary>
public override int Read(byte[] buffer, int offset, int count) public override int Read(byte[] buffer, int offset, int count)
{ {
count = _decoder.DecodeFrame(buffer, offset, count, _buffer, 0); count = _decoder.DecodeFrame(buffer, offset, count, _buffer, 0);
return base.Read(_buffer, 0, count); return base.Read(_buffer, 0, count);
} }


/// <inheritdoc/>
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
base.Dispose(disposing); base.Dispose(disposing);


+ 6
- 1
src/Discord.Net/Audio/Streams/OpusEncodeStream.cs View File

@@ -1,8 +1,11 @@
namespace Discord.Audio namespace Discord.Audio
{ {
/// <summary> A stream which encodes Opus frames as raw PCM data is written. </summary>
public class OpusEncodeStream : RTPWriteStream public class OpusEncodeStream : RTPWriteStream
{ {
public int SampleRate = 48000;
/// <summary> The sample rate of the Opus stream. </summary>
public int SampleRate = 48000; // TODO: shouldn't these be readonly?
/// <summary> The number of channels of the Opus stream. </summary>
public int Channels = 2; public int Channels = 2;
private readonly OpusEncoder _encoder; private readonly OpusEncoder _encoder;
@@ -18,12 +21,14 @@
_encoder.SetBitrate(bitrate.Value); _encoder.SetBitrate(bitrate.Value);
} }


/// <summary> Writes Opus-encoded PCM data to the stream. </summary>
public override void Write(byte[] buffer, int offset, int count) public override void Write(byte[] buffer, int offset, int count)
{ {
count = _encoder.EncodeFrame(buffer, offset, count, _buffer, 0); count = _encoder.EncodeFrame(buffer, offset, count, _buffer, 0);
base.Write(_buffer, 0, count); base.Write(_buffer, 0, count);
} }


/// <inheritdoc/>
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
base.Dispose(disposing); base.Dispose(disposing);


+ 11
- 0
src/Discord.Net/Audio/Streams/RTPReadStream.cs View File

@@ -4,14 +4,18 @@ using System.IO;


namespace Discord.Audio namespace Discord.Audio
{ {
/// <summary> A stream used for reading raw audio data from Discord. </summary>
public class RTPReadStream : Stream public class RTPReadStream : Stream
{ {
private readonly BlockingCollection<byte[]> _queuedData; //TODO: Replace with max-length ring buffer private readonly BlockingCollection<byte[]> _queuedData; //TODO: Replace with max-length ring buffer
private readonly AudioClient _audioClient; private readonly AudioClient _audioClient;
private readonly byte[] _buffer, _nonce, _secretKey; private readonly byte[] _buffer, _nonce, _secretKey;


/// <inheritdoc/>
public override bool CanRead => true; public override bool CanRead => true;
/// <inheritdoc/>
public override bool CanSeek => false; public override bool CanSeek => false;
/// <inheritdoc/>
public override bool CanWrite => true; public override bool CanWrite => true;


internal RTPReadStream(AudioClient audioClient, byte[] secretKey, int bufferSize = 4000) internal RTPReadStream(AudioClient audioClient, byte[] secretKey, int bufferSize = 4000)
@@ -23,12 +27,14 @@ namespace Discord.Audio
_nonce = new byte[24]; _nonce = new byte[24];
} }


/// <inheritdoc/>
public override int Read(byte[] buffer, int offset, int count) public override int Read(byte[] buffer, int offset, int count)
{ {
var queuedData = _queuedData.Take(); var queuedData = _queuedData.Take();
Buffer.BlockCopy(queuedData, 0, buffer, offset, Math.Min(queuedData.Length, count)); Buffer.BlockCopy(queuedData, 0, buffer, offset, Math.Min(queuedData.Length, count));
return queuedData.Length; return queuedData.Length;
} }
/// <inheritdoc/>
public override void Write(byte[] buffer, int offset, int count) public override void Write(byte[] buffer, int offset, int count)
{ {
Buffer.BlockCopy(buffer, 0, _nonce, 0, 12); Buffer.BlockCopy(buffer, 0, _nonce, 0, 12);
@@ -38,16 +44,21 @@ namespace Discord.Audio
_queuedData.Add(newBuffer); _queuedData.Add(newBuffer);
} }


/// <inheritdoc/>
public override void Flush() { throw new NotSupportedException(); } public override void Flush() { throw new NotSupportedException(); }


/// <inheritdoc/>
public override long Length { get { throw new NotSupportedException(); } } public override long Length { get { throw new NotSupportedException(); } }
/// <inheritdoc/>
public override long Position public override long Position
{ {
get { throw new NotSupportedException(); } get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); } set { throw new NotSupportedException(); }
} }


/// <inheritdoc/>
public override void SetLength(long value) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); }
/// <inheritdoc/>
public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
} }
} }

+ 12
- 0
src/Discord.Net/Audio/Streams/RTPWriteStream.cs View File

@@ -3,6 +3,7 @@ using System.IO;


namespace Discord.Audio namespace Discord.Audio
{ {
/// <summary> A stream used for writing raw audio data to Discord. </summary>
public class RTPWriteStream : Stream public class RTPWriteStream : Stream
{ {
private readonly AudioClient _audioClient; private readonly AudioClient _audioClient;
@@ -10,10 +11,14 @@ namespace Discord.Audio
private int _samplesPerFrame; private int _samplesPerFrame;
private uint _ssrc, _timestamp = 0; private uint _ssrc, _timestamp = 0;


/// <summary> The current output buffer. </summary>
protected readonly byte[] _buffer; protected readonly byte[] _buffer;


/// <inheritdoc/>
public override bool CanRead => false; public override bool CanRead => false;
/// <inheritdoc/>
public override bool CanSeek => false; public override bool CanSeek => false;
/// <inheritdoc/>
public override bool CanWrite => true; public override bool CanWrite => true;


internal RTPWriteStream(AudioClient audioClient, byte[] secretKey, int samplesPerFrame, uint ssrc, int bufferSize = 4000) internal RTPWriteStream(AudioClient audioClient, byte[] secretKey, int samplesPerFrame, uint ssrc, int bufferSize = 4000)
@@ -32,6 +37,7 @@ namespace Discord.Audio
_nonce[11] = (byte)(_ssrc >> 0); _nonce[11] = (byte)(_ssrc >> 0);
} }


/// <inheritdoc/>
public override void Write(byte[] buffer, int offset, int count) public override void Write(byte[] buffer, int offset, int count)
{ {
unchecked unchecked
@@ -51,17 +57,23 @@ namespace Discord.Audio
_audioClient.Send(_buffer, count + 12); _audioClient.Send(_buffer, count + 12);
} }


/// <inheritdoc/>
public override void Flush() { } public override void Flush() { }


/// <inheritdoc/>
public override long Length { get { throw new NotSupportedException(); } } public override long Length { get { throw new NotSupportedException(); } }
/// <inheritdoc/>
public override long Position public override long Position
{ {
get { throw new NotSupportedException(); } get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); } set { throw new NotSupportedException(); }
} }


/// <inheritdoc/>
public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
/// <inheritdoc/>
public override void SetLength(long value) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); }
/// <inheritdoc/>
public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
} }
} }

+ 5
- 0
src/Discord.Net/ConnectionState.cs View File

@@ -1,10 +1,15 @@
namespace Discord namespace Discord
{ {
/// <summary> Connection state for clients </summary>
public enum ConnectionState : byte public enum ConnectionState : byte
{ {
/// <summary> Not connected to Discord </summary>
Disconnected, Disconnected,
/// <summary> Currently connecting to Discord </summary>
Connecting, Connecting,
/// <summary> Connected to Discord </summary>
Connected, Connected,
/// <summary> Disconnecting from Discord </summary>
Disconnecting Disconnecting
} }
} }

+ 10
- 1
src/Discord.Net/DiscordConfig.cs View File

@@ -2,20 +2,29 @@


namespace Discord namespace Discord
{ {
/// <summary> Stores common configuration settings </summary>
public class DiscordConfig public class DiscordConfig
{ {
public const int APIVersion = 6;
/// <summary> The version of Discord's REST API which is used </summary>
public const int APIVersion = 6;
/// <summary> Version information about Discord.Net </summary>
public static string Version { get; } = public static string Version { get; } =
typeof(DiscordConfig).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? typeof(DiscordConfig).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ??
typeof(DiscordConfig).GetTypeInfo().Assembly.GetName().Version.ToString(3) ?? typeof(DiscordConfig).GetTypeInfo().Assembly.GetName().Version.ToString(3) ??
"Unknown"; "Unknown";


/// <summary> The base URL for all REST API requests </summary>
public static readonly string ClientAPIUrl = $"https://discordapp.com/api/v{APIVersion}/"; public static readonly string ClientAPIUrl = $"https://discordapp.com/api/v{APIVersion}/";
/// <summary> The base URL for all CDN requests </summary>
public const string CDNUrl = "https://discordcdn.com/"; public const string CDNUrl = "https://discordcdn.com/";
/// <summary> The base URL for all invite links </summary>
public const string InviteUrl = "https://discord.gg/"; public const string InviteUrl = "https://discord.gg/";


/// <summary> The maximum amount of characters which can be sent in a message </summary>
public const int MaxMessageSize = 2000; public const int MaxMessageSize = 2000;
/// <summary> The maximum number of messages which can be received in a batch </summary>
public const int MaxMessagesPerBatch = 100; public const int MaxMessagesPerBatch = 100;
/// <summary> The maximum number of users which can be received in a batch </summary>
public const int MaxUsersPerBatch = 1000; public const int MaxUsersPerBatch = 1000;


/// <summary> Gets or sets the minimum log level severity that will be sent to the LogMessage event. </summary> /// <summary> Gets or sets the minimum log level severity that will be sent to the LogMessage event. </summary>


+ 5
- 0
src/Discord.Net/Entities/Channels/ChannelType.cs View File

@@ -1,10 +1,15 @@
namespace Discord namespace Discord
{ {
/// <summary> Specifies the type of channel a message was sent to or eceived from. </summary>
public enum ChannelType public enum ChannelType
{ {
///<summary> A text channel </summary>
Text = 0, Text = 0,
///<summary> A direct-message text channel </summary>
DM = 1, DM = 1,
///<summary> A voice channel </summary>
Voice = 2, Voice = 2,
///<summary> A group channel </summary>
Group = 3 Group = 3
} }
} }

+ 1
- 0
src/Discord.Net/Format.cs View File

@@ -1,5 +1,6 @@
namespace Discord namespace Discord
{ {
/// <summary> Contains common macros for formatting text using Markdown </summary>
public static class Format public static class Format
{ {
// Characters which need escaping // Characters which need escaping


+ 7
- 0
src/Discord.Net/LogSeverity.cs View File

@@ -1,12 +1,19 @@
namespace Discord namespace Discord
{ {
/// <summary> The severity of a log message </summary>
public enum LogSeverity public enum LogSeverity
{ {
/// <summary> Used when a critical, non-recoverable error occurs </summary>
Critical = 0, Critical = 0,
/// <summary> Used when a recoverable error occurs </summary>
Error = 1, Error = 1,
/// <summary> Used when a warning occurs </summary>
Warning = 2, Warning = 2,
/// <summary> Used for general, informative messages </summary>
Info = 3, Info = 3,
/// <summary> Used for debugging purposes </summary>
Verbose = 4, Verbose = 4,
/// <summary> Used for debugging purposes </summary>
Debug = 5 Debug = 5
} }
} }

+ 5
- 0
src/Discord.Net/LoginState.cs View File

@@ -1,10 +1,15 @@
namespace Discord namespace Discord
{ {
/// <summary> Login state for clients </summary>
public enum LoginState : byte public enum LoginState : byte
{ {
/// <summary> Logged out </summary>
LoggedOut, LoggedOut,
/// <summary> Logging in </summary>
LoggingIn, LoggingIn,
/// <summary> Logged in </summary>
LoggedIn, LoggedIn,
/// <summary> Logging out </summary>
LoggingOut LoggingOut
} }
} }

+ 8
- 0
src/Discord.Net/Net/Rest/DefaultRestClient.cs View File

@@ -13,6 +13,7 @@ using System.Threading.Tasks;


namespace Discord.Net.Rest namespace Discord.Net.Rest
{ {
///<summary> A default implementation of a <see cref="IRestClient"/> </summary>
public sealed class DefaultRestClient : IRestClient public sealed class DefaultRestClient : IRestClient
{ {
private const int HR_SECURECHANNELFAILED = -2146233079; private const int HR_SECURECHANNELFAILED = -2146233079;
@@ -24,6 +25,7 @@ namespace Discord.Net.Rest
private CancellationToken _cancelToken, _parentToken; private CancellationToken _cancelToken, _parentToken;
private bool _isDisposed; private bool _isDisposed;


/// <summary> Creates a new instance of <see cref="DefaultRestClient"/> </summary>
public DefaultRestClient(string baseUrl) public DefaultRestClient(string baseUrl)
{ {
_baseUrl = baseUrl; _baseUrl = baseUrl;
@@ -50,29 +52,34 @@ namespace Discord.Net.Rest
_isDisposed = true; _isDisposed = true;
} }
} }
/// <summary> Disposes any resources allocated by this instance. </summary>
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);
} }


/// <inheritdoc/>
public void SetHeader(string key, string value) public void SetHeader(string key, string value)
{ {
_client.DefaultRequestHeaders.Remove(key); _client.DefaultRequestHeaders.Remove(key);
if (value != null) if (value != null)
_client.DefaultRequestHeaders.Add(key, value); _client.DefaultRequestHeaders.Add(key, value);
} }
/// <inheritdoc/>
public void SetCancelToken(CancellationToken cancelToken) public void SetCancelToken(CancellationToken cancelToken)
{ {
_parentToken = cancelToken; _parentToken = cancelToken;
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token;
} }


/// <inheritdoc/>
public async Task<Stream> SendAsync(string method, string endpoint, bool headerOnly = false) public async Task<Stream> SendAsync(string method, string endpoint, bool headerOnly = false)
{ {
string uri = Path.Combine(_baseUrl, endpoint); string uri = Path.Combine(_baseUrl, endpoint);
using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) using (var restRequest = new HttpRequestMessage(GetMethod(method), uri))
return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false); return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false);
} }
/// <inheritdoc/>
public async Task<Stream> SendAsync(string method, string endpoint, string json, bool headerOnly = false) public async Task<Stream> SendAsync(string method, string endpoint, string json, bool headerOnly = false)
{ {
string uri = Path.Combine(_baseUrl, endpoint); string uri = Path.Combine(_baseUrl, endpoint);
@@ -82,6 +89,7 @@ namespace Discord.Net.Rest
return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false); return await SendInternalAsync(restRequest, headerOnly).ConfigureAwait(false);
} }
} }
/// <inheritdoc/>
public async Task<Stream> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly = false) public async Task<Stream> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly = false)
{ {
string uri = Path.Combine(_baseUrl, endpoint); string uri = Path.Combine(_baseUrl, endpoint);


+ 5
- 0
src/Discord.Net/Net/Rest/IRestClient.cs View File

@@ -8,11 +8,16 @@ namespace Discord.Net.Rest
//TODO: Add docstrings //TODO: Add docstrings
public interface IRestClient public interface IRestClient
{ {
/// <summary> Sets a header to be used in REST requests. </summary>
void SetHeader(string key, string value); void SetHeader(string key, string value);
/// <summary> Sets the global cancellation token for any requests made by this instance. </summary>
void SetCancelToken(CancellationToken cancelToken); void SetCancelToken(CancellationToken cancelToken);


/// <summary> Sends a request with no body to the given endpoint. </summary>
Task<Stream> SendAsync(string method, string endpoint, bool headerOnly = false); Task<Stream> SendAsync(string method, string endpoint, bool headerOnly = false);
/// <summary> Sends a request with a body to the given endpoint. </summary>
Task<Stream> SendAsync(string method, string endpoint, string json, bool headerOnly = false); Task<Stream> SendAsync(string method, string endpoint, string json, bool headerOnly = false);
/// <summary> Sends a multipart request with the given parameters to the given endpoint. </summary>
Task<Stream> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly = false); Task<Stream> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly = false);
} }
} }

+ 1
- 0
src/Discord.Net/Net/Rest/RestClientProvider.cs View File

@@ -1,4 +1,5 @@
namespace Discord.Net.Rest namespace Discord.Net.Rest
{ {
/// <summary> A delegate for creating a user-defined implementation of <see cref="IRestClient"/> </summary>
public delegate IRestClient RestClientProvider(string baseUrl); public delegate IRestClient RestClientProvider(string baseUrl);
} }

+ 4
- 0
src/Discord.Net/Net/RpcException.cs View File

@@ -2,11 +2,15 @@


namespace Discord namespace Discord
{ {
/// <summary> An exception thrown whenever an RPC error occurs. </summary>
public class RpcException : Exception public class RpcException : Exception
{ {
/// <summary> The code for this error. </summary>
public int ErrorCode { get; } public int ErrorCode { get; }
/// <summary> The reason this error occured. </summary>
public string Reason { get; } public string Reason { get; }


/// <summary> Creates a new instance of <see cref="RpcException"/> </summary>
public RpcException(int errorCode, string reason = null) public RpcException(int errorCode, string reason = null)
: base($"The server sent error {errorCode}{(reason != null ? $": \"{reason}\"" : "")}") : base($"The server sent error {errorCode}{(reason != null ? $": \"{reason}\"" : "")}")
{ {


+ 3
- 0
src/Discord.Net/RequestOptions.cs View File

@@ -1,12 +1,15 @@
namespace Discord namespace Discord
{ {
/// <summary> Contains options specific to requests </summary>
public class RequestOptions public class RequestOptions
{ {
/// <summary> Returns the default options for a request. </summary>
public static RequestOptions Default => new RequestOptions(); public static RequestOptions Default => new RequestOptions();


/// <summary> The max time, in milliseconds, to wait for this request to complete. If null, a request will not time out. If a rate limit has been triggered for this request's bucket and will not be unpaused in time, this request will fail immediately. </summary> /// <summary> The max time, in milliseconds, to wait for this request to complete. If null, a request will not time out. If a rate limit has been triggered for this request's bucket and will not be unpaused in time, this request will fail immediately. </summary>
public int? Timeout { get; set; } public int? Timeout { get; set; }


/// <summary> Creates a new instance of the RequestOptions class </summary>
public RequestOptions() public RequestOptions()
{ {
Timeout = 30000; Timeout = 30000;


+ 10
- 0
src/Discord.Net/Rest/DiscordRestClient.cs View File

@@ -15,15 +15,19 @@ using Discord.WebSocket;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary> A client which invokes Discord's REST API. </summary>
public class DiscordRestClient : IDiscordClient public class DiscordRestClient : IDiscordClient
{ {
private readonly object _eventLock = new object(); private readonly object _eventLock = new object();


/// <summary> Fired whenever a message is logged. </summary>
public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } }
private readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>(); private readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>();


/// <summary> Fired whenever the client logs in. </summary>
public event Func<Task> LoggedIn { add { _loggedInEvent.Add(value); } remove { _loggedInEvent.Remove(value); } } public event Func<Task> LoggedIn { add { _loggedInEvent.Add(value); } remove { _loggedInEvent.Remove(value); } }
private readonly AsyncEvent<Func<Task>> _loggedInEvent = new AsyncEvent<Func<Task>>(); private readonly AsyncEvent<Func<Task>> _loggedInEvent = new AsyncEvent<Func<Task>>();
/// <summary> Fired whenever the client logs out. </summary>
public event Func<Task> LoggedOut { add { _loggedOutEvent.Add(value); } remove { _loggedOutEvent.Remove(value); } } public event Func<Task> LoggedOut { add { _loggedOutEvent.Add(value); } remove { _loggedOutEvent.Remove(value); } }
private readonly AsyncEvent<Func<Task>> _loggedOutEvent = new AsyncEvent<Func<Task>>(); private readonly AsyncEvent<Func<Task>> _loggedOutEvent = new AsyncEvent<Func<Task>>();


@@ -33,12 +37,15 @@ namespace Discord.Rest
private bool _isFirstLogSub; private bool _isFirstLogSub;
internal bool _isDisposed; internal bool _isDisposed;


/// <summary> The API client used for making API calls. </summary>
public API.DiscordRestApiClient ApiClient { get; } public API.DiscordRestApiClient ApiClient { get; }
internal LogManager LogManager { get; } internal LogManager LogManager { get; }
/// <summary> The current login state of the client. </summary>
public LoginState LoginState { get; private set; } public LoginState LoginState { get; private set; }


/// <summary> Creates a new REST-only discord client. </summary> /// <summary> Creates a new REST-only discord client. </summary>
public DiscordRestClient() : this(new DiscordRestConfig()) { } public DiscordRestClient() : this(new DiscordRestConfig()) { }
/// <summary> Creates a new REST-only discord client. </summary>
public DiscordRestClient(DiscordRestConfig config) : this(config, CreateApiClient(config)) { } public DiscordRestClient(DiscordRestConfig config) : this(config, CreateApiClient(config)) { }
/// <summary> Creates a new REST-only discord client. </summary> /// <summary> Creates a new REST-only discord client. </summary>
internal DiscordRestClient(DiscordRestConfig config, API.DiscordRestApiClient client) internal DiscordRestClient(DiscordRestConfig config, API.DiscordRestApiClient client)
@@ -103,6 +110,7 @@ namespace Discord.Rest


await _loggedInEvent.InvokeAsync().ConfigureAwait(false); await _loggedInEvent.InvokeAsync().ConfigureAwait(false);
} }
/// <summary> Validates a token with the given type. </summary>
protected virtual async Task ValidateTokenAsync(TokenType tokenType, string token) protected virtual async Task ValidateTokenAsync(TokenType tokenType, string token)
{ {
try try
@@ -121,6 +129,7 @@ namespace Discord.Rest
throw new ArgumentException("Token validation failed", nameof(token), ex); throw new ArgumentException("Token validation failed", nameof(token), ex);
} }
} }
/// <summary> A Promise for when the client successfully logs in. </summary>
protected virtual Task OnLoginAsync(TokenType tokenType, string token) => Task.CompletedTask; protected virtual Task OnLoginAsync(TokenType tokenType, string token) => Task.CompletedTask;




@@ -149,6 +158,7 @@ namespace Discord.Rest


await _loggedOutEvent.InvokeAsync().ConfigureAwait(false); await _loggedOutEvent.InvokeAsync().ConfigureAwait(false);
} }
/// <summary> A Promise for when the client successfully logs out. </summary>
protected virtual Task OnLogoutAsync() => Task.CompletedTask; protected virtual Task OnLogoutAsync() => Task.CompletedTask;


/// <inheritdoc /> /// <inheritdoc />


+ 2
- 0
src/Discord.Net/Rest/DiscordRestConfig.cs View File

@@ -2,8 +2,10 @@


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary> A set of common configuration options for REST clients. </summary>
public class DiscordRestConfig : DiscordConfig public class DiscordRestConfig : DiscordConfig
{ {
/// <summary> Gets the user agent used in REST API calls </summary>
public static string UserAgent { get; } = $"DiscordBot (https://github.com/RogueException/Discord.Net, v{Version})"; public static string UserAgent { get; } = $"DiscordBot (https://github.com/RogueException/Discord.Net, v{Version})";
internal const int RestTimeout = 10000; internal const int RestTimeout = 10000;


Loading…
Cancel
Save