Browse Source

Init

pull/2305/head
Armano den Boef 3 years ago
parent
commit
ec0492af0f
27 changed files with 173 additions and 201 deletions
  1. +4
    -1
      samples/InteractionFramework/InteractionHandler.cs
  2. +2
    -2
      samples/InteractionFramework/Modules/ExampleModule.cs
  3. +4
    -1
      samples/InteractionFramework/Program.cs
  4. +14
    -15
      samples/TextCommandFramework/Program.cs
  5. +1
    -1
      samples/TextCommandFramework/Services/CommandHandlingService.cs
  6. +10
    -14
      samples/WebhookClient/Program.cs
  7. +1
    -1
      src/Discord.Net.Analyzers/GuildAccessAnalyzer.cs
  8. +2
    -10
      src/Discord.Net.Commands/CommandService.cs
  9. +5
    -5
      src/Discord.Net.Core/CDN.cs
  10. +5
    -5
      src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs
  11. +2
    -2
      src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
  12. +2
    -2
      src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
  13. +6
    -6
      src/Discord.Net.Core/Format.cs
  14. +0
    -4
      src/Discord.Net.Rest/BaseDiscordClient.cs
  15. +87
    -92
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  16. +1
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs
  17. +1
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ThreadUpdateAuditLogData.cs
  18. +1
    -3
      src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
  19. +2
    -4
      src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponent.cs
  20. +1
    -2
      src/Discord.Net.Rest/Entities/Interactions/SlashCommands/RestAutocompleteInteraction.cs
  21. +2
    -2
      src/Discord.Net.Rest/Net/Queue/RequestQueue.cs
  22. +2
    -2
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  23. +1
    -9
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
  24. +4
    -4
      src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs
  25. +6
    -6
      test/Discord.Net.Tests.Integration/ChannelsTests.cs
  26. +6
    -6
      test/Discord.Net.Tests.Integration/GuildTests.cs
  27. +1
    -0
      test/Discord.Net.Tests.Unit/ColorTests.cs

+ 4
- 1
samples/InteractionFramework/InteractionHandler.cs View File

@@ -37,7 +37,10 @@ namespace InteractionFramework
} }


private async Task LogAsync(LogMessage log) private async Task LogAsync(LogMessage log)
=> Console.WriteLine(log);
{
Console.WriteLine(log);
await Task.CompletedTask;
}


private async Task ReadyAsync() private async Task ReadyAsync()
{ {


+ 2
- 2
samples/InteractionFramework/Modules/ExampleModule.cs View File

@@ -12,7 +12,7 @@ namespace InteractionFramework.Modules
// Dependencies can be accessed through Property injection, public properties with public setters will be set by the service provider // Dependencies can be accessed through Property injection, public properties with public setters will be set by the service provider
public InteractionService Commands { get; set; } public InteractionService Commands { get; set; }


private InteractionHandler _handler;
private readonly InteractionHandler _handler;


// Constructor injection is also a valid way to access the dependencies // Constructor injection is also a valid way to access the dependencies
public ExampleModule(InteractionHandler handler) public ExampleModule(InteractionHandler handler)
@@ -63,7 +63,7 @@ namespace InteractionFramework.Modules
[ComponentInteraction("roleSelect")] [ComponentInteraction("roleSelect")]
public async Task RoleSelect(string[] selections) public async Task RoleSelect(string[] selections)
{ {
throw new NotImplementedException();
await DeferAsync();
} }


// With the Attribute DoUserCheck you can make sure that only the user this button targets can click it. This is defined by the first wildcard: *. // With the Attribute DoUserCheck you can make sure that only the user this button targets can click it. This is defined by the first wildcard: *.


+ 4
- 1
samples/InteractionFramework/Program.cs View File

@@ -60,7 +60,10 @@ namespace InteractionFramework
} }


private async Task LogAsync(LogMessage message) private async Task LogAsync(LogMessage message)
=> Console.WriteLine(message.ToString());
{
Console.WriteLine(message.ToString());
await Task.CompletedTask;
}


public static bool IsDebug() public static bool IsDebug()
{ {


+ 14
- 15
samples/TextCommandFramework/Program.cs View File

@@ -22,7 +22,7 @@ namespace TextCommandFramework
{ {
// There is no need to implement IDisposable like before as we are // There is no need to implement IDisposable like before as we are
// using dependency injection, which handles calling Dispose for us. // using dependency injection, which handles calling Dispose for us.
static void Main(string[] args)
static void Main(string[] _)
=> new Program().MainAsync().GetAwaiter().GetResult(); => new Program().MainAsync().GetAwaiter().GetResult();


public async Task MainAsync() public async Task MainAsync()
@@ -31,23 +31,22 @@ namespace TextCommandFramework
// when you are finished using it, at the end of your app's lifetime. // when you are finished using it, at the end of your app's lifetime.
// If you use another dependency injection framework, you should inspect // If you use another dependency injection framework, you should inspect
// its documentation for the best way to do this. // its documentation for the best way to do this.
using (var services = ConfigureServices())
{
var client = services.GetRequiredService<DiscordSocketClient>();
using var services = ConfigureServices();


client.Log += LogAsync;
services.GetRequiredService<CommandService>().Log += LogAsync;
var client = services.GetRequiredService<DiscordSocketClient>();


// Tokens should be considered secret data and never hard-coded.
// We can read from the environment variable to avoid hard coding.
await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token"));
await client.StartAsync();
client.Log += LogAsync;
services.GetRequiredService<CommandService>().Log += LogAsync;


// Here we initialize the logic required to register our commands.
await services.GetRequiredService<CommandHandlingService>().InitializeAsync();
// Tokens should be considered secret data and never hard-coded.
// We can read from the environment variable to avoid hard coding.
await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token"));
await client.StartAsync();


await Task.Delay(Timeout.Infinite);
}
// Here we initialize the logic required to register our commands.
await services.GetRequiredService<CommandHandlingService>().InitializeAsync();

await Task.Delay(Timeout.Infinite);
} }


private Task LogAsync(LogMessage log) private Task LogAsync(LogMessage log)
@@ -57,7 +56,7 @@ namespace TextCommandFramework
return Task.CompletedTask; return Task.CompletedTask;
} }


private ServiceProvider ConfigureServices()
private static ServiceProvider ConfigureServices()
{ {
return new ServiceCollection() return new ServiceCollection()
.AddSingleton<DiscordSocketClient>() .AddSingleton<DiscordSocketClient>()


+ 1
- 1
samples/TextCommandFramework/Services/CommandHandlingService.cs View File

@@ -58,7 +58,7 @@ namespace TextCommandFramework.Services
// we will handle the result in CommandExecutedAsync, // we will handle the result in CommandExecutedAsync,
} }


public async Task CommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result)
public static async Task CommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result)
{ {
// command is unspecified when there was a search failure (command not found); we don't care about these errors // command is unspecified when there was a search failure (command not found); we don't care about these errors
if (!command.IsSpecified) if (!command.IsSpecified)


+ 10
- 14
samples/WebhookClient/Program.cs View File

@@ -9,26 +9,22 @@ namespace WebHookClient
// To a channel specific URL to send a message to that channel. // To a channel specific URL to send a message to that channel.
class Program class Program
{ {
static void Main(string[] args)
=> new Program().MainAsync().GetAwaiter().GetResult();

public async Task MainAsync()
async static Task Main(string[] _)
{ {
// The webhook url follows the format https://discord.com/api/webhooks/{id}/{token} // The webhook url follows the format https://discord.com/api/webhooks/{id}/{token}
// Because anyone with the webhook URL can use your webhook // Because anyone with the webhook URL can use your webhook
// you should NOT hard code the URL or ID + token into your application. // you should NOT hard code the URL or ID + token into your application.
using (var client = new DiscordWebhookClient("https://discord.com/api/webhooks/123/abc123"))
using var client = new DiscordWebhookClient("https://discord.com/api/webhooks/123/abc123");

var embed = new EmbedBuilder
{ {
var embed = new EmbedBuilder
{
Title = "Test Embed",
Description = "Test Description"
};
Title = "Test Embed",
Description = "Test Description"
};


// Webhooks are able to send multiple embeds per message
// As such, your embeds must be passed as a collection.
await client.SendMessageAsync(text: "Send a message to this webhook!", embeds: new[] { embed.Build() });
}
// Webhooks are able to send multiple embeds per message
// As such, your embeds must be passed as a collection.
await client.SendMessageAsync(text: "Send a message to this webhook!", embeds: new[] { embed.Build() });
} }
} }
} }

