Added support for .NET Standard 1.1 and 1.2

This commit is contained in:
RogueException
2016-12-16 05:51:07 -04:00
parent a1addd4016
commit 8f87b2cc71
49 changed files with 533 additions and 348 deletions

View File

@@ -86,7 +86,7 @@ namespace Discord.Rest
await _loggedInEvent.InvokeAsync().ConfigureAwait(false);
}
protected virtual Task OnLoginAsync(TokenType tokenType, string token) { return Task.CompletedTask; }
protected virtual Task OnLoginAsync(TokenType tokenType, string token) { return Task.Delay(0); }
/// <inheritdoc />
public async Task LogoutAsync()
@@ -111,7 +111,7 @@ namespace Discord.Rest
await _loggedOutEvent.InvokeAsync().ConfigureAwait(false);
}
protected virtual Task OnLogoutAsync() { return Task.CompletedTask; }
protected virtual Task OnLogoutAsync() { return Task.Delay(0); }
internal virtual void Dispose(bool disposing)
{

View File

@@ -1,44 +1,28 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Description>A core Discord.Net library containing the REST client and models.</Description>
<VersionPrefix>1.0.0-beta2</VersionPrefix>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFrameworks>netstandard1.1;netstandard1.3</TargetFrameworks>
<AssemblyName>Discord.Net.Rest</AssemblyName>
<PackageTags>discord;discordapp</PackageTags>
<PackageProjectUrl>https://github.com/RogueException/Discord.Net</PackageProjectUrl>
<PackageLicenseUrl>http://opensource.org/licenses/MIT</PackageLicenseUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>git://github.com/RogueException/Discord.Net</RepositoryUrl>
<PackageTargetFallback Condition=" '$(TargetFramework)' == 'netstandard1.3' ">$(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8</PackageTargetFallback>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
<EmbeddedResource Include="compiler\resources\**\*" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="System.IO.FileSystem">
<Version>4.3.0</Version>
</PackageReference>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
</ItemGroup>
<ItemGroup />
<PropertyGroup Label="Configuration">
<SignAssembly>False</SignAssembly>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
<WarningsAsErrors>true</WarningsAsErrors>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -21,12 +21,12 @@ namespace Discord.Rest
protected override Task OnLoginAsync(TokenType tokenType, string token)
{
base.CurrentUser = RestSelfUser.Create(this, ApiClient.CurrentUser);
return Task.CompletedTask;
return Task.Delay(0);
}
protected override Task OnLogoutAsync()
{
_applicationInfo = null;
return Task.CompletedTask;
return Task.Delay(0);
}
/// <inheritdoc />

View File

@@ -139,6 +139,7 @@ namespace Discord.Rest
return RestUserMessage.Create(client, channel, client.CurrentUser, model);
}
#if NETSTANDARD1_3
public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client,
string filePath, string text, bool isTTS, RequestOptions options)
{
@@ -146,6 +147,7 @@ namespace Discord.Rest
using (var file = File.OpenRead(filePath))
return await SendFileAsync(channel, client, file, filename, text, isTTS, options).ConfigureAwait(false);
}
#endif
public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client,
Stream stream, string filename, string text, bool isTTS, RequestOptions options)
{

View File

@@ -8,8 +8,10 @@ namespace Discord.Rest
{
/// <summary> Sends a message to this message channel. </summary>
new Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null);
#if NETSTANDARD1_3
/// <summary> Sends a file to this text channel, with an optional caption. </summary>
new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null);
#endif
/// <summary> Sends a file to this text channel, with an optional caption. </summary>
new Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, RequestOptions options = null);

View File

@@ -65,8 +65,10 @@ namespace Discord.Rest
public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if NETSTANDARD1_3
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options);
#endif
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options);
@@ -122,8 +124,10 @@ namespace Discord.Rest
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false);
#if NETSTANDARD1_3
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false);
#endif
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false);
async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options)

View File

