Browse Source

Merge branch 'dev' into docs/faq-n-patches-offline

pull/1161/head
Still Hsu 7 years ago
parent
commit
596b47e1b8
No known key found for this signature in database GPG Key ID: 8601A145FDA95209
65 changed files with 484 additions and 328 deletions
  1. +1
    -7
      Discord.Net.targets
  2. +1
    -1
      samples/02_commands_framework/02_commands_framework.csproj
  3. +1
    -1
      src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj
  4. +28
    -6
      src/Discord.Net.Commands/CommandParser.cs
  5. +7
    -2
      src/Discord.Net.Commands/CommandService.cs
  6. +6
    -3
      src/Discord.Net.Commands/CommandServiceConfig.cs
  7. +7
    -3
      src/Discord.Net.Commands/Discord.Net.Commands.csproj
  8. +3
    -2
      src/Discord.Net.Commands/Info/CommandInfo.cs
  9. +2
    -2
      src/Discord.Net.Commands/PrimitiveParsers.cs
  10. +35
    -0
      src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs
  11. +95
    -0
      src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs
  12. +5
    -5
      src/Discord.Net.Core/Discord.Net.Core.csproj
  13. +1
    -0
      src/Discord.Net.Core/DiscordConfig.cs
  14. +6
    -3
      src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
  15. +2
    -4
      src/Discord.Net.Core/Entities/Guilds/IGuild.cs
  16. +2
    -3
      src/Discord.Net.Core/Entities/Image.cs
  17. +1
    -1
      src/Discord.Net.Core/Entities/Messages/IUserMessage.cs
  18. +3
    -7
      src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs
  19. +13
    -9
      src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
  20. +10
    -0
      src/Discord.Net.Core/Entities/Roles/Color.cs
  21. +2
    -3
      src/Discord.Net.Core/Extensions/UserExtensions.cs
  22. +9
    -12
      src/Discord.Net.Core/Logging/LogManager.cs
  23. +12
    -13
      src/Discord.Net.Core/Logging/Logger.cs
  24. +2
    -45
      src/Discord.Net.Core/Utils/DateTimeUtils.cs
  25. +2
    -2
      src/Discord.Net.Core/Utils/SnowflakeUtils.cs
  26. +12
    -1
      src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs
  27. +1
    -1
      src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs
  28. +6
    -7
      src/Discord.Net.Rest/Discord.Net.Rest.csproj
  29. +2
    -2
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  30. +5
    -2
      src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
  31. +1
    -3
      src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs
  32. +8
    -7
      src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs
  33. +8
    -5
      src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
  34. +9
    -5
      src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs
  35. +11
    -8
      src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs
  36. +20
    -4
      src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
  37. +8
    -8
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  38. +35
    -8
      src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
  39. +3
    -3
      src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
  40. +1
    -2
      src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs
  41. +1
    -1
      src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs
  42. +1
    -1
      src/Discord.Net.Rest/Net/RateLimitInfo.cs
  43. +4
    -4
      src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj
  44. +1
    -1
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  45. +2
    -2
      src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs
  46. +1
    -3
      src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs
  47. +7
    -7
      src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs
  48. +8
    -5
      src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs
  49. +8
    -5
      src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs
  50. +8
    -10
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
  51. +2
    -2
      src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs
  52. +0
    -6
      src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs
  53. +1
    -9
      src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs
  54. +3
    -10
      src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs
  55. +0
    -8
      src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs
  56. +2
    -2
      src/Discord.Net.Webhook/Discord.Net.Webhook.csproj
  57. +1
    -4
      src/Discord.Net.Webhook/DiscordWebhookClient.cs
  58. +0
    -2
      src/Discord.Net.Webhook/WebhookClientHelper.cs
  59. +5
    -5
      test/Discord.Net.Tests/Discord.Net.Tests.csproj
  60. +1
    -1
      test/Discord.Net.Tests/Tests.ChannelPermissions.cs
  61. +26
    -26
      test/Discord.Net.Tests/Tests.Channels.cs
  62. +2
    -1
      test/Discord.Net.Tests/Tests.Colors.cs
  63. +2
    -1
      test/Discord.Net.Tests/Tests.Emotes.cs
  64. +5
    -5
      test/Discord.Net.Tests/Tests.GuildPermissions.cs
  65. +7
    -7
      test/Discord.Net.Tests/Tests.Permissions.cs

+ 1
- 7
Discord.Net.targets View File

@@ -16,13 +16,7 @@
<PropertyGroup Condition=" '$(BuildNumber)' != '' And $(IsTagBuild) != 'true' "> <PropertyGroup Condition=" '$(BuildNumber)' != '' And $(IsTagBuild) != 'true' ">
<VersionSuffix Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)-$(BuildNumber)</VersionSuffix> <VersionSuffix Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>
<VersionSuffix Condition=" '$(VersionSuffix)' == '' ">build-$(BuildNumber)</VersionSuffix> <VersionSuffix Condition=" '$(VersionSuffix)' == '' ">build-$(BuildNumber)</VersionSuffix>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' Or '$(TargetFramework)' == 'net45' ">
<DefineConstants>$(DefineConstants);FILESYSTEM;DEFAULTUDPCLIENT;DEFAULTWEBSOCKET</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<DefineConstants>$(DefineConstants);FORMATSTR;UNIXTIME;MSTRYBUFFER;UDPDISPOSE</DefineConstants>
</PropertyGroup>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn> <NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
<WarningsAsErrors>true</WarningsAsErrors> <WarningsAsErrors>true</WarningsAsErrors>


+ 1
- 1
samples/02_commands_framework/02_commands_framework.csproj View File

@@ -6,7 +6,7 @@
</PropertyGroup> </PropertyGroup>


<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.0-preview1-final" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
</ItemGroup> </ItemGroup>


<ItemGroup> <ItemGroup>


+ 1
- 1
src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj View File

@@ -7,7 +7,7 @@
<TargetFramework>netstandard1.3</TargetFramework> <TargetFramework>netstandard1.3</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="2.6.0" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="2.8.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Discord.Net.Commands\Discord.Net.Commands.csproj" /> <ProjectReference Include="..\Discord.Net.Commands\Discord.Net.Commands.csproj" />


+ 28
- 6
src/Discord.Net.Commands/CommandParser.cs View File

@@ -1,4 +1,5 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -13,8 +14,7 @@ namespace Discord.Commands
Parameter, Parameter,
QuotedParameter QuotedParameter
} }
public static async Task<ParseResult> ParseArgsAsync(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos)
public static async Task<ParseResult> ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos, IReadOnlyDictionary<char, char> aliasMap)
{ {
ParameterInfo curParam = null; ParameterInfo curParam = null;
StringBuilder argBuilder = new StringBuilder(input.Length); StringBuilder argBuilder = new StringBuilder(input.Length);
@@ -24,7 +24,27 @@ namespace Discord.Commands
var argList = ImmutableArray.CreateBuilder<TypeReaderResult>(); var argList = ImmutableArray.CreateBuilder<TypeReaderResult>();
var paramList = ImmutableArray.CreateBuilder<TypeReaderResult>(); var paramList = ImmutableArray.CreateBuilder<TypeReaderResult>();
bool isEscaping = false; bool isEscaping = false;
char c;
char c, matchQuote = '\0';

// local helper functions
bool IsOpenQuote(IReadOnlyDictionary<char, char> dict, char ch)
{
// return if the key is contained in the dictionary if it is populated
if (dict.Count != 0)
return dict.ContainsKey(ch);
// or otherwise if it is the default double quote
return c == '\"';
}

char GetMatch(IReadOnlyDictionary<char, char> dict, char ch)
{
// get the corresponding value for the key, if it exists
// and if the dictionary is populated
if (dict.Count != 0 && dict.TryGetValue(c, out var value))
return value;
// or get the default pair of the default double quote
return '\"';
}


for (int curPos = startPos; curPos <= endPos; curPos++) for (int curPos = startPos; curPos <= endPos; curPos++)
{ {
@@ -74,9 +94,11 @@ namespace Discord.Commands
argBuilder.Append(c); argBuilder.Append(c);
continue; continue;
} }
if (c == '\"')
if (IsOpenQuote(aliasMap, c))
{ {
curPart = ParserPart.QuotedParameter; curPart = ParserPart.QuotedParameter;
matchQuote = GetMatch(aliasMap, c);
continue; continue;
} }
curPart = ParserPart.Parameter; curPart = ParserPart.Parameter;
@@ -97,7 +119,7 @@ namespace Discord.Commands
} }
else if (curPart == ParserPart.QuotedParameter) else if (curPart == ParserPart.QuotedParameter)
{ {
if (c == '\"')
if (c == matchQuote)
{ {
argString = argBuilder.ToString(); //Remove quotes argString = argBuilder.ToString(); //Remove quotes
lastArgEndPos = curPos + 1; lastArgEndPos = curPos + 1;


+ 7
- 2
src/Discord.Net.Commands/CommandService.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
@@ -38,6 +38,7 @@ namespace Discord.Commands
internal readonly RunMode _defaultRunMode; internal readonly RunMode _defaultRunMode;
internal readonly Logger _cmdLogger; internal readonly Logger _cmdLogger;
internal readonly LogManager _logManager; internal readonly LogManager _logManager;
internal readonly IReadOnlyDictionary<char, char> _quotationMarkAliasMap;


/// <summary> /// <summary>
/// Represents all modules loaded within <see cref="CommandService" />. /// Represents all modules loaded within <see cref="CommandService" />.
@@ -73,6 +74,7 @@ namespace Discord.Commands
_ignoreExtraArgs = config.IgnoreExtraArgs; _ignoreExtraArgs = config.IgnoreExtraArgs;
_separatorChar = config.SeparatorChar; _separatorChar = config.SeparatorChar;
_defaultRunMode = config.DefaultRunMode; _defaultRunMode = config.DefaultRunMode;
_quotationMarkAliasMap = (config.QuotationMarkAliasMap ?? new Dictionary<char, char>()).ToImmutableDictionary();
if (_defaultRunMode == RunMode.Default) if (_defaultRunMode == RunMode.Default)
throw new InvalidOperationException("The default run mode cannot be set to Default."); throw new InvalidOperationException("The default run mode cannot be set to Default.");


@@ -93,6 +95,10 @@ namespace Discord.Commands
_defaultTypeReaders[typeof(Nullable<>).MakeGenericType(type)] = NullableTypeReader.Create(type, _defaultTypeReaders[type]); _defaultTypeReaders[typeof(Nullable<>).MakeGenericType(type)] = NullableTypeReader.Create(type, _defaultTypeReaders[type]);
} }


var tsreader = new TimeSpanTypeReader();
_defaultTypeReaders[typeof(TimeSpan)] = tsreader;
_defaultTypeReaders[typeof(TimeSpan?)] = NullableTypeReader.Create(typeof(TimeSpan), tsreader);

_defaultTypeReaders[typeof(string)] = _defaultTypeReaders[typeof(string)] =
new PrimitiveTypeReader<string>((string x, out string y) => { y = x; return true; }, 0); new PrimitiveTypeReader<string>((string x, out string y) => { y = x; return true; }, 0);


@@ -447,7 +453,6 @@ namespace Discord.Commands
public async Task<IResult> ExecuteAsync(ICommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) public async Task<IResult> ExecuteAsync(ICommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
{ {
services = services ?? EmptyServiceProvider.Instance; services = services ?? EmptyServiceProvider.Instance;

var searchResult = Search(context, input); var searchResult = Search(context, input);
if (!searchResult.IsSuccess) if (!searchResult.IsSuccess)
return searchResult; return searchResult;


+ 6
- 3
src/Discord.Net.Commands/CommandServiceConfig.cs View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;


namespace Discord.Commands namespace Discord.Commands
{ {
@@ -33,9 +34,11 @@ namespace Discord.Commands
/// </summary> /// </summary>
public bool ThrowOnError { get; set; } = true; public bool ThrowOnError { get; set; } = true;


/// <summary>
/// Gets or sets whether extra parameters should be ignored.
/// </summary>
/// <summary> Collection of aliases that can wrap strings for command parsing.
/// represents the opening quotation mark and the value is the corresponding closing mark.</summary>
public Dictionary<char, char> QuotationMarkAliasMap { get; set; } = QuotationAliasUtils.GetDefaultAliasMap;

/// <summary> Determines whether extra parameters should be ignored. </summary>
public bool IgnoreExtraArgs { get; set; } = false; public bool IgnoreExtraArgs { get; set; } = false;
} }
} }

+ 7
- 3
src/Discord.Net.Commands/Discord.Net.Commands.csproj View File

@@ -1,15 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../Discord.Net.targets" /> <Import Project="../../Discord.Net.targets" />
<PropertyGroup> <PropertyGroup>
<AssemblyName>Discord.Net.Commands</AssemblyName> <AssemblyName>Discord.Net.Commands</AssemblyName>
<RootNamespace>Discord.Commands</RootNamespace> <RootNamespace>Discord.Commands</RootNamespace>
<Description>A Discord.Net extension adding support for bot commands.</Description> <Description>A Discord.Net extension adding support for bot commands.</Description>
<TargetFrameworks>netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.3;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> <ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'netstandard2.0' ">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

+ 3
- 2
src/Discord.Net.Commands/Info/CommandInfo.cs View File

@@ -1,4 +1,4 @@
using Discord.Commands.Builders;
using Discord.Commands.Builders;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
@@ -172,7 +172,8 @@ namespace Discord.Commands
return ParseResult.FromError(preconditionResult); return ParseResult.FromError(preconditionResult);


string input = searchResult.Text.Substring(startIndex); string input = searchResult.Text.Substring(startIndex);
return await CommandParser.ParseArgsAsync(this, context, services, input, 0).ConfigureAwait(false);

return await CommandParser.ParseArgsAsync(this, context, _commandService._ignoreExtraArgs, services, input, 0, _commandService._quotationMarkAliasMap).ConfigureAwait(false);
} }
public Task<IResult> ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services) public Task<IResult> ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services)