+ 1
- 1
src/Discord.Net.Analyzers/GuildAccessAnalyzer.cs View File

@@ -18,7 +18,7 @@ namespace Discord.Analyzers
private const string Description = "Accessing 'Context.Guild' in a command without limiting the command to run only in guilds."; private const string Description = "Accessing 'Context.Guild' in a command without limiting the command to run only in guilds.";
private const string Category = "API Usage"; private const string Category = "API Usage";


private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
private static DiagnosticDescriptor Rule = new(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);


public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);




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

@@ -503,18 +503,10 @@ namespace Discord.Commands
/// <param name="argPos">The position of which the command starts at.</param> /// <param name="argPos">The position of which the command starts at.</param>
/// <returns>The result containing the matching commands.</returns> /// <returns>The result containing the matching commands.</returns>
public SearchResult Search(ICommandContext context, int argPos) public SearchResult Search(ICommandContext context, int argPos)
=> Search(context.Message.Content.Substring(argPos));
/// <summary>
/// Searches for the command.
/// </summary>
/// <param name="context">The context of the command.</param>
/// <param name="input">The command string.</param>
/// <returns>The result containing the matching commands.</returns>
public SearchResult Search(ICommandContext context, string input)
=> Search(input);
=> Search(context.Message.Content[argPos..]);
public SearchResult Search(string input) public SearchResult Search(string input)
{ {
string searchInput = _caseSensitive ? input : input.ToLowerInvariant();
var searchInput = _caseSensitive ? input : input.ToLowerInvariant();
var matches = _map.GetCommands(searchInput).OrderByDescending(x => x.Command.Priority).ToImmutableArray(); var matches = _map.GetCommands(searchInput).OrderByDescending(x => x.Command.Priority).ToImmutableArray();


if (matches.Length > 0) if (matches.Length > 0)


+ 5
- 5
src/Discord.Net.Core/CDN.cs View File

@@ -43,7 +43,7 @@ namespace Discord
{ {
if (avatarId == null) if (avatarId == null)
return null; return null;
string extension = FormatToExtension(format, avatarId);
var extension = FormatToExtension(format, avatarId);
return $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.{extension}?size={size}"; return $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.{extension}?size={size}";
} }


@@ -51,7 +51,7 @@ namespace Discord
{ {
if (avatarId == null) if (avatarId == null)
return null; return null;
string extension = FormatToExtension(format, avatarId);
var extension = FormatToExtension(format, avatarId);
return $"{DiscordConfig.CDNUrl}guilds/{guildId}/users/{userId}/avatars/{avatarId}.{extension}?size={size}"; return $"{DiscordConfig.CDNUrl}guilds/{guildId}/users/{userId}/avatars/{avatarId}.{extension}?size={size}";
} }


@@ -69,7 +69,7 @@ namespace Discord
{ {
if (bannerId == null) if (bannerId == null)
return null; return null;
string extension = FormatToExtension(format, bannerId);
var extension = FormatToExtension(format, bannerId);
return $"{DiscordConfig.CDNUrl}banners/{userId}/{bannerId}.{extension}?size={size}"; return $"{DiscordConfig.CDNUrl}banners/{userId}/{bannerId}.{extension}?size={size}";
} }
/// <summary> /// <summary>
@@ -148,7 +148,7 @@ namespace Discord
{ {
if (string.IsNullOrEmpty(bannerId)) if (string.IsNullOrEmpty(bannerId))
return null; return null;
string extension = FormatToExtension(format, bannerId);
var extension = FormatToExtension(format, bannerId);
return $"{DiscordConfig.CDNUrl}banners/{guildId}/{bannerId}.{extension}" + (size.HasValue ? $"?size={size}" : string.Empty); return $"{DiscordConfig.CDNUrl}banners/{guildId}/{bannerId}.{extension}" + (size.HasValue ? $"?size={size}" : string.Empty);
} }
/// <summary> /// <summary>
@@ -174,7 +174,7 @@ namespace Discord
/// </returns> /// </returns>
public static string GetRichAssetUrl(ulong appId, string assetId, ushort size, ImageFormat format) public static string GetRichAssetUrl(ulong appId, string assetId, ushort size, ImageFormat format)
{ {
string extension = FormatToExtension(format, "");
var extension = FormatToExtension(format, "");
return $"{DiscordConfig.CDNUrl}app-assets/{appId}/{assetId}.{extension}?size={size}"; return $"{DiscordConfig.CDNUrl}app-assets/{appId}/{assetId}.{extension}?size={size}";
} }




+ 5
- 5
src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs View File

@@ -145,11 +145,11 @@ namespace Discord
{ {
get get
{ {
int titleLength = Title?.Length ?? 0;
int authorLength = Author?.Name?.Length ?? 0;
int descriptionLength = Description?.Length ?? 0;
int footerLength = Footer?.Text?.Length ?? 0;
int fieldSum = Fields.Sum(f => f.Name.Length + f.Value.ToString().Length);
var titleLength = Title?.Length ?? 0;
var authorLength = Author?.Name?.Length ?? 0;
var descriptionLength = Description?.Length ?? 0;
var footerLength = Footer?.Text?.Length ?? 0;
var fieldSum = Fields.Sum(f => f.Name.Length + f.Value.ToString().Length);


return titleLength + authorLength + descriptionLength + footerLength + fieldSum; return titleLength + authorLength + descriptionLength + footerLength + fieldSum;
} }


+ 2
- 2
src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs View File

@@ -141,7 +141,7 @@ namespace Discord
bool? sendMessagesInThreads = null, bool? sendMessagesInThreads = null,
bool? startEmbeddedActivities = null) bool? startEmbeddedActivities = null)
{ {
ulong value = initialValue;
var value = initialValue;


Permissions.SetValue(ref value, createInstantInvite, ChannelPermission.CreateInstantInvite); Permissions.SetValue(ref value, createInstantInvite, ChannelPermission.CreateInstantInvite);
Permissions.SetValue(ref value, manageChannel, ChannelPermission.ManageChannels); Permissions.SetValue(ref value, manageChannel, ChannelPermission.ManageChannels);
@@ -287,7 +287,7 @@ namespace Discord
var perms = new List<ChannelPermission>(); var perms = new List<ChannelPermission>();
for (byte i = 0; i < Permissions.MaxBits; i++) for (byte i = 0; i < Permissions.MaxBits; i++)
{ {
ulong flag = ((ulong)1 << i);
var flag = ((ulong)1 << i);
if ((RawValue & flag) != 0) if ((RawValue & flag) != 0)
perms.Add((ChannelPermission)flag); perms.Add((ChannelPermission)flag);
} }


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

@@ -153,7 +153,7 @@ namespace Discord
bool? startEmbeddedActivities = null, bool? startEmbeddedActivities = null,
bool? moderateMembers = null) bool? moderateMembers = null)
{ {
ulong value = initialValue;
var value = initialValue;


Permissions.SetValue(ref value, createInstantInvite, GuildPermission.CreateInstantInvite); Permissions.SetValue(ref value, createInstantInvite, GuildPermission.CreateInstantInvite);
Permissions.SetValue(ref value, banMembers, GuildPermission.BanMembers); Permissions.SetValue(ref value, banMembers, GuildPermission.BanMembers);
@@ -358,7 +358,7 @@ namespace Discord
// each of the GuildPermissions increments by 2^i from 0 to MaxBits // each of the GuildPermissions increments by 2^i from 0 to MaxBits
for (byte i = 0; i < Permissions.MaxBits; i++) for (byte i = 0; i < Permissions.MaxBits; i++)
{ {
ulong flag = ((ulong)1 << i);
var flag = ((ulong)1 << i);
if ((RawValue & flag) != 0) if ((RawValue & flag) != 0)
perms.Add((GuildPermission)flag); perms.Add((GuildPermission)flag);
} }


+ 6
- 6
src/Discord.Net.Core/Format.cs View File

@@ -28,7 +28,7 @@ namespace Discord
/// <summary> Returns a markdown-formatted string with codeblock formatting. </summary> /// <summary> Returns a markdown-formatted string with codeblock formatting. </summary>
public static string Code(string text, string language = null) public static string Code(string text, string language = null)
{ {
if (language != null || text.Contains("\n"))
if (language != null || text.Contains('\n'))
return $"```{language ?? ""}\n{text}\n```"; return $"```{language ?? ""}\n{text}\n```";
else else
return $"`{text}`"; return $"`{text}`";
@@ -38,7 +38,7 @@ namespace Discord
public static string Sanitize(string text) public static string Sanitize(string text)
{ {
if (text != null) if (text != null)
foreach (string unsafeChar in SensitiveCharacters)
foreach (var unsafeChar in SensitiveCharacters)
text = text.Replace(unsafeChar, $"\\{unsafeChar}"); text = text.Replace(unsafeChar, $"\\{unsafeChar}");
return text; return text;
} }
@@ -55,9 +55,9 @@ namespace Discord
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
return text; return text;


StringBuilder result = new StringBuilder();
StringBuilder result = new();


int startIndex = 0;
var startIndex = 0;
int newLineIndex; int newLineIndex;
do do
{ {
@@ -65,13 +65,13 @@ namespace Discord
if (newLineIndex == -1) if (newLineIndex == -1)
{ {
// read the rest of the string // read the rest of the string
var str = text.Substring(startIndex);
var str = text[startIndex..];
result.Append($"> {str}"); result.Append($"> {str}");
} }
else else
{ {
// read until the next newline // read until the next newline
var str = text.Substring(startIndex, newLineIndex - startIndex);
var str = text[startIndex..newLineIndex];
result.Append($"> {str}\n"); result.Append($"> {str}\n");
} }
startIndex = newLineIndex + 1; startIndex = newLineIndex + 1;


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

@@ -144,9 +144,7 @@ namespace Discord.Rest
{ {
if (!_isDisposed) if (!_isDisposed)
{ {
#pragma warning disable IDISP007
ApiClient.Dispose(); ApiClient.Dispose();
#pragma warning restore IDISP007
_stateLock?.Dispose(); _stateLock?.Dispose();
_isDisposed = true; _isDisposed = true;
} }
@@ -156,9 +154,7 @@ namespace Discord.Rest
{ {
if (!_isDisposed) if (!_isDisposed)
{ {
#pragma warning disable IDISP007
await ApiClient.DisposeAsync().ConfigureAwait(false); await ApiClient.DisposeAsync().ConfigureAwait(false);
#pragma warning restore IDISP007
_stateLock?.Dispose(); _stateLock?.Dispose();
_isDisposed = true; _isDisposed = true;
} }


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

@@ -24,10 +24,10 @@ namespace Discord.API
internal class DiscordRestApiClient : IDisposable, IAsyncDisposable internal class DiscordRestApiClient : IDisposable, IAsyncDisposable
{ {
#region DiscordRestApiClient #region DiscordRestApiClient
private static readonly ConcurrentDictionary<string, Func<BucketIds, BucketId>> _bucketIdGenerators = new ConcurrentDictionary<string, Func<BucketIds, BucketId>>();
private static readonly ConcurrentDictionary<string, Func<BucketIds, BucketId>> _bucketIdGenerators = new();


public event Func<string, string, double, Task> SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } } public event Func<string, string, double, Task> SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } }
private readonly AsyncEvent<Func<string, string, double, Task>> _sentRequestEvent = new AsyncEvent<Func<string, string, double, Task>>();
private readonly AsyncEvent<Func<string, string, double, Task>> _sentRequestEvent = new();


protected readonly JsonSerializer _serializer; protected readonly JsonSerializer _serializer;
protected readonly SemaphoreSlim _stateLock; protected readonly SemaphoreSlim _stateLock;
@@ -110,7 +110,7 @@ namespace Discord.API
_loginCancelToken?.Dispose(); _loginCancelToken?.Dispose();
RestClient?.Dispose(); RestClient?.Dispose();


if (!(RequestQueue is null))
if (RequestQueue is not null)
await RequestQueue.DisposeAsync().ConfigureAwait(false); await RequestQueue.DisposeAsync().ConfigureAwait(false);


_stateLock?.Dispose(); _stateLock?.Dispose();
@@ -123,16 +123,16 @@ namespace Discord.API


public ValueTask DisposeAsync() => DisposeAsync(true); public ValueTask DisposeAsync() => DisposeAsync(true);


public async Task LoginAsync(TokenType tokenType, string token, RequestOptions options = null)
public async Task LoginAsync(TokenType tokenType, string token)
{ {
await _stateLock.WaitAsync().ConfigureAwait(false); await _stateLock.WaitAsync().ConfigureAwait(false);
try try
{ {
await LoginInternalAsync(tokenType, token, options).ConfigureAwait(false);
await LoginInternalAsync(tokenType, token).ConfigureAwait(false);
} }
finally { _stateLock.Release(); } finally { _stateLock.Release(); }
} }
private async Task LoginInternalAsync(TokenType tokenType, string token, RequestOptions options = null)
private async Task LoginInternalAsync(TokenType tokenType, string token)
{ {
if (LoginState != LoginState.LoggedOut) if (LoginState != LoginState.LoggedOut)
await LogoutInternalAsync().ConfigureAwait(false); await LogoutInternalAsync().ConfigureAwait(false);
@@ -196,11 +196,10 @@ namespace Discord.API
#endregion #endregion


#region Core #region Core
internal Task SendAsync(string method, Expression<Func<string>> endpointExpr, BucketIds ids,
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null)
=> SendAsync(method, GetEndpoint(endpointExpr), GetBucketId(method, ids, endpointExpr, funcName), clientBucket, options);
internal Task SendAsync(string method, Expression<Func<string>> endpointExpr, BucketIds ids, RequestOptions options = null, [CallerMemberName] string funcName = null)
=> SendAsync(method, GetEndpoint(endpointExpr), GetBucketId(method, ids, endpointExpr, funcName), options);
public async Task SendAsync(string method, string endpoint, public async Task SendAsync(string method, string endpoint,
BucketId bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null)
BucketId bucketId = null, RequestOptions options = null)
{ {
options ??= new RequestOptions(); options ??= new RequestOptions();
options.HeaderOnly = true; options.HeaderOnly = true;
@@ -210,26 +209,24 @@ namespace Discord.API
await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); await SendInternalAsync(method, endpoint, request).ConfigureAwait(false);
} }


internal Task SendJsonAsync(string method, Expression<Func<string>> endpointExpr, object payload, BucketIds ids,
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null)
=> SendJsonAsync(method, GetEndpoint(endpointExpr), payload, GetBucketId(method, ids, endpointExpr, funcName), clientBucket, options);
internal Task SendJsonAsync(string method, Expression<Func<string>> endpointExpr, object payload, BucketIds ids, RequestOptions options = null, [CallerMemberName] string funcName = null)
=> SendJsonAsync(method, GetEndpoint(endpointExpr), payload, GetBucketId(method, ids, endpointExpr, funcName), options);
public async Task SendJsonAsync(string method, string endpoint, object payload, public async Task SendJsonAsync(string method, string endpoint, object payload,
BucketId bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null)
BucketId bucketId = null, RequestOptions options = null)
{ {
options ??= new RequestOptions(); options ??= new RequestOptions();
options.HeaderOnly = true; options.HeaderOnly = true;
options.BucketId = bucketId; options.BucketId = bucketId;


string json = payload != null ? SerializeJson(payload) : null;
var json = payload != null ? SerializeJson(payload) : null;
var request = new JsonRestRequest(RestClient, method, endpoint, json, options); var request = new JsonRestRequest(RestClient, method, endpoint, json, options);
await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); await SendInternalAsync(method, endpoint, request).ConfigureAwait(false);
} }


internal Task SendMultipartAsync(string method, Expression<Func<string>> endpointExpr, IReadOnlyDictionary<string, object> multipartArgs, BucketIds ids,
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null)
=> SendMultipartAsync(method, GetEndpoint(endpointExpr), multipartArgs, GetBucketId(method, ids, endpointExpr, funcName), clientBucket, options);
internal Task SendMultipartAsync(string method, Expression<Func<string>> endpointExpr, IReadOnlyDictionary<string, object> multipartArgs, BucketIds ids, RequestOptions options = null, [CallerMemberName] string funcName = null)
=> SendMultipartAsync(method, GetEndpoint(endpointExpr), multipartArgs, GetBucketId(method, ids, endpointExpr, funcName), options);
public async Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, public async Task SendMultipartAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs,
BucketId bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null)
BucketId bucketId = null, RequestOptions options = null)
{ {
options ??= new RequestOptions(); options ??= new RequestOptions();
options.HeaderOnly = true; options.HeaderOnly = true;
@@ -239,11 +236,10 @@ namespace Discord.API
await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); await SendInternalAsync(method, endpoint, request).ConfigureAwait(false);
} }


internal Task<TResponse> SendAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, BucketIds ids,
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null) where TResponse : class
=> SendAsync<TResponse>(method, GetEndpoint(endpointExpr), GetBucketId(method, ids, endpointExpr, funcName), clientBucket, options);
internal Task<TResponse> SendAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, BucketIds ids, RequestOptions options = null, [CallerMemberName] string funcName = null) where TResponse : class
=> SendAsync<TResponse>(method, GetEndpoint(endpointExpr), GetBucketId(method, ids, endpointExpr, funcName), options);
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint,
BucketId bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) where TResponse : class
BucketId bucketId = null, RequestOptions options = null) where TResponse : class
{ {
options ??= new RequestOptions(); options ??= new RequestOptions();
options.BucketId = bucketId; options.BucketId = bucketId;
@@ -252,26 +248,25 @@ namespace Discord.API
return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false));
} }


internal Task<TResponse> SendJsonAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, object payload, BucketIds ids,
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null) where TResponse : class
=> SendJsonAsync<TResponse>(method, GetEndpoint(endpointExpr), payload, GetBucketId(method, ids, endpointExpr, funcName), clientBucket, options);
internal Task<TResponse> SendJsonAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, object payload, BucketIds ids, RequestOptions options = null, [CallerMemberName] string funcName = null) where TResponse : class
=> SendJsonAsync<TResponse>(method, GetEndpoint(endpointExpr), payload, GetBucketId(method, ids, endpointExpr, funcName), options);
public async Task<TResponse> SendJsonAsync<TResponse>(string method, string endpoint, object payload, public async Task<TResponse> SendJsonAsync<TResponse>(string method, string endpoint, object payload,
BucketId bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) where TResponse : class
BucketId bucketId = null, RequestOptions options = null) where TResponse : class
{ {
options ??= new RequestOptions(); options ??= new RequestOptions();
options.BucketId = bucketId; options.BucketId = bucketId;


string json = payload != null ? SerializeJson(payload) : null;
var json = payload != null ? SerializeJson(payload) : null;


var request = new JsonRestRequest(RestClient, method, endpoint, json, options); var request = new JsonRestRequest(RestClient, method, endpoint, json, options);
return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false));
} }