@@ -78,8 +78,10 @@ namespace Discord.Rest
public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if NETSTANDARD1_3
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options);
#endif
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options);
@@ -132,8 +134,10 @@ namespace Discord.Rest
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false);
#if NETSTANDARD1_3
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false);
#endif
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false);
async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options)

View File

@@ -57,8 +57,10 @@ namespace Discord.Rest
public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if NETSTANDARD1_3
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options);
#endif
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options);
@@ -104,8 +106,10 @@ namespace Discord.Rest
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false);
#if NETSTANDARD1_3
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false);
#endif
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false);
async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options)

View File

@@ -35,8 +35,10 @@ namespace Discord.Rest
public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, EmbedBuilder embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if NETSTANDARD1_3
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options);
#endif
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options);
@@ -82,8 +84,10 @@ namespace Discord.Rest
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options);
#if NETSTANDARD1_3
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, options);
#endif
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, options);
async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options)

View File

@@ -69,7 +69,7 @@ namespace Discord.Rest
public static ImmutableArray<ITag> ParseTags(string text, IMessageChannel channel, IGuild guild, ImmutableArray<IUser> userMentions)
{
var tags = new SortedList<int, ITag>();
var tags = ImmutableArray.CreateBuilder<ITag>();
int index = 0;
while (true)
@@ -94,27 +94,27 @@ namespace Discord.Rest
break;
}
}
tags.Add(index, new Tag<IUser>(TagType.UserMention, index, content.Length, id, mentionedUser));
tags.Add(new Tag<IUser>(TagType.UserMention, index, content.Length, id, mentionedUser));
}
else if (MentionUtils.TryParseChannel(content, out id))
{
IChannel mentionedChannel = null;
if (guild != null)
mentionedChannel = guild.GetChannelAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult();
tags.Add(index, new Tag<IChannel>(TagType.ChannelMention, index, content.Length, id, mentionedChannel));
tags.Add(new Tag<IChannel>(TagType.ChannelMention, index, content.Length, id, mentionedChannel));
}
else if (MentionUtils.TryParseRole(content, out id))
{
IRole mentionedRole = null;
if (guild != null)
mentionedRole = guild.GetRole(id);
tags.Add(index, new Tag<IRole>(TagType.RoleMention, index, content.Length, id, mentionedRole));
tags.Add(new Tag<IRole>(TagType.RoleMention, index, content.Length, id, mentionedRole));
}
else
{
Emoji emoji;
if (Emoji.TryParse(content, out emoji))
tags.Add(index, new Tag<Emoji>(TagType.Emoji, index, content.Length, id, emoji));
tags.Add(new Tag<Emoji>(TagType.Emoji, index, content.Length, id, emoji));
}
index = endIndex + 1;
}
@@ -125,7 +125,7 @@ namespace Discord.Rest
index = text.IndexOf("@everyone", index);
if (index == -1) break;
tags.Add(index, new Tag<object>(TagType.EveryoneMention, index, "@everyone".Length, 0, null));
tags.Add(new Tag<object>(TagType.EveryoneMention, index, "@everyone".Length, 0, null));
index++;
}
@@ -135,11 +135,11 @@ namespace Discord.Rest
index = text.IndexOf("@here", index);
if (index == -1) break;
tags.Add(index, new Tag<object>(TagType.HereMention, index, "@here".Length, 0, null));
tags.Add(new Tag<object>(TagType.HereMention, index, "@here".Length, 0, null));
index++;
}
return tags.Values.ToImmutableArray();
return tags.ToImmutable();
}
public static ImmutableArray<ulong> FilterTagsByKey(TagType type, ImmutableArray<ITag> tags)
{

View File

@@ -0,0 +1,144 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Discord.Net.Rest
{
internal sealed class DefaultRestClient : IRestClient
{
private const int HR_SECURECHANNELFAILED = -2146233079;
private readonly HttpClient _client;
private readonly string _baseUrl;
private readonly JsonSerializer _errorDeserializer;
private CancellationTokenSource _cancelTokenSource;
private CancellationToken _cancelToken, _parentToken;
private bool _isDisposed;
public DefaultRestClient(string baseUrl)
{
_baseUrl = baseUrl;
_client = new HttpClient(new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
UseCookies = false,
UseProxy = false
});
SetHeader("accept-encoding", "gzip, deflate");
_cancelTokenSource = new CancellationTokenSource();
_cancelToken = CancellationToken.None;
_parentToken = CancellationToken.None;
_errorDeserializer = new JsonSerializer();
}
private void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
_client.Dispose();
_isDisposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
public void SetHeader(string key, string value)
{
_client.DefaultRequestHeaders.Remove(key);
if (value != null)
_client.DefaultRequestHeaders.Add(key, value);
}
public void SetCancelToken(CancellationToken cancelToken)
{
_parentToken = cancelToken;
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token;
}
public async Task<RestResponse> SendAsync(string method, string endpoint, CancellationToken cancelToken, bool headerOnly)
{
string uri = Path.Combine(_baseUrl, endpoint);
using (var restRequest = new HttpRequestMessage(GetMethod(method), uri))
return await SendInternalAsync(restRequest, cancelToken, headerOnly).ConfigureAwait(false);
}
public async Task<RestResponse> SendAsync(string method, string endpoint, string json, CancellationToken cancelToken, bool headerOnly)
{
string uri = Path.Combine(_baseUrl, endpoint);
using (var restRequest = new HttpRequestMessage(GetMethod(method), uri))
{
restRequest.Content = new StringContent(json, Encoding.UTF8, "application/json");
return await SendInternalAsync(restRequest, cancelToken, headerOnly).ConfigureAwait(false);
}
}
public async Task<RestResponse> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, CancellationToken cancelToken, bool headerOnly)
{
string uri = Path.Combine(_baseUrl, endpoint);
using (var restRequest = new HttpRequestMessage(GetMethod(method), uri))
{
var content = new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture));
if (multipartParams != null)
{
foreach (var p in multipartParams)
{
//TODO: C#7 Typeswitch candidate
var stringValue = p.Value as string;
if (stringValue != null) { content.Add(new StringContent(stringValue), p.Key); continue; }
var byteArrayValue = p.Value as byte[];
if (byteArrayValue != null) { content.Add(new ByteArrayContent(byteArrayValue), p.Key); continue; }
var streamValue = p.Value as Stream;
if (streamValue != null) { content.Add(new StreamContent(streamValue), p.Key); continue; }
if (p.Value is MultipartFile)
{
var fileValue = (MultipartFile)p.Value;
content.Add(new StreamContent(fileValue.Stream), p.Key, fileValue.Filename);
continue;
}
throw new InvalidOperationException($"Unsupported param type \"{p.Value.GetType().Name}\"");
}
}
restRequest.Content = content;
return await SendInternalAsync(restRequest, cancelToken, headerOnly).ConfigureAwait(false);
}
}
private async Task<RestResponse> SendInternalAsync(HttpRequestMessage request, CancellationToken cancelToken, bool headerOnly)
{
while (true)
{
cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelToken, cancelToken).Token;
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;
return new RestResponse(response.StatusCode, headers, stream);
}
}
private static readonly HttpMethod _patch = new HttpMethod("PATCH");
private HttpMethod GetMethod(string method)
{
switch (method)
{
case "DELETE": return HttpMethod.Delete;
case "GET": return HttpMethod.Get;
case "PATCH": return _patch;
case "POST": return HttpMethod.Post;
case "PUT": return HttpMethod.Put;
default: throw new ArgumentOutOfRangeException(nameof(method), $"Unknown HttpMethod: {method}");
}
}
}
}

View File

@@ -29,16 +29,14 @@
"Discord.Net.Core": {
"target": "project"
},
"System.IO.FileSystem": "4.3.0"
},
"frameworks": {
"netstandard1.1": {},
"netstandard1.3": {
"imports": [
"dotnet5.4",
"dnxcore50",
"portable-net45+win8"
]
"dependencies": {
"System.IO.FileSystem": "4.3.0"
}
}
}
}