+ 2
- 2
src/Discord.Net.Commands/PrimitiveParsers.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;


@@ -29,7 +29,7 @@ namespace Discord.Commands
parserBuilder[typeof(decimal)] = (TryParseDelegate<decimal>)decimal.TryParse; parserBuilder[typeof(decimal)] = (TryParseDelegate<decimal>)decimal.TryParse;
parserBuilder[typeof(DateTime)] = (TryParseDelegate<DateTime>)DateTime.TryParse; parserBuilder[typeof(DateTime)] = (TryParseDelegate<DateTime>)DateTime.TryParse;
parserBuilder[typeof(DateTimeOffset)] = (TryParseDelegate<DateTimeOffset>)DateTimeOffset.TryParse; parserBuilder[typeof(DateTimeOffset)] = (TryParseDelegate<DateTimeOffset>)DateTimeOffset.TryParse;
parserBuilder[typeof(TimeSpan)] = (TryParseDelegate<TimeSpan>)TimeSpan.TryParse;
//parserBuilder[typeof(TimeSpan)] = (TryParseDelegate<TimeSpan>)TimeSpan.TryParse;
parserBuilder[typeof(char)] = (TryParseDelegate<char>)char.TryParse; parserBuilder[typeof(char)] = (TryParseDelegate<char>)char.TryParse;
return parserBuilder.ToImmutable(); return parserBuilder.ToImmutable();
} }


+ 35
- 0
src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs View File

@@ -0,0 +1,35 @@
using System;
using System.Globalization;
using System.Threading.Tasks;

namespace Discord.Commands
{
internal class TimeSpanTypeReader : TypeReader
{
private static readonly string[] _formats = new[]
{
"%d'd'%h'h'%m'm'%s's'", //4d3h2m1s
"%d'd'%h'h'%m'm'", //4d3h2m
"%d'd'%h'h'%s's'", //4d3h 1s
"%d'd'%h'h'", //4d3h
"%d'd'%m'm'%s's'", //4d 2m1s
"%d'd'%m'm'", //4d 2m
"%d'd'%s's'", //4d 1s
"%d'd'", //4d
"%h'h'%m'm'%s's'", // 3h2m1s
"%h'h'%m'm'", // 3h2m
"%h'h'%s's'", // 3h 1s
"%h'h'", // 3h
"%m'm'%s's'", // 2m1s
"%m'm'", // 2m
"%s's'", // 1s
};

public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
{
return (TimeSpan.TryParseExact(input.ToLowerInvariant(), _formats, CultureInfo.InvariantCulture, out var timeSpan))
? Task.FromResult(TypeReaderResult.FromSuccess(timeSpan))
: Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse TimeSpan"));
}
}
}

+ 95
- 0
src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;

namespace Discord.Commands
{
/// <summary>
/// Utility methods for generating matching pairs of unicode quotation marks for CommandServiceConfig
/// </summary>
internal static class QuotationAliasUtils
{
/// <summary>
/// Generates an IEnumerable of characters representing open-close pairs of
/// quotation punctuation.
/// </summary>
internal static Dictionary<char, char> GetDefaultAliasMap
{
get
{
// Output of a gist provided by https://gist.github.com/ufcpp
// https://gist.github.com/ufcpp/5b2cf9a9bf7d0b8743714a0b88f7edc5
// This was not used for the implementation because of incompatibility with netstandard1.1
return new Dictionary<char, char> {
{'\"', '\"' },
{'«', '»' },
{'‘', '’' },
{'“', '”' },
{'„', '‟' },
{'‹', '›' },
{'‚', '‛' },
{'《', '》' },
{'〈', '〉' },
{'「', '」' },
{'『', '』' },
{'〝', '〞' },
{'﹁', '﹂' },
{'﹃', '﹄' },
{'"', '"' },
{''', ''' },
{'「', '」' },
{'(', ')' },
{'༺', '༻' },
{'༼', '༽' },
{'᚛', '᚜' },
{'⁅', '⁆' },
{'⌈', '⌉' },
{'⌊', '⌋' },
{'❨', '❩' },
{'❪', '❫' },
{'❬', '❭' },
{'❮', '❯' },
{'❰', '❱' },
{'❲', '❳' },
{'❴', '❵' },
{'⟅', '⟆' },
{'⟦', '⟧' },
{'⟨', '⟩' },
{'⟪', '⟫' },
{'⟬', '⟭' },
{'⟮', '⟯' },
{'⦃', '⦄' },
{'⦅', '⦆' },
{'⦇', '⦈' },
{'⦉', '⦊' },
{'⦋', '⦌' },
{'⦍', '⦎' },
{'⦏', '⦐' },
{'⦑', '⦒' },
{'⦓', '⦔' },
{'⦕', '⦖' },
{'⦗', '⦘' },
{'⧘', '⧙' },
{'⧚', '⧛' },
{'⧼', '⧽' },
{'⸂', '⸃' },
{'⸄', '⸅' },
{'⸉', '⸊' },
{'⸌', '⸍' },
{'⸜', '⸝' },
{'⸠', '⸡' },
{'⸢', '⸣' },
{'⸤', '⸥' },
{'⸦', '⸧' },
{'⸨', '⸩' },
{'【', '】'},
{'〔', '〕' },
{'〖', '〗' },
{'〘', '〙' },
{'〚', '〛' }
};
}
}
}
}

+ 5
- 5
src/Discord.Net.Core/Discord.Net.Core.csproj View File

@@ -1,15 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../Discord.Net.targets" /> <Import Project="../../Discord.Net.targets" />
<PropertyGroup> <PropertyGroup>
<AssemblyName>Discord.Net.Core</AssemblyName> <AssemblyName>Discord.Net.Core</AssemblyName>
<RootNamespace>Discord</RootNamespace> <RootNamespace>Discord</RootNamespace>
<Description>The core components for the Discord.Net library.</Description> <Description>The core components for the Discord.Net library.</Description>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net45;netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.3;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="System.Collections.Immutable" Version="1.4.0" />
<PackageReference Include="System.Interactive.Async" Version="3.1.1" /> <PackageReference Include="System.Interactive.Async" Version="3.1.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

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

@@ -93,6 +93,7 @@ namespace Discord
/// The maximum number of guilds that can be gotten per-batch. /// The maximum number of guilds that can be gotten per-batch.
/// </returns> /// </returns>
public const int MaxGuildsPerBatch = 100; public const int MaxGuildsPerBatch = 100;
public const int MaxUserReactionsPerBatch = 100;
public const int MaxAuditLogEntriesPerBatch = 100; public const int MaxAuditLogEntriesPerBatch = 100;


/// <summary> /// <summary>


+ 6
- 3
src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs View File

@@ -20,8 +20,7 @@ namespace Discord
/// <returns> /// <returns>
/// An awaitable Task containing the message sent to the channel. /// An awaitable Task containing the message sent to the channel.
/// </returns> /// </returns>
Task<IUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null);
#if FILESYSTEM
Task<IUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
/// <summary> /// <summary>
/// Sends a file to this message channel with an optional caption. /// Sends a file to this message channel with an optional caption.
/// </summary> /// </summary>
@@ -39,7 +38,6 @@ namespace Discord
/// An awaitable Task containing the message sent to the channel. /// An awaitable Task containing the message sent to the channel.
/// </returns> /// </returns>
Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
#endif
/// <summary> /// <summary>
/// Sends a file to this message channel with an optional caption. /// Sends a file to this message channel with an optional caption.
/// </summary> /// </summary>
@@ -121,6 +119,11 @@ namespace Discord
/// </returns> /// </returns>
Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync(RequestOptions options = null); Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync(RequestOptions options = null);


/// <summary> Deletes a message based on the message ID in this channel. </summary>
Task DeleteMessageAsync(ulong messageId, RequestOptions options = null);
/// <summary> Deletes a message based on the provided message in this channel. </summary>
Task DeleteMessageAsync(IMessage message, RequestOptions options = null);

/// <summary> /// <summary>
/// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds. /// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds.
/// </summary> /// </summary>


+ 2
- 4
src/Discord.Net.Core/Entities/Guilds/IGuild.cs View File

@@ -414,7 +414,7 @@ namespace Discord
/// <returns> /// <returns>
/// An awaitable <see cref="Task"/> containing the newly created text channel. /// An awaitable <see cref="Task"/> containing the newly created text channel.
/// </returns> /// </returns>
Task<ITextChannel> CreateTextChannelAsync(string name, RequestOptions options = null);
Task<ITextChannel> CreateTextChannelAsync(string name, Action<TextChannelProperties> func = null, RequestOptions options = null);
/// <summary> /// <summary>
/// Creates a new voice channel. /// Creates a new voice channel.
/// </summary> /// </summary>
@@ -423,13 +423,11 @@ namespace Discord
/// <returns> /// <returns>
/// An awaitable <see cref="Task"/> containing the newly created voice channel. /// An awaitable <see cref="Task"/> containing the newly created voice channel.
/// </returns> /// </returns>
Task<IVoiceChannel> CreateVoiceChannelAsync(string name, RequestOptions options = null);
Task<IVoiceChannel> CreateVoiceChannelAsync(string name, Action<VoiceChannelProperties> func = null, RequestOptions options = null);
/// <summary> /// <summary>
/// Creates a new channel category. /// Creates a new channel category.
/// </summary> /// </summary>
/// <param name="name">The new name for the category.</param> /// <param name="name">The new name for the category.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// An awaitable <see cref="Task"/> containing the newly created category channel. /// An awaitable <see cref="Task"/> containing the newly created category channel.
/// </returns> /// </returns>
Task<ICategoryChannel> CreateCategoryAsync(string name, RequestOptions options = null); Task<ICategoryChannel> CreateCategoryAsync(string name, RequestOptions options = null);