internal Task<TResponse> SendMultipartAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, IReadOnlyDictionary<string, object> multipartArgs, BucketIds ids, internal Task<TResponse> SendMultipartAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, IReadOnlyDictionary<string, object> multipartArgs, BucketIds ids,
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null)
=> SendMultipartAsync<TResponse>(method, GetEndpoint(endpointExpr), multipartArgs, GetBucketId(method, ids, endpointExpr, funcName), clientBucket, options);
RequestOptions options = null, [CallerMemberName] string funcName = null)
=> SendMultipartAsync<TResponse>(method, GetEndpoint(endpointExpr), multipartArgs, GetBucketId(method, ids, endpointExpr, funcName), options);
public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs,
BucketId bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null)
BucketId bucketId = null, RequestOptions options = null)
{ {
options ??= new RequestOptions(); options ??= new RequestOptions();
options.BucketId = bucketId; options.BucketId = bucketId;
@@ -293,7 +288,7 @@ namespace Discord.API
var responseStream = await RequestQueue.SendAsync(request).ConfigureAwait(false); var responseStream = await RequestQueue.SendAsync(request).ConfigureAwait(false);
stopwatch.Stop(); stopwatch.Stop();


double milliseconds = ToMilliseconds(stopwatch);
var milliseconds = ToMilliseconds(stopwatch);
await _sentRequestEvent.InvokeAsync(method, endpoint, milliseconds).ConfigureAwait(false); await _sentRequestEvent.InvokeAsync(method, endpoint, milliseconds).ConfigureAwait(false);


return responseStream; return responseStream;
@@ -585,15 +580,15 @@ namespace Discord.API


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


string query = "";
var query = "";


if (limit.HasValue) if (limit.HasValue)
{ {
query = $"?before={before.GetValueOrDefault(DateTimeOffset.UtcNow).ToString("O")}&limit={limit.Value}";
query = $"?before={before.GetValueOrDefault(DateTimeOffset.UtcNow):O}&limit={limit.Value}";
} }
else if (before.HasValue) else if (before.HasValue)
{ {
query = $"?before={before.Value.ToString("O")}";
query = $"?before={before.Value:O}";
} }


return await SendAsync<ChannelThreads>("GET", () => $"channels/{channelId}/threads/archived/public{query}", bucket, options: options); return await SendAsync<ChannelThreads>("GET", () => $"channels/{channelId}/threads/archived/public{query}", bucket, options: options);
@@ -608,15 +603,15 @@ namespace Discord.API


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


string query = "";
var query = "";


if (limit.HasValue) if (limit.HasValue)
{ {
query = $"?before={before.GetValueOrDefault(DateTimeOffset.UtcNow).ToString("O")}&limit={limit.Value}";
query = $"?before={before.GetValueOrDefault(DateTimeOffset.UtcNow):O}&limit={limit.Value}";
} }
else if (before.HasValue) else if (before.HasValue)
{ {
query = $"?before={before.Value.ToString("O")}";
query = $"?before={before.Value:O}";
} }


return await SendAsync<ChannelThreads>("GET", () => $"channels/{channelId}/threads/archived/private{query}", bucket, options: options); return await SendAsync<ChannelThreads>("GET", () => $"channels/{channelId}/threads/archived/private{query}", bucket, options: options);
@@ -631,7 +626,7 @@ namespace Discord.API


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


string query = "";
var query = "";


if (limit.HasValue) if (limit.HasValue)
{ {
@@ -639,7 +634,7 @@ namespace Discord.API
} }
else if (before.HasValue) else if (before.HasValue)
{ {
query = $"?before={before.Value.ToString("O")}";
query = $"?before={before.Value:O}";
} }


return await SendAsync<ChannelThreads>("GET", () => $"channels/{channelId}/users/@me/threads/archived/private{query}", bucket, options: options); return await SendAsync<ChannelThreads>("GET", () => $"channels/{channelId}/users/@me/threads/archived/private{query}", bucket, options: options);
@@ -769,8 +764,8 @@ namespace Discord.API
Preconditions.AtMost(args.Limit, DiscordConfig.MaxMessagesPerBatch, nameof(args.Limit)); Preconditions.AtMost(args.Limit, DiscordConfig.MaxMessagesPerBatch, nameof(args.Limit));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxMessagesPerBatch);
ulong? relativeId = args.RelativeMessageId.IsSpecified ? args.RelativeMessageId.Value : (ulong?)null;
var limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxMessagesPerBatch);
var relativeId = args.RelativeMessageId.IsSpecified ? args.RelativeMessageId.Value : (ulong?)null;
var relativeDir = args.RelativeDirection.GetValueOrDefault(Direction.Before) switch var relativeDir = args.RelativeDirection.GetValueOrDefault(Direction.Before) switch
{ {
Direction.After => "after", Direction.After => "after",
@@ -794,11 +789,11 @@ namespace Discord.API
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content));


