@@ -1,6 +1,6 @@ | |||
Microsoft Visual Studio Solution File, Format Version 12.00 | |||
# Visual Studio 15 | |||
VisualStudioVersion = 15.0.26228.4 | |||
VisualStudioVersion = 15.0.26711.1 | |||
MinimumVisualStudioVersion = 10.0.40219.1 | |||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.csproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" | |||
EndProject | |||
@@ -14,8 +14,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Commands", "src | |||
EndProject | |||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.WebSocket", "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj", "{688FD1D8-7F01-4539-B2E9-F473C5D699C7}" | |||
EndProject | |||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Providers", "Providers", "{B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012}" | |||
EndProject | |||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Providers.WS4Net", "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj", "{6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}" | |||
EndProject | |||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A}" | |||
@@ -24,6 +22,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Tests", "test\D | |||
EndProject | |||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Webhook", "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj", "{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}" | |||
EndProject | |||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{1876A445-1C70-4F84-912B-4D3CEA21E0C3}" | |||
EndProject | |||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Serialization", "src\Discord.Net.Serialization\Discord.Net.Serialization.csproj", "{AA3B67BE-767E-4230-9810-F7948B6AE689}" | |||
EndProject | |||
Global | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
Debug|Any CPU = Debug|Any CPU | |||
@@ -130,6 +132,18 @@ Global | |||
{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x64.Build.0 = Release|Any CPU | |||
{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.ActiveCfg = Release|Any CPU | |||
{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.Build.0 = Release|Any CPU | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Debug|x64.ActiveCfg = Debug|Any CPU | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Debug|x64.Build.0 = Debug|Any CPU | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Debug|x86.ActiveCfg = Debug|Any CPU | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Debug|x86.Build.0 = Debug|Any CPU | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Release|Any CPU.Build.0 = Release|Any CPU | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Release|x64.ActiveCfg = Release|Any CPU | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Release|x64.Build.0 = Release|Any CPU | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Release|x86.ActiveCfg = Release|Any CPU | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Release|x86.Build.0 = Release|Any CPU | |||
EndGlobalSection | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
@@ -139,7 +153,11 @@ Global | |||
{5688A353-121E-40A1-8BFA-B17B91FB48FB} = {288C363D-A636-4EAE-9AC1-4698B641B26E} | |||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} | |||
{688FD1D8-7F01-4539-B2E9-F473C5D699C7} = {288C363D-A636-4EAE-9AC1-4698B641B26E} | |||
{6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} | |||
{6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {1876A445-1C70-4F84-912B-4D3CEA21E0C3} | |||
{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} | |||
{AA3B67BE-767E-4230-9810-F7948B6AE689} = {1876A445-1C70-4F84-912B-4D3CEA21E0C3} | |||
EndGlobalSection | |||
GlobalSection(ExtensibilityGlobals) = postSolution | |||
SolutionGuid = {9828C525-49C7-48F4-A9E7-94E223052DA2} | |||
EndGlobalSection | |||
EndGlobal |
@@ -1,7 +1,8 @@ | |||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<PropertyGroup> | |||
<VersionPrefix>1.0.2</VersionPrefix> | |||
<VersionPrefix>1.1.0-alpha</VersionPrefix> | |||
<VersionSuffix></VersionSuffix> | |||
<LangVersion>latest</LangVersion> | |||
<Authors>RogueException</Authors> | |||
<PackageTags>discord;discordapp</PackageTags> | |||
<PackageProjectUrl>https://github.com/RogueException/Discord.Net</PackageProjectUrl> | |||
@@ -18,7 +19,7 @@ | |||
<VersionSuffix Condition=" '$(VersionSuffix)' == '' ">build-$(BuildNumber)</VersionSuffix> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' Or '$(TargetFramework)' == 'net45' "> | |||
<DefineConstants>$(DefineConstants);FILESYSTEM;DEFAULTUDPCLIENT;DEFAULTWEBSOCKET</DefineConstants> | |||
<DefineConstants>$(DefineConstants);FILESYSTEM;DEFAULTUDPCLIENT;DEFAULTWEBSOCKET;MSBUFFER</DefineConstants> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' "> | |||
<DefineConstants>$(DefineConstants);FORMATSTR;UNIXTIME;MSTRYBUFFER;UDPDISPOSE</DefineConstants> | |||
@@ -309,14 +309,14 @@ namespace Discord.Commands | |||
if (match.Command.Parameters.Count > 0) | |||
{ | |||
var argValuesSum = parseResult.ArgValues?.Sum(x => x.Values.OrderByDescending(y => y.Score).FirstOrDefault().Score) ?? 0; | |||
var paramValuesSum = parseResult.ParamValues?.Sum(x => x.Values.OrderByDescending(y => y.Score).FirstOrDefault().Score) ?? 0; | |||
float argValuesSum = parseResult.ArgValues?.Sum(x => x.Values.OrderByDescending(y => y.Score).FirstOrDefault().Score) ?? 0; | |||
float paramValuesSum = parseResult.ParamValues?.Sum(x => x.Values.OrderByDescending(y => y.Score).FirstOrDefault().Score) ?? 0; | |||
argValuesScore = argValuesSum / match.Command.Parameters.Count; | |||
paramValuesScore = paramValuesSum / match.Command.Parameters.Count; | |||
} | |||
var totalArgsScore = (argValuesScore + paramValuesScore) / 2; | |||
float totalArgsScore = (argValuesScore + paramValuesScore) / 2; | |||
return match.Command.Priority + totalArgsScore * 0.99f; | |||
} | |||
@@ -31,5 +31,8 @@ namespace Discord.Audio | |||
AudioOutStream CreatePCMStream(AudioApplication application, int? bitrate = null, int bufferMillis = 1000, int packetLoss = 30); | |||
/// <summary>Creates a new direct outgoing stream accepting PCM (raw) data. This is a direct stream with no internal timer.</summary> | |||
AudioOutStream CreateDirectPCMStream(AudioApplication application, int? bitrate = null, int packetLoss = 30); | |||
/// <summary>Recycles an RTPFrame's payload buffer. Do not call more than once for a given frame.</summary> | |||
void RecycleFrame(RTPFrame frame); | |||
} | |||
} |
@@ -5,7 +5,7 @@ namespace Discord.Audio | |||
public readonly ushort Sequence; | |||
public readonly uint Timestamp; | |||
public readonly byte[] Payload; | |||
public readonly bool Missed; | |||
public readonly bool Missed; | |||
public RTPFrame(ushort sequence, uint timestamp, byte[] payload, bool missed) | |||
{ | |||
@@ -7,8 +7,13 @@ | |||
<TargetFrameworks>net45;netstandard1.1;netstandard1.3</TargetFrameworks> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" /> | |||
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" /> | |||
<PackageReference Include="System.Buffers" Version="4.4.0" /> | |||
<PackageReference Include="System.Collections.Immutable" Version="1.4.0" /> | |||
<PackageReference Include="System.Interactive.Async" Version="3.1.1" /> | |||
<PackageReference Include="System.Memory" Version="4.4.0-preview2-25405-01" /> | |||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\Discord.Net.Serialization\Discord.Net.Serialization.csproj" /> | |||
</ItemGroup> | |||
</Project> |
@@ -1,8 +1,13 @@ | |||
namespace Discord | |||
using Discord.Serialization; | |||
namespace Discord | |||
{ | |||
[ModelStringEnum] | |||
public enum PermissionTarget | |||
{ | |||
[ModelEnumValue("role")] | |||
Role, | |||
[ModelEnumValue("member")] | |||
User | |||
} | |||
} |
@@ -7,13 +7,16 @@ namespace Discord | |||
public struct Image | |||
{ | |||
public Stream Stream { get; } | |||
public ImageFormat Format { get; } | |||
/// <summary> | |||
/// Create the image with a Stream. | |||
/// </summary> | |||
/// <param name="stream">This must be some type of stream with the contents of a file in it.</param> | |||
public Image(Stream stream) | |||
public Image(Stream stream, ImageFormat format = ImageFormat.Jpeg) | |||
{ | |||
Stream = stream; | |||
Format = format; | |||
} | |||
#if FILESYSTEM | |||
/// <summary> | |||
@@ -23,9 +26,10 @@ namespace Discord | |||
/// This file path is NOT validated, and is passed directly into a <see cref="File.OpenRead(string)"/> | |||
/// </remarks> | |||
/// <param name="path">The path to the file.</param> | |||
public Image(string path) | |||
public Image(string path, ImageFormat format = ImageFormat.Jpeg) | |||
{ | |||
Stream = File.OpenRead(path); | |||
Format = format; | |||
} | |||
#endif | |||
} | |||
@@ -1,13 +1,16 @@ | |||
namespace Discord | |||
using Discord.Serialization; | |||
namespace Discord | |||
{ | |||
[ModelStringEnum] | |||
public enum EmbedType | |||
{ | |||
Rich, | |||
Link, | |||
Video, | |||
Image, | |||
Gifv, | |||
Article, | |||
Tweet | |||
[ModelEnumValue("rich")] Rich, | |||
[ModelEnumValue("link")] Link, | |||
[ModelEnumValue("video")] Video, | |||
[ModelEnumValue("image")] Image, | |||
[ModelEnumValue("gifv")] Gifv, | |||
[ModelEnumValue("article")] Article, | |||
[ModelEnumValue("tweet")] Tweet | |||
} | |||
} |
@@ -1,4 +1,6 @@ | |||
namespace Discord | |||
using Discord.Serialization; | |||
namespace Discord | |||
{ | |||
public struct Overwrite | |||
{ | |||
@@ -1,12 +1,21 @@ | |||
namespace Discord | |||
using Discord.Serialization; | |||
namespace Discord | |||
{ | |||
[ModelStringEnum] | |||
public enum UserStatus | |||
{ | |||
[ModelEnumValue("offline", EnumValueType.ReadOnly)] | |||
Offline, | |||
[ModelEnumValue("online")] | |||
Online, | |||
[ModelEnumValue("idle")] | |||
Idle, | |||
[ModelEnumValue("idle", EnumValueType.WriteOnly)] | |||
AFK, | |||
[ModelEnumValue("dnd")] | |||
DoNotDisturb, | |||
[ModelEnumValue("invisible", EnumValueType.WriteOnly)] | |||
Invisible, | |||
} | |||
} |
@@ -3,7 +3,7 @@ | |||
public static class Format | |||
{ | |||
// Characters which need escaping | |||
private static string[] SensitiveCharacters = { "\\", "*", "_", "~", "`" }; | |||
private static string[] _sensitiveCharacters = { "\\", "*", "_", "~", "`" }; | |||
/// <summary> Returns a markdown-formatted string with bold formatting. </summary> | |||
public static string Bold(string text) => $"**{text}**"; | |||
@@ -26,7 +26,7 @@ | |||
/// <summary> Sanitizes the string, safely escaping any Markdown sequences. </summary> | |||
public static string Sanitize(string text) | |||
{ | |||
foreach (string unsafeChar in SensitiveCharacters) | |||
foreach (string unsafeChar in _sensitiveCharacters) | |||
text = text.Replace(unsafeChar, $"\\{unsafeChar}"); | |||
return text; | |||
} | |||
@@ -1,3 +1,4 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
@@ -10,7 +11,7 @@ namespace Discord.Net.Rest | |||
void SetCancelToken(CancellationToken cancelToken); | |||
Task<RestResponse> SendAsync(string method, string endpoint, CancellationToken cancelToken, bool headerOnly = false, string reason = null); | |||
Task<RestResponse> SendAsync(string method, string endpoint, string json, CancellationToken cancelToken, bool headerOnly = false, string reason = null); | |||
Task<RestResponse> SendAsync(string method, string endpoint, ReadOnlyBuffer<byte> jsonPayload, CancellationToken cancelToken, bool headerOnly = false, string reason = null); | |||
Task<RestResponse> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, CancellationToken cancelToken, bool headerOnly = false, string reason = null); | |||
} | |||
} |
@@ -1,5 +1,5 @@ | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Net; | |||
namespace Discord.Net.Rest | |||
@@ -8,13 +8,13 @@ namespace Discord.Net.Rest | |||
{ | |||
public HttpStatusCode StatusCode { get; } | |||
public Dictionary<string, string> Headers { get; } | |||
public Stream Stream { get; } | |||
public ReadOnlyBuffer<byte> Data { get; } | |||
public RestResponse(HttpStatusCode statusCode, Dictionary<string, string> headers, Stream stream) | |||
public RestResponse(HttpStatusCode statusCode, Dictionary<string, string> headers, ReadOnlyBuffer<byte> data) | |||
{ | |||
StatusCode = statusCode; | |||
Headers = headers; | |||
Stream = stream; | |||
Data = data; | |||
} | |||
} | |||
} |
@@ -6,8 +6,7 @@ namespace Discord.Net.WebSockets | |||
{ | |||
public interface IWebSocketClient | |||
{ | |||
event Func<byte[], int, int, Task> BinaryMessage; | |||
event Func<string, Task> TextMessage; | |||
event Func<ReadOnlyBuffer<byte>, bool, Task> Message; | |||
event Func<Exception, Task> Closed; | |||
void SetHeader(string key, string value); | |||
@@ -16,6 +15,6 @@ namespace Discord.Net.WebSockets | |||
Task ConnectAsync(string host); | |||
Task DisconnectAsync(); | |||
Task SendAsync(byte[] data, int index, int count, bool isText); | |||
Task SendAsync(ReadOnlyBuffer<byte> data, bool isText); | |||
} | |||
} |
@@ -3,6 +3,7 @@ using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Text.Utf8; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using WebSocket4Net; | |||
@@ -12,8 +13,7 @@ namespace Discord.Net.Providers.WS4Net | |||
{ | |||
internal class WS4NetClient : IWebSocketClient, IDisposable | |||
{ | |||
public event Func<byte[], int, int, Task> BinaryMessage; | |||
public event Func<string, Task> TextMessage; | |||
public event Func<ReadOnlyBuffer<byte>, bool, Task> Message; | |||
public event Func<Exception, Task> Closed; | |||
private readonly SemaphoreSlim _lock; | |||
@@ -129,15 +129,20 @@ namespace Discord.Net.Providers.WS4Net | |||
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; | |||
} | |||
public async Task SendAsync(byte[] data, int index, int count, bool isText) | |||
public async Task SendAsync(ReadOnlyBuffer<byte> data, bool isText) | |||
{ | |||
await _lock.WaitAsync(_cancelToken).ConfigureAwait(false); | |||
try | |||
{ | |||
if (isText) | |||
_client.Send(Encoding.UTF8.GetString(data, index, count)); | |||
_client.Send(new Utf8String(data.Span).ToString()); | |||
else | |||
_client.Send(data, index, count); | |||
{ | |||
if (data.DangerousTryGetArray(out var array)) | |||
_client.Send(array.Array, 0, data.Length); | |||
else | |||
_client.Send(data.ToArray(), 0, data.Length); | |||
} | |||
} | |||
finally | |||
{ | |||
@@ -147,11 +152,12 @@ namespace Discord.Net.Providers.WS4Net | |||
private void OnTextMessage(object sender, MessageReceivedEventArgs e) | |||
{ | |||
TextMessage(e.Message).GetAwaiter().GetResult(); | |||
//TODO: Inefficient, but were dropping this plugin ASAP | |||
Message(new ReadOnlyBuffer<byte>(Encoding.UTF8.GetBytes(e.Message)), true).GetAwaiter().GetResult(); | |||
} | |||
private void OnBinaryMessage(object sender, DataReceivedEventArgs e) | |||
{ | |||
BinaryMessage(e.Data, 0, e.Data.Count()).GetAwaiter().GetResult(); | |||
Message(new ReadOnlyBuffer<byte>(e.Data, 0, e.Data.Count()), false).GetAwaiter().GetResult(); | |||
} | |||
private void OnConnected(object sender, object e) | |||
{ | |||
@@ -1,24 +1,24 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Application | |||
{ | |||
[JsonProperty("description")] | |||
[ModelProperty("description")] | |||
public string Description { get; set; } | |||
[JsonProperty("rpc_origins")] | |||
[ModelProperty("rpc_origins")] | |||
public string[] RPCOrigins { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("icon")] | |||
[ModelProperty("icon")] | |||
public string Icon { get; set; } | |||
[JsonProperty("flags"), Int53] | |||
[ModelProperty("flags"), Int53] | |||
public Optional<ulong> Flags { get; set; } | |||
[JsonProperty("owner")] | |||
[ModelProperty("owner")] | |||
public Optional<User> Owner { get; set; } | |||
} | |||
} |
@@ -1,23 +1,23 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Attachment | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("filename")] | |||
[ModelProperty("filename")] | |||
public string Filename { get; set; } | |||
[JsonProperty("size")] | |||
[ModelProperty("size")] | |||
public int Size { get; set; } | |||
[JsonProperty("url")] | |||
[ModelProperty("url")] | |||
public string Url { get; set; } | |||
[JsonProperty("proxy_url")] | |||
[ModelProperty("proxy_url")] | |||
public string ProxyUrl { get; set; } | |||
[JsonProperty("height")] | |||
[ModelProperty("height")] | |||
public Optional<int> Height { get; set; } | |||
[JsonProperty("width")] | |||
[ModelProperty("width")] | |||
public Optional<int> Width { get; set; } | |||
} | |||
} |
@@ -1,13 +1,13 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Ban | |||
{ | |||
[JsonProperty("user")] | |||
[ModelProperty("user")] | |||
public User User { get; set; } | |||
[JsonProperty("reason")] | |||
[ModelProperty("reason")] | |||
public string Reason { get; set; } | |||
} | |||
} |
@@ -1,5 +1,5 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
using System; | |||
namespace Discord.API | |||
@@ -7,41 +7,41 @@ namespace Discord.API | |||
internal class Channel | |||
{ | |||
//Shared | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("type")] | |||
[ModelProperty("type")] | |||
public ChannelType Type { get; set; } | |||
[JsonProperty("last_message_id")] | |||
[ModelProperty("last_message_id")] | |||
public ulong? LastMessageId { get; set; } | |||
//GuildChannel | |||
[JsonProperty("guild_id")] | |||
[ModelProperty("guild_id")] | |||
public Optional<ulong> GuildId { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public Optional<string> Name { get; set; } | |||
[JsonProperty("position")] | |||
[ModelProperty("position")] | |||
public Optional<int> Position { get; set; } | |||
[JsonProperty("permission_overwrites")] | |||
[ModelProperty("permission_overwrites")] | |||
public Optional<Overwrite[]> PermissionOverwrites { get; set; } | |||
//TextChannel | |||
[JsonProperty("topic")] | |||
[ModelProperty("topic")] | |||
public Optional<string> Topic { get; set; } | |||
[JsonProperty("last_pin_timestamp")] | |||
[ModelProperty("last_pin_timestamp")] | |||
public Optional<DateTimeOffset?> LastPinTimestamp { get; set; } | |||
//VoiceChannel | |||
[JsonProperty("bitrate")] | |||
[ModelProperty("bitrate")] | |||
public Optional<int> Bitrate { get; set; } | |||
[JsonProperty("user_limit")] | |||
[ModelProperty("user_limit")] | |||
public Optional<int> UserLimit { get; set; } | |||
//PrivateChannel | |||
[JsonProperty("recipients")] | |||
[ModelProperty("recipients")] | |||
public Optional<User[]> Recipients { get; set; } | |||
//GroupChannel | |||
[JsonProperty("icon")] | |||
[ModelProperty("icon")] | |||
public Optional<string> Icon { get; set; } | |||
} | |||
} |
@@ -1,21 +1,21 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
using System.Collections.Generic; | |||
namespace Discord.API | |||
{ | |||
internal class Connection | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public string Id { get; set; } | |||
[JsonProperty("type")] | |||
[ModelProperty("type")] | |||
public string Type { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("revoked")] | |||
[ModelProperty("revoked")] | |||
public bool Revoked { get; set; } | |||
[JsonProperty("integrations")] | |||
[ModelProperty("integrations")] | |||
public IReadOnlyCollection<ulong> Integrations { get; set; } | |||
} | |||
} |
@@ -1,37 +1,36 @@ | |||
#pragma warning disable CS1591 | |||
using System; | |||
using Newtonsoft.Json; | |||
using Newtonsoft.Json.Converters; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Embed | |||
{ | |||
[JsonProperty("title")] | |||
[ModelProperty("title")] | |||
public string Title { get; set; } | |||
[JsonProperty("description")] | |||
[ModelProperty("description")] | |||
public string Description { get; set; } | |||
[JsonProperty("url")] | |||
[ModelProperty("url")] | |||
public string Url { get; set; } | |||
[JsonProperty("color")] | |||
[ModelProperty("color")] | |||
public uint? Color { get; set; } | |||
[JsonProperty("type"), JsonConverter(typeof(StringEnumConverter))] | |||
[ModelProperty("type")] | |||
public EmbedType Type { get; set; } | |||
[JsonProperty("timestamp")] | |||
[ModelProperty("timestamp")] | |||
public DateTimeOffset? Timestamp { get; set; } | |||
[JsonProperty("author")] | |||
[ModelProperty("author")] | |||
public Optional<EmbedAuthor> Author { get; set; } | |||
[JsonProperty("footer")] | |||
[ModelProperty("footer")] | |||
public Optional<EmbedFooter> Footer { get; set; } | |||
[JsonProperty("video")] | |||
[ModelProperty("video")] | |||
public Optional<EmbedVideo> Video { get; set; } | |||
[JsonProperty("thumbnail")] | |||
[ModelProperty("thumbnail")] | |||
public Optional<EmbedThumbnail> Thumbnail { get; set; } | |||
[JsonProperty("image")] | |||
[ModelProperty("image")] | |||
public Optional<EmbedImage> Image { get; set; } | |||
[JsonProperty("provider")] | |||
[ModelProperty("provider")] | |||
public Optional<EmbedProvider> Provider { get; set; } | |||
[JsonProperty("fields")] | |||
[ModelProperty("fields")] | |||
public Optional<EmbedField[]> Fields { get; set; } | |||
} | |||
} |
@@ -1,17 +1,17 @@ | |||
using System; | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class EmbedAuthor | |||
{ | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("url")] | |||
[ModelProperty("url")] | |||
public string Url { get; set; } | |||
[JsonProperty("icon_url")] | |||
[ModelProperty("icon_url")] | |||
public string IconUrl { get; set; } | |||
[JsonProperty("proxy_icon_url")] | |||
[ModelProperty("proxy_icon_url")] | |||
public string ProxyIconUrl { get; set; } | |||
} | |||
} |
@@ -1,14 +1,14 @@ | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class EmbedField | |||
{ | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("value")] | |||
[ModelProperty("value")] | |||
public string Value { get; set; } | |||
[JsonProperty("inline")] | |||
[ModelProperty("inline")] | |||
public bool Inline { get; set; } | |||
} | |||
} |
@@ -1,15 +1,15 @@ | |||
using System; | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class EmbedFooter | |||
{ | |||
[JsonProperty("text")] | |||
[ModelProperty("text")] | |||
public string Text { get; set; } | |||
[JsonProperty("icon_url")] | |||
[ModelProperty("icon_url")] | |||
public string IconUrl { get; set; } | |||
[JsonProperty("proxy_icon_url")] | |||
[ModelProperty("proxy_icon_url")] | |||
public string ProxyIconUrl { get; set; } | |||
} | |||
} |
@@ -1,18 +1,18 @@ | |||
#pragma warning disable CS1591 | |||
using System; | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class EmbedImage | |||
{ | |||
[JsonProperty("url")] | |||
[ModelProperty("url")] | |||
public string Url { get; set; } | |||
[JsonProperty("proxy_url")] | |||
[ModelProperty("proxy_url")] | |||
public string ProxyUrl { get; set; } | |||
[JsonProperty("height")] | |||
[ModelProperty("height")] | |||
public Optional<int> Height { get; set; } | |||
[JsonProperty("width")] | |||
[ModelProperty("width")] | |||
public Optional<int> Width { get; set; } | |||
} | |||
} |
@@ -1,14 +1,14 @@ | |||
#pragma warning disable CS1591 | |||
using System; | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class EmbedProvider | |||
{ | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("url")] | |||
[ModelProperty("url")] | |||
public string Url { get; set; } | |||
} | |||
} |
@@ -1,18 +1,18 @@ | |||
#pragma warning disable CS1591 | |||
using System; | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class EmbedThumbnail | |||
{ | |||
[JsonProperty("url")] | |||
[ModelProperty("url")] | |||
public string Url { get; set; } | |||
[JsonProperty("proxy_url")] | |||
[ModelProperty("proxy_url")] | |||
public string ProxyUrl { get; set; } | |||
[JsonProperty("height")] | |||
[ModelProperty("height")] | |||
public Optional<int> Height { get; set; } | |||
[JsonProperty("width")] | |||
[ModelProperty("width")] | |||
public Optional<int> Width { get; set; } | |||
} | |||
} |
@@ -1,16 +1,16 @@ | |||
#pragma warning disable CS1591 | |||
using System; | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class EmbedVideo | |||
{ | |||
[JsonProperty("url")] | |||
[ModelProperty("url")] | |||
public string Url { get; set; } | |||
[JsonProperty("height")] | |||
[ModelProperty("height")] | |||
public Optional<int> Height { get; set; } | |||
[JsonProperty("width")] | |||
[ModelProperty("width")] | |||
public Optional<int> Width { get; set; } | |||
} | |||
} |
@@ -1,19 +1,19 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Emoji | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong? Id { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("roles")] | |||
[ModelProperty("roles")] | |||
public ulong[] Roles { get; set; } | |||
[JsonProperty("require_colons")] | |||
[ModelProperty("require_colons")] | |||
public bool RequireColons { get; set; } | |||
[JsonProperty("managed")] | |||
[ModelProperty("managed")] | |||
public bool Managed { get; set; } | |||
} | |||
} |
@@ -1,23 +1,15 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Newtonsoft.Json.Serialization; | |||
using System.Runtime.Serialization; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Game | |||
{ | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("url")] | |||
[ModelProperty("url")] | |||
public Optional<string> StreamUrl { get; set; } | |||
[JsonProperty("type")] | |||
[ModelProperty("type")] | |||
public Optional<StreamType?> StreamType { get; set; } | |||
[OnError] | |||
internal void OnError(StreamingContext context, ErrorContext errorContext) | |||
{ | |||
errorContext.Handled = true; | |||
} | |||
} | |||
} |
@@ -1,43 +1,43 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Guild | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("icon")] | |||
[ModelProperty("icon")] | |||
public string Icon { get; set; } | |||
[JsonProperty("splash")] | |||
[ModelProperty("splash")] | |||
public string Splash { get; set; } | |||
[JsonProperty("owner_id")] | |||
[ModelProperty("owner_id")] | |||
public ulong OwnerId { get; set; } | |||
[JsonProperty("region")] | |||
[ModelProperty("region")] | |||
public string Region { get; set; } | |||
[JsonProperty("afk_channel_id")] | |||
[ModelProperty("afk_channel_id")] | |||
public ulong? AFKChannelId { get; set; } | |||
[JsonProperty("afk_timeout")] | |||
[ModelProperty("afk_timeout")] | |||
public int AFKTimeout { get; set; } | |||
[JsonProperty("embed_enabled")] | |||
[ModelProperty("embed_enabled")] | |||
public bool EmbedEnabled { get; set; } | |||
[JsonProperty("embed_channel_id")] | |||
[ModelProperty("embed_channel_id")] | |||
public ulong? EmbedChannelId { get; set; } | |||
[JsonProperty("verification_level")] | |||
[ModelProperty("verification_level")] | |||
public VerificationLevel VerificationLevel { get; set; } | |||
[JsonProperty("voice_states")] | |||
[ModelProperty("voice_states")] | |||
public VoiceState[] VoiceStates { get; set; } | |||
[JsonProperty("roles")] | |||
[ModelProperty("roles")] | |||
public Role[] Roles { get; set; } | |||
[JsonProperty("emojis")] | |||
[ModelProperty("emojis")] | |||
public Emoji[] Emojis { get; set; } | |||
[JsonProperty("features")] | |||
[ModelProperty("features")] | |||
public string[] Features { get; set; } | |||
[JsonProperty("mfa_level")] | |||
[ModelProperty("mfa_level")] | |||
public MfaLevel MfaLevel { get; set; } | |||
[JsonProperty("default_message_notifications")] | |||
[ModelProperty("default_message_notifications")] | |||
public DefaultMessageNotifications DefaultMessageNotifications { get; set; } | |||
} | |||
} |
@@ -1,13 +1,13 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class GuildEmbed | |||
{ | |||
[JsonProperty("enabled")] | |||
[ModelProperty("enabled")] | |||
public bool Enabled { get; set; } | |||
[JsonProperty("channel_id")] | |||
[ModelProperty("channel_id")] | |||
public ulong ChannelId { get; set; } | |||
} | |||
} |
@@ -1,22 +1,22 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
using System; | |||
namespace Discord.API | |||
{ | |||
internal class GuildMember | |||
{ | |||
[JsonProperty("user")] | |||
[ModelProperty("user")] | |||
public User User { get; set; } | |||
[JsonProperty("nick")] | |||
[ModelProperty("nick")] | |||
public Optional<string> Nick { get; set; } | |||
[JsonProperty("roles")] | |||
[ModelProperty("roles")] | |||
public Optional<ulong[]> Roles { get; set; } | |||
[JsonProperty("joined_at")] | |||
[ModelProperty("joined_at")] | |||
public Optional<DateTimeOffset> JoinedAt { get; set; } | |||
[JsonProperty("deaf")] | |||
[ModelProperty("deaf")] | |||
public Optional<bool> Deaf { get; set; } | |||
[JsonProperty("mute")] | |||
[ModelProperty("mute")] | |||
public Optional<bool> Mute { get; set; } | |||
} | |||
} |
@@ -1,32 +1,32 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
using System; | |||
namespace Discord.API | |||
{ | |||
internal class Integration | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("type")] | |||
[ModelProperty("type")] | |||
public string Type { get; set; } | |||
[JsonProperty("enabled")] | |||
[ModelProperty("enabled")] | |||
public bool Enabled { get; set; } | |||
[JsonProperty("syncing")] | |||
[ModelProperty("syncing")] | |||
public bool Syncing { get; set; } | |||
[JsonProperty("role_id")] | |||
[ModelProperty("role_id")] | |||
public ulong RoleId { get; set; } | |||
[JsonProperty("expire_behavior")] | |||
[ModelProperty("expire_behavior")] | |||
public ulong ExpireBehavior { get; set; } | |||
[JsonProperty("expire_grace_period")] | |||
[ModelProperty("expire_grace_period")] | |||
public ulong ExpireGracePeriod { get; set; } | |||
[JsonProperty("user")] | |||
[ModelProperty("user")] | |||
public User User { get; set; } | |||
[JsonProperty("account")] | |||
[ModelProperty("account")] | |||
public IntegrationAccount Account { get; set; } | |||
[JsonProperty("synced_at")] | |||
[ModelProperty("synced_at")] | |||
public DateTimeOffset SyncedAt { get; set; } | |||
} | |||
} |
@@ -1,13 +1,13 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class IntegrationAccount | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
} | |||
} |
@@ -1,15 +1,15 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Invite | |||
{ | |||
[JsonProperty("code")] | |||
[ModelProperty("code")] | |||
public string Code { get; set; } | |||
[JsonProperty("guild")] | |||
[ModelProperty("guild")] | |||
public InviteGuild Guild { get; set; } | |||
[JsonProperty("channel")] | |||
[ModelProperty("channel")] | |||
public InviteChannel Channel { get; set; } | |||
} | |||
} |
@@ -1,15 +1,15 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class InviteChannel | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("type")] | |||
[ModelProperty("type")] | |||
public string Type { get; set; } | |||
} | |||
} |
@@ -1,15 +1,15 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class InviteGuild | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("splash_hash")] | |||
[ModelProperty("splash_hash")] | |||
public string SplashHash { get; set; } | |||
} | |||
} |
@@ -1,24 +1,24 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
using System; | |||
namespace Discord.API | |||
{ | |||
internal class InviteMetadata : Invite | |||
{ | |||
[JsonProperty("inviter")] | |||
[ModelProperty("inviter")] | |||
public User Inviter { get; set; } | |||
[JsonProperty("uses")] | |||
[ModelProperty("uses")] | |||
public int Uses { get; set; } | |||
[JsonProperty("max_uses")] | |||
[ModelProperty("max_uses")] | |||
public int MaxUses { get; set; } | |||
[JsonProperty("max_age")] | |||
[ModelProperty("max_age")] | |||
public int MaxAge { get; set; } | |||
[JsonProperty("temporary")] | |||
[ModelProperty("temporary")] | |||
public bool Temporary { get; set; } | |||
[JsonProperty("created_at")] | |||
[ModelProperty("created_at")] | |||
public DateTimeOffset CreatedAt { get; set; } | |||
[JsonProperty("revoked")] | |||
[ModelProperty("revoked")] | |||
public bool Revoked { get; set; } | |||
} | |||
} |
@@ -1,42 +1,42 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
using System; | |||
namespace Discord.API | |||
{ | |||
internal class Message | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("type")] | |||
[ModelProperty("type")] | |||
public MessageType Type { get; set; } | |||
[JsonProperty("channel_id")] | |||
[ModelProperty("channel_id")] | |||
public ulong ChannelId { get; set; } | |||
[JsonProperty("webhook_id")] | |||
[ModelProperty("webhook_id")] | |||
public Optional<ulong> WebhookId { get; set; } | |||
[JsonProperty("author")] | |||
[ModelProperty("author")] | |||
public Optional<User> Author { get; set; } | |||
[JsonProperty("content")] | |||
[ModelProperty("content")] | |||
public Optional<string> Content { get; set; } | |||
[JsonProperty("timestamp")] | |||
[ModelProperty("timestamp")] | |||
public Optional<DateTimeOffset> Timestamp { get; set; } | |||
[JsonProperty("edited_timestamp")] | |||
[ModelProperty("edited_timestamp")] | |||
public Optional<DateTimeOffset?> EditedTimestamp { get; set; } | |||
[JsonProperty("tts")] | |||
[ModelProperty("tts")] | |||
public Optional<bool> IsTextToSpeech { get; set; } | |||
[JsonProperty("mention_everyone")] | |||
[ModelProperty("mention_everyone")] | |||
public Optional<bool> MentionEveryone { get; set; } | |||
[JsonProperty("mentions")] | |||
[ModelProperty("mentions")] | |||
public Optional<EntityOrId<User>[]> UserMentions { get; set; } | |||
[JsonProperty("mention_roles")] | |||
[ModelProperty("mention_roles")] | |||
public Optional<ulong[]> RoleMentions { get; set; } | |||
[JsonProperty("attachments")] | |||
[ModelProperty("attachments")] | |||
public Optional<Attachment[]> Attachments { get; set; } | |||
[JsonProperty("embeds")] | |||
[ModelProperty("embeds")] | |||
public Optional<Embed[]> Embeds { get; set; } | |||
[JsonProperty("pinned")] | |||
[ModelProperty("pinned")] | |||
public Optional<bool> Pinned { get; set; } | |||
[JsonProperty("reactions")] | |||
[ModelProperty("reactions")] | |||
public Optional<Reaction[]> Reactions { get; set; } | |||
} | |||
} |
@@ -1,17 +1,17 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Overwrite | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong TargetId { get; set; } | |||
[JsonProperty("type")] | |||
[ModelProperty("type")] | |||
public PermissionTarget TargetType { get; set; } | |||
[JsonProperty("deny"), Int53] | |||
[ModelProperty("deny"), Int53] | |||
public ulong Deny { get; set; } | |||
[JsonProperty("allow"), Int53] | |||
[ModelProperty("allow"), Int53] | |||
public ulong Allow { get; set; } | |||
} | |||
} |
@@ -1,22 +1,22 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Presence | |||
{ | |||
[JsonProperty("user")] | |||
[ModelProperty("user")] | |||
public User User { get; set; } | |||
[JsonProperty("guild_id")] | |||
[ModelProperty("guild_id")] | |||
public Optional<ulong> GuildId { get; set; } | |||
[JsonProperty("status")] | |||
[ModelProperty("status")] | |||
public UserStatus Status { get; set; } | |||
[JsonProperty("game")] | |||
[ModelProperty("game")] | |||
public Game Game { get; set; } | |||
[JsonProperty("roles")] | |||
[ModelProperty("roles")] | |||
public Optional<ulong[]> Roles { get; set; } | |||
[JsonProperty("nick")] | |||
[ModelProperty("nick")] | |||
public Optional<string> Nick { get; set; } | |||
} | |||
} |
@@ -1,14 +1,14 @@ | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Reaction | |||
{ | |||
[JsonProperty("count")] | |||
[ModelProperty("count")] | |||
public int Count { get; set; } | |||
[JsonProperty("me")] | |||
[ModelProperty("me")] | |||
public bool Me { get; set; } | |||
[JsonProperty("emoji")] | |||
[ModelProperty("emoji")] | |||
public Emoji Emoji { get; set; } | |||
} | |||
} |
@@ -1,15 +1,15 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class ReadState | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("mention_count")] | |||
[ModelProperty("mention_count")] | |||
public int MentionCount { get; set; } | |||
[JsonProperty("last_message_id")] | |||
[ModelProperty("last_message_id")] | |||
public Optional<ulong> LastMessageId { get; set; } | |||
} | |||
} |
@@ -1,15 +1,15 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Relationship | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("user")] | |||
[ModelProperty("user")] | |||
public User User { get; set; } | |||
[JsonProperty("type")] | |||
[ModelProperty("type")] | |||
public RelationshipType Type { get; set; } | |||
} | |||
} |
@@ -1,25 +1,25 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class Role | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("color")] | |||
[ModelProperty("color")] | |||
public uint Color { get; set; } | |||
[JsonProperty("hoist")] | |||
[ModelProperty("hoist")] | |||
public bool Hoist { get; set; } | |||
[JsonProperty("mentionable")] | |||
[ModelProperty("mentionable")] | |||
public bool Mentionable { get; set; } | |||
[JsonProperty("position")] | |||
[ModelProperty("position")] | |||
public int Position { get; set; } | |||
[JsonProperty("permissions"), Int53] | |||
[ModelProperty("permissions"), Int53] | |||
public ulong Permissions { get; set; } | |||
[JsonProperty("managed")] | |||
[ModelProperty("managed")] | |||
public bool Managed { get; set; } | |||
} | |||
} |
@@ -1,27 +1,27 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class User | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("username")] | |||
[ModelProperty("username")] | |||
public Optional<string> Username { get; set; } | |||
[JsonProperty("discriminator")] | |||
[ModelProperty("discriminator")] | |||
public Optional<string> Discriminator { get; set; } | |||
[JsonProperty("bot")] | |||
[ModelProperty("bot")] | |||
public Optional<bool> Bot { get; set; } | |||
[JsonProperty("avatar")] | |||
[ModelProperty("avatar")] | |||
public Optional<string> Avatar { get; set; } | |||
//CurrentUser | |||
[JsonProperty("verified")] | |||
[ModelProperty("verified")] | |||
public Optional<bool> Verified { get; set; } | |||
[JsonProperty("email")] | |||
[ModelProperty("email")] | |||
public Optional<string> Email { get; set; } | |||
[JsonProperty("mfa_enabled")] | |||
[ModelProperty("mfa_enabled")] | |||
public Optional<bool> MfaEnabled { get; set; } | |||
} | |||
} |
@@ -1,19 +1,19 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class UserGuild | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("icon")] | |||
[ModelProperty("icon")] | |||
public string Icon { get; set; } | |||
[JsonProperty("owner")] | |||
[ModelProperty("owner")] | |||
public bool Owner { get; set; } | |||
[JsonProperty("permissions"), Int53] | |||
[ModelProperty("permissions"), Int53] | |||
public ulong Permissions { get; set; } | |||
} | |||
} |
@@ -1,21 +1,21 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class VoiceRegion | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public string Id { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("vip")] | |||
[ModelProperty("vip")] | |||
public bool IsVip { get; set; } | |||
[JsonProperty("optimal")] | |||
[ModelProperty("optimal")] | |||
public bool IsOptimal { get; set; } | |||
[JsonProperty("sample_hostname")] | |||
[ModelProperty("sample_hostname")] | |||
public string SampleHostname { get; set; } | |||
[JsonProperty("sample_port")] | |||
[ModelProperty("sample_port")] | |||
public int SamplePort { get; set; } | |||
} | |||
} |
@@ -1,27 +1,27 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API | |||
{ | |||
internal class VoiceState | |||
{ | |||
[JsonProperty("guild_id")] | |||
[ModelProperty("guild_id")] | |||
public ulong? GuildId { get; set; } | |||
[JsonProperty("channel_id")] | |||
[ModelProperty("channel_id")] | |||
public ulong? ChannelId { get; set; } | |||
[JsonProperty("user_id")] | |||
[ModelProperty("user_id")] | |||
public ulong UserId { get; set; } | |||
[JsonProperty("session_id")] | |||
[ModelProperty("session_id")] | |||
public string SessionId { get; set; } | |||
[JsonProperty("deaf")] | |||
[ModelProperty("deaf")] | |||
public bool Deaf { get; set; } | |||
[JsonProperty("mute")] | |||
[ModelProperty("mute")] | |||
public bool Mute { get; set; } | |||
[JsonProperty("self_deaf")] | |||
[ModelProperty("self_deaf")] | |||
public bool SelfDeaf { get; set; } | |||
[JsonProperty("self_mute")] | |||
[ModelProperty("self_mute")] | |||
public bool SelfMute { get; set; } | |||
[JsonProperty("suppress")] | |||
[ModelProperty("suppress")] | |||
public bool Suppress { get; set; } | |||
} | |||
} |
@@ -5,16 +5,19 @@ namespace Discord.API | |||
internal struct Image | |||
{ | |||
public Stream Stream { get; } | |||
public ImageFormat StreamFormat { get; } | |||
public string Hash { get; } | |||
public Image(Stream stream) | |||
public Image(Stream stream, ImageFormat format) | |||
{ | |||
Stream = stream; | |||
StreamFormat = format; | |||
Hash = null; | |||
} | |||
public Image(string hash) | |||
{ | |||
Stream = null; | |||
StreamFormat = ImageFormat.Jpeg; | |||
Hash = hash; | |||
} | |||
} | |||
@@ -1,18 +1,17 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class CreateChannelInviteParams | |||
{ | |||
[JsonProperty("max_age")] | |||
[ModelProperty("max_age")] | |||
public Optional<int> MaxAge { get; set; } | |||
[JsonProperty("max_uses")] | |||
[ModelProperty("max_uses")] | |||
public Optional<int> MaxUses { get; set; } | |||
[JsonProperty("temporary")] | |||
[ModelProperty("temporary")] | |||
public Optional<bool> IsTemporary { get; set; } | |||
[JsonProperty("unique")] | |||
[ModelProperty("unique")] | |||
public Optional<bool> IsUnique { get; set; } | |||
} | |||
} |
@@ -1,12 +1,11 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class CreateDMChannelParams | |||
{ | |||
[JsonProperty("recipient_id")] | |||
[ModelProperty("recipient_id")] | |||
public ulong RecipientId { get; } | |||
public CreateDMChannelParams(ulong recipientId) | |||
@@ -1,17 +1,16 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class CreateGuildChannelParams | |||
{ | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; } | |||
[JsonProperty("type")] | |||
[ModelProperty("type")] | |||
public ChannelType Type { get; } | |||
[JsonProperty("bitrate")] | |||
[ModelProperty("bitrate")] | |||
public Optional<int> Bitrate { get; set; } | |||
public CreateGuildChannelParams(string name, ChannelType type) | |||
@@ -1,14 +1,13 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class CreateGuildIntegrationParams | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; } | |||
[JsonProperty("type")] | |||
[ModelProperty("type")] | |||
public string Type { get; } | |||
public CreateGuildIntegrationParams(ulong id, string type) | |||
@@ -1,17 +1,16 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class CreateGuildParams | |||
{ | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public string Name { get; } | |||
[JsonProperty("region")] | |||
[ModelProperty("region")] | |||
public string RegionId { get; } | |||
[JsonProperty("icon")] | |||
[ModelProperty("icon")] | |||
public Optional<Image?> Icon { get; set; } | |||
public CreateGuildParams(string name, string regionId) | |||
@@ -1,19 +1,18 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class CreateMessageParams | |||
{ | |||
[JsonProperty("content")] | |||
[ModelProperty("content")] | |||
public string Content { get; } | |||
[JsonProperty("nonce")] | |||
[ModelProperty("nonce")] | |||
public Optional<string> Nonce { get; set; } | |||
[JsonProperty("tts")] | |||
[ModelProperty("tts")] | |||
public Optional<bool> IsTTS { get; set; } | |||
[JsonProperty("embed")] | |||
[ModelProperty("embed")] | |||
public Optional<Embed> Embed { get; set; } | |||
public CreateMessageParams(string content) | |||
@@ -1,23 +1,22 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class CreateWebhookMessageParams | |||
{ | |||
[JsonProperty("content")] | |||
[ModelProperty("content")] | |||
public string Content { get; } | |||
[JsonProperty("nonce")] | |||
[ModelProperty("nonce")] | |||
public Optional<string> Nonce { get; set; } | |||
[JsonProperty("tts")] | |||
[ModelProperty("tts")] | |||
public Optional<bool> IsTTS { get; set; } | |||
[JsonProperty("embeds")] | |||
[ModelProperty("embeds")] | |||
public Optional<Embed[]> Embeds { get; set; } | |||
[JsonProperty("username")] | |||
[ModelProperty("username")] | |||
public Optional<string> Username { get; set; } | |||
[JsonProperty("avatar_url")] | |||
[ModelProperty("avatar_url")] | |||
public Optional<string> AvatarUrl { get; set; } | |||
public CreateWebhookMessageParams(string content) | |||
@@ -1,12 +1,11 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class DeleteMessagesParams | |||
{ | |||
[JsonProperty("messages")] | |||
[ModelProperty("messages")] | |||
public ulong[] MessageIds { get; } | |||
public DeleteMessagesParams(ulong[] messageIds) | |||
@@ -1,13 +1,13 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
internal class GetBotGatewayResponse | |||
{ | |||
[JsonProperty("url")] | |||
[ModelProperty("url")] | |||
public string Url { get; set; } | |||
[JsonProperty("shards")] | |||
[ModelProperty("shards")] | |||
public int Shards { get; set; } | |||
} | |||
} |
@@ -1,11 +1,11 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
internal class GetGatewayResponse | |||
{ | |||
[JsonProperty("url")] | |||
[ModelProperty("url")] | |||
public string Url { get; set; } | |||
} | |||
} |
@@ -1,11 +1,11 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
internal class GetGuildPruneCountResponse | |||
{ | |||
[JsonProperty("pruned")] | |||
[ModelProperty("pruned")] | |||
public int Pruned { get; set; } | |||
} | |||
} |
@@ -1,12 +1,11 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class GuildPruneParams | |||
{ | |||
[JsonProperty("days")] | |||
[ModelProperty("days")] | |||
public int Days { get; } | |||
public GuildPruneParams(int days) | |||
@@ -1,16 +1,15 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyChannelPermissionsParams | |||
{ | |||
[JsonProperty("type")] | |||
[ModelProperty("type")] | |||
public string Type { get; } | |||
[JsonProperty("allow")] | |||
[ModelProperty("allow")] | |||
public ulong Allow { get; } | |||
[JsonProperty("deny")] | |||
[ModelProperty("deny")] | |||
public ulong Deny { get; } | |||
public ModifyChannelPermissionsParams(string type, ulong allow, ulong deny) | |||
@@ -1,12 +1,11 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyCurrentUserNickParams | |||
{ | |||
[JsonProperty("nick")] | |||
[ModelProperty("nick")] | |||
public string Nickname { get; } | |||
public ModifyCurrentUserNickParams(string nickname) | |||
@@ -1,14 +1,13 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyCurrentUserParams | |||
{ | |||
[JsonProperty("username")] | |||
[ModelProperty("username")] | |||
public Optional<string> Username { get; set; } | |||
[JsonProperty("avatar")] | |||
[ModelProperty("avatar")] | |||
public Optional<Image?> Avatar { get; set; } | |||
} | |||
} |
@@ -1,14 +1,13 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyGuildChannelParams | |||
{ | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public Optional<string> Name { get; set; } | |||
[JsonProperty("position")] | |||
[ModelProperty("position")] | |||
public Optional<int> Position { get; set; } | |||
} | |||
} |
@@ -1,14 +1,13 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyGuildChannelsParams | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; } | |||
[JsonProperty("position")] | |||
[ModelProperty("position")] | |||
public int Position { get; } | |||
public ModifyGuildChannelsParams(ulong id, int position) | |||
@@ -1,14 +1,13 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyGuildEmbedParams | |||
{ | |||
[JsonProperty("enabled")] | |||
[ModelProperty("enabled")] | |||
public Optional<bool> Enabled { get; set; } | |||
[JsonProperty("channel")] | |||
[ModelProperty("channel")] | |||
public Optional<ulong?> ChannelId { get; set; } | |||
} | |||
} |
@@ -1,16 +1,15 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyGuildIntegrationParams | |||
{ | |||
[JsonProperty("expire_behavior")] | |||
[ModelProperty("expire_behavior")] | |||
public Optional<int> ExpireBehavior { get; set; } | |||
[JsonProperty("expire_grace_period")] | |||
[ModelProperty("expire_grace_period")] | |||
public Optional<int> ExpireGracePeriod { get; set; } | |||
[JsonProperty("enable_emoticons")] | |||
[ModelProperty("enable_emoticons")] | |||
public Optional<bool> EnableEmoticons { get; set; } | |||
} | |||
} |
@@ -1,20 +1,19 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyGuildMemberParams | |||
{ | |||
[JsonProperty("mute")] | |||
[ModelProperty("mute")] | |||
public Optional<bool> Mute { get; set; } | |||
[JsonProperty("deaf")] | |||
[ModelProperty("deaf")] | |||
public Optional<bool> Deaf { get; set; } | |||
[JsonProperty("nick")] | |||
[ModelProperty("nick")] | |||
public Optional<string> Nickname { get; set; } | |||
[JsonProperty("roles")] | |||
[ModelProperty("roles")] | |||
public Optional<ulong[]> RoleIds { get; set; } | |||
[JsonProperty("channel_id")] | |||
[ModelProperty("channel_id")] | |||
public Optional<ulong> ChannelId { get; set; } | |||
} | |||
} |
@@ -1,30 +1,29 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyGuildParams | |||
{ | |||
[JsonProperty("username")] | |||
[ModelProperty("username")] | |||
public Optional<string> Username { get; set; } | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public Optional<string> Name { get; set; } | |||
[JsonProperty("region")] | |||
[ModelProperty("region")] | |||
public Optional<string> RegionId { get; set; } | |||
[JsonProperty("verification_level")] | |||
[ModelProperty("verification_level")] | |||
public Optional<VerificationLevel> VerificationLevel { get; set; } | |||
[JsonProperty("default_message_notifications")] | |||
[ModelProperty("default_message_notifications")] | |||
public Optional<DefaultMessageNotifications> DefaultMessageNotifications { get; set; } | |||
[JsonProperty("afk_timeout")] | |||
[ModelProperty("afk_timeout")] | |||
public Optional<int> AfkTimeout { get; set; } | |||
[JsonProperty("icon")] | |||
[ModelProperty("icon")] | |||
public Optional<Image?> Icon { get; set; } | |||
[JsonProperty("splash")] | |||
[ModelProperty("splash")] | |||
public Optional<Image?> Splash { get; set; } | |||
[JsonProperty("afk_channel_id")] | |||
[ModelProperty("afk_channel_id")] | |||
public Optional<ulong?> AfkChannelId { get; set; } | |||
[JsonProperty("owner_id")] | |||
[ModelProperty("owner_id")] | |||
public Optional<ulong> OwnerId { get; set; } | |||
} | |||
} |
@@ -1,20 +1,19 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyGuildRoleParams | |||
{ | |||
[JsonProperty("name")] | |||
[ModelProperty("name")] | |||
public Optional<string> Name { get; set; } | |||
[JsonProperty("permissions")] | |||
[ModelProperty("permissions")] | |||
public Optional<ulong> Permissions { get; set; } | |||
[JsonProperty("color")] | |||
[ModelProperty("color")] | |||
public Optional<uint> Color { get; set; } | |||
[JsonProperty("hoist")] | |||
[ModelProperty("hoist")] | |||
public Optional<bool> Hoist { get; set; } | |||
[JsonProperty("mentionable")] | |||
[ModelProperty("mentionable")] | |||
public Optional<bool> Mentionable { get; set; } | |||
} | |||
} |
@@ -1,14 +1,13 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyGuildRolesParams : ModifyGuildRoleParams | |||
{ | |||
[JsonProperty("id")] | |||
[ModelProperty("id")] | |||
public ulong Id { get; } | |||
[JsonProperty("position")] | |||
[ModelProperty("position")] | |||
public int Position { get; } | |||
public ModifyGuildRolesParams(ulong id, int position) | |||
@@ -1,14 +1,13 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyMessageParams | |||
{ | |||
[JsonProperty("content")] | |||
[ModelProperty("content")] | |||
public Optional<string> Content { get; set; } | |||
[JsonProperty("embed")] | |||
[ModelProperty("embed")] | |||
public Optional<Embed> Embed { get; set; } | |||
} | |||
} |
@@ -1,12 +1,11 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyTextChannelParams : ModifyGuildChannelParams | |||
{ | |||
[JsonProperty("topic")] | |||
[ModelProperty("topic")] | |||
public Optional<string> Topic { get; set; } | |||
} | |||
} |
@@ -1,14 +1,13 @@ | |||
#pragma warning disable CS1591 | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
namespace Discord.API.Rest | |||
{ | |||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||
internal class ModifyVoiceChannelParams : ModifyGuildChannelParams | |||
{ | |||
[JsonProperty("bitrate")] | |||
[ModelProperty("bitrate")] | |||
public Optional<int> Bitrate { get; set; } | |||
[JsonProperty("user_limit")] | |||
[ModelProperty("user_limit")] | |||
public Optional<int> UserLimit { get; set; } | |||
} | |||
} |
@@ -22,23 +22,25 @@ namespace Discord.Rest | |||
private readonly SemaphoreSlim _stateLock; | |||
private bool _isFirstLogin, _isDisposed; | |||
internal API.DiscordRestApiClient ApiClient { get; } | |||
internal LogManager LogManager { get; } | |||
internal API.DiscordRestApiClient ApiClient { get; private set; } | |||
public LoginState LoginState { get; private set; } | |||
public ISelfUser CurrentUser { get; protected set; } | |||
public TokenType TokenType => ApiClient.AuthTokenType; | |||
/// <summary> Creates a new REST-only discord client. </summary> | |||
internal BaseDiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client) | |||
internal BaseDiscordClient(DiscordRestConfig config) | |||
{ | |||
ApiClient = client; | |||
LogManager = new LogManager(config.LogLevel); | |||
LogManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false); | |||
_stateLock = new SemaphoreSlim(1, 1); | |||
_restLogger = LogManager.CreateLogger("Rest"); | |||
_isFirstLogin = config.DisplayInitialLog; | |||
} | |||
internal void SetApiClient(API.DiscordRestApiClient client) | |||
{ | |||
ApiClient = client; | |||
ApiClient.RequestQueue.RateLimitTriggered += async (id, info) => | |||
{ | |||
if (info == null) | |||
@@ -7,7 +7,16 @@ | |||
<TargetFrameworks>net45;netstandard1.1;netstandard1.3</TargetFrameworks> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Compile Remove="Serialization\System.Buffers\**" /> | |||
<Compile Remove="_corefxlab\**" /> | |||
<EmbeddedResource Remove="Serialization\System.Buffers\**" /> | |||
<EmbeddedResource Remove="_corefxlab\**" /> | |||
<None Remove="Serialization\System.Buffers\**" /> | |||
<None Remove="_corefxlab\**" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> | |||
<ProjectReference Include="..\Discord.Net.Serialization\Discord.Net.Serialization.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup Condition=" '$(TargetFramework)' != 'net45' "> | |||
<PackageReference Include="System.Net.Http" Version="4.3.2" /> <!-- https://github.com/dotnet/corefx/issues/19535 --> | |||
@@ -1,21 +1,19 @@ | |||
#pragma warning disable CS1591 | |||
using Discord.API.Rest; | |||
using Discord.Net; | |||
using Discord.Net.Converters; | |||
using Discord.Net.Queue; | |||
using Discord.Net.Rest; | |||
using Newtonsoft.Json; | |||
using Discord.Serialization; | |||
using System; | |||
using System.Collections.Concurrent; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.Globalization; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Linq.Expressions; | |||
using System.Net; | |||
using System.Runtime.CompilerServices; | |||
using System.Text; | |||
using System.Text.Formatting; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
@@ -27,9 +25,10 @@ namespace Discord.API | |||
public event Func<string, string, double, Task> SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } } | |||
private readonly AsyncEvent<Func<string, string, double, Task>> _sentRequestEvent = new AsyncEvent<Func<string, string, double, Task>>(); | |||
protected readonly JsonSerializer _serializer; | |||
protected readonly SemaphoreSlim _stateLock; | |||
protected readonly Serializer _serializer; | |||
protected readonly Pool<ArrayFormatter> _formatters; | |||
private readonly RestClientProvider _restClientProvider; | |||
protected bool _isDisposed; | |||
@@ -45,16 +44,16 @@ namespace Discord.API | |||
internal IRestClient RestClient { get; private set; } | |||
internal ulong? CurrentUserId { get; set;} | |||
public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, | |||
JsonSerializer serializer = null) | |||
public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, Serializer serializer, RetryMode defaultRetryMode = RetryMode.AlwaysRetry) | |||
{ | |||
_restClientProvider = restClientProvider; | |||
UserAgent = userAgent; | |||
_serializer = serializer; | |||
DefaultRetryMode = defaultRetryMode; | |||
_serializer = serializer ?? new JsonSerializer { DateFormatString = "yyyy-MM-ddTHH:mm:ssZ", ContractResolver = new DiscordContractResolver() }; | |||
RequestQueue = new RequestQueue(); | |||
_stateLock = new SemaphoreSlim(1, 1); | |||
_formatters = new Pool<ArrayFormatter>(() => new ArrayFormatter(128, SymbolTable.InvariantUtf8)); | |||
SetBaseUrl(DiscordConfig.APIUrl); | |||
} | |||
@@ -188,10 +187,18 @@ namespace Discord.API | |||
options.HeaderOnly = true; | |||
options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | |||
options.IsClientBucket = AuthTokenType == TokenType.User; | |||
string json = payload != null ? SerializeJson(payload) : null; | |||
var request = new JsonRestRequest(RestClient, method, endpoint, json, options); | |||
await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); | |||
var data = _formatters.Rent(); | |||
try | |||
{ | |||
var request = new JsonRestRequest(RestClient, method, endpoint, SerializeJson(data, payload), options); | |||
await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); | |||
} | |||
finally | |||
{ | |||
data.Clear(); | |||
_formatters.Return(data); | |||
} | |||
} | |||
internal Task SendMultipartAsync(string method, Expression<Func<string>> endpointExpr, IReadOnlyDictionary<string, object> multipartArgs, BucketIds ids, | |||
@@ -210,10 +217,10 @@ namespace Discord.API | |||
} | |||
internal Task<TResponse> SendAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, BucketIds ids, | |||
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null) where TResponse : class | |||
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null) where TResponse : class, new() | |||
=> SendAsync<TResponse>(method, GetEndpoint(endpointExpr), GetBucketId(ids, endpointExpr, AuthTokenType, funcName), clientBucket, options); | |||
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, | |||
string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) where TResponse : class | |||
string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) where TResponse : class, new() | |||
{ | |||
options = options ?? new RequestOptions(); | |||
options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | |||
@@ -224,25 +231,33 @@ namespace Discord.API | |||
} | |||
internal Task<TResponse> SendJsonAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, object payload, BucketIds ids, | |||
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null) where TResponse : class | |||
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null) where TResponse : class, new() | |||
=> SendJsonAsync<TResponse>(method, GetEndpoint(endpointExpr), payload, GetBucketId(ids, endpointExpr, AuthTokenType, funcName), clientBucket, options); | |||
public async Task<TResponse> SendJsonAsync<TResponse>(string method, string endpoint, object payload, | |||
string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) where TResponse : class | |||
string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) where TResponse : class, new() | |||
{ | |||
options = options ?? new RequestOptions(); | |||
options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | |||
options.IsClientBucket = AuthTokenType == TokenType.User; | |||
string json = payload != null ? SerializeJson(payload) : null; | |||
var request = new JsonRestRequest(RestClient, method, endpoint, json, options); | |||
return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); | |||
var data = _formatters.Rent(); | |||
try | |||
{ | |||
var request = new JsonRestRequest(RestClient, method, endpoint, SerializeJson(data, payload), options); | |||
return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); | |||
} | |||
finally | |||
{ | |||
data.Clear(); | |||
_formatters.Return(data); | |||
} | |||
} | |||
internal Task<TResponse> SendMultipartAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, IReadOnlyDictionary<string, object> multipartArgs, BucketIds ids, | |||
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null) | |||
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null) where TResponse : class, new() | |||
=> SendMultipartAsync<TResponse>(method, GetEndpoint(endpointExpr), multipartArgs, GetBucketId(ids, endpointExpr, AuthTokenType, funcName), clientBucket, options); | |||
public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, | |||
string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) | |||
string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) where TResponse : class, new() | |||
{ | |||
options = options ?? new RequestOptions(); | |||
options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; | |||
@@ -252,7 +267,7 @@ namespace Discord.API | |||
return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); | |||
} | |||
private async Task<Stream> SendInternalAsync(string method, string endpoint, RestRequest request) | |||
private async Task<ReadOnlyBuffer<byte>> SendInternalAsync(string method, string endpoint, RestRequest request) | |||
{ | |||
if (!request.Options.IgnoreState) | |||
CheckState(); | |||
@@ -260,13 +275,13 @@ namespace Discord.API | |||
request.Options.RetryMode = DefaultRetryMode; | |||
var stopwatch = Stopwatch.StartNew(); | |||
var responseStream = await RequestQueue.SendAsync(request).ConfigureAwait(false); | |||
var response = await RequestQueue.SendAsync(request).ConfigureAwait(false); | |||
stopwatch.Stop(); | |||
double milliseconds = ToMilliseconds(stopwatch); | |||
await _sentRequestEvent.InvokeAsync(method, endpoint, milliseconds).ConfigureAwait(false); | |||
return responseStream; | |||
return response; | |||
} | |||
//Auth | |||
@@ -311,7 +326,7 @@ namespace Discord.API | |||
options = RequestOptions.CreateOrClone(options); | |||
var ids = new BucketIds(guildId: guildId); | |||
return await SendAsync<IReadOnlyCollection<Channel>>("GET", () => $"guilds/{guildId}/channels", ids, options: options).ConfigureAwait(false); | |||
return await SendAsync<List<Channel>>("GET", () => $"guilds/{guildId}/channels", ids, options: options).ConfigureAwait(false); | |||
} | |||
public async Task<Channel> CreateGuildChannelAsync(ulong guildId, CreateGuildChannelParams args, RequestOptions options = null) | |||
{ | |||
@@ -454,7 +469,7 @@ namespace Discord.API | |||
endpoint = () => $"channels/{channelId}/messages?limit={limit}&{relativeDir}={relativeId}"; | |||
else | |||
endpoint = () => $"channels/{channelId}/messages?limit={limit}"; | |||
return await SendAsync<IReadOnlyCollection<Message>>("GET", endpoint, ids, options: options).ConfigureAwait(false); | |||
return await SendAsync<List<Message>>("GET", endpoint, ids, options: options).ConfigureAwait(false); | |||
} | |||
public async Task<Message> CreateMessageAsync(ulong channelId, CreateMessageParams args, RequestOptions options = null) | |||
{ | |||
@@ -620,7 +635,7 @@ namespace Discord.API | |||
var ids = new BucketIds(channelId: channelId); | |||
Expression<Func<string>> endpoint = () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}"; | |||
return await SendAsync<IReadOnlyCollection<User>>("GET", endpoint, ids, options: options).ConfigureAwait(false); | |||
return await SendAsync<List<User>>("GET", endpoint, ids, options: options).ConfigureAwait(false); | |||
} | |||
public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||
{ | |||
@@ -687,7 +702,7 @@ namespace Discord.API | |||
options = RequestOptions.CreateOrClone(options); | |||
var ids = new BucketIds(channelId: channelId); | |||
return await SendAsync<IReadOnlyCollection<Message>>("GET", () => $"channels/{channelId}/pins", ids, options: options).ConfigureAwait(false); | |||
return await SendAsync<List<Message>>("GET", () => $"channels/{channelId}/pins", ids, options: options).ConfigureAwait(false); | |||
} | |||
//Channel Recipients | |||
@@ -791,7 +806,7 @@ namespace Discord.API | |||
options = RequestOptions.CreateOrClone(options); | |||
var ids = new BucketIds(guildId: guildId); | |||
return await SendAsync<IReadOnlyCollection<Ban>>("GET", () => $"guilds/{guildId}/bans", ids, options: options).ConfigureAwait(false); | |||
return await SendAsync<List<Ban>>("GET", () => $"guilds/{guildId}/bans", ids, options: options).ConfigureAwait(false); | |||
} | |||
public async Task CreateGuildBanAsync(ulong guildId, ulong userId, CreateGuildBanParams args, RequestOptions options = null) | |||
{ | |||
@@ -846,7 +861,7 @@ namespace Discord.API | |||
options = RequestOptions.CreateOrClone(options); | |||
var ids = new BucketIds(guildId: guildId); | |||
return await SendAsync<IReadOnlyCollection<Integration>>("GET", () => $"guilds/{guildId}/integrations", ids, options: options).ConfigureAwait(false); | |||
return await SendAsync<List<Integration>>("GET", () => $"guilds/{guildId}/integrations", ids, options: options).ConfigureAwait(false); | |||
} | |||
public async Task<Integration> CreateGuildIntegrationAsync(ulong guildId, CreateGuildIntegrationParams args, RequestOptions options = null) | |||
{ | |||
@@ -915,7 +930,7 @@ namespace Discord.API | |||
options = RequestOptions.CreateOrClone(options); | |||
var ids = new BucketIds(guildId: guildId); | |||
return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", () => $"guilds/{guildId}/invites", ids, options: options).ConfigureAwait(false); | |||
return await SendAsync< List<InviteMetadata>>("GET", () => $"guilds/{guildId}/invites", ids, options: options).ConfigureAwait(false); | |||
} | |||
public async Task<IReadOnlyCollection<InviteMetadata>> GetChannelInvitesAsync(ulong channelId, RequestOptions options = null) | |||
{ | |||
@@ -923,7 +938,7 @@ namespace Discord.API | |||
options = RequestOptions.CreateOrClone(options); | |||
var ids = new BucketIds(channelId: channelId); | |||
return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", () => $"channels/{channelId}/invites", ids, options: options).ConfigureAwait(false); | |||
return await SendAsync<List<InviteMetadata>>("GET", () => $"channels/{channelId}/invites", ids, options: options).ConfigureAwait(false); | |||
} | |||
public async Task<InviteMetadata> CreateChannelInviteAsync(ulong channelId, CreateChannelInviteParams args, RequestOptions options = null) | |||
{ | |||
@@ -979,7 +994,7 @@ namespace Discord.API | |||
var ids = new BucketIds(guildId: guildId); | |||
Expression<Func<string>> endpoint = () => $"guilds/{guildId}/members?limit={limit}&after={afterUserId}"; | |||
return await SendAsync<IReadOnlyCollection<GuildMember>>("GET", endpoint, ids, options: options).ConfigureAwait(false); | |||
return await SendAsync< List<GuildMember>>("GET", endpoint, ids, options: options).ConfigureAwait(false); | |||
} | |||
public async Task RemoveGuildMemberAsync(ulong guildId, ulong userId, string reason, RequestOptions options = null) | |||
{ | |||
@@ -1020,7 +1035,7 @@ namespace Discord.API | |||
options = RequestOptions.CreateOrClone(options); | |||
var ids = new BucketIds(guildId: guildId); | |||
return await SendAsync<IReadOnlyCollection<Role>>("GET", () => $"guilds/{guildId}/roles", ids, options: options).ConfigureAwait(false); | |||
return await SendAsync<List<Role>>("GET", () => $"guilds/{guildId}/roles", ids, options: options).ConfigureAwait(false); | |||
} | |||
public async Task<Role> CreateGuildRoleAsync(ulong guildId, RequestOptions options = null) | |||
{ | |||
@@ -1058,7 +1073,7 @@ namespace Discord.API | |||
options = RequestOptions.CreateOrClone(options); | |||
var ids = new BucketIds(guildId: guildId); | |||
return await SendJsonAsync<IReadOnlyCollection<Role>>("PATCH", () => $"guilds/{guildId}/roles", args, ids, options: options).ConfigureAwait(false); | |||
return await SendJsonAsync<List<Role>>("PATCH", () => $"guilds/{guildId}/roles", args, ids, options: options).ConfigureAwait(false); | |||
} | |||
//Users | |||
@@ -1083,12 +1098,12 @@ namespace Discord.API | |||
public async Task<IReadOnlyCollection<Connection>> GetMyConnectionsAsync(RequestOptions options = null) | |||
{ | |||
options = RequestOptions.CreateOrClone(options); | |||
return await SendAsync<IReadOnlyCollection<Connection>>("GET", () => "users/@me/connections", new BucketIds(), options: options).ConfigureAwait(false); | |||
return await SendAsync<List<Connection>>("GET", () => "users/@me/connections", new BucketIds(), options: options).ConfigureAwait(false); | |||
} | |||
public async Task<IReadOnlyCollection<Channel>> GetMyPrivateChannelsAsync(RequestOptions options = null) | |||
{ | |||
options = RequestOptions.CreateOrClone(options); | |||
return await SendAsync<IReadOnlyCollection<Channel>>("GET", () => "users/@me/channels", new BucketIds(), options: options).ConfigureAwait(false); | |||
return await SendAsync<List<Channel>>("GET", () => "users/@me/channels", new BucketIds(), options: options).ConfigureAwait(false); | |||
} | |||
public async Task<IReadOnlyCollection<UserGuild>> GetMyGuildsAsync(GetGuildSummariesParams args, RequestOptions options = null) | |||
{ | |||
@@ -1101,7 +1116,7 @@ namespace Discord.API | |||
int limit = args.Limit.GetValueOrDefault(int.MaxValue); | |||
ulong afterGuildId = args.AfterGuildId.GetValueOrDefault(0); | |||
return await SendAsync<IReadOnlyCollection<UserGuild>>("GET", () => $"users/@me/guilds?limit={limit}&after={afterGuildId}", new BucketIds(), options: options).ConfigureAwait(false); | |||
return await SendAsync<List<UserGuild>>("GET", () => $"users/@me/guilds?limit={limit}&after={afterGuildId}", new BucketIds(), options: options).ConfigureAwait(false); | |||
} | |||
public async Task<Application> GetMyApplicationAsync(RequestOptions options = null) | |||
{ | |||
@@ -1138,7 +1153,7 @@ namespace Discord.API | |||
public async Task<IReadOnlyCollection<VoiceRegion>> GetVoiceRegionsAsync(RequestOptions options = null) | |||
{ | |||
options = RequestOptions.CreateOrClone(options); | |||
return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", () => "voice/regions", new BucketIds(), options: options).ConfigureAwait(false); | |||
return await SendAsync<List<VoiceRegion>>("GET", () => "voice/regions", new BucketIds(), options: options).ConfigureAwait(false); | |||
} | |||
public async Task<IReadOnlyCollection<VoiceRegion>> GetGuildVoiceRegionsAsync(ulong guildId, RequestOptions options = null) | |||
{ | |||
@@ -1146,7 +1161,7 @@ namespace Discord.API | |||
options = RequestOptions.CreateOrClone(options); | |||
var ids = new BucketIds(guildId: guildId); | |||
return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", () => $"guilds/{guildId}/regions", ids, options: options).ConfigureAwait(false); | |||
return await SendAsync<List<VoiceRegion>>("GET", () => $"guilds/{guildId}/regions", ids, options: options).ConfigureAwait(false); | |||
} | |||
//Helpers | |||
@@ -1156,19 +1171,14 @@ namespace Discord.API | |||
throw new InvalidOperationException("Client is not logged in."); | |||
} | |||
protected static double ToMilliseconds(Stopwatch stopwatch) => Math.Round((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0, 2); | |||
protected string SerializeJson(object value) | |||
protected ReadOnlyBuffer<byte> SerializeJson<T>(ArrayFormatter data, T value) | |||
{ | |||
var sb = new StringBuilder(256); | |||
using (TextWriter text = new StringWriter(sb, CultureInfo.InvariantCulture)) | |||
using (JsonWriter writer = new JsonTextWriter(text)) | |||
_serializer.Serialize(writer, value); | |||
return sb.ToString(); | |||
_serializer.Write(data, value); | |||
return new ReadOnlyBuffer<byte>(data.Formatted.Array, 0, data.Formatted.Count); | |||
} | |||
protected T DeserializeJson<T>(Stream jsonStream) | |||
protected T DeserializeJson<T>(ReadOnlyBuffer<byte> data) | |||
{ | |||
using (TextReader text = new StreamReader(jsonStream)) | |||
using (JsonReader reader = new JsonTextReader(text)) | |||
return _serializer.Deserialize<T>(reader); | |||
return _serializer.Read<T>(data); | |||
} | |||
internal class BucketIds | |||
@@ -1,4 +1,6 @@ | |||
using System.Collections.Generic; | |||
using Discord.Serialization; | |||
using Discord.Serialization.Json; | |||
using System.Collections.Generic; | |||
using System.Collections.Immutable; | |||
using System.IO; | |||
using System.Threading.Tasks; | |||
@@ -7,15 +9,29 @@ namespace Discord.Rest | |||
{ | |||
public class DiscordRestClient : BaseDiscordClient, IDiscordClient | |||
{ | |||
private readonly Serializer _serializer; | |||
private RestApplication _applicationInfo; | |||
public new RestSelfUser CurrentUser => base.CurrentUser as RestSelfUser; | |||
public DiscordRestClient() : this(new DiscordRestConfig()) { } | |||
public DiscordRestClient(DiscordRestConfig config) : base(config, CreateApiClient(config)) { } | |||
public DiscordRestClient(DiscordRestConfig config) : base(config) | |||
{ | |||
_serializer = DiscordRestJsonSerializer.Global.CreateScope(); | |||
if (config.LogLevel >= LogSeverity.Warning) | |||
{ | |||
_serializer.ModelError += (path, ex) | |||
=> _restLogger.WarningAsync($"Failed to deserialize {path}", ex).GetAwaiter().GetResult(); | |||
} | |||
if (config.LogLevel >= LogSeverity.Debug) | |||
{ | |||
_serializer.UnmappedProperty += path | |||
=> _restLogger.DebugAsync($"Unmapped property: {path}"); | |||
} | |||
SetApiClient(new API.DiscordRestApiClient(config.RestClientProvider, DiscordConfig.UserAgent, _serializer, config.DefaultRetryMode)); | |||
} | |||
private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) | |||
=> new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent); | |||
internal override void Dispose(bool disposing) | |||
{ | |||
if (disposing) | |||
@@ -114,7 +114,7 @@ namespace Discord.Rest | |||
public static API.Image ToModel(this Image entity) | |||
{ | |||
return new API.Image(entity.Stream); | |||
return new API.Image(entity.Stream, entity.Format); | |||
} | |||
public static Overwrite ToEntity(this API.Overwrite model) | |||
@@ -0,0 +1,25 @@ | |||
using System; | |||
using System.IO; | |||
namespace Discord | |||
{ | |||
internal static class StreamExtensions | |||
{ | |||
#if MSTRYBUFFER | |||
public static byte[] GetBuffer(this MemoryStream stream) | |||
{ | |||
if (stream.TryGetBuffer(out var streamBuffer)) | |||
return streamBuffer.Array; | |||
else | |||
return stream.ToArray(); | |||
} | |||
#elif !MSBUFFER | |||
public static byte[] GetBuffer(this MemoryStream stream) => stream.ToArray(); | |||
#endif | |||
public static ReadOnlyBuffer<byte> ToReadOnlyBuffer(this MemoryStream stream) | |||
=> new ReadOnlyBuffer<byte>(stream.GetBuffer(), 0, (int)stream.Length); | |||
public static ReadOnlySpan<byte> ToSpan(this MemoryStream stream) | |||
=> new ReadOnlySpan<byte>(stream.GetBuffer(), 0, (int)stream.Length); | |||
} | |||
} |
@@ -1,59 +0,0 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Collections.Generic; | |||
namespace Discord.Net.Converters | |||
{ | |||
internal class ArrayConverter<T> : JsonConverter | |||
{ | |||
private readonly JsonConverter _innerConverter; | |||
public override bool CanConvert(Type objectType) => true; | |||
public override bool CanRead => true; | |||
public override bool CanWrite => true; | |||
public ArrayConverter(JsonConverter innerConverter) | |||
{ | |||
_innerConverter = innerConverter; | |||
} | |||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||
{ | |||
var result = new List<T>(); | |||
if (reader.TokenType == JsonToken.StartArray) | |||
{ | |||
reader.Read(); | |||
while (reader.TokenType != JsonToken.EndArray) | |||
{ | |||
T obj; | |||
if (_innerConverter != null) | |||
obj = (T)_innerConverter.ReadJson(reader, typeof(T), null, serializer); | |||
else | |||
obj = serializer.Deserialize<T>(reader); | |||
result.Add(obj); | |||
reader.Read(); | |||
} | |||
} | |||
return result.ToArray(); | |||
} | |||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||
{ | |||
if (value != null) | |||
{ | |||
writer.WriteStartArray(); | |||
var a = (T[])value; | |||
for (int i = 0; i < a.Length; i++) | |||
{ | |||
if (_innerConverter != null) | |||
_innerConverter.WriteJson(writer, a[i], serializer); | |||
else | |||
serializer.Serialize(writer, a[i], typeof(T)); | |||
} | |||
writer.WriteEndArray(); | |||
} | |||
else | |||
writer.WriteNull(); | |||
} | |||
} | |||
} |
@@ -1,102 +0,0 @@ | |||
using Discord.API; | |||
using Newtonsoft.Json; | |||
using Newtonsoft.Json.Serialization; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Reflection; | |||
namespace Discord.Net.Converters | |||
{ | |||
internal class DiscordContractResolver : DefaultContractResolver | |||
{ | |||
private static readonly TypeInfo _ienumerable = typeof(IEnumerable<ulong[]>).GetTypeInfo(); | |||
private static readonly MethodInfo _shouldSerialize = typeof(DiscordContractResolver).GetTypeInfo().GetDeclaredMethod("ShouldSerialize"); | |||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) | |||
{ | |||
var property = base.CreateProperty(member, memberSerialization); | |||
if (property.Ignored) | |||
return property; | |||
if (member is PropertyInfo propInfo) | |||
{ | |||
var converter = GetConverter(property, propInfo, propInfo.PropertyType, 0); | |||
if (converter != null) | |||
{ | |||
property.Converter = converter; | |||
property.MemberConverter = converter; | |||
} | |||
} | |||
else | |||
throw new InvalidOperationException($"{member.DeclaringType.FullName}.{member.Name} is not a property."); | |||
return property; | |||
} | |||
private static JsonConverter GetConverter(JsonProperty property, PropertyInfo propInfo, Type type, int depth) | |||
{ | |||
if (type.IsArray) | |||
return MakeGenericConverter(property, propInfo, typeof(ArrayConverter<>), type.GetElementType(), depth); | |||
if (type.IsConstructedGenericType) | |||
{ | |||
Type genericType = type.GetGenericTypeDefinition(); | |||
if (depth == 0 && genericType == typeof(Optional<>)) | |||
{ | |||
var typeInput = propInfo.DeclaringType; | |||
var innerTypeOutput = type.GenericTypeArguments[0]; | |||
var getter = typeof(Func<,>).MakeGenericType(typeInput, type); | |||
var getterDelegate = propInfo.GetMethod.CreateDelegate(getter); | |||
var shouldSerialize = _shouldSerialize.MakeGenericMethod(typeInput, innerTypeOutput); | |||
var shouldSerializeDelegate = (Func<object, Delegate, bool>)shouldSerialize.CreateDelegate(typeof(Func<object, Delegate, bool>)); | |||
property.ShouldSerialize = x => shouldSerializeDelegate(x, getterDelegate); | |||
return MakeGenericConverter(property, propInfo, typeof(OptionalConverter<>), innerTypeOutput, depth); | |||
} | |||
else if (genericType == typeof(Nullable<>)) | |||
return MakeGenericConverter(property, propInfo, typeof(NullableConverter<>), type.GenericTypeArguments[0], depth); | |||
else if (genericType == typeof(EntityOrId<>)) | |||
return MakeGenericConverter(property, propInfo, typeof(UInt64EntityOrIdConverter<>), type.GenericTypeArguments[0], depth); | |||
} | |||
//Primitives | |||
bool hasInt53 = propInfo.GetCustomAttribute<Int53Attribute>() != null; | |||
if (!hasInt53) | |||
{ | |||
if (type == typeof(ulong)) | |||
return UInt64Converter.Instance; | |||
} | |||
//Enums | |||
if (type == typeof(PermissionTarget)) | |||
return PermissionTargetConverter.Instance; | |||
if (type == typeof(UserStatus)) | |||
return UserStatusConverter.Instance; | |||
//Special | |||
if (type == typeof(API.Image)) | |||
return ImageConverter.Instance; | |||
//Entities | |||
var typeInfo = type.GetTypeInfo(); | |||
if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEntity<ulong>))) | |||
return UInt64EntityConverter.Instance; | |||
if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEntity<string>))) | |||
return StringEntityConverter.Instance; | |||
return null; | |||
} | |||
private static bool ShouldSerialize<TOwner, TValue>(object owner, Delegate getter) | |||
{ | |||
return (getter as Func<TOwner, Optional<TValue>>)((TOwner)owner).IsSpecified; | |||
} | |||
private static JsonConverter MakeGenericConverter(JsonProperty property, PropertyInfo propInfo, Type converterType, Type innerType, int depth) | |||
{ | |||
var genericType = converterType.MakeGenericType(innerType).GetTypeInfo(); | |||
var innerConverter = GetConverter(property, propInfo, innerType, depth + 1); | |||
return genericType.DeclaredConstructors.First().Invoke(new object[] { innerConverter }) as JsonConverter; | |||
} | |||
} | |||
} |
@@ -1,36 +0,0 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
using Model = Discord.API.Image; | |||
namespace Discord.Net.Converters | |||
{ | |||
internal class ImageConverter : JsonConverter | |||
{ | |||
public static readonly ImageConverter Instance = new ImageConverter(); | |||
public override bool CanConvert(Type objectType) => true; | |||
public override bool CanRead => true; | |||
public override bool CanWrite => true; | |||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||
{ | |||
throw new InvalidOperationException(); | |||
} | |||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||
{ | |||
var image = (Model)value; | |||
if (image.Stream != null) | |||
{ | |||
byte[] bytes = new byte[image.Stream.Length - image.Stream.Position]; | |||
image.Stream.Read(bytes, 0, bytes.Length); | |||
string base64 = Convert.ToBase64String(bytes); | |||
writer.WriteValue($"data:image/jpeg;base64,{base64}"); | |||
} | |||
else if (image.Hash != null) | |||
writer.WriteValue(image.Hash); | |||
} | |||
} | |||
} |
@@ -1,50 +0,0 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
namespace Discord.Net.Converters | |||
{ | |||
internal class NullableConverter<T> : JsonConverter | |||
where T : struct | |||
{ | |||
private readonly JsonConverter _innerConverter; | |||
public override bool CanConvert(Type objectType) => true; | |||
public override bool CanRead => true; | |||
public override bool CanWrite => true; | |||
public NullableConverter(JsonConverter innerConverter) | |||
{ | |||
_innerConverter = innerConverter; | |||
} | |||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||
{ | |||
object value = reader.Value; | |||
if (value == null) | |||
return null; | |||
else | |||
{ | |||
T obj; | |||
if (_innerConverter != null) | |||
obj = (T)_innerConverter.ReadJson(reader, typeof(T), null, serializer); | |||
else | |||
obj = serializer.Deserialize<T>(reader); | |||
return obj; | |||
} | |||
} | |||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||
{ | |||
if (value == null) | |||
writer.WriteNull(); | |||
else | |||
{ | |||
var nullable = (T?)value; | |||
if (_innerConverter != null) | |||
_innerConverter.WriteJson(writer, nullable.Value, serializer); | |||
else | |||
serializer.Serialize(writer, nullable.Value, typeof(T)); | |||
} | |||
} | |||
} | |||
} |
@@ -1,38 +0,0 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
namespace Discord.Net.Converters | |||
{ | |||
internal class OptionalConverter<T> : JsonConverter | |||
{ | |||
private readonly JsonConverter _innerConverter; | |||
public override bool CanConvert(Type objectType) => true; | |||
public override bool CanRead => true; | |||
public override bool CanWrite => true; | |||
public OptionalConverter(JsonConverter innerConverter) | |||
{ | |||
_innerConverter = innerConverter; | |||
} | |||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||
{ | |||
T obj; | |||
if (_innerConverter != null) | |||
obj = (T)_innerConverter.ReadJson(reader, typeof(T), null, serializer); | |||
else | |||
obj = serializer.Deserialize<T>(reader); | |||
return new Optional<T>(obj); | |||
} | |||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||
{ | |||
value = ((Optional<T>)value).Value; | |||
if (_innerConverter != null) | |||
_innerConverter.WriteJson(writer, value, serializer); | |||
else | |||
serializer.Serialize(writer, value, typeof(T)); | |||
} | |||
} | |||
} |
@@ -1,42 +0,0 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
namespace Discord.Net.Converters | |||
{ | |||
internal class PermissionTargetConverter : JsonConverter | |||
{ | |||
public static readonly PermissionTargetConverter Instance = new PermissionTargetConverter(); | |||
public override bool CanConvert(Type objectType) => true; | |||
public override bool CanRead => true; | |||
public override bool CanWrite => true; | |||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||
{ | |||
switch ((string)reader.Value) | |||
{ | |||
case "member": | |||
return PermissionTarget.User; | |||
case "role": | |||
return PermissionTarget.Role; | |||
default: | |||
throw new JsonSerializationException("Unknown permission target"); | |||
} | |||
} | |||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||
{ | |||
switch ((PermissionTarget)value) | |||
{ | |||
case PermissionTarget.User: | |||
writer.WriteValue("member"); | |||
break; | |||
case PermissionTarget.Role: | |||
writer.WriteValue("role"); | |||
break; | |||
default: | |||
throw new JsonSerializationException("Invalid permission target"); | |||
} | |||
} | |||
} | |||
} |
@@ -1,27 +0,0 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
namespace Discord.Net.Converters | |||
{ | |||
internal class StringEntityConverter : JsonConverter | |||
{ | |||
public static readonly StringEntityConverter Instance = new StringEntityConverter(); | |||
public override bool CanConvert(Type objectType) => true; | |||
public override bool CanRead => false; | |||
public override bool CanWrite => true; | |||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||
{ | |||
throw new InvalidOperationException(); | |||
} | |||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||
{ | |||
if (value != null) | |||
writer.WriteValue((value as IEntity<string>).Id); | |||
else | |||
writer.WriteNull(); | |||
} | |||
} | |||
} |
@@ -1,25 +0,0 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Globalization; | |||
namespace Discord.Net.Converters | |||
{ | |||
internal class UInt64Converter : JsonConverter | |||
{ | |||
public static readonly UInt64Converter Instance = new UInt64Converter(); | |||
public override bool CanConvert(Type objectType) => true; | |||
public override bool CanRead => true; | |||
public override bool CanWrite => true; | |||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||
{ | |||
return ulong.Parse((string)reader.Value, NumberStyles.None, CultureInfo.InvariantCulture); | |||
} | |||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||
{ | |||
writer.WriteValue(((ulong)value).ToString(CultureInfo.InvariantCulture)); | |||
} | |||
} | |||
} |
@@ -1,28 +0,0 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Globalization; | |||
namespace Discord.Net.Converters | |||
{ | |||
internal class UInt64EntityConverter : JsonConverter | |||
{ | |||
public static readonly UInt64EntityConverter Instance = new UInt64EntityConverter(); | |||
public override bool CanConvert(Type objectType) => true; | |||
public override bool CanRead => false; | |||
public override bool CanWrite => true; | |||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||
{ | |||
throw new InvalidOperationException(); | |||
} | |||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||
{ | |||
if (value != null) | |||
writer.WriteValue((value as IEntity<ulong>).Id.ToString(CultureInfo.InvariantCulture)); | |||
else | |||
writer.WriteNull(); | |||
} | |||
} | |||
} |
@@ -1,42 +0,0 @@ | |||
using Discord.API; | |||
using Newtonsoft.Json; | |||
using System; | |||
namespace Discord.Net.Converters | |||
{ | |||
internal class UInt64EntityOrIdConverter<T> : JsonConverter | |||
{ | |||
private readonly JsonConverter _innerConverter; | |||
public override bool CanConvert(Type objectType) => true; | |||
public override bool CanRead => true; | |||
public override bool CanWrite => false; | |||
public UInt64EntityOrIdConverter(JsonConverter innerConverter) | |||
{ | |||
_innerConverter = innerConverter; | |||
} | |||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||
{ | |||
switch (reader.TokenType) | |||
{ | |||
case JsonToken.String: | |||
case JsonToken.Integer: | |||
return new EntityOrId<T>(ulong.Parse(reader.ReadAsString())); | |||
default: | |||
T obj; | |||
if (_innerConverter != null) | |||
obj = (T)_innerConverter.ReadJson(reader, typeof(T), null, serializer); | |||
else | |||
obj = serializer.Deserialize<T>(reader); | |||
return new EntityOrId<T>(obj); | |||
} | |||
} | |||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||
{ | |||
throw new InvalidOperationException(); | |||
} | |||
} | |||
} |
@@ -1,58 +0,0 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
namespace Discord.Net.Converters | |||
{ | |||
internal class UserStatusConverter : JsonConverter | |||
{ | |||
public static readonly UserStatusConverter Instance = new UserStatusConverter(); | |||
public override bool CanConvert(Type objectType) => true; | |||
public override bool CanRead => true; | |||
public override bool CanWrite => true; | |||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||
{ | |||
switch ((string)reader.Value) | |||
{ | |||
case "online": | |||
return UserStatus.Online; | |||
case "idle": | |||
return UserStatus.Idle; | |||
case "dnd": | |||
return UserStatus.DoNotDisturb; | |||
case "invisible": | |||
return UserStatus.Invisible; //Should never happen | |||
case "offline": | |||
return UserStatus.Offline; | |||
default: | |||
throw new JsonSerializationException("Unknown user status"); | |||
} | |||
} | |||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||
{ | |||
switch ((UserStatus)value) | |||
{ | |||
case UserStatus.Online: | |||
writer.WriteValue("online"); | |||
break; | |||
case UserStatus.Idle: | |||
case UserStatus.AFK: | |||
writer.WriteValue("idle"); | |||
break; | |||
case UserStatus.DoNotDisturb: | |||
writer.WriteValue("dnd"); | |||
break; | |||
case UserStatus.Invisible: | |||
writer.WriteValue("invisible"); | |||
break; | |||
case UserStatus.Offline: | |||
writer.WriteValue("offline"); | |||
break; | |||
default: | |||
throw new JsonSerializationException("Invalid user status"); | |||
} | |||
} | |||
} | |||
} |
@@ -1,11 +1,11 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Globalization; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Net.Http; | |||
using System.Net.Http.Headers; | |||
using System.Text; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
@@ -18,7 +18,6 @@ namespace Discord.Net.Rest | |||
private readonly HttpClient _client; | |||
private readonly string _baseUrl; | |||
private readonly JsonSerializer _errorDeserializer; | |||
private CancellationToken _cancelToken; | |||
private bool _isDisposed; | |||
@@ -35,7 +34,6 @@ namespace Discord.Net.Rest | |||
SetHeader("accept-encoding", "gzip, deflate"); | |||
_cancelToken = CancellationToken.None; | |||
_errorDeserializer = new JsonSerializer(); | |||
} | |||
private void Dispose(bool disposing) | |||
{ | |||
@@ -71,13 +69,15 @@ namespace Discord.Net.Rest | |||
return await SendInternalAsync(restRequest, cancelToken, headerOnly).ConfigureAwait(false); | |||
} | |||
} | |||
public async Task<RestResponse> SendAsync(string method, string endpoint, string json, CancellationToken cancelToken, bool headerOnly, string reason = null) | |||
public async Task<RestResponse> SendAsync(string method, string endpoint, ReadOnlyBuffer<byte> json, CancellationToken cancelToken, bool headerOnly, string reason = null) | |||
{ | |||
string uri = Path.Combine(_baseUrl, endpoint); | |||
using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) | |||
{ | |||
if (reason != null) restRequest.Headers.Add("X-Audit-Log-Reason", Uri.EscapeDataString(reason)); | |||
restRequest.Content = new StringContent(json, Encoding.UTF8, "application/json"); | |||
var content = new ByteArrayContent(json.ToArray()); | |||
content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); | |||
restRequest.Content = content; | |||
return await SendInternalAsync(restRequest, cancelToken, headerOnly).ConfigureAwait(false); | |||
} | |||
} | |||
@@ -125,9 +125,9 @@ namespace Discord.Net.Rest | |||
HttpResponseMessage response = await _client.SendAsync(request, cancelToken).ConfigureAwait(false); | |||
var headers = response.Headers.ToDictionary(x => x.Key, x => x.Value.FirstOrDefault(), StringComparer.OrdinalIgnoreCase); | |||
var stream = !headerOnly ? await response.Content.ReadAsStreamAsync().ConfigureAwait(false) : null; | |||
var data = !headerOnly ? await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false) : null; | |||
return new RestResponse(response.StatusCode, headers, stream); | |||
return new RestResponse(response.StatusCode, headers, new ReadOnlyBuffer<byte>(data)); | |||
} | |||
private static readonly HttpMethod _patch = new HttpMethod("PATCH"); | |||
@@ -63,7 +63,7 @@ namespace Discord.Net.Queue | |||
finally { _tokenLock.Release(); } | |||
} | |||
public async Task<Stream> SendAsync(RestRequest request) | |||
public async Task<ReadOnlyBuffer<byte>> SendAsync(RestRequest request) | |||
{ | |||
if (request.Options.CancelToken.CanBeCanceled) | |||
request.Options.CancelToken = CancellationTokenSource.CreateLinkedTokenSource(_requestCancelToken, request.Options.CancelToken).Token; | |||
@@ -1,5 +1,6 @@ | |||
using Newtonsoft.Json; | |||
using Newtonsoft.Json.Linq; | |||
using Discord.Rest; | |||
using Discord.Serialization; | |||
using Discord.Serialization.Json; | |||
using System; | |||
#if DEBUG_LIMITS | |||
using System.Diagnostics; | |||
@@ -13,6 +14,16 @@ namespace Discord.Net.Queue | |||
{ | |||
internal class RequestBucket | |||
{ | |||
private class Error | |||
{ | |||
[ModelProperty("code")] | |||
public int Code { get; set; } | |||
[ModelProperty("message")] | |||
public string Message { get; set; } | |||
} | |||
private static int _nextId = 0; | |||
private readonly object _lock; | |||
private readonly RequestQueue _queue; | |||
private int _semaphore; | |||
@@ -38,10 +49,9 @@ namespace Discord.Net.Queue | |||
LastAttemptAt = DateTimeOffset.UtcNow; | |||
} | |||
static int nextId = 0; | |||
public async Task<Stream> SendAsync(RestRequest request) | |||
public async Task<ReadOnlyBuffer<byte>> SendAsync(RestRequest request) | |||
{ | |||
int id = Interlocked.Increment(ref nextId); | |||
int id = Interlocked.Increment(ref _nextId); | |||
#if DEBUG_LIMITS | |||
Debug.WriteLine($"[{id}] Start"); | |||
#endif | |||
@@ -54,7 +64,7 @@ namespace Discord.Net.Queue | |||
#if DEBUG_LIMITS | |||
Debug.WriteLine($"[{id}] Sending..."); | |||
#endif | |||
RateLimitInfo info = default(RateLimitInfo); | |||
var info = default(RateLimitInfo); | |||
try | |||
{ | |||
var response = await request.SendAsync().ConfigureAwait(false); | |||
@@ -92,17 +102,13 @@ namespace Discord.Net.Queue | |||
default: | |||
int? code = null; | |||
string reason = null; | |||
if (response.Stream != null) | |||
if (response.Data.Length > 0) | |||
{ | |||
try | |||
{ | |||
using (var reader = new StreamReader(response.Stream)) | |||
using (var jsonReader = new JsonTextReader(reader)) | |||
{ | |||
var json = JToken.Load(jsonReader); | |||
try { code = json.Value<int>("code"); } catch { }; | |||
try { reason = json.Value<string>("message"); } catch { }; | |||
} | |||
var error = DiscordRestJsonSerializer.Global.Read<Error>(response.Data); | |||
code = error.Code; | |||
reason = error.Message; | |||
} | |||
catch { } | |||
} | |||
@@ -114,7 +120,7 @@ namespace Discord.Net.Queue | |||
#if DEBUG_LIMITS | |||
Debug.WriteLine($"[{id}] Success"); | |||
#endif | |||
return response.Stream; | |||
return response.Data; | |||
} | |||
} | |||
//catch (HttpException) { throw; } //Pass through | |||
@@ -231,7 +237,7 @@ namespace Discord.Net.Queue | |||
#endif | |||
} | |||
var now = DateTimeUtils.ToUnixSeconds(DateTimeOffset.UtcNow); | |||
long now = DateTimeUtils.ToUnixSeconds(DateTimeOffset.UtcNow); | |||
DateTimeOffset? resetTick = null; | |||
//Using X-RateLimit-Remaining causes a race condition | |||
@@ -1,21 +1,22 @@ | |||
using Discord.Net.Rest; | |||
using System; | |||
using System.Threading.Tasks; | |||
namespace Discord.Net.Queue | |||
{ | |||
public class JsonRestRequest : RestRequest | |||
{ | |||
public string Json { get; } | |||
public ReadOnlyBuffer<byte> Payload { get; } | |||
public JsonRestRequest(IRestClient client, string method, string endpoint, string json, RequestOptions options) | |||
public JsonRestRequest(IRestClient client, string method, string endpoint, ReadOnlyBuffer<byte> payload, RequestOptions options) | |||
: base(client, method, endpoint, options) | |||
{ | |||
Json = json; | |||
Payload = payload; | |||
} | |||
public override async Task<RestResponse> SendAsync() | |||
{ | |||
return await Client.SendAsync(Method, Endpoint, Json, Options.CancelToken, Options.HeaderOnly, Options.AuditLogReason).ConfigureAwait(false); | |||
return await Client.SendAsync(Method, Endpoint, Payload, Options.CancelToken, Options.HeaderOnly, Options.AuditLogReason).ConfigureAwait(false); | |||
} | |||
} | |||
} |
@@ -10,14 +10,14 @@ namespace Discord.Net.Queue | |||
{ | |||
public IWebSocketClient Client { get; } | |||
public string BucketId { get; } | |||
public byte[] Data { get; } | |||
public ReadOnlyBuffer<byte> Data { get; } | |||
public bool IsText { get; } | |||
public DateTimeOffset? TimeoutAt { get; } | |||
public TaskCompletionSource<Stream> Promise { get; } | |||
public RequestOptions Options { get; } | |||
public CancellationToken CancelToken { get; internal set; } | |||
public WebSocketRequest(IWebSocketClient client, string bucketId, byte[] data, bool isText, RequestOptions options) | |||
public WebSocketRequest(IWebSocketClient client, string bucketId, ReadOnlyBuffer<byte> data, bool isText, RequestOptions options) | |||
{ | |||
Preconditions.NotNull(options, nameof(options)); | |||
@@ -32,7 +32,7 @@ namespace Discord.Net.Queue | |||
public async Task SendAsync() | |||
{ | |||
await Client.SendAsync(Data, 0, Data.Length, IsText).ConfigureAwait(false); | |||
await Client.SendAsync(Data, IsText).ConfigureAwait(false); | |||
} | |||
} | |||
} |