+ 2
- 3
src/Discord.Net.Core/Entities/Image.cs View File

@@ -1,4 +1,3 @@
using System;
using System.IO; using System.IO;
namespace Discord namespace Discord
{ {
@@ -22,7 +21,7 @@ namespace Discord
{ {
Stream = stream; Stream = stream;
} }
#if FILESYSTEM
/// <summary> /// <summary>
/// Create the image from a file path. /// Create the image from a file path.
/// </summary> /// </summary>
@@ -55,6 +54,6 @@ namespace Discord
{ {
Stream = File.OpenRead(path); Stream = File.OpenRead(path);
} }
#endif
} }
} }

+ 1
- 1
src/Discord.Net.Core/Entities/Messages/IUserMessage.cs View File

@@ -42,7 +42,7 @@ namespace Discord
/// <summary> /// <summary>
/// Gets all users that reacted to a message with a given emote. /// Gets all users that reacted to a message with a given emote.
/// </summary> /// </summary>
Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null);
IAsyncEnumerable<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emoji, int limit, RequestOptions options = null);


/// <summary> /// <summary>
/// Transforms this message's text into a human-readable form by resolving its tags. /// Transforms this message's text into a human-readable form by resolving its tags.


+ 3
- 7
src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs View File

@@ -41,13 +41,9 @@ namespace Discord
/// Allows for viewing of audit logs. /// Allows for viewing of audit logs.
/// </summary> /// </summary>
ViewAuditLog = 0x00_00_00_80, ViewAuditLog = 0x00_00_00_80,
/// <summary>
/// Allows for reading of message.
/// </summary>
ReadMessages = 0x00_00_04_00,
/// <summary>
/// Allows for sending messages in a channel.
/// </summary>
[Obsolete("Use ViewChannel instead.")]
ReadMessages = ViewChannel,
ViewChannel = 0x00_00_04_00,
SendMessages = 0x00_00_08_00, SendMessages = 0x00_00_08_00,
/// <summary> /// <summary>
/// Allows for sending of text-to-speech messages. /// Allows for sending of text-to-speech messages.


+ 13
- 9
src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;


@@ -34,9 +35,12 @@ namespace Discord
/// <summary> If <c>true</c>, a user may view the audit log. </summary> /// <summary> If <c>true</c>, a user may view the audit log. </summary>
public bool ViewAuditLog => Permissions.GetValue(RawValue, GuildPermission.ViewAuditLog); public bool ViewAuditLog => Permissions.GetValue(RawValue, GuildPermission.ViewAuditLog);


/// <summary> If <c>true</c>, a user may join channels. </summary>
public bool ReadMessages => Permissions.GetValue(RawValue, GuildPermission.ReadMessages);
/// <summary> If <c>true</c>, a user may send messages. </summary>
/// <summary> If True, a user may join channels. </summary>
[Obsolete("Use ViewChannel instead.")]
public bool ReadMessages => ViewChannel;
/// <summary> If True, a user may view channels. </summary>
public bool ViewChannel => Permissions.GetValue(RawValue, GuildPermission.ViewChannel);
/// <summary> If True, a user may send messages. </summary>
public bool SendMessages => Permissions.GetValue(RawValue, GuildPermission.SendMessages); public bool SendMessages => Permissions.GetValue(RawValue, GuildPermission.SendMessages);
/// <summary> If <c>true</c>, a user may send text-to-speech messages. </summary> /// <summary> If <c>true</c>, a user may send text-to-speech messages. </summary>
public bool SendTTSMessages => Permissions.GetValue(RawValue, GuildPermission.SendTTSMessages); public bool SendTTSMessages => Permissions.GetValue(RawValue, GuildPermission.SendTTSMessages);
@@ -83,7 +87,7 @@ namespace Discord
private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, bool? kickMembers = null, private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, bool? kickMembers = null,
bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null, bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null,
bool? addReactions = null, bool? viewAuditLog = null, bool? addReactions = null, bool? viewAuditLog = null,
bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? viewChannel = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null,
bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null, bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null,
@@ -99,7 +103,7 @@ namespace Discord
Permissions.SetValue(ref value, manageGuild, GuildPermission.ManageGuild); Permissions.SetValue(ref value, manageGuild, GuildPermission.ManageGuild);
Permissions.SetValue(ref value, addReactions, GuildPermission.AddReactions); Permissions.SetValue(ref value, addReactions, GuildPermission.AddReactions);
Permissions.SetValue(ref value, viewAuditLog, GuildPermission.ViewAuditLog); Permissions.SetValue(ref value, viewAuditLog, GuildPermission.ViewAuditLog);
Permissions.SetValue(ref value, readMessages, GuildPermission.ReadMessages);
Permissions.SetValue(ref value, viewChannel, GuildPermission.ViewChannel);
Permissions.SetValue(ref value, sendMessages, GuildPermission.SendMessages); Permissions.SetValue(ref value, sendMessages, GuildPermission.SendMessages);
Permissions.SetValue(ref value, sendTTSMessages, GuildPermission.SendTTSMessages); Permissions.SetValue(ref value, sendTTSMessages, GuildPermission.SendTTSMessages);
Permissions.SetValue(ref value, manageMessages, GuildPermission.ManageMessages); Permissions.SetValue(ref value, manageMessages, GuildPermission.ManageMessages);
@@ -127,14 +131,14 @@ namespace Discord
public GuildPermissions(bool createInstantInvite = false, bool kickMembers = false, public GuildPermissions(bool createInstantInvite = false, bool kickMembers = false,
bool banMembers = false, bool administrator = false, bool manageChannels = false, bool manageGuild = false, bool banMembers = false, bool administrator = false, bool manageChannels = false, bool manageGuild = false,
bool addReactions = false, bool viewAuditLog = false, bool addReactions = false, bool viewAuditLog = false,
bool readMessages = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false,
bool viewChannel = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false,
bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false, bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false,
bool useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false, bool useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false,
bool moveMembers = false, bool useVoiceActivation = false, bool? changeNickname = false, bool? manageNicknames = false, bool moveMembers = false, bool useVoiceActivation = false, bool? changeNickname = false, bool? manageNicknames = false,
bool manageRoles = false, bool manageWebhooks = false, bool manageEmojis = false) bool manageRoles = false, bool manageWebhooks = false, bool manageEmojis = false)
: this(0, createInstantInvite: createInstantInvite, manageRoles: manageRoles, kickMembers: kickMembers, banMembers: banMembers, : this(0, createInstantInvite: createInstantInvite, manageRoles: manageRoles, kickMembers: kickMembers, banMembers: banMembers,
administrator: administrator, manageChannels: manageChannels, manageGuild: manageGuild, addReactions: addReactions, administrator: administrator, manageChannels: manageChannels, manageGuild: manageGuild, addReactions: addReactions,
viewAuditLog: viewAuditLog, readMessages: readMessages, sendMessages: sendMessages, sendTTSMessages: sendTTSMessages,
viewAuditLog: viewAuditLog, viewChannel: viewChannel, sendMessages: sendMessages, sendTTSMessages: sendTTSMessages,
manageMessages: manageMessages, embedLinks: embedLinks, attachFiles: attachFiles, readMessageHistory: readMessageHistory, manageMessages: manageMessages, embedLinks: embedLinks, attachFiles: attachFiles, readMessageHistory: readMessageHistory,
mentionEveryone: mentionEveryone, useExternalEmojis: useExternalEmojis, connect: connect, speak: speak, muteMembers: muteMembers, mentionEveryone: mentionEveryone, useExternalEmojis: useExternalEmojis, connect: connect, speak: speak, muteMembers: muteMembers,
deafenMembers: deafenMembers, moveMembers: moveMembers, useVoiceActivation: useVoiceActivation, changeNickname: changeNickname, deafenMembers: deafenMembers, moveMembers: moveMembers, useVoiceActivation: useVoiceActivation, changeNickname: changeNickname,
@@ -145,13 +149,13 @@ namespace Discord
public GuildPermissions Modify(bool? createInstantInvite = null, bool? kickMembers = null, public GuildPermissions Modify(bool? createInstantInvite = null, bool? kickMembers = null,
bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null, bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null,
bool? addReactions = null, bool? viewAuditLog = null, bool? addReactions = null, bool? viewAuditLog = null,
bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? viewChannel = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null,
bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null, bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null,
bool? manageRoles = null, bool? manageWebhooks = null, bool? manageEmojis = null) bool? manageRoles = null, bool? manageWebhooks = null, bool? manageEmojis = null)
=> new GuildPermissions(RawValue, createInstantInvite, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions, => new GuildPermissions(RawValue, createInstantInvite, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions,
viewAuditLog, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles,
viewAuditLog, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles,
readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers,
useVoiceActivation, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis); useVoiceActivation, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis);




+ 10
- 0
src/Discord.Net.Core/Entities/Roles/Color.cs View File

@@ -1,5 +1,8 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
#if NETSTANDARD2_0 || NET45
using StandardColor = System.Drawing.Color;
#endif


namespace Discord namespace Discord
{ {
@@ -125,6 +128,13 @@ namespace Discord
(uint)(b * 255.0f); (uint)(b * 255.0f);
} }


#if NETSTANDARD2_0 || NET45
public static implicit operator StandardColor(Color color) =>
StandardColor.FromArgb((int)color.RawValue);
public static explicit operator Color(StandardColor color) =>
new Color((uint)color.ToArgb() << 8 >> 8);
#endif

/// <summary> /// <summary>
/// Gets the hexadecimal representation of the color (e.g. <c>#000ccc</c>). /// Gets the hexadecimal representation of the color (e.g. <c>#000ccc</c>).
/// </summary> /// </summary>


+ 2
- 3
src/Discord.Net.Core/Extensions/UserExtensions.cs View File

@@ -18,7 +18,7 @@ namespace Discord
/// An awaitable Task containing the message sent to the channel. /// An awaitable Task containing the message sent to the channel.
/// </returns> /// </returns>
public static async Task<IUserMessage> SendMessageAsync(this IUser user, public static async Task<IUserMessage> SendMessageAsync(this IUser user,
string text,
string text = null,
bool isTTS = false, bool isTTS = false,
Embed embed = null, Embed embed = null,
RequestOptions options = null) RequestOptions options = null)
@@ -55,7 +55,6 @@ namespace Discord
return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);
} }


#if FILESYSTEM
/// <summary> /// <summary>
/// Sends a file via DM with an optional caption. /// Sends a file via DM with an optional caption.
/// </summary> /// </summary>
@@ -81,7 +80,7 @@ namespace Discord
{ {
return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
} }
#endif
/// <summary> /// <summary>
/// Bans the provided user from the guild and optionally prunes their recent messages. /// Bans the provided user from the guild and optionally prunes their recent messages.
/// </summary> /// </summary>


+ 9
- 12
src/Discord.Net.Core/Logging/LogManager.cs View File