if (args.Content?.Length > DiscordConfig.MaxMessageSize) if (args.Content?.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content));
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


var ids = new BucketIds(channelId: channelId); var ids = new BucketIds(channelId: channelId);
return await SendJsonAsync<Message>("POST", () => $"channels/{channelId}/messages", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
return await SendJsonAsync<Message>("POST", () => $"channels/{channelId}/messages", args, ids, options: options).ConfigureAwait(false);
} }




@@ -815,12 +810,12 @@ namespace Discord.API
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content));


if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize) if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content));
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args));


options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


var ids = new BucketIds(webhookId: webhookId); var ids = new BucketIds(webhookId: webhookId);
return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?{WebhookQuery(true, threadId)}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?{WebhookQuery(true, threadId)}", args, ids, options: options).ConfigureAwait(false);
} }


/// <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>
@@ -837,11 +832,11 @@ namespace Discord.API
if (args.Embeds.IsSpecified) if (args.Embeds.IsSpecified)
Preconditions.AtMost(args.Embeds.Value.Length, 10, nameof(args.Embeds), "A max of 10 Embeds are allowed."); Preconditions.AtMost(args.Embeds.Value.Length, 10, nameof(args.Embeds), "A max of 10 Embeds are allowed.");
if (args.Content.IsSpecified && args.Content.Value.Length > DiscordConfig.MaxMessageSize) if (args.Content.IsSpecified && args.Content.Value.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content));
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