@@ -41,7 +41,7 @@ namespace Discord.Logging
// ignored // ignored
} }
} }
#if FORMATSTR
public async Task LogAsync(LogSeverity severity, string source, FormattableString message, Exception ex = null) public async Task LogAsync(LogSeverity severity, string source, FormattableString message, Exception ex = null)
{ {
try try
@@ -51,52 +51,49 @@ namespace Discord.Logging
} }
catch { } catch { }
} }
#endif


public Task ErrorAsync(string source, Exception ex) public Task ErrorAsync(string source, Exception ex)
=> LogAsync(LogSeverity.Error, source, ex); => LogAsync(LogSeverity.Error, source, ex);
public Task ErrorAsync(string source, string message, Exception ex = null) public Task ErrorAsync(string source, string message, Exception ex = null)
=> LogAsync(LogSeverity.Error, source, message, ex); => LogAsync(LogSeverity.Error, source, message, ex);
#if FORMATSTR
public Task ErrorAsync(string source, FormattableString message, Exception ex = null) public Task ErrorAsync(string source, FormattableString message, Exception ex = null)
=> LogAsync(LogSeverity.Error, source, message, ex); => LogAsync(LogSeverity.Error, source, message, ex);
#endif


public Task WarningAsync(string source, Exception ex) public Task WarningAsync(string source, Exception ex)
=> LogAsync(LogSeverity.Warning, source, ex); => LogAsync(LogSeverity.Warning, source, ex);
public Task WarningAsync(string source, string message, Exception ex = null) public Task WarningAsync(string source, string message, Exception ex = null)
=> LogAsync(LogSeverity.Warning, source, message, ex); => LogAsync(LogSeverity.Warning, source, message, ex);
#if FORMATSTR
public Task WarningAsync(string source, FormattableString message, Exception ex = null) public Task WarningAsync(string source, FormattableString message, Exception ex = null)
=> LogAsync(LogSeverity.Warning, source, message, ex); => LogAsync(LogSeverity.Warning, source, message, ex);
#endif


public Task InfoAsync(string source, Exception ex) public Task InfoAsync(string source, Exception ex)
=> LogAsync(LogSeverity.Info, source, ex); => LogAsync(LogSeverity.Info, source, ex);
public Task InfoAsync(string source, string message, Exception ex = null) public Task InfoAsync(string source, string message, Exception ex = null)
=> LogAsync(LogSeverity.Info, source, message, ex); => LogAsync(LogSeverity.Info, source, message, ex);
#if FORMATSTR
public Task InfoAsync(string source, FormattableString message, Exception ex = null) public Task InfoAsync(string source, FormattableString message, Exception ex = null)
=> LogAsync(LogSeverity.Info, source, message, ex); => LogAsync(LogSeverity.Info, source, message, ex);
#endif


public Task VerboseAsync(string source, Exception ex) public Task VerboseAsync(string source, Exception ex)
=> LogAsync(LogSeverity.Verbose, source, ex); => LogAsync(LogSeverity.Verbose, source, ex);
public Task VerboseAsync(string source, string message, Exception ex = null) public Task VerboseAsync(string source, string message, Exception ex = null)
=> LogAsync(LogSeverity.Verbose, source, message, ex); => LogAsync(LogSeverity.Verbose, source, message, ex);
#if FORMATSTR
public Task VerboseAsync(string source, FormattableString message, Exception ex = null) public Task VerboseAsync(string source, FormattableString message, Exception ex = null)
=> LogAsync(LogSeverity.Verbose, source, message, ex); => LogAsync(LogSeverity.Verbose, source, message, ex);
#endif


public Task DebugAsync(string source, Exception ex) public Task DebugAsync(string source, Exception ex)
=> LogAsync(LogSeverity.Debug, source, ex); => LogAsync(LogSeverity.Debug, source, ex);
public Task DebugAsync(string source, string message, Exception ex = null) public Task DebugAsync(string source, string message, Exception ex = null)
=> LogAsync(LogSeverity.Debug, source, message, ex); => LogAsync(LogSeverity.Debug, source, message, ex);
#if FORMATSTR
public Task DebugAsync(string source, FormattableString message, Exception ex = null) public Task DebugAsync(string source, FormattableString message, Exception ex = null)
=> LogAsync(LogSeverity.Debug, source, message, ex); => LogAsync(LogSeverity.Debug, source, message, ex);
#endif


public Logger CreateLogger(string name) => new Logger(this, name); public Logger CreateLogger(string name) => new Logger(this, name);




+ 12
- 13
src/Discord.Net.Core/Logging/Logger.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks; using System.Threading.Tasks;


namespace Discord.Logging namespace Discord.Logging
@@ -20,54 +20,53 @@ namespace Discord.Logging
=> _manager.LogAsync(severity, Name, exception); => _manager.LogAsync(severity, Name, exception);
public Task LogAsync(LogSeverity severity, string message, Exception exception = null) public Task LogAsync(LogSeverity severity, string message, Exception exception = null)
=> _manager.LogAsync(severity, Name, message, exception); => _manager.LogAsync(severity, Name, message, exception);
#if FORMATSTR
public Task LogAsync(LogSeverity severity, FormattableString message, Exception exception = null) public Task LogAsync(LogSeverity severity, FormattableString message, Exception exception = null)
=> _manager.LogAsync(severity, Name, message, exception); => _manager.LogAsync(severity, Name, message, exception);
#endif


public Task ErrorAsync(Exception exception) public Task ErrorAsync(Exception exception)
=> _manager.ErrorAsync(Name, exception); => _manager.ErrorAsync(Name, exception);
public Task ErrorAsync(string message, Exception exception = null) public Task ErrorAsync(string message, Exception exception = null)
=> _manager.ErrorAsync(Name, message, exception); => _manager.ErrorAsync(Name, message, exception);
#if FORMATSTR
public Task ErrorAsync(FormattableString message, Exception exception = null) public Task ErrorAsync(FormattableString message, Exception exception = null)
=> _manager.ErrorAsync(Name, message, exception); => _manager.ErrorAsync(Name, message, exception);
#endif


public Task WarningAsync(Exception exception) public Task WarningAsync(Exception exception)
=> _manager.WarningAsync(Name, exception); => _manager.WarningAsync(Name, exception);
public Task WarningAsync(string message, Exception exception = null) public Task WarningAsync(string message, Exception exception = null)
=> _manager.WarningAsync(Name, message, exception); => _manager.WarningAsync(Name, message, exception);
#if FORMATSTR
public Task WarningAsync(FormattableString message, Exception exception = null) public Task WarningAsync(FormattableString message, Exception exception = null)
=> _manager.WarningAsync(Name, message, exception); => _manager.WarningAsync(Name, message, exception);
#endif


public Task InfoAsync(Exception exception) public Task InfoAsync(Exception exception)
=> _manager.InfoAsync(Name, exception); => _manager.InfoAsync(Name, exception);
public Task InfoAsync(string message, Exception exception = null) public Task InfoAsync(string message, Exception exception = null)
=> _manager.InfoAsync(Name, message, exception); => _manager.InfoAsync(Name, message, exception);
#if FORMATSTR
public Task InfoAsync(FormattableString message, Exception exception = null) public Task InfoAsync(FormattableString message, Exception exception = null)
=> _manager.InfoAsync(Name, message, exception); => _manager.InfoAsync(Name, message, exception);
#endif


public Task VerboseAsync(Exception exception) public Task VerboseAsync(Exception exception)
=> _manager.VerboseAsync(Name, exception); => _manager.VerboseAsync(Name, exception);
public Task VerboseAsync(string message, Exception exception = null) public Task VerboseAsync(string message, Exception exception = null)
=> _manager.VerboseAsync(Name, message, exception); => _manager.VerboseAsync(Name, message, exception);
#if FORMATSTR
public Task VerboseAsync(FormattableString message, Exception exception = null) public Task VerboseAsync(FormattableString message, Exception exception = null)
=> _manager.VerboseAsync(Name, message, exception); => _manager.VerboseAsync(Name, message, exception);
#endif


public Task DebugAsync(Exception exception) public Task DebugAsync(Exception exception)
=> _manager.DebugAsync(Name, exception); => _manager.DebugAsync(Name, exception);
public Task DebugAsync(string message, Exception exception = null) public Task DebugAsync(string message, Exception exception = null)
=> _manager.DebugAsync(Name, message, exception); => _manager.DebugAsync(Name, message, exception);
#if FORMATSTR
public Task DebugAsync(FormattableString message, Exception exception = null) public Task DebugAsync(FormattableString message, Exception exception = null)
=> _manager.DebugAsync(Name, message, exception); => _manager.DebugAsync(Name, message, exception);
#endif
} }
} }

+ 2
- 45
src/Discord.Net.Core/Utils/DateTimeUtils.cs View File

@@ -1,57 +1,14 @@
using System;
using System;


namespace Discord namespace Discord
{ {
//Source: https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/DateTimeOffset.cs //Source: https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/DateTimeOffset.cs
internal static class DateTimeUtils internal static class DateTimeUtils
{ {
#if !UNIXTIME
private const long UnixEpochTicks = 621_355_968_000_000_000;
private const long UnixEpochSeconds = 62_135_596_800;
private const long UnixEpochMilliseconds = 62_135_596_800_000;
#endif

public static DateTimeOffset FromTicks(long ticks) public static DateTimeOffset FromTicks(long ticks)
=> new DateTimeOffset(ticks, TimeSpan.Zero); => new DateTimeOffset(ticks, TimeSpan.Zero);
public static DateTimeOffset? FromTicks(long? ticks) public static DateTimeOffset? FromTicks(long? ticks)
=> ticks != null ? new DateTimeOffset(ticks.Value, TimeSpan.Zero) : (DateTimeOffset?)null; => ticks != null ? new DateTimeOffset(ticks.Value, TimeSpan.Zero) : (DateTimeOffset?)null;

public static DateTimeOffset FromUnixSeconds(long seconds)
{
#if UNIXTIME
return DateTimeOffset.FromUnixTimeSeconds(seconds);
#else
long ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks;
return new DateTimeOffset(ticks, TimeSpan.Zero);
#endif
}
public static DateTimeOffset FromUnixMilliseconds(long milliseconds)
{
#if UNIXTIME
return DateTimeOffset.FromUnixTimeMilliseconds(milliseconds);
#else
long ticks = milliseconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks;
return new DateTimeOffset(ticks, TimeSpan.Zero);
#endif
}

public static long ToUnixSeconds(DateTimeOffset dto)
{
#if UNIXTIME
return dto.ToUnixTimeSeconds();
#else
long seconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerSecond;
return seconds - UnixEpochSeconds;
#endif
}
public static long ToUnixMilliseconds(DateTimeOffset dto)
{
#if UNIXTIME
return dto.ToUnixTimeMilliseconds();
#else
long milliseconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerMillisecond;
return milliseconds - UnixEpochMilliseconds;
#endif
}
} }
} }

+ 2
- 2
src/Discord.Net.Core/Utils/SnowflakeUtils.cs View File

@@ -5,8 +5,8 @@ namespace Discord
public static class SnowflakeUtils public static class SnowflakeUtils
{ {
public static DateTimeOffset FromSnowflake(ulong value) public static DateTimeOffset FromSnowflake(ulong value)
=> DateTimeUtils.FromUnixMilliseconds((long)((value >> 22) + 1420070400000UL));
=> DateTimeOffset.FromUnixTimeMilliseconds((long)((value >> 22) + 1420070400000UL));
public static ulong ToSnowflake(DateTimeOffset value) public static ulong ToSnowflake(DateTimeOffset value)
=> ((ulong)DateTimeUtils.ToUnixMilliseconds(value) - 1420070400000UL) << 22;
=> ((ulong)value.ToUnixTimeMilliseconds() - 1420070400000UL) << 22;
} }
} }

+ 12
- 1
src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs View File

@@ -1,4 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
using Newtonsoft.Json; using Newtonsoft.Json;


namespace Discord.API.Rest namespace Discord.API.Rest
@@ -10,9 +10,20 @@ namespace Discord.API.Rest
public string Name { get; } public string Name { get; }
[JsonProperty("type")] [JsonProperty("type")]
public ChannelType Type { get; } public ChannelType Type { get; }
[JsonProperty("parent_id")]
public Optional<ulong?> CategoryId { get; set; }


//Text channels
[JsonProperty("topic")]
public Optional<string> Topic { get; set; }
[JsonProperty("nsfw")]
public Optional<bool> IsNsfw { get; set; }

//Voice channels
[JsonProperty("bitrate")] [JsonProperty("bitrate")]
public Optional<int> Bitrate { get; set; } public Optional<int> Bitrate { get; set; }
[JsonProperty("user_limit")]
public Optional<int?> UserLimit { get; set; }


public CreateGuildChannelParams(string name, ChannelType type) public CreateGuildChannelParams(string name, ChannelType type)
{ {


+ 1
- 1
src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs View File

@@ -1,4 +1,4 @@
namespace Discord.API.Rest
namespace Discord.API.Rest
{ {
internal class GetReactionUsersParams internal class GetReactionUsersParams
{ {


+ 6
- 7
src/Discord.Net.Rest/Discord.Net.Rest.csproj View File

@@ -1,20 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../Discord.Net.targets" /> <Import Project="../../Discord.Net.targets" />
<PropertyGroup> <PropertyGroup>
<AssemblyName>Discord.Net.Rest</AssemblyName> <AssemblyName>Discord.Net.Rest</AssemblyName>
<RootNamespace>Discord.Rest</RootNamespace> <RootNamespace>Discord.Rest</RootNamespace>
<Description>A core Discord.Net library containing the REST client and models.</Description> <Description>A core Discord.Net library containing the REST client and models.</Description>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net45;netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.3;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> <ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net45' ">
<PackageReference Include="System.Net.Http" Version="4.3.2" />
<!-- https://github.com/dotnet/corefx/issues/19535 -->
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.Net.Http" Version="4.3.3" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
</ItemGroup> </ItemGroup>
</Project> </Project>

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

@@ -630,11 +630,11 @@ namespace Discord.API
Preconditions.NotNullOrWhitespace(emoji, nameof(emoji)); Preconditions.NotNullOrWhitespace(emoji, nameof(emoji));
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit)); Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit));
Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit));
Preconditions.AtMost(args.Limit, DiscordConfig.MaxUserReactionsPerBatch, nameof(args.Limit));
Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


int limit = args.Limit.GetValueOrDefault(int.MaxValue);
int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxUserReactionsPerBatch);
ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); ulong afterUserId = args.AfterUserId.GetValueOrDefault(0);


var ids = new BucketIds(channelId: channelId); var ids = new BucketIds(channelId: channelId);


+ 5
- 2
src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs View File

@@ -162,7 +162,6 @@ namespace Discord.Rest
return RestUserMessage.Create(client, channel, client.CurrentUser, model); return RestUserMessage.Create(client, channel, client.CurrentUser, model);
} }


#if FILESYSTEM
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="filePath" /> is a zero-length string, contains only white space, or contains one or more /// <paramref name="filePath" /> is a zero-length string, contains only white space, or contains one or more
/// invalid characters as defined by <see cref="System.IO.Path.InvalidPathChars" />. /// invalid characters as defined by <see cref="System.IO.Path.InvalidPathChars" />.
@@ -194,7 +193,7 @@ namespace Discord.Rest
using (var file = File.OpenRead(filePath)) using (var file = File.OpenRead(filePath))
return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, options).ConfigureAwait(false); return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, options).ConfigureAwait(false);
} }
#endif
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client, public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client,
Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
@@ -204,6 +203,10 @@ namespace Discord.Rest
return RestUserMessage.Create(client, channel, client.CurrentUser, model); return RestUserMessage.Create(client, channel, client.CurrentUser, model);
} }


public static Task DeleteMessageAsync(IMessageChannel channel, ulong messageId, BaseDiscordClient client,
RequestOptions options)
=> MessageHelper.DeleteAsync(channel.Id, messageId, client, options);

public static async Task DeleteMessagesAsync(ITextChannel channel, BaseDiscordClient client, public static async Task DeleteMessagesAsync(ITextChannel channel, BaseDiscordClient client,
IEnumerable<ulong> messageIds, RequestOptions options) IEnumerable<ulong> messageIds, RequestOptions options)
{ {


+ 1
- 3
src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs View File

@@ -19,8 +19,7 @@ namespace Discord.Rest
/// <returns> /// <returns>
/// An awaitable Task containing the message sent to the channel. /// An awaitable Task containing the message sent to the channel.
/// </returns> /// </returns>
new Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null);
#if FILESYSTEM
new Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
/// <summary> /// <summary>
/// Sends a file to this message channel, with an optional caption. /// Sends a file to this message channel, with an optional caption.
/// </summary> /// </summary>
@@ -38,7 +37,6 @@ namespace Discord.Rest
/// An awaitable Task containing the message sent to the channel. /// An awaitable Task containing the message sent to the channel.
/// </returns> /// </returns>
new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
#endif
/// <summary> /// <summary>
/// Sends a file to this message channel, with an optional caption. /// Sends a file to this message channel, with an optional caption.
/// </summary> /// </summary>


+ 8
- 7
src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs View File

@@ -74,17 +74,21 @@ namespace Discord.Rest
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options);


/// <inheritdoc /> /// <inheritdoc />
public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if FILESYSTEM
/// <inheritdoc /> /// <inheritdoc />
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options);
#endif
/// <inheritdoc /> /// <inheritdoc />
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options);


public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

/// <inheritdoc /> /// <inheritdoc />
public Task TriggerTypingAsync(RequestOptions options = null) public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options); => ChannelHelper.TriggerTypingAsync(this, Discord, options);
@@ -143,12 +147,9 @@ namespace Discord.Rest
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false); => await GetPinnedMessagesAsync(options).ConfigureAwait(false);


#if FILESYSTEM
/// <inheritdoc />
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
#endif
/// <inheritdoc />

async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);
/// <inheritdoc /> /// <inheritdoc />


+ 8
- 5
src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs View File

@@ -83,11 +83,16 @@ namespace Discord.Rest
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null)
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options);


public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if FILESYSTEM
/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="filePath" /> is a zero-length string, contains only white space, or contains one or more /// <paramref name="filePath" /> is a zero-length string, contains only white space, or contains one or more
@@ -115,7 +120,6 @@ namespace Discord.Rest
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options);
#endif
/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
@@ -168,10 +172,9 @@ namespace Discord.Rest
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false); => await GetPinnedMessagesAsync(options).ConfigureAwait(false);


#if FILESYSTEM
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
#endif
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);
async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options)


+ 9
- 5
src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs View File

@@ -58,15 +58,20 @@ namespace Discord.Rest
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null)
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options);


public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if FILESYSTEM
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options);
#endif
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options);


public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null)
=> ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options); => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options);
public Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null) public Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null)
@@ -126,10 +131,9 @@ namespace Discord.Rest
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false); => await GetPinnedMessagesAsync(options).ConfigureAwait(false);


#if FILESYSTEM
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
#endif
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);
async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options)


+ 11
- 8
src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs View File

@@ -33,15 +33,19 @@ namespace Discord.Rest
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null)
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options);


public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if FILESYSTEM
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options);
#endif
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options);


public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

public Task TriggerTypingAsync(RequestOptions options = null) public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options); => ChannelHelper.TriggerTypingAsync(this, Discord, options);
public IDisposable EnterTypingState(RequestOptions options = null) public IDisposable EnterTypingState(RequestOptions options = null)
@@ -81,10 +85,9 @@ namespace Discord.Rest
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false); => await GetPinnedMessagesAsync(options).ConfigureAwait(false);


#if FILESYSTEM
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
#endif
=> await SendFileAsync(filePath, text, isTTS, embed, options);
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);
async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options)


+ 20
- 4
src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs View File

@@ -147,21 +147,37 @@ namespace Discord.Rest
} }
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c>.</exception>
public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, BaseDiscordClient client, public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, BaseDiscordClient client,
string name, RequestOptions options)
string name, RequestOptions options, Action<TextChannelProperties> func = null)
{ {
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));


var args = new CreateGuildChannelParams(name, ChannelType.Text);
var props = new TextChannelProperties();
func?.Invoke(props);

var args = new CreateGuildChannelParams(name, ChannelType.Text)
{
CategoryId = props.CategoryId,
Topic = props.Topic,
IsNsfw = props.IsNsfw
};
var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false);
return RestTextChannel.Create(client, guild, model); return RestTextChannel.Create(client, guild, model);
} }
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c>.</exception>
public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client, public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client,
string name, RequestOptions options)
string name, RequestOptions options, Action<VoiceChannelProperties> func = null)
{ {
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));


var args = new CreateGuildChannelParams(name, ChannelType.Voice);
var props = new VoiceChannelProperties();
func?.Invoke(props);

var args = new CreateGuildChannelParams(name, ChannelType.Voice)
{
CategoryId = props.CategoryId,
Bitrate = props.Bitrate,
UserLimit = props.UserLimit
};
var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false);
return RestVoiceChannel.Create(client, guild, model); return RestVoiceChannel.Create(client, guild, model);
} }


+ 8
- 8
src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs View File

@@ -258,10 +258,10 @@ namespace Discord.Rest
} }
return null; return null;
} }
public Task<RestTextChannel> CreateTextChannelAsync(string name, RequestOptions options = null)
=> GuildHelper.CreateTextChannelAsync(this, Discord, name, options);
public Task<RestVoiceChannel> CreateVoiceChannelAsync(string name, RequestOptions options = null)
=> GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options);
public Task<RestTextChannel> CreateTextChannelAsync(string name, Action<TextChannelProperties> func = null, RequestOptions options = null)
=> GuildHelper.CreateTextChannelAsync(this, Discord, name, options, func);
public Task<RestVoiceChannel> CreateVoiceChannelAsync(string name, Action<VoiceChannelProperties> func = null, RequestOptions options = null)
=> GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options, func);
public Task<RestCategoryChannel> CreateCategoryChannelAsync(string name, RequestOptions options = null) public Task<RestCategoryChannel> CreateCategoryChannelAsync(string name, RequestOptions options = null)
=> GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options); => GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options);


@@ -448,11 +448,11 @@ namespace Discord.Rest
return null; return null;
} }
/// <inheritdoc /> /// <inheritdoc />
async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name, RequestOptions options)
=> await CreateTextChannelAsync(name, options).ConfigureAwait(false);
async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name, Action<TextChannelProperties> func, RequestOptions options)
=> await CreateTextChannelAsync(name, func, options).ConfigureAwait(false);
/// <inheritdoc /> /// <inheritdoc />
async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name, RequestOptions options)
=> await CreateVoiceChannelAsync(name, options).ConfigureAwait(false);
async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name, Action<VoiceChannelProperties> func, RequestOptions options)
=> await CreateVoiceChannelAsync(name, func, options).ConfigureAwait(false);
/// <inheritdoc /> /// <inheritdoc />
async Task<ICategoryChannel> IGuild.CreateCategoryAsync(string name, RequestOptions options) async Task<ICategoryChannel> IGuild.CreateCategoryAsync(string name, RequestOptions options)
=> await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false);