var ids = new BucketIds(webhookId: webhookId); var ids = new BucketIds(webhookId: webhookId);
await SendJsonAsync<Message>("PATCH", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}${WebhookQuery(false, threadId)}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
await SendJsonAsync<Message>("PATCH", () => $"webhooks/{webhookId}/{AuthToken}/messages/{messageId}${WebhookQuery(false, threadId)}", args, ids, options: options).ConfigureAwait(false);
} }


/// <exception cref="InvalidOperationException">This operation may only be called with a <see cref="TokenType.Webhook"/> token.</exception> /// <exception cref="InvalidOperationException">This operation may only be called with a <see cref="TokenType.Webhook"/> token.</exception>
@@ -872,7 +867,7 @@ namespace Discord.API
throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content));


var ids = new BucketIds(channelId: channelId); var ids = new BucketIds(channelId: channelId);
return await SendMultipartAsync<Message>("POST", () => $"channels/{channelId}/messages", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
return await SendMultipartAsync<Message>("POST", () => $"channels/{channelId}/messages", args.ToDictionary(), ids, options: options).ConfigureAwait(false);
} }


/// <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>
@@ -897,7 +892,7 @@ namespace Discord.API
} }


var ids = new BucketIds(webhookId: webhookId); var ids = new BucketIds(webhookId: webhookId);
return await SendMultipartAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?{WebhookQuery(true, threadId)}", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
return await SendMultipartAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?{WebhookQuery(true, threadId)}", args.ToDictionary(), ids, options: options).ConfigureAwait(false);
} }
public async Task DeleteMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) public async Task DeleteMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null)
{ {
@@ -941,7 +936,7 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


var ids = new BucketIds(channelId: channelId); var ids = new BucketIds(channelId: channelId);
return await SendJsonAsync<Message>("PATCH", () => $"channels/{channelId}/messages/{messageId}", args, ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
return await SendJsonAsync<Message>("PATCH", () => $"channels/{channelId}/messages/{messageId}", args, ids, options: options).ConfigureAwait(false);
} }


public async Task<Message> ModifyMessageAsync(ulong channelId, ulong messageId, Rest.UploadFileParams args, RequestOptions options = null) public async Task<Message> ModifyMessageAsync(ulong channelId, ulong messageId, Rest.UploadFileParams args, RequestOptions options = null)
@@ -954,7 +949,7 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


var ids = new BucketIds(channelId: channelId); var ids = new BucketIds(channelId: channelId);
return await SendMultipartAsync<Message>("PATCH", () => $"channels/{channelId}/messages/{messageId}", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
return await SendMultipartAsync<Message>("PATCH", () => $"channels/{channelId}/messages/{messageId}", args.ToDictionary(), ids, options: options).ConfigureAwait(false);
} }
#endregion #endregion


@@ -1082,8 +1077,8 @@ namespace Discord.API
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(DiscordConfig.MaxUserReactionsPerBatch);
ulong afterUserId = args.AfterUserId.GetValueOrDefault(0);
var limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxUserReactionsPerBatch);
var afterUserId = args.AfterUserId.GetValueOrDefault(0);


var ids = new BucketIds(channelId: channelId); var ids = new BucketIds(channelId: channelId);
Expression<Func<string>> endpoint = () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}?limit={limit}&after={afterUserId}"; Expression<Func<string>> endpoint = () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}?limit={limit}&after={afterUserId}";
@@ -1345,12 +1340,12 @@ namespace Discord.API
Preconditions.NotNullOrEmpty(response.Content, nameof(response.Content)); Preconditions.NotNullOrEmpty(response.Content, nameof(response.Content));


if (response.Content.IsSpecified && response.Content.Value.Length > DiscordConfig.MaxMessageSize) if (response.Content.IsSpecified && response.Content.Value.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(response.Content));
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(response));


options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


var ids = new BucketIds(); var ids = new BucketIds();
await SendMultipartAsync("POST", () => $"interactions/{interactionId}/{interactionToken}/callback", response.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
await SendMultipartAsync("POST", () => $"interactions/{interactionId}/{interactionToken}/callback", response.ToDictionary(), ids, options: options).ConfigureAwait(false);
} }
public async Task<Message> GetInteractionResponseAsync(string interactionToken, RequestOptions options = null) public async Task<Message> GetInteractionResponseAsync(string interactionToken, RequestOptions options = null)
{ {
@@ -1385,7 +1380,7 @@ namespace Discord.API
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content));


if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize) if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content));
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args));


options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


@@ -1401,12 +1396,12 @@ namespace Discord.API
Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content));


if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize) if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content));
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args));


options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


var ids = new BucketIds(); var ids = new BucketIds();
return await SendMultipartAsync<Message>("POST", () => $"webhooks/{CurrentApplicationId}/{token}?wait=true", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
return await SendMultipartAsync<Message>("POST", () => $"webhooks/{CurrentApplicationId}/{token}?wait=true", args.ToDictionary(), ids, options: options).ConfigureAwait(false);
} }


public async Task<Message> ModifyInteractionFollowupMessageAsync(ModifyInteractionResponseParams args, ulong id, string token, RequestOptions options = null) public async Task<Message> ModifyInteractionFollowupMessageAsync(ModifyInteractionResponseParams args, ulong id, string token, RequestOptions options = null)
@@ -1415,7 +1410,7 @@ namespace Discord.API
Preconditions.NotEqual(id, 0, nameof(id)); Preconditions.NotEqual(id, 0, nameof(id));


if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize) if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args.Content));
throw new ArgumentException(message: $"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", paramName: nameof(args));


options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


@@ -1540,7 +1535,7 @@ namespace Discord.API
Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(guildId, 0, nameof(guildId));
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.AtLeast(args.Days, 1, nameof(args.Days)); Preconditions.AtLeast(args.Days, 1, nameof(args.Days));
string endpointRoleIds = args.IncludeRoleIds?.Length > 0 ? $"&include_roles={string.Join(",", args.IncludeRoleIds)}" : "";
var endpointRoleIds = args.IncludeRoleIds?.Length > 0 ? $"&include_roles={string.Join(",", args.IncludeRoleIds)}" : "";
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


var ids = new BucketIds(guildId: guildId); var ids = new BucketIds(guildId: guildId);
@@ -1557,8 +1552,8 @@ namespace Discord.API
Preconditions.AtMost(args.Limit, DiscordConfig.MaxBansPerBatch, nameof(args.Limit)); Preconditions.AtMost(args.Limit, DiscordConfig.MaxBansPerBatch, nameof(args.Limit));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxBansPerBatch);
ulong? relativeId = args.RelativeUserId.IsSpecified ? args.RelativeUserId.Value : (ulong?)null;
var limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxBansPerBatch);
var relativeId = args.RelativeUserId.IsSpecified ? args.RelativeUserId.Value : (ulong?)null;
var relativeDir = args.RelativeDirection.GetValueOrDefault(Direction.Before) switch var relativeDir = args.RelativeDirection.GetValueOrDefault(Direction.Before) switch
{ {
Direction.After => "after", Direction.After => "after",
@@ -1602,7 +1597,7 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


var ids = new BucketIds(guildId: guildId); var ids = new BucketIds(guildId: guildId);
string reason = string.IsNullOrWhiteSpace(args.Reason) ? "" : $"&reason={Uri.EscapeDataString(args.Reason)}";
var reason = string.IsNullOrWhiteSpace(args.Reason) ? "" : $"&reason={Uri.EscapeDataString(args.Reason)}";
await SendAsync("PUT", () => $"guilds/{guildId}/bans/{userId}?delete_message_days={args.DeleteMessageDays}{reason}", ids, options: options).ConfigureAwait(false); await SendAsync("PUT", () => $"guilds/{guildId}/bans/{userId}?delete_message_days={args.DeleteMessageDays}{reason}", ids, options: options).ConfigureAwait(false);
} }
/// <exception cref="ArgumentException"><paramref name="guildId"/> and <paramref name="userId"/> must not be equal to zero.</exception> /// <exception cref="ArgumentException"><paramref name="guildId"/> and <paramref name="userId"/> must not be equal to zero.</exception>
@@ -1674,12 +1669,12 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


//Remove trailing slash //Remove trailing slash
if (inviteId[inviteId.Length - 1] == '/')
inviteId = inviteId.Substring(0, inviteId.Length - 1);
//Remove leading URL
int index = inviteId.LastIndexOf('/');
if (inviteId[^1] == '/')
inviteId = inviteId[0..^1];
//Remove leading URL
var index = inviteId.LastIndexOf('/');
if (index >= 0) if (index >= 0)
inviteId = inviteId.Substring(index + 1);
inviteId = inviteId[(index + 1)..];