+ 35
- 8
src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs View File

@@ -27,10 +27,12 @@ namespace Discord.Rest
}; };
return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false);
} }
public static async Task DeleteAsync(IMessage msg, BaseDiscordClient client,
public static Task DeleteAsync(IMessage msg, BaseDiscordClient client, RequestOptions options)
=> DeleteAsync(msg.Channel.Id, msg.Id, client, options);
public static async Task DeleteAsync(ulong channelId, ulong msgId, BaseDiscordClient client,
RequestOptions options) RequestOptions options)
{ {
await client.ApiClient.DeleteMessageAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false);
await client.ApiClient.DeleteMessageAsync(channelId, msgId, options).ConfigureAwait(false);
} }


public static async Task AddReactionAsync(IMessage msg, IEmote emote, BaseDiscordClient client, RequestOptions options) public static async Task AddReactionAsync(IMessage msg, IEmote emote, BaseDiscordClient client, RequestOptions options)
@@ -48,13 +50,38 @@ namespace Discord.Rest
await client.ApiClient.RemoveAllReactionsAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false); await client.ApiClient.RemoveAllReactionsAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false);
} }


public static async Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IMessage msg, IEmote emote,
Action<GetReactionUsersParams> func, BaseDiscordClient client, RequestOptions options)
public static IAsyncEnumerable<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IMessage msg, IEmote emote,
int? limit, BaseDiscordClient client, RequestOptions options)
{ {
var args = new GetReactionUsersParams();
func(args);
string emoji = (emote is Emote e ? $"{e.Name}:{e.Id}" : emote.Name);
return (await client.ApiClient.GetReactionUsersAsync(msg.Channel.Id, msg.Id, emoji, args, options).ConfigureAwait(false)).Select(u => RestUser.Create(client, u)).ToImmutableArray();
Preconditions.NotNull(emote, nameof(emote));
var emoji = (emote is Emote e ? $"{e.Name}:{e.Id}" : emote.Name);

return new PagedAsyncEnumerable<IUser>(
DiscordConfig.MaxUserReactionsPerBatch,
async (info, ct) =>
{
var args = new GetReactionUsersParams
{
Limit = info.PageSize
};

if (info.Position != null)
args.AfterUserId = info.Position.Value;

var models = await client.ApiClient.GetReactionUsersAsync(msg.Channel.Id, msg.Id, emoji, args, options).ConfigureAwait(false);
return models.Select(x => RestUser.Create(client, x)).ToImmutableArray();
},
nextPage: (info, lastPage) =>
{
if (lastPage.Count != DiscordConfig.MaxUsersPerBatch)
return false;

info.Position = lastPage.Max(x => x.Id);
return true;
},
count: limit
);

} }


public static async Task PinAsync(IMessage msg, BaseDiscordClient client, public static async Task PinAsync(IMessage msg, BaseDiscordClient client,


+ 3
- 3
src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs View File

@@ -151,9 +151,9 @@ namespace Discord.Rest
public Task RemoveAllReactionsAsync(RequestOptions options = null) public Task RemoveAllReactionsAsync(RequestOptions options = null)
=> MessageHelper.RemoveAllReactionsAsync(this, Discord, options); => MessageHelper.RemoveAllReactionsAsync(this, Discord, options);
/// <inheritdoc /> /// <inheritdoc />
public Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emote, int limit = 100, ulong? afterUserId = null, RequestOptions options = null)
=> MessageHelper.GetReactionUsersAsync(this, emote, x => { x.Limit = limit; x.AfterUserId = afterUserId ?? Optional.Create<ulong>(); }, Discord, options);
public IAsyncEnumerable<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null)
=> MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options);
/// <inheritdoc /> /// <inheritdoc />
public Task PinAsync(RequestOptions options = null) public Task PinAsync(RequestOptions options = null)
=> MessageHelper.PinAsync(this, Discord, options); => MessageHelper.PinAsync(this, Discord, options);


+ 1
- 2
src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs View File

@@ -1,4 +1,4 @@
using Discord.API;
using Discord.API;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using System; using System;
@@ -25,7 +25,6 @@ namespace Discord.Net.Converters
if (converter != null) if (converter != null)
{ {
property.Converter = converter; property.Converter = converter;
property.MemberConverter = converter;
} }
} }
else else


+ 1
- 1
src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs View File

@@ -230,7 +230,7 @@ namespace Discord.Net.Queue
#endif #endif
} }


var now = DateTimeUtils.ToUnixSeconds(DateTimeOffset.UtcNow);
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
DateTimeOffset? resetTick = null; DateTimeOffset? resetTick = null;


//Using X-RateLimit-Remaining causes a race condition //Using X-RateLimit-Remaining causes a race condition


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

@@ -21,7 +21,7 @@ namespace Discord.Net
Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) && Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) &&
int.TryParse(temp, out var remaining) ? remaining : (int?)null; int.TryParse(temp, out var remaining) ? remaining : (int?)null;
Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) && Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) &&
int.TryParse(temp, out var reset) ? DateTimeUtils.FromUnixSeconds(reset) : (DateTimeOffset?)null;
int.TryParse(temp, out var reset) ? DateTimeOffset.FromUnixTimeSeconds(reset) : (DateTimeOffset?)null;
RetryAfter = headers.TryGetValue("Retry-After", out temp) && RetryAfter = headers.TryGetValue("Retry-After", out temp) &&
int.TryParse(temp, out var retryAfter) ? retryAfter : (int?)null; int.TryParse(temp, out var retryAfter) ? retryAfter : (int?)null;
Lag = headers.TryGetValue("Date", out temp) && Lag = headers.TryGetValue("Date", out temp) &&


+ 4
- 4
src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj View File

@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../Discord.Net.targets" /> <Import Project="../../Discord.Net.targets" />
<PropertyGroup> <PropertyGroup>
<AssemblyName>Discord.Net.WebSocket</AssemblyName> <AssemblyName>Discord.Net.WebSocket</AssemblyName>
<RootNamespace>Discord.WebSocket</RootNamespace> <RootNamespace>Discord.WebSocket</RootNamespace>
<Description>A core Discord.Net library containing the WebSocket client and models.</Description> <Description>A core Discord.Net library containing the WebSocket client and models.</Description>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net45;netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.3;netstandard2.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -13,6 +13,6 @@
<ProjectReference Include="..\Discord.Net.Rest\Discord.Net.Rest.csproj" /> <ProjectReference Include="..\Discord.Net.Rest\Discord.Net.Rest.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' "> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" />
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

+ 1
- 1
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -378,7 +378,7 @@ namespace Discord.WebSocket
await ApiClient.SendStatusUpdateAsync( await ApiClient.SendStatusUpdateAsync(
status, status,
status == UserStatus.AFK, status == UserStatus.AFK,
statusSince != null ? DateTimeUtils.ToUnixMilliseconds(_statusSince.Value) : (long?)null,
statusSince != null ? _statusSince.Value.ToUnixTimeMilliseconds() : (long?)null,
gameModel).ConfigureAwait(false); gameModel).ConfigureAwait(false);
} }




+ 2
- 2
src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs View File

@@ -1,4 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
using Discord.API; using Discord.API;
using Discord.API.Voice; using Discord.API.Voice;
using Discord.Net.Converters; using Discord.Net.Converters;
@@ -129,7 +129,7 @@ namespace Discord.Audio
//WebSocket //WebSocket
public async Task SendHeartbeatAsync(RequestOptions options = null) public async Task SendHeartbeatAsync(RequestOptions options = null)
{ {
await SendAsync(VoiceOpCode.Heartbeat, DateTimeUtils.ToUnixMilliseconds(DateTimeOffset.UtcNow), options: options).ConfigureAwait(false);
await SendAsync(VoiceOpCode.Heartbeat, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), options: options).ConfigureAwait(false);
} }
public async Task SendIdentityAsync(ulong userId, string sessionId, string token) public async Task SendIdentityAsync(ulong userId, string sessionId, string token)
{ {


+ 1
- 3
src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs View File

@@ -28,8 +28,7 @@ namespace Discord.WebSocket
/// <returns> /// <returns>
/// An awaitable Task containing the message sent to the channel. /// An awaitable Task containing the message sent to the channel.
/// </returns> /// </returns>
new Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null);
#if FILESYSTEM
new Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
/// <summary> /// <summary>
/// Sends a file to this message channel, with an optional caption. /// Sends a file to this message channel, with an optional caption.
/// </summary> /// </summary>
@@ -47,7 +46,6 @@ namespace Discord.WebSocket
/// An awaitable Task containing the message sent to the channel. /// An awaitable Task containing the message sent to the channel.
/// </returns> /// </returns>
new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
#endif
/// <summary> /// <summary>
/// Sends a file to this message channel, with an optional caption. /// Sends a file to this message channel, with an optional caption.
/// </summary> /// </summary>


+ 7
- 7
src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs View File

@@ -79,18 +79,22 @@ namespace Discord.WebSocket


/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if FILESYSTEM
/// <inheritdoc /> /// <inheritdoc />
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options);
#endif
/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options);


public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

/// <inheritdoc /> /// <inheritdoc />
public Task TriggerTypingAsync(RequestOptions options = null) public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options); => ChannelHelper.TriggerTypingAsync(this, Discord, options);
@@ -160,12 +164,8 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false); => await GetPinnedMessagesAsync(options).ConfigureAwait(false);
#if FILESYSTEM
/// <inheritdoc />
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
#endif
/// <inheritdoc />
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);
/// <inheritdoc /> /// <inheritdoc />


+ 8
- 5
src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs View File

@@ -109,17 +109,21 @@ namespace Discord.WebSocket


/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if FILESYSTEM
/// <inheritdoc /> /// <inheritdoc />
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options);
#endif
/// <inheritdoc /> /// <inheritdoc />
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options);


public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

/// <inheritdoc /> /// <inheritdoc />
public Task TriggerTypingAsync(RequestOptions options = null) public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options); => ChannelHelper.TriggerTypingAsync(this, Discord, options);
@@ -235,11 +239,10 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false); => await GetPinnedMessagesAsync(options).ConfigureAwait(false);
#if FILESYSTEM
/// <inheritdoc /> /// <inheritdoc />
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
#endif
/// <inheritdoc /> /// <inheritdoc />
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);


+ 8
- 5
src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs View File

@@ -90,13 +90,12 @@ namespace Discord.WebSocket
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options);


/// <inheritdoc /> /// <inheritdoc />
public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if FILESYSTEM
/// <inheritdoc /> /// <inheritdoc />
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options);
#endif
/// <inheritdoc /> /// <inheritdoc />
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options);
@@ -108,6 +107,11 @@ namespace Discord.WebSocket
public Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null) public Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null)
=> ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options); => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options);


public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

/// <inheritdoc /> /// <inheritdoc />
public Task TriggerTypingAsync(RequestOptions options = null) public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options); => ChannelHelper.TriggerTypingAsync(this, Discord, options);
@@ -209,11 +213,10 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false); => await GetPinnedMessagesAsync(options).ConfigureAwait(false);
#if FILESYSTEM
/// <inheritdoc /> /// <inheritdoc />
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
#endif
/// <inheritdoc /> /// <inheritdoc />
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);


+ 8
- 10
src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs View File

@@ -463,9 +463,8 @@ namespace Discord.WebSocket
/// <returns> /// <returns>
/// The created text channel. /// The created text channel.
/// </returns> /// </returns>
public Task<RestTextChannel> CreateTextChannelAsync(string name, RequestOptions options = null)
=> GuildHelper.CreateTextChannelAsync(this, Discord, name, options);

public Task<RestTextChannel> CreateTextChannelAsync(string name, Action<TextChannelProperties> func = null, RequestOptions options = null)
=> GuildHelper.CreateTextChannelAsync(this, Discord, name, options, func);
/// <summary> /// <summary>
/// Creates a voice channel with the provided name. /// Creates a voice channel with the provided name.
/// </summary> /// </summary>
@@ -475,9 +474,8 @@ namespace Discord.WebSocket
/// <returns> /// <returns>
/// The created voice channel. /// The created voice channel.
/// </returns> /// </returns>
public Task<RestVoiceChannel> CreateVoiceChannelAsync(string name, RequestOptions options = null)
=> GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options);

public Task<RestVoiceChannel> CreateVoiceChannelAsync(string name, Action<VoiceChannelProperties> func = null, RequestOptions options = null)
=> GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options, func);
/// <summary> /// <summary>
/// Creates a category channel with the provided name. /// Creates a category channel with the provided name.
/// </summary> /// </summary>
@@ -930,11 +928,11 @@ namespace Discord.WebSocket
Task<ITextChannel> IGuild.GetSystemChannelAsync(CacheMode mode, RequestOptions options) Task<ITextChannel> IGuild.GetSystemChannelAsync(CacheMode mode, RequestOptions options)
=> Task.FromResult<ITextChannel>(SystemChannel); => Task.FromResult<ITextChannel>(SystemChannel);
/// <inheritdoc /> /// <inheritdoc />
async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name, RequestOptions options)
=> await CreateTextChannelAsync(name, options).ConfigureAwait(false);
async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name, Action<TextChannelProperties> func, RequestOptions options)
=> await CreateTextChannelAsync(name, func, options).ConfigureAwait(false);
/// <inheritdoc /> /// <inheritdoc />
async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name, RequestOptions options)
=> await CreateVoiceChannelAsync(name, options).ConfigureAwait(false);
async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name, Action<VoiceChannelProperties> func, RequestOptions options)
=> await CreateVoiceChannelAsync(name, func, options).ConfigureAwait(false);
/// <inheritdoc /> /// <inheritdoc />
async Task<ICategoryChannel> IGuild.CreateCategoryAsync(string name, RequestOptions options) async Task<ICategoryChannel> IGuild.CreateCategoryAsync(string name, RequestOptions options)
=> await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false);


+ 2
- 2
src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs View File

@@ -150,8 +150,8 @@ namespace Discord.WebSocket
public Task RemoveAllReactionsAsync(RequestOptions options = null) public Task RemoveAllReactionsAsync(RequestOptions options = null)
=> MessageHelper.RemoveAllReactionsAsync(this, Discord, options); => MessageHelper.RemoveAllReactionsAsync(this, Discord, options);
/// <inheritdoc /> /// <inheritdoc />
public Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emote, int limit = 100, ulong? afterUserId = null, RequestOptions options = null)
=> MessageHelper.GetReactionUsersAsync(this, emote, x => { x.Limit = limit; x.AfterUserId = afterUserId ?? Optional.Create<ulong>(); }, Discord, options);
public IAsyncEnumerable<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null)
=> MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options);


/// <inheritdoc /> /// <inheritdoc />
public Task PinAsync(RequestOptions options = null) public Task PinAsync(RequestOptions options = null)


+ 0
- 6
src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs View File

@@ -1,4 +1,3 @@
#if DEFAULTUDPCLIENT
using System; using System;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
@@ -85,11 +84,7 @@ namespace Discord.Net.Udp


if (_udp != null) if (_udp != null)
{ {
#if UDPDISPOSE
try { _udp.Dispose(); } try { _udp.Dispose(); }
#else
try { _udp.Close(); }
#endif
catch { } catch { }
_udp = null; _udp = null;
} }
@@ -132,4 +127,3 @@ namespace Discord.Net.Udp
} }
} }
} }
#endif

+ 1
- 9
src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs View File

@@ -4,7 +4,6 @@ namespace Discord.Net.Udp
{ {
public static class DefaultUdpSocketProvider public static class DefaultUdpSocketProvider
{ {
#if DEFAULTUDPCLIENT
public static readonly UdpSocketProvider Instance = () => public static readonly UdpSocketProvider Instance = () =>
{ {
try try
@@ -16,12 +15,5 @@ namespace Discord.Net.Udp
throw new PlatformNotSupportedException("The default UdpSocketProvider is not supported on this platform.", ex); throw new PlatformNotSupportedException("The default UdpSocketProvider is not supported on this platform.", ex);
} }
}; };
#else
public static readonly UdpSocketProvider Instance = () =>
{
throw new PlatformNotSupportedException("The default UdpSocketProvider is not supported on this platform.\n" +
"You must specify a UdpSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+.");
};
#endif
} }
}
}

+ 3
- 10
src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs View File

@@ -1,4 +1,3 @@
#if DEFAULTWEBSOCKET
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@@ -209,14 +208,9 @@ namespace Discord.Net.WebSockets


//Use the internal buffer if we can get it //Use the internal buffer if we can get it
resultCount = (int)stream.Length; resultCount = (int)stream.Length;
#if MSTRYBUFFER
if (stream.TryGetBuffer(out var streamBuffer))
result = streamBuffer.Array;
else
result = stream.ToArray();
#else
result = stream.GetBuffer();
#endif

result = stream.TryGetBuffer(out var streamBuffer) ? streamBuffer.Array : stream.ToArray();

} }
} }
else else
@@ -248,4 +242,3 @@ namespace Discord.Net.WebSockets
} }
} }
} }
#endif

+ 0
- 8
src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs View File

@@ -5,7 +5,6 @@ namespace Discord.Net.WebSockets
{ {
public static class DefaultWebSocketProvider public static class DefaultWebSocketProvider
{ {
#if DEFAULTWEBSOCKET
public static readonly WebSocketProvider Instance = Create(); public static readonly WebSocketProvider Instance = Create();


/// <exception cref="PlatformNotSupportedException">The default WebSocketProvider is not supported on this platform.</exception> /// <exception cref="PlatformNotSupportedException">The default WebSocketProvider is not supported on this platform.</exception>
@@ -23,12 +22,5 @@ namespace Discord.Net.WebSockets
} }
}; };
} }
#else
public static readonly WebSocketProvider Instance = () =>
{
throw new PlatformNotSupportedException("The default WebSocketProvider is not supported on this platform.\n" +
"You must specify a WebSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+.");
};
#endif
} }
} }

+ 2
- 2
src/Discord.Net.Webhook/Discord.Net.Webhook.csproj View File

@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../Discord.Net.targets" /> <Import Project="../../Discord.Net.targets" />
<PropertyGroup> <PropertyGroup>
<AssemblyName>Discord.Net.Webhook</AssemblyName> <AssemblyName>Discord.Net.Webhook</AssemblyName>
<RootNamespace>Discord.Webhook</RootNamespace> <RootNamespace>Discord.Webhook</RootNamespace>
<Description>A core Discord.Net library containing the Webhook client and models.</Description> <Description>A core Discord.Net library containing the Webhook client and models.</Description>
<TargetFrameworks>netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks>netstandard1.3</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> <ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />


+ 1
- 4
src/Discord.Net.Webhook/DiscordWebhookClient.cs View File

@@ -62,20 +62,17 @@ namespace Discord.Webhook
} }
private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config)
=> new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent); => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent);

/// <summary> Sends a message using to the channel for this webhook. </summary> /// <summary> Sends a message using to the channel for this webhook. </summary>
/// <returns> Returns the ID of the created message. </returns> /// <returns> Returns the ID of the created message. </returns>
public Task<ulong> SendMessageAsync(string text, bool isTTS = false, IEnumerable<Embed> embeds = null,
public Task<ulong> SendMessageAsync(string text = null, bool isTTS = false, IEnumerable<Embed> embeds = null,
string username = null, string avatarUrl = null, RequestOptions options = null) string username = null, string avatarUrl = null, RequestOptions options = null)
=> WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, options); => WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, options);


#if FILESYSTEM
/// <summary> Sends a message to the channel for this webhook with an attachment. </summary> /// <summary> Sends a message to the channel for this webhook with an attachment. </summary>
/// <returns> Returns the ID of the created message. </returns> /// <returns> Returns the ID of the created message. </returns>
public Task<ulong> SendFileAsync(string filePath, string text, bool isTTS = false, public Task<ulong> SendFileAsync(string filePath, string text, bool isTTS = false,
IEnumerable<Embed> embeds = null, string username = null, string avatarUrl = null, RequestOptions options = null) IEnumerable<Embed> embeds = null, string username = null, string avatarUrl = null, RequestOptions options = null)
=> WebhookClientHelper.SendFileAsync(this, filePath, text, isTTS, embeds, username, avatarUrl, options); => WebhookClientHelper.SendFileAsync(this, filePath, text, isTTS, embeds, username, avatarUrl, options);
#endif
/// <summary> Sends a message to the channel for this webhook with an attachment. </summary> /// <summary> Sends a message to the channel for this webhook with an attachment. </summary>
/// <returns> Returns the ID of the created message. </returns> /// <returns> Returns the ID of the created message. </returns>
public Task<ulong> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, public Task<ulong> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false,


+ 0
- 2
src/Discord.Net.Webhook/WebhookClientHelper.cs View File