try try
{ {
@@ -1794,8 +1789,8 @@ namespace Discord.API
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);
ulong afterUserId = args.AfterUserId.GetValueOrDefault(0);
var limit = args.Limit.GetValueOrDefault(int.MaxValue);
var afterUserId = args.AfterUserId.GetValueOrDefault(0);


var ids = new BucketIds(guildId: guildId); var ids = new BucketIds(guildId: guildId);
Expression<Func<string>> endpoint = () => $"guilds/{guildId}/members?limit={limit}&after={afterUserId}"; Expression<Func<string>> endpoint = () => $"guilds/{guildId}/members?limit={limit}&after={afterUserId}";
@@ -1818,7 +1813,7 @@ namespace Discord.API
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


bool isCurrentUser = userId == CurrentUserId;
var isCurrentUser = userId == CurrentUserId;


if (args.RoleIds.IsSpecified) if (args.RoleIds.IsSpecified)
Preconditions.NotEveryoneRole(args.RoleIds.Value, guildId, nameof(args.RoleIds)); Preconditions.NotEveryoneRole(args.RoleIds.Value, guildId, nameof(args.RoleIds));
@@ -1843,8 +1838,8 @@ namespace Discord.API
Preconditions.NotNullOrEmpty(args.Query, nameof(args.Query)); Preconditions.NotNullOrEmpty(args.Query, nameof(args.Query));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxUsersPerBatch);
string query = args.Query;
var limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxUsersPerBatch);
var query = args.Query;


var ids = new BucketIds(guildId: guildId); var ids = new BucketIds(guildId: guildId);
Expression<Func<string>> endpoint = () => $"guilds/{guildId}/members/search?limit={limit}&query={query}"; Expression<Func<string>> endpoint = () => $"guilds/{guildId}/members/search?limit={limit}&query={query}";
@@ -2034,8 +2029,8 @@ namespace Discord.API
Preconditions.AtMost(args.Limit, DiscordConfig.MaxMessagesPerBatch, nameof(args.Limit)); Preconditions.AtMost(args.Limit, DiscordConfig.MaxMessagesPerBatch, nameof(args.Limit));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxGuildEventUsersPerBatch);
ulong? relativeId = args.RelativeUserId.IsSpecified ? args.RelativeUserId.Value : (ulong?)null;
var limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxGuildEventUsersPerBatch);
var relativeId = args.RelativeUserId.IsSpecified ? args.RelativeUserId.Value : (ulong?)null;
var relativeDir = args.RelativeDirection.GetValueOrDefault(Direction.Before) switch var relativeDir = args.RelativeDirection.GetValueOrDefault(Direction.Before) switch
{ {
Direction.After => "after", Direction.After => "after",
@@ -2092,8 +2087,8 @@ namespace Discord.API
Preconditions.GreaterThan(args.AfterGuildId, 0, nameof(args.AfterGuildId)); Preconditions.GreaterThan(args.AfterGuildId, 0, nameof(args.AfterGuildId));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


int limit = args.Limit.GetValueOrDefault(int.MaxValue);
ulong afterGuildId = args.AfterGuildId.GetValueOrDefault(0);
var limit = args.Limit.GetValueOrDefault(int.MaxValue);
var afterGuildId = args.AfterGuildId.GetValueOrDefault(0);


return await SendAsync<IReadOnlyCollection<UserGuild>>("GET", () => $"users/@me/guilds?limit={limit}&after={afterGuildId}", new BucketIds(), options: options).ConfigureAwait(false); return await SendAsync<IReadOnlyCollection<UserGuild>>("GET", () => $"users/@me/guilds?limit={limit}&after={afterGuildId}", new BucketIds(), options: options).ConfigureAwait(false);
} }
@@ -2152,7 +2147,7 @@ namespace Discord.API
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


int limit = args.Limit.GetValueOrDefault(int.MaxValue);
var limit = args.Limit.GetValueOrDefault(int.MaxValue);


var ids = new BucketIds(guildId: guildId); var ids = new BucketIds(guildId: guildId);
Expression<Func<string>> endpoint; Expression<Func<string>> endpoint;
@@ -2175,7 +2170,7 @@ namespace Discord.API
} }


// Still use string interp for the query w/o params, as this is necessary for CreateBucketId // Still use string interp for the query w/o params, as this is necessary for CreateBucketId
endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}{queryArgs.ToString()}";
endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}{queryArgs}";
return await SendAsync<AuditLog>("GET", endpoint, ids, options: options).ConfigureAwait(false); return await SendAsync<AuditLog>("GET", endpoint, ids, options: options).ConfigureAwait(false);
} }
#endregion #endregion
@@ -2263,9 +2258,9 @@ namespace Discord.API
} }
protected T DeserializeJson<T>(Stream jsonStream) protected T DeserializeJson<T>(Stream jsonStream)
{ {
using (TextReader text = new StreamReader(jsonStream))
using (JsonReader reader = new JsonTextReader(text))
return _serializer.Deserialize<T>(reader);
using TextReader text = new StreamReader(jsonStream);
using JsonReader reader = new JsonTextReader(text);
return _serializer.Deserialize<T>(reader);
} }


protected async Task<T> NullifyNotFound<T>(Task<T> sendTask) where T : class protected async Task<T> NullifyNotFound<T>(Task<T> sendTask) where T : class
@@ -2357,7 +2352,7 @@ namespace Discord.API
var builder = new StringBuilder(); var builder = new StringBuilder();
var methodCall = endpoint.Body as MethodCallExpression; var methodCall = endpoint.Body as MethodCallExpression;
var methodArgs = methodCall.Arguments.ToArray(); var methodArgs = methodCall.Arguments.ToArray();
string format = (methodArgs[0] as ConstantExpression).Value as string;
var format = (methodArgs[0] as ConstantExpression).Value as string;


//Unpack the array, if one exists (happens with 4+ parameters) //Unpack the array, if one exists (happens with 4+ parameters)
if (methodArgs.Length > 1 && methodArgs[1].NodeType == ExpressionType.NewArrayInit) if (methodArgs.Length > 1 && methodArgs[1].NodeType == ExpressionType.NewArrayInit)
@@ -2368,24 +2363,24 @@ namespace Discord.API
Array.Copy(elements, 0, methodArgs, 1, elements.Length); Array.Copy(elements, 0, methodArgs, 1, elements.Length);
} }


int endIndex = format.IndexOf('?'); //Don't include params
var endIndex = format.IndexOf('?'); //Don't include params
if (endIndex == -1) if (endIndex == -1)
endIndex = format.Length; endIndex = format.Length;


int lastIndex = 0;
var lastIndex = 0;
while (true) while (true)
{ {
int leftIndex = format.IndexOf("{", lastIndex);
var leftIndex = format.IndexOf("{", lastIndex);
if (leftIndex == -1 || leftIndex > endIndex) if (leftIndex == -1 || leftIndex > endIndex)
{ {
builder.Append(format, lastIndex, endIndex - lastIndex); builder.Append(format, lastIndex, endIndex - lastIndex);
break; break;
} }
builder.Append(format, lastIndex, leftIndex - lastIndex); builder.Append(format, lastIndex, leftIndex - lastIndex);
int rightIndex = format.IndexOf("}", leftIndex);
var rightIndex = format.IndexOf("}", leftIndex);


int argId = int.Parse(format.Substring(leftIndex + 1, rightIndex - leftIndex - 1), NumberStyles.None, CultureInfo.InvariantCulture);
string fieldName = GetFieldName(methodArgs[argId + 1]);
var argId = int.Parse(format.AsSpan(leftIndex + 1, rightIndex - leftIndex - 1).ToString(), NumberStyles.None, CultureInfo.InvariantCulture);
var fieldName = GetFieldName(methodArgs[argId + 1]);


var mappedId = BucketIds.GetIndex(fieldName); var mappedId = BucketIds.GetIndex(fieldName);


@@ -2397,7 +2392,7 @@ namespace Discord.API


lastIndex = rightIndex + 1; lastIndex = rightIndex + 1;
} }
if (builder[builder.Length - 1] == '/')
if (builder[^1] == '/')
builder.Remove(builder.Length - 1, 1); builder.Remove(builder.Length - 1, 1);