@@ -34,7 +34,6 @@ namespace Discord.Webhook
var model = await client.ApiClient.CreateWebhookMessageAsync(client.Webhook.Id, args, options: options).ConfigureAwait(false); var model = await client.ApiClient.CreateWebhookMessageAsync(client.Webhook.Id, args, options: options).ConfigureAwait(false);
return model.Id; return model.Id;
} }
#if FILESYSTEM
public static async Task<ulong> SendFileAsync(DiscordWebhookClient client, string filePath, string text, bool isTTS, public static async Task<ulong> SendFileAsync(DiscordWebhookClient client, string filePath, string text, bool isTTS,
IEnumerable<Embed> embeds, string username, string avatarUrl, RequestOptions options) IEnumerable<Embed> embeds, string username, string avatarUrl, RequestOptions options)
{ {
@@ -42,7 +41,6 @@ namespace Discord.Webhook
using (var file = File.OpenRead(filePath)) using (var file = File.OpenRead(filePath))
return await SendFileAsync(client, file, filename, text, isTTS, embeds, username, avatarUrl, options).ConfigureAwait(false); return await SendFileAsync(client, file, filename, text, isTTS, embeds, username, avatarUrl, options).ConfigureAwait(false);
} }
#endif
public static async Task<ulong> SendFileAsync(DiscordWebhookClient client, Stream stream, string filename, string text, bool isTTS, public static async Task<ulong> SendFileAsync(DiscordWebhookClient client, Stream stream, string filename, string text, bool isTTS,
IEnumerable<Embed> embeds, string username, string avatarUrl, RequestOptions options) IEnumerable<Embed> embeds, string username, string avatarUrl, RequestOptions options)
{ {


+ 5
- 5
test/Discord.Net.Tests/Discord.Net.Tests.csproj View File

@@ -21,10 +21,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Akavache" Version="5.0.0" /> <PackageReference Include="Akavache" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
<PackageReference Include="xunit.runner.reporters" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<PackageReference Include="xunit.runner.reporters" Version="2.3.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

+ 1
- 1
test/Discord.Net.Tests/Tests.ChannelPermissions.cs View File

@@ -84,7 +84,7 @@ namespace Discord
Assert.Equal(groupChannel, ChannelPermissions.Group.RawValue); Assert.Equal(groupChannel, ChannelPermissions.Group.RawValue);
return Task.CompletedTask; return Task.CompletedTask;
} }
[Fact]
public Task TestChannelPermissionModify() public Task TestChannelPermissionModify()
{ {
// test channel permission modify // test channel permission modify


+ 26
- 26
test/Discord.Net.Tests/Tests.Channels.cs View File

@@ -49,35 +49,35 @@ namespace Discord
} }
private static void CheckTextChannels(RestGuild guild, params RestTextChannel[] textChannels) private static void CheckTextChannels(RestGuild guild, params RestTextChannel[] textChannels)
{ {
Assert.Equal(textChannels.Length, 5);
Assert.Equal(5, textChannels.Length);
Assert.All(textChannels, x => Assert.All(textChannels, x =>
{ {
Assert.NotNull(x); Assert.NotNull(x);
Assert.NotEqual(x.Id, 0UL);
Assert.NotEqual(0UL, x.Id);
Assert.True(x.Position >= 0); Assert.True(x.Position >= 0);
}); });


var text1 = textChannels.Where(x => x.Name == "text1").FirstOrDefault();
var text2 = textChannels.Where(x => x.Name == "text2").FirstOrDefault();
var text3 = textChannels.Where(x => x.Name == "text3").FirstOrDefault();
var text4 = textChannels.Where(x => x.Name == "text4").FirstOrDefault();
var text5 = textChannels.Where(x => x.Name == "text5").FirstOrDefault();
var text1 = textChannels.FirstOrDefault(x => x.Name == "text1");
var text2 = textChannels.FirstOrDefault(x => x.Name == "text2");
var text3 = textChannels.FirstOrDefault(x => x.Name == "text3");
var text4 = textChannels.FirstOrDefault(x => x.Name == "text4");
var text5 = textChannels.FirstOrDefault(x => x.Name == "text5");


Assert.NotNull(text1); Assert.NotNull(text1);
//Assert.True(text1.Id == guild.DefaultChannelId); //Assert.True(text1.Id == guild.DefaultChannelId);
Assert.Equal(text1.Position, 1);
Assert.Equal(text1.Topic, "Topic1");
Assert.Equal(1, text1.Position);
Assert.Equal("Topic1", text1.Topic);


Assert.NotNull(text2); Assert.NotNull(text2);
Assert.Equal(text2.Position, 2);
Assert.Equal(2, text2.Position);
Assert.Null(text2.Topic); Assert.Null(text2.Topic);


Assert.NotNull(text3); Assert.NotNull(text3);
Assert.Equal(text3.Topic, "Topic2");
Assert.Equal("Topic2", text3.Topic);


Assert.NotNull(text4); Assert.NotNull(text4);
Assert.Equal(text4.Position, 3);
Assert.Equal(text4.Topic, "Topic2");
Assert.Equal(3, text4.Position);
Assert.Equal("Topic2", text4.Topic);


Assert.NotNull(text5); Assert.NotNull(text5);
Assert.Null(text5.Topic); Assert.Null(text5.Topic);
@@ -114,31 +114,31 @@ namespace Discord
} }
private static void CheckVoiceChannels(params RestVoiceChannel[] voiceChannels) private static void CheckVoiceChannels(params RestVoiceChannel[] voiceChannels)
{ {
Assert.Equal(voiceChannels.Length, 3);
Assert.Equal(3, voiceChannels.Length);
Assert.All(voiceChannels, x => Assert.All(voiceChannels, x =>
{ {
Assert.NotNull(x); Assert.NotNull(x);
Assert.NotEqual(x.Id, 0UL);
Assert.NotEqual(x.UserLimit, 0);
Assert.NotEqual(0UL, x.Id);
Assert.NotEqual(0, x.UserLimit);
Assert.True(x.Bitrate > 0); Assert.True(x.Bitrate > 0);
Assert.True(x.Position >= 0); Assert.True(x.Position >= 0);
}); });


var voice1 = voiceChannels.Where(x => x.Name == "voice1").FirstOrDefault();
var voice2 = voiceChannels.Where(x => x.Name == "voice2").FirstOrDefault();
var voice3 = voiceChannels.Where(x => x.Name == "voice3").FirstOrDefault();
var voice1 = voiceChannels.FirstOrDefault(x => x.Name == "voice1");
var voice2 = voiceChannels.FirstOrDefault(x => x.Name == "voice2");
var voice3 = voiceChannels.FirstOrDefault(x => x.Name == "voice3");


Assert.NotNull(voice1); Assert.NotNull(voice1);
Assert.Equal(voice1.Bitrate, 96000);
Assert.Equal(voice1.Position, 1);
Assert.Equal(96000, voice1.Bitrate);
Assert.Equal(1, voice1.Position);


Assert.NotNull(voice2); Assert.NotNull(voice2);
Assert.Equal(voice2.UserLimit, null);
Assert.Null(voice2.UserLimit);


Assert.NotNull(voice3); Assert.NotNull(voice3);
Assert.Equal(voice3.Bitrate, 8000);
Assert.Equal(voice3.Position, 1);
Assert.Equal(voice3.UserLimit, 16);
Assert.Equal(8000, voice3.Bitrate);
Assert.Equal(1, voice3.Position);
Assert.Equal(16, voice3.UserLimit);
} }
} }
}
}

+ 2
- 1
test/Discord.Net.Tests/Tests.Colors.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using Xunit; using Xunit;


namespace Discord namespace Discord
@@ -12,6 +12,7 @@ namespace Discord
Assert.Equal(uint.MinValue, new Color(uint.MinValue).RawValue); Assert.Equal(uint.MinValue, new Color(uint.MinValue).RawValue);
Assert.Equal(uint.MaxValue, new Color(uint.MaxValue).RawValue); Assert.Equal(uint.MaxValue, new Color(uint.MaxValue).RawValue);
} }
[Fact]
public void Color_Default() public void Color_Default()
{ {
Assert.Equal(0u, Color.Default.RawValue); Assert.Equal(0u, Color.Default.RawValue);


+ 2
- 1
test/Discord.Net.Tests/Tests.Emotes.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using Xunit; using Xunit;


namespace Discord namespace Discord
@@ -34,6 +34,7 @@ namespace Discord
Assert.Equal(DateTimeOffset.FromUnixTimeMilliseconds(1514056829775), emote.CreatedAt); Assert.Equal(DateTimeOffset.FromUnixTimeMilliseconds(1514056829775), emote.CreatedAt);
Assert.EndsWith("gif", emote.Url); Assert.EndsWith("gif", emote.Url);
} }
[Fact]
public void Test_Invalid_Amimated_Emote_Parse() public void Test_Invalid_Amimated_Emote_Parse()
{ {
Assert.False(Emote.TryParse("<x:typingstatus:394207658351263745>", out _)); Assert.False(Emote.TryParse("<x:typingstatus:394207658351263745>", out _));


+ 5
- 5
test/Discord.Net.Tests/Tests.GuildPermissions.cs View File

@@ -138,12 +138,12 @@ namespace Discord




// individual permission test // individual permission test
perm = perm.Modify(readMessages: true);
Assert.True(perm.ReadMessages);
Assert.Equal(perm.RawValue, (ulong)GuildPermission.ReadMessages);
perm = perm.Modify(viewChannel: true);
Assert.True(perm.ViewChannel);
Assert.Equal(perm.RawValue, (ulong)GuildPermission.ViewChannel);


perm = perm.Modify(readMessages: false);
Assert.False(perm.ReadMessages);
perm = perm.Modify(viewChannel: false);
Assert.False(perm.ViewChannel);
Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue);






+ 7
- 7
test/Discord.Net.Tests/Tests.Permissions.cs View File

@@ -25,15 +25,15 @@ namespace Discord


// check that toggling the bit works // check that toggling the bit works
Permissions.UnsetFlag(ref rawValue, flagValue); Permissions.UnsetFlag(ref rawValue, flagValue);
Assert.Equal(false, Permissions.GetValue(rawValue, flagValue));
Assert.False(Permissions.GetValue(rawValue, flagValue));
Permissions.SetFlag(ref rawValue, flagValue); Permissions.SetFlag(ref rawValue, flagValue);
Assert.Equal(true, Permissions.GetValue(rawValue, flagValue));
Assert.True(Permissions.GetValue(rawValue, flagValue));


// do the same, but with the SetValue method // do the same, but with the SetValue method
Permissions.SetValue(ref rawValue, true, flagValue); Permissions.SetValue(ref rawValue, true, flagValue);
Assert.Equal(true, Permissions.GetValue(rawValue, flagValue));
Assert.True(Permissions.GetValue(rawValue, flagValue));
Permissions.SetValue(ref rawValue, false, flagValue); Permissions.SetValue(ref rawValue, false, flagValue);
Assert.Equal(false, Permissions.GetValue(rawValue, flagValue));
Assert.False(Permissions.GetValue(rawValue, flagValue));
} }


/// <summary> /// <summary>
@@ -280,7 +280,7 @@ namespace Discord
TestHelper(value, GuildPermission.ManageGuild, false); TestHelper(value, GuildPermission.ManageGuild, false);
TestHelper(value, GuildPermission.AddReactions, false); TestHelper(value, GuildPermission.AddReactions, false);
TestHelper(value, GuildPermission.ViewAuditLog, false); TestHelper(value, GuildPermission.ViewAuditLog, false);
TestHelper(value, GuildPermission.ReadMessages, false);
TestHelper(value, GuildPermission.ViewChannel, false);
TestHelper(value, GuildPermission.SendMessages, false); TestHelper(value, GuildPermission.SendMessages, false);
TestHelper(value, GuildPermission.SendTTSMessages, false); TestHelper(value, GuildPermission.SendTTSMessages, false);
TestHelper(value, GuildPermission.ManageMessages, false); TestHelper(value, GuildPermission.ManageMessages, false);
@@ -323,7 +323,7 @@ namespace Discord
TestHelper(value, GuildPermission.ManageGuild, true); TestHelper(value, GuildPermission.ManageGuild, true);
TestHelper(value, GuildPermission.AddReactions, true); TestHelper(value, GuildPermission.AddReactions, true);
TestHelper(value, GuildPermission.ViewAuditLog, true); TestHelper(value, GuildPermission.ViewAuditLog, true);
TestHelper(value, GuildPermission.ReadMessages, true);
TestHelper(value, GuildPermission.ViewChannel, true);
TestHelper(value, GuildPermission.SendMessages, true); TestHelper(value, GuildPermission.SendMessages, true);
TestHelper(value, GuildPermission.SendTTSMessages, true); TestHelper(value, GuildPermission.SendTTSMessages, true);
TestHelper(value, GuildPermission.ManageMessages, true); TestHelper(value, GuildPermission.ManageMessages, true);
@@ -367,7 +367,7 @@ namespace Discord
TestHelper(value, GuildPermission.ManageGuild, false); TestHelper(value, GuildPermission.ManageGuild, false);
TestHelper(value, GuildPermission.AddReactions, false); TestHelper(value, GuildPermission.AddReactions, false);
TestHelper(value, GuildPermission.ViewAuditLog, false); TestHelper(value, GuildPermission.ViewAuditLog, false);
TestHelper(value, GuildPermission.ReadMessages, false);
TestHelper(value, GuildPermission.ViewChannel, false);
TestHelper(value, GuildPermission.SendMessages, true); TestHelper(value, GuildPermission.SendMessages, true);
TestHelper(value, GuildPermission.SendTTSMessages, true); TestHelper(value, GuildPermission.SendTTSMessages, true);
TestHelper(value, GuildPermission.ManageMessages, false); TestHelper(value, GuildPermission.ManageMessages, false);


Loading…
Cancel
Save