format = builder.ToString(); format = builder.ToString();
@@ -2423,7 +2418,7 @@ namespace Discord.API


private static string WebhookQuery(bool wait = false, ulong? threadId = null) private static string WebhookQuery(bool wait = false, ulong? threadId = null)
{ {
List<string> querys = new List<string>() { };
List<string> querys = new() { };
if (wait) if (wait)
querys.Add("wait=true"); querys.Add("wait=true");
if (threadId.HasValue) if (threadId.HasValue)


+ 1
- 1
src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs View File

@@ -9,7 +9,7 @@ namespace Discord.Rest
internal static class AuditLogHelper internal static class AuditLogHelper
{ {
private static readonly Dictionary<ActionType, Func<BaseDiscordClient, Model, EntryModel, IAuditLogData>> CreateMapping private static readonly Dictionary<ActionType, Func<BaseDiscordClient, Model, EntryModel, IAuditLogData>> CreateMapping
= new Dictionary<ActionType, Func<BaseDiscordClient, Model, EntryModel, IAuditLogData>>()
= new()
{ {
[ActionType.GuildUpdated] = GuildUpdateAuditLogData.Create, [ActionType.GuildUpdated] = GuildUpdateAuditLogData.Create,




+ 1
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ThreadUpdateAuditLogData.cs View File

@@ -15,7 +15,7 @@ namespace Discord.Rest
Thread = thread; Thread = thread;
ThreadType = type; ThreadType = type;
Before = before; Before = before;
After = After;
After = after;
} }


internal static ThreadUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) internal static ThreadUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)


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

@@ -17,11 +17,11 @@ namespace Discord.Rest
public class RestGroupChannel : RestChannel, IGroupChannel, IRestPrivateChannel, IRestMessageChannel, IRestAudioChannel public class RestGroupChannel : RestChannel, IGroupChannel, IRestPrivateChannel, IRestMessageChannel, IRestAudioChannel
{ {
#region RestGroupChannel #region RestGroupChannel
private string _iconId;
private ImmutableDictionary<ulong, RestGroupUser> _users; private ImmutableDictionary<ulong, RestGroupUser> _users;


/// <inheritdoc /> /// <inheritdoc />
public string Name { get; private set; } public string Name { get; private set; }

/// <inheritdoc/> /// <inheritdoc/>
public string RTCRegion { get; private set; } public string RTCRegion { get; private set; }


@@ -43,8 +43,6 @@ namespace Discord.Rest
{ {
if (model.Name.IsSpecified) if (model.Name.IsSpecified)
Name = model.Name.Value; Name = model.Name.Value;
if (model.Icon.IsSpecified)
_iconId = model.Icon.Value;


if (model.Recipients.IsSpecified) if (model.Recipients.IsSpecified)
UpdateUsers(model.Recipients.Value); UpdateUsers(model.Recipients.Value);


+ 2
- 4
src/Discord.Net.Rest/Entities/Interactions/MessageComponents/RestMessageComponent.cs View File

@@ -142,9 +142,8 @@ namespace Discord.Rest
/// Updates the message which this component resides in with the type <see cref="InteractionResponseType.UpdateMessage"/> /// Updates the message which this component resides in with the type <see cref="InteractionResponseType.UpdateMessage"/>
/// </summary> /// </summary>
/// <param name="func">A delegate containing the properties to modify the message with.</param> /// <param name="func">A delegate containing the properties to modify the message with.</param>
/// <param name="options">The request options for this <see langword="async"/> request.</param>
/// <returns>A string that contains json to write back to the incoming http request.</returns> /// <returns>A string that contains json to write back to the incoming http request.</returns>
public string Update(Action<MessageProperties> func, RequestOptions options = null)
public string Update(Action<MessageProperties> func)
{ {
var args = new MessageProperties(); var args = new MessageProperties();
func(args); func(args);
@@ -384,11 +383,10 @@ namespace Discord.Rest
/// Defers an interaction and responds with type 5 (<see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>) /// Defers an interaction and responds with type 5 (<see cref="InteractionResponseType.DeferredChannelMessageWithSource"/>)
/// </summary> /// </summary>
/// <param name="ephemeral"><see langword="true"/> to send this message ephemerally, otherwise <see langword="false"/>.</param> /// <param name="ephemeral"><see langword="true"/> to send this message ephemerally, otherwise <see langword="false"/>.</param>
/// <param name="options">The request options for this <see langword="async"/> request.</param>
/// <returns> /// <returns>
/// A string that contains json to write back to the incoming http request. /// A string that contains json to write back to the incoming http request.
/// </returns> /// </returns>
public string DeferLoading(bool ephemeral = false, RequestOptions options = null)
public string DeferLoading(bool ephemeral = false)
{ {
if (!InteractionHelper.CanSendResponse(this)) if (!InteractionHelper.CanSendResponse(this))
throw new TimeoutException($"Cannot defer an interaction after {InteractionHelper.ResponseTimeLimit} seconds of no response/acknowledgement"); throw new TimeoutException($"Cannot defer an interaction after {InteractionHelper.ResponseTimeLimit} seconds of no response/acknowledgement");


+ 1
- 2
src/Discord.Net.Rest/Entities/Interactions/SlashCommands/RestAutocompleteInteraction.cs View File

@@ -49,11 +49,10 @@ namespace Discord.Rest
/// there is no choices for their autocompleted input. /// there is no choices for their autocompleted input.
/// </remarks> /// </remarks>
/// </param> /// </param>
/// <param name="options">The request options for this response.</param>
/// <returns> /// <returns>
/// A string that contains json to write back to the incoming http request. /// A string that contains json to write back to the incoming http request.
/// </returns> /// </returns>
public string Respond(IEnumerable<AutocompleteResult> result, RequestOptions options = null)
public string Respond(IEnumerable<AutocompleteResult> result)
{ {
if (!InteractionHelper.CanSendResponse(this)) if (!InteractionHelper.CanSendResponse(this))
throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!"); throw new TimeoutException($"Cannot respond to an interaction after {InteractionHelper.ResponseTimeLimit} seconds!");


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

@@ -24,7 +24,7 @@ namespace Discord.Net.Queue
private CancellationToken _requestCancelToken; //Parent token + Clear token private CancellationToken _requestCancelToken; //Parent token + Clear token
private DateTimeOffset _waitUntil; private DateTimeOffset _waitUntil;


private Task _cleanupTask;
private readonly Task _cleanupTask;


public RequestQueue() public RequestQueue()
{ {
@@ -42,7 +42,7 @@ namespace Discord.Net.Queue


public async Task SetCancelTokenAsync(CancellationToken cancelToken) public async Task SetCancelTokenAsync(CancellationToken cancelToken)
{ {
await _tokenLock.WaitAsync().ConfigureAwait(false);
await _tokenLock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
try try
{ {
_parentToken = cancelToken; _parentToken = cancelToken;


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

@@ -2242,7 +2242,7 @@ namespace Discord.WebSocket
//Only strip out the port if the endpoint contains it //Only strip out the port if the endpoint contains it
var portBegin = endpoint.LastIndexOf(':'); var portBegin = endpoint.LastIndexOf(':');
if (portBegin > 0) if (portBegin > 0)
endpoint = endpoint.Substring(0, portBegin);
endpoint = endpoint[..portBegin];


var _ = guild.FinishConnectAudio(endpoint, data.Token).ConfigureAwait(false); var _ = guild.FinishConnectAudio(endpoint, data.Token).ConfigureAwait(false);
} }
@@ -2978,7 +2978,7 @@ namespace Discord.WebSocket
{ {
return SocketDMChannel.Create(this, state, channelId, model); return SocketDMChannel.Create(this, state, channelId, model);
} }
internal SocketDMChannel CreateDMChannel(ulong channelId, SocketUser user, ClientState state)
internal SocketDMChannel CreateDMChannel(ulong channelId, SocketUser user, ClientState _)
{ {
return new SocketDMChannel(this, channelId, user); return new SocketDMChannel(this, channelId, user);
} }


+ 1
- 9
src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs View File

@@ -33,7 +33,6 @@ namespace Discord.WebSocket
public class SocketGuild : SocketEntity<ulong>, IGuild, IDisposable public class SocketGuild : SocketEntity<ulong>, IGuild, IDisposable
{ {
#region SocketGuild #region SocketGuild
#pragma warning disable IDISP002, IDISP006
private readonly SemaphoreSlim _audioLock; private readonly SemaphoreSlim _audioLock;
private TaskCompletionSource<bool> _syncPromise, _downloaderPromise; private TaskCompletionSource<bool> _syncPromise, _downloaderPromise;
private TaskCompletionSource<AudioClient> _audioConnectPromise; private TaskCompletionSource<AudioClient> _audioConnectPromise;
@@ -47,7 +46,6 @@ namespace Discord.WebSocket


private AudioClient _audioClient; private AudioClient _audioClient;
private VoiceStateUpdateParams _voiceStateUpdateParams; private VoiceStateUpdateParams _voiceStateUpdateParams;
#pragma warning restore IDISP002, IDISP006


/// <inheritdoc /> /// <inheritdoc />
public string Name { get; private set; } public string Name { get; private set; }
@@ -585,7 +583,7 @@ namespace Discord.WebSocket
// _ = _downloaderPromise.TrySetResultAsync(true); // _ = _downloaderPromise.TrySetResultAsync(true);
}*/ }*/


internal void Update(ClientState state, EmojiUpdateModel model)
internal void Update(ClientState _, EmojiUpdateModel model)
{ {
var emotes = ImmutableArray.CreateBuilder<GuildEmote>(model.Emojis.Length); var emotes = ImmutableArray.CreateBuilder<GuildEmote>(model.Emojis.Length);
for (int i = 0; i < model.Emojis.Length; i++) for (int i = 0; i < model.Emojis.Length; i++)
@@ -1607,11 +1605,9 @@ namespace Discord.WebSocket


if (external) if (external)
{ {
#pragma warning disable IDISP001
var _ = promise.TrySetResultAsync(null); var _ = promise.TrySetResultAsync(null);
await Discord.ApiClient.SendVoiceStateUpdateAsync(_voiceStateUpdateParams).ConfigureAwait(false); await Discord.ApiClient.SendVoiceStateUpdateAsync(_voiceStateUpdateParams).ConfigureAwait(false);
return null; return null;
#pragma warning restore IDISP001
} }


if (_audioClient == null) if (_audioClient == null)
@@ -1634,14 +1630,10 @@ namespace Discord.WebSocket
}; };
audioClient.Connected += () => audioClient.Connected += () =>
{ {
#pragma warning disable IDISP001
var _ = promise.TrySetResultAsync(_audioClient); var _ = promise.TrySetResultAsync(_audioClient);
#pragma warning restore IDISP001
return Task.Delay(0); return Task.Delay(0);
}; };
#pragma warning disable IDISP003
_audioClient = audioClient; _audioClient = audioClient;
#pragma warning restore IDISP003
} }


await Discord.ApiClient.SendVoiceStateUpdateAsync(_voiceStateUpdateParams).ConfigureAwait(false); await Discord.ApiClient.SendVoiceStateUpdateAsync(_voiceStateUpdateParams).ConfigureAwait(false);


+ 4
- 4
src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs View File

@@ -143,7 +143,7 @@ namespace Discord.WebSocket
{ {
var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model)); var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model));
entity.Update(state, model); entity.Update(state, model);
entity.UpdateRoles(new ulong[0]);
entity.UpdateRoles(Array.Empty<ulong>());
return entity; return entity;
} }
internal static SocketGuildUser Create(SocketGuild guild, ClientState state, MemberModel model) internal static SocketGuildUser Create(SocketGuild guild, ClientState state, MemberModel model)
@@ -151,7 +151,7 @@ namespace Discord.WebSocket
var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model.User)); var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model.User));
entity.Update(state, model); entity.Update(state, model);
if (!model.Roles.IsSpecified) if (!model.Roles.IsSpecified)
entity.UpdateRoles(new ulong[0]);
entity.UpdateRoles(Array.Empty<ulong>());
return entity; return entity;
} }
internal static SocketGuildUser Create(SocketGuild guild, ClientState state, PresenceModel model) internal static SocketGuildUser Create(SocketGuild guild, ClientState state, PresenceModel model)
@@ -159,7 +159,7 @@ namespace Discord.WebSocket
var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model.User)); var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model.User));
entity.Update(state, model, false); entity.Update(state, model, false);
if (!model.Roles.IsSpecified) if (!model.Roles.IsSpecified)
entity.UpdateRoles(new ulong[0]);
entity.UpdateRoles(Array.Empty<ulong>());
return entity; return entity;
} }
internal void Update(ClientState state, MemberModel model) internal void Update(ClientState state, MemberModel model)
@@ -180,7 +180,7 @@ namespace Discord.WebSocket
if (model.Pending.IsSpecified) if (model.Pending.IsSpecified)
IsPending = model.Pending.Value; IsPending = model.Pending.Value;
} }
internal void Update(ClientState state, PresenceModel model, bool updatePresence)
internal void Update(ClientState _, PresenceModel model, bool updatePresence)
{ {
if (updatePresence) if (updatePresence)
{ {


+ 6
- 6
test/Discord.Net.Tests.Integration/ChannelsTests.cs View File

@@ -13,20 +13,20 @@ namespace Discord
[CollectionDefinition("ChannelsTests", DisableParallelization = true)] [CollectionDefinition("ChannelsTests", DisableParallelization = true)]
public class ChannelsTests : IClassFixture<RestGuildFixture> public class ChannelsTests : IClassFixture<RestGuildFixture>
{ {
private IGuild guild;
private readonly ITestOutputHelper output;
private readonly IGuild guild;
private readonly ITestOutputHelper _output;


public ChannelsTests(RestGuildFixture guildFixture, ITestOutputHelper output) public ChannelsTests(RestGuildFixture guildFixture, ITestOutputHelper output)
{ {
guild = guildFixture.Guild; guild = guildFixture.Guild;
output = output;
output.WriteLine($"RestGuildFixture using guild: {guild.Id}");
_output = output;
_output.WriteLine($"RestGuildFixture using guild: {guild.Id}");
// capture all console output // capture all console output
guildFixture.Client.Log += LogAsync; guildFixture.Client.Log += LogAsync;
} }
private Task LogAsync(LogMessage message) private Task LogAsync(LogMessage message)
{ {
output.WriteLine(message.ToString());
_output.WriteLine(message.ToString());
return Task.CompletedTask; return Task.CompletedTask;
} }


@@ -100,7 +100,7 @@ namespace Discord
public async Task ModifyChannelCategories() public async Task ModifyChannelCategories()
{ {
// util method for checking if a category is set // util method for checking if a category is set
async Task CheckAsync(INestedChannel channel, ICategoryChannel cat)
static async Task CheckAsync(INestedChannel channel, ICategoryChannel cat)
{ {
// check that the category is not set // check that the category is not set
if (cat == null) if (cat == null)


+ 6
- 6
test/Discord.Net.Tests.Integration/GuildTests.cs View File

@@ -10,21 +10,21 @@ namespace Discord
[CollectionDefinition("GuildTests", DisableParallelization = true)] [CollectionDefinition("GuildTests", DisableParallelization = true)]
public class GuildTests : IClassFixture<RestGuildFixture> public class GuildTests : IClassFixture<RestGuildFixture>
{ {
private IDiscordClient client;
private IGuild guild;
private readonly ITestOutputHelper output;
private readonly IDiscordClient client;
private readonly IGuild guild;
private readonly ITestOutputHelper _output;


public GuildTests(RestGuildFixture guildFixture, ITestOutputHelper output) public GuildTests(RestGuildFixture guildFixture, ITestOutputHelper output)
{ {
client = guildFixture.Client; client = guildFixture.Client;
guild = guildFixture.Guild; guild = guildFixture.Guild;
output = output;
output.WriteLine($"RestGuildFixture using guild: {guild.Id}");
_output = output;
_output.WriteLine($"RestGuildFixture using guild: {guild.Id}");
guildFixture.Client.Log += LogAsync; guildFixture.Client.Log += LogAsync;
} }
private Task LogAsync(LogMessage message) private Task LogAsync(LogMessage message)
{ {
output.WriteLine(message.ToString());
_output.WriteLine(message.ToString());
return Task.CompletedTask; return Task.CompletedTask;
} }
/// <summary> /// <summary>


+ 1
- 0
test/Discord.Net.Tests.Unit/ColorTests.cs View File

@@ -10,6 +10,7 @@ namespace Discord
/// </summary> /// </summary>
public class ColorTests public class ColorTests
{ {
[Fact]
public void Color_New() public void Color_New()
{ {
Assert.Equal(0u, new Color().RawValue); Assert.Equal(0u, new Color().RawValue);


Loading…
Cancel
Save