From 24e9321ac75dee156408350a7dab20a5788d4bb6 Mon Sep 17 00:00:00 2001
From: Plerx <50530305+Plerx2493@users.noreply.github.com>
Date: Fri, 9 May 2025 19:27:39 +0200
Subject: [PATCH 1/2] feat: reintroduce user, guild and channel directly on the
VoiceStateUpdatedEventArgs
---
.../Entities/Voice/DiscordVoiceState.cs | 3 +--
.../Voice/VoiceStateUpdatedEventArgs.cs | 24 +++++++++++++++++++
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/DSharpPlus/Entities/Voice/DiscordVoiceState.cs b/DSharpPlus/Entities/Voice/DiscordVoiceState.cs
index e890dad2f..30f5871b4 100644
--- a/DSharpPlus/Entities/Voice/DiscordVoiceState.cs
+++ b/DSharpPlus/Entities/Voice/DiscordVoiceState.cs
@@ -93,8 +93,7 @@ public class DiscordVoiceState
///
/// Gets the guild associated with this voice state.
///
- ///
- ///
+ /// Returns the guild associated with this voicestate
public async ValueTask GetGuildAsync(bool skipCache = false)
{
if (this.GuildId is null)
diff --git a/DSharpPlus/EventArgs/Voice/VoiceStateUpdatedEventArgs.cs b/DSharpPlus/EventArgs/Voice/VoiceStateUpdatedEventArgs.cs
index 7e6a921b8..59168a0d6 100644
--- a/DSharpPlus/EventArgs/Voice/VoiceStateUpdatedEventArgs.cs
+++ b/DSharpPlus/EventArgs/Voice/VoiceStateUpdatedEventArgs.cs
@@ -1,3 +1,4 @@
+using System.Threading.Tasks;
using DSharpPlus.Entities;
namespace DSharpPlus.EventArgs;
@@ -7,6 +8,29 @@ namespace DSharpPlus.EventArgs;
///
public class VoiceStateUpdatedEventArgs : DiscordEventArgs
{
+ ///
+ /// Gets the member associated with this voice state.
+ ///
+ /// Whether to skip the cache and always fetch the member from the API.
+ /// Returns the member associated with this voice state. Null if the voice state is not associated with a guild.
+ public async ValueTask GetUserAsync(bool skipCache = false)
+ => await this.After.GetUserAsync(skipCache);
+
+ ///
+ /// Gets the guild associated with this voice state.
+ ///
+ /// Returns the guild associated with this voicestate
+ public async ValueTask GetGuildAsync(bool skipCache = false)
+ => await this.After.GetGuildAsync(skipCache);
+
+ ///
+ /// Gets the channel associated with this voice state.
+ ///
+ /// Whether to skip the cache and always fetch the channel from the API.
+ /// Returns the channel associated with this voice state. Null if the voice state is not associated with a guild.
+ public async ValueTask GetChannelAsync(bool skipCache = false)
+ => await this.After.GetChannelAsync(skipCache);
+
///
/// Gets the voice state pre-update.
///
From 430a89dd121f5f6dfaab3e5b8290b5cdb171a495 Mon Sep 17 00:00:00 2001
From: Plerx <50530305+Plerx2493@users.noreply.github.com>
Date: Sat, 17 May 2025 18:41:59 +0200
Subject: [PATCH 2/2] apply gitattributes
---
.editorconfig | 478 +-
.gitattributes | 9 +
.github/workflows/build-commit.yml | 224 +-
.github/workflows/build-pr.yml | 94 +-
.github/workflows/release.yml | 182 +-
.gitignore | 980 +-
DSharpPlus.Commands/AbstractContext.cs | 40 +-
.../ChannelTypesAttribute.cs | 36 +-
.../ArgumentModifiers/FromCode/CodeType.cs | 50 +-
.../FromCode/FromCodeAttribute.cs | 42 +-
.../MinMaxLengthAttribute.cs | 84 +-
.../ArgumentModifiers/MinMaxValueAttribute.cs | 120 +-
.../RemainingTextAttribute.cs | 12 +-
DSharpPlus.Commands/CommandAttribute.cs | 60 +-
DSharpPlus.Commands/CommandContext.cs | 276 +-
DSharpPlus.Commands/CommandsConfiguration.cs | 68 +-
DSharpPlus.Commands/CommandsExtension.cs | 1186 +-
.../ContextChecks/ContextCheckAttribute.cs | 12 +-
.../ContextChecks/ContextCheckFailedData.cs | 26 +-
.../ContextChecks/ContextCheckMapEntry.cs | 36 +-
.../DirectMessageUsageAttribute.cs | 32 +-
.../ContextChecks/DirectMessageUsageCheck.cs | 62 +-
.../ContextChecks/IContextCheck.cs | 50 +-
.../ParameterChecks/IParameterCheck.cs | 52 +-
.../ParameterCheckAttribute.cs | 18 +-
.../ParameterCheckFailedData.cs | 48 +-
.../ParameterChecks/ParameterCheckInfo.cs | 20 +-
.../ParameterChecks/ParameterCheckMapEntry.cs | 36 +-
.../ParameterChecks/RequireHierarchyCheck.cs | 80 +-
.../RequireHigherBotHierarchyAttribute.cs | 14 +-
.../RequireHigherUserHierarchyAttribute.cs | 14 +-
.../RequireApplicationOwnerAttribute.cs | 12 +-
.../RequireApplicationOwnerCheck.cs | 26 +-
.../ContextChecks/RequireGuildAttribute.cs | 12 +-
.../ContextChecks/RequireGuildCheck.cs | 22 +-
.../ContextChecks/RequireNsfwAttribute.cs | 12 +-
.../ContextChecks/RequireNsfwCheck.cs | 24 +-
.../RequirePermissionsAttribute.cs | 38 +-
.../ContextChecks/RequirePermissionsCheck.cs | 74 +-
.../UnconditionalCheckAttribute.cs | 18 +-
.../Converters/BooleanConverter.cs | 42 +-
.../Converters/ByteConverter.cs | 38 +-
.../Converters/ConverterContext.cs | 166 +-
.../Converters/ConverterDelegate`1.cs | 12 +-
.../Converters/DateTimeOffsetConverter.cs | 40 +-
.../Converters/DiscordAttachmentConverter.cs | 134 +-
.../Converters/DiscordChannelConverter.cs | 152 +-
.../Converters/DiscordEmojiConverter.cs | 46 +-
.../Converters/DiscordMemberConverter.cs | 152 +-
.../Converters/DiscordMessageConverter.cs | 228 +-
.../Converters/DiscordRoleConverter.cs | 128 +-
.../DiscordSnowflakeObjectConverter.cs | 80 +-
.../DiscordThreadChannelConverter.cs | 124 +-
.../Converters/DiscordUserConverter.cs | 158 +-
.../Converters/DoubleConverter.cs | 38 +-
.../Converters/EnumArgumentConverter.cs | 76 +-
.../Converters/FloatConverter.cs | 38 +-
.../Converters/IArgumentConverter.cs | 94 +-
.../Converters/Int16Converter.cs | 38 +-
.../Converters/Int32Converter.cs | 38 +-
.../Converters/Int64Converter.cs | 44 +-
.../Results/ArgumentFailedConversionResult.cs | 38 +-
.../Results/ArgumentNotParsedResult.cs | 12 +-
.../Converters/SByteConverter.cs | 38 +-
.../Converters/StringConverter.cs | 128 +-
.../Converters/TimeSpanConverter.cs | 152 +-
.../Converters/UInt16Converter.cs | 38 +-
.../Converters/UInt32Converter.cs | 38 +-
.../Converters/UInt64Converter.cs | 44 +-
DSharpPlus.Commands/DefaultCommandExecutor.cs | 732 +-
.../EventArgs/CommandErroredEventArgs.cs | 22 +-
.../EventArgs/CommandExecutedEventArgs.cs | 18 +-
.../EventArgs/ConfigureCommandsEventArgs.cs | 38 +-
.../Exceptions/ChecksFailedException.cs | 34 +-
.../CommandNotExecutableException.cs | 30 +-
.../Exceptions/CommandNotFoundException.cs | 28 +-
.../Exceptions/CommandsException.cs | 20 +-
.../ParameterChecksFailedException.cs | 34 +-
.../Exceptions/ParseArgumentException.cs | 70 +-
DSharpPlus.Commands/ExtensionMethods.cs | 166 +-
DSharpPlus.Commands/ICommandExecutor.cs | 30 +-
.../Invocation/AnonymousDelegateUtil.cs | 62 +-
.../Invocation/CommandEmitUtil.cs | 268 +-
DSharpPlus.Commands/ParameterAttribute.cs | 60 +-
.../BaseCommandLogging.cs | 26 +-
...mmandProcessor.ConverterDelegateFactory.cs | 372 +-
.../BaseCommandProcessor.cs | 804 +-
.../BaseCommandProcessor/ICommandProcessor.cs | 68 +-
.../MessageCommands/MessageCommandContext.cs | 16 +-
.../MessageCommands/MessageCommandLogging.cs | 28 +-
.../MessageCommandProcessor.cs | 394 +-
.../ChoiceDisplayNameAttribute.cs | 24 +-
.../SlashAutoCompleteProviderAttribute.cs | 142 +-
.../SlashChoiceProviderAttribute.cs | 182 +-
.../SlashCommands/AutoCompleteContext.cs | 28 +-
.../SlashCommands/ISlashArgumentConverter.cs | 22 +-
.../InteractionConverterContext.cs | 110 +-
.../IInteractionNamingPolicy.cs | 66 +-
.../KebabCaseNamingPolicy.cs | 96 +-
.../LowercaseNamingPolicy.cs | 94 +-
.../SnakeCaseNamingPolicy.cs | 96 +-
.../SlashCommands/InteractionTypes.cs | 20 +-
.../Localization/DiscordLocale.cs | 78 +-
.../Localization/IInteractionLocalizer.cs | 18 +-
.../InteractionLocalizerAttribute.cs | 30 +-
.../Localization/LocalesHelper.cs | 188 +-
.../InteractionAllowedContextsAttribute.cs | 34 +-
.../InteractionInstallTypeAttribute.cs | 34 +-
.../SlashCommandConfiguration.cs | 92 +-
.../SlashCommands/SlashCommandContext.cs | 322 +-
.../SlashCommandProcessor.Registration.cs | 1422 +-
.../Processors/SlashCommands/SlashLogging.cs | 28 +-
.../ContextChecks/TextChannelTypesCheck.cs | 56 +-
.../TextMessageReplyAttribute.cs | 20 +-
.../ContextChecks/TextMessageReplyCheck.cs | 26 +-
.../ContextChecks/TextMinMaxLengthCheck.cs | 60 +-
.../ContextChecks/TextMinMaxValueCheck.cs | 136 +-
.../TextCommands/ConverterRequiresText.cs | 54 +-
.../TextCommands/ITextArgumentConverter.cs | 20 +-
.../Parsing/DefaultPrefixResolver.cs | 122 +-
.../Parsing/DefaultTextArgumentSplicer.cs | 238 +-
.../TextCommands/Parsing/IPrefixResolver.cs | 36 +-
.../TextCommands/TextCommandConfiguration.cs | 58 +-
.../TextCommands/TextCommandContext.cs | 298 +-
.../TextCommands/TextCommandProcessor.cs | 754 +-
.../TextCommands/TextConverterContext.cs | 286 +-
.../Processors/TextCommands/TextLogging.cs | 22 +-
.../UserCommands/UserCommandContext.cs | 16 +-
.../UserCommands/UserCommandLogging.cs | 28 +-
.../UserCommands/UserCommandProcessor.cs | 394 +-
.../Properties/AssemblyProperties.cs | 6 +-
.../RegisterToGuildsAttribute.cs | 54 +-
DSharpPlus.Commands/Trees/Command.cs | 118 +-
DSharpPlus.Commands/Trees/CommandBuilder.cs | 576 +-
DSharpPlus.Commands/Trees/CommandParameter.cs | 56 +-
.../Trees/CommandParameterBuilder.cs | 322 +-
.../Trees/Metadata/AllowDMUsageAttribute.cs | 12 +-
.../Metadata/AllowedProcessorsAttribute.cs | 204 +-
.../Metadata/DefaultGroupCommandAttribute.cs | 12 +-
.../Trees/Metadata/TextAliasAttribute.cs | 18 +-
.../Attributes/AliasesAttribute.cs | 64 +-
.../Attributes/CategoryAttribute.cs | 38 +-
.../Attributes/CheckBaseAttribute.cs | 38 +-
.../Attributes/CommandAttribute.cs | 106 +-
.../Attributes/CooldownAttribute.cs | 660 +-
.../Attributes/DescriptionAttribute.cs | 42 +-
.../Attributes/DontInjectAttribute.cs | 20 +-
.../Attributes/GroupAttribute.cs | 80 +-
.../Attributes/HiddenAttribute.cs | 20 +-
.../Attributes/ModuleLifespanAttribute.cs | 74 +-
.../Attributes/PriorityAttribute.cs | 42 +-
.../Attributes/RemainingTextAttribute.cs | 20 +-
.../RequireBotPermissionsAttribute.cs | 112 +-
.../RequireDirectMessageAttribute.cs | 38 +-
.../Attributes/RequireGuildAttribute.cs | 36 +-
.../Attributes/RequireNsfwAttribute.cs | 28 +-
.../Attributes/RequireOwnerAttribute.cs | 40 +-
.../Attributes/RequirePermissionsAttribute.cs | 144 +-
.../Attributes/RequirePrefixesAttribute.cs | 80 +-
.../RequireReferencedMessageAttribute.cs | 36 +-
.../Attributes/RequireRolesAttribute.cs | 282 +-
.../RequireUserPermissionsAttribute.cs | 112 +-
DSharpPlus.CommandsNext/BaseCommandModule.cs | 50 +-
.../CommandsNextConfiguration.cs | 282 +-
DSharpPlus.CommandsNext/CommandsNextEvents.cs | 38 +-
.../CommandsNextExtension.cs | 2438 +-
.../CommandsNextUtilities.cs | 878 +-
.../Converters/ArgumentBindingResult.cs | 56 +-
.../Converters/BaseHelpFormatter.cs | 92 +-
.../Converters/DefaultHelpFormatter.cs | 230 +-
.../Converters/EntityConverters.cs | 658 +-
.../Converters/EnumConverter.cs | 48 +-
.../Converters/HelpFormatterFactory.cs | 36 +-
.../Converters/IArgumentConverter.cs | 50 +-
.../Converters/NullableConverter.cs | 60 +-
.../Converters/NumericConverters.cs | 178 +-
.../Converters/StringConverter.cs | 62 +-
.../Converters/TimeConverters.cs | 120 +-
.../Entities/Builders/CommandBuilder.cs | 594 +-
.../Entities/Builders/CommandGroupBuilder.cs | 140 +-
.../Entities/Builders/CommandModuleBuilder.cs | 124 +-
.../Builders/CommandOverloadBuilder.cs | 324 +-
DSharpPlus.CommandsNext/Entities/Command.cs | 414 +-
.../Entities/CommandArgument.cs | 94 +-
.../Entities/CommandGroup.cs | 160 +-
.../Entities/CommandHelpMessage.cs | 60 +-
.../Entities/CommandModule.cs | 158 +-
.../Entities/CommandOverload.cs | 58 +-
.../Entities/CommandResult.cs | 48 +-
.../EventArgs/CommandContext.cs | 308 +-
.../EventArgs/CommandErrorEventArgs.cs | 22 +-
.../EventArgs/CommandEventArgs.cs | 40 +-
.../EventArgs/CommandExecutionEventArgs.cs | 28 +-
.../Exceptions/ChecksFailedException.cs | 82 +-
.../Exceptions/CommandNotFoundException.cs | 54 +-
.../Exceptions/DuplicateCommandException.cs | 54 +-
.../Exceptions/DuplicateOverloadException.cs | 86 +-
.../Exceptions/InvalidOverloadException.cs | 104 +-
.../Executors/AsynchronousCommandExecutor.cs | 38 +-
.../Executors/ICommandExecutor.cs | 34 +-
.../ParallelQueuedCommandExecutor.cs | 160 +-
.../Executors/SynchronousCommandExecutor.cs | 32 +-
DSharpPlus.CommandsNext/ExtensionMethods.cs | 122 +-
.../Enums/ButtonDisableBehavior.cs | 62 +-
.../Enums/ButtonPaginationBehavior.cs | 50 +-
.../Enums/InteractionResponseBehavior.cs | 36 +-
.../Enums/PaginationBehaviour.cs | 38 +-
.../Enums/PaginationButtonType.cs | 68 +-
.../Enums/PaginationDeletion.cs | 46 +-
.../Enums/PollBehaviour.cs | 36 +-
DSharpPlus.Interactivity/Enums/SplitType.cs | 36 +-
.../ComponentBased/ComponentEventWaiter.cs | 320 +-
.../ComponentBased/ComponentPaginator.cs | 278 +-
.../ComponentBased/PaginationButtons.cs | 80 +-
.../Requests/ButtonPaginationRequest.cs | 362 +-
.../Requests/ComponentCollectRequest.cs | 38 +-
.../Requests/ComponentMatchRequest.cs | 72 +-
.../Requests/InteractionPaginationRequest.cs | 390 +-
.../EventHandling/EventWaiter.cs | 268 +-
.../EventHandling/IPaginator.cs | 36 +-
.../EventHandling/ModalEventWaiter.cs | 136 +-
.../EventHandling/Paginator.cs | 552 +-
.../EventHandling/Poller.cs | 252 +-
.../EventHandling/ReactionCollector.cs | 402 +-
.../EventHandling/Requests/CollectRequest.cs | 122 +-
.../Requests/IPaginationRequest.cs | 160 +-
.../EventHandling/Requests/MatchRequest.cs | 92 +-
.../Requests/ModalMatchRequest.cs | 90 +-
.../Requests/PaginationEmojis.cs | 170 +-
.../Requests/PaginationRequest.cs | 390 +-
.../EventHandling/Requests/PollRequest.cs | 204 +-
.../Extensions/ChannelExtensions.cs | 232 +-
.../Extensions/ClientExtensions.cs | 114 +-
.../Extensions/InteractionExtensions.cs | 72 +-
.../Extensions/MessageExtensions.cs | 444 +-
.../InteractivityConfiguration.cs | 190 +-
.../InteractivityEvents.cs | 68 +-
.../InteractivityExtension.cs | 2348 +-
.../InteractivityResult.cs | 48 +-
DSharpPlus.Rest/DiscordRestClient.cs | 4750 ++--
DSharpPlus.Tests/Commands/.editorconfig | 8 +-
.../Commands/TestMultiLevelSubCommands.cs | 70 +-
.../Commands/TestSingleLevelSubCommands.cs | 44 +-
.../Cases/Commands/TestTopLevelCommands.cs | 42 +-
DSharpPlus.Tests/Commands/Cases/UserInput.cs | 100 +-
.../Commands/CommandFiltering/Tests.cs | 394 +-
.../DefaultTextArgumentSplicerTests.cs | 262 +-
.../Commands/Trees/CommandBuilderTests.cs | 220 +-
DSharpPlus.Tests/DSharpPlus.Tests.csproj | 36 +-
DSharpPlus.VoiceNext/AudioFormat.cs | 252 +-
DSharpPlus.VoiceNext/Codec/Helpers.cs | 60 +-
DSharpPlus.VoiceNext/Codec/Interop.cs | 524 +-
DSharpPlus.VoiceNext/Codec/Opus.cs | 430 +-
DSharpPlus.VoiceNext/Codec/Rtp.cs | 156 +-
DSharpPlus.VoiceNext/Codec/Sodium.cs | 362 +-
.../DiscordClientExtensions.cs | 186 +-
DSharpPlus.VoiceNext/Entities/AudioSender.cs | 224 +-
.../Entities/VoiceDispatch.cs | 36 +-
.../Entities/VoiceIdentifyPayload.cs | 36 +-
DSharpPlus.VoiceNext/Entities/VoicePacket.cs | 34 +-
.../Entities/VoiceReadyPayload.cs | 44 +-
.../Entities/VoiceSelectProtocolPayload.cs | 24 +-
.../VoiceSelectProtocolPayloadData.cs | 30 +-
.../Entities/VoiceServerUpdatePayload.cs | 30 +-
.../VoiceSessionDescriptionPayload.cs | 24 +-
.../Entities/VoiceSpeakingPayload.cs | 36 +-
.../Entities/VoiceStateUpdatePayload.cs | 48 +-
.../Entities/VoiceUserJoinPayload.cs | 24 +-
.../Entities/VoiceUserLeavePayload.cs | 18 +-
.../EventArgs/VoiceReceiveEventArgs.cs | 100 +-
.../EventArgs/VoiceUserJoinEventArgs.cs | 44 +-
.../EventArgs/VoiceUserLeaveEventArgs.cs | 44 +-
DSharpPlus.VoiceNext/IVoiceFilter.cs | 34 +-
DSharpPlus.VoiceNext/RawVoicePacket.cs | 46 +-
DSharpPlus.VoiceNext/StreamExtensions.cs | 94 +-
DSharpPlus.VoiceNext/VoiceApplication.cs | 46 +-
.../VoiceNextConfiguration.cs | 84 +-
DSharpPlus.VoiceNext/VoiceNextConnection.cs | 2224 +-
DSharpPlus.VoiceNext/VoiceNextEvents.cs | 118 +-
DSharpPlus.VoiceNext/VoiceNextExtension.cs | 472 +-
DSharpPlus.VoiceNext/VoiceTransmitSink.cs | 470 +-
DSharpPlus/AnsiColor.cs | 72 +-
DSharpPlus/AsyncEvents/AsyncEvent.cs | 38 +-
DSharpPlus/AsyncEvents/AsyncEventArgs.cs | 12 +-
DSharpPlus/AsyncEvents/AsyncEventHandler.cs | 78 +-
DSharpPlus/AsyncEvents/AsyncEvent`2.cs | 210 +-
DSharpPlus/AsyncManualResetEvent.cs | 94 +-
DSharpPlus/Clients/BaseDiscordClient.cs | 364 +-
DSharpPlus/Clients/DiscordClient.Dispatch.cs | 6082 ++--
DSharpPlus/Clients/DiscordClient.cs | 2692 +-
DSharpPlus/Clients/DiscordWebhookClient.cs | 550 +-
DSharpPlus/Clients/NullShardOrchestrator.cs | 110 +-
DSharpPlus/DiscordConfiguration.cs | 142 +-
DSharpPlus/DiscordIntents.cs | 392 +-
.../Application/DiscordApplication.cs | 1280 +-
.../Application/DiscordApplicationAsset.cs | 166 +-
.../DiscordApplicationAssetType.cs | 46 +-
.../Application/DiscordApplicationFlags.cs | 118 +-
.../DiscordApplicationUpdateType.cs | 44 +-
.../Entities/Application/DiscordAsset.cs | 32 +-
.../Entities/Application/DiscordOAuthScope.cs | 302 +-
.../Application/DiscordSpotifyAsset.cs | 46 +-
.../AuditLogs/AuditLogActionCategory.cs | 102 +-
.../Entities/AuditLogs/AuditLogActionType.cs | 608 +-
.../Entities/AuditLogs/AuditLogParser.cs | 3200 +--
.../AuditLogs/DiscordAuditLogEntry.cs | 102 +-
...ditLogApplicationCommandPermissionEntry.cs | 88 +-
...cordAuditLogAutoModerationExecutedEntry.cs | 106 +-
.../DiscordAuditLogAutoModerationRuleEntry.cs | 230 +-
.../Entries/DiscordAuditLogBanEntry.cs | 70 +-
.../Entries/DiscordAuditLogBotAddEntry.cs | 66 +-
.../Entries/DiscordAuditLogChannelEntry.cs | 154 +-
.../Entries/DiscordAuditLogEmojiEntry.cs | 80 +-
.../Entries/DiscordAuditLogGuildEntry.cs | 190 +-
...DiscordAuditLogGuildScheduledEventEntry.cs | 150 +-
.../DiscordAuditLogIntegrationEntry.cs | 106 +-
.../Entries/DiscordAuditLogInviteEntry.cs | 140 +-
.../Entries/DiscordAuditLogKickEntry.cs | 70 +-
.../DiscordAuditLogMemberDisconnectEntry.cs | 66 +-
.../Entries/DiscordAuditLogMemberMoveEntry.cs | 76 +-
.../DiscordAuditLogMemberUpdateEntry.cs | 134 +-
.../Entries/DiscordAuditLogMessageEntry.cs | 100 +-
.../Entries/DiscordAuditLogMessagePinEntry.cs | 90 +-
.../Entries/DiscordAuditLogOverwriteEntry.cs | 120 +-
.../Entries/DiscordAuditLogPruneEntry.cs | 82 +-
.../Entries/DiscordAuditLogRoleUpdateEntry.cs | 84 +-
.../Entries/DiscordAuditLogStickerEntry.cs | 160 +-
.../DiscordAuditLogThreadEventEntry.cs | 150 +-
.../Entries/DiscordAuditLogWebhookEntry.cs | 120 +-
.../Entities/AuditLogs/PropertyChange.cs | 118 +-
.../Action/DiscordAutoModerationAction.cs | 42 +-
.../DiscordAutoModerationActionBuilder.cs | 110 +-
.../DiscordAutoModerationActionExecution.cs | 154 +-
.../Action/DiscordRuleActionMetadata.cs | 64 +-
.../DiscordRuleActionMetadataBuilder.cs | 160 +-
.../Action/DiscordRuleActionType.cs | 48 +-
.../DiscordAutoModerationRule.cs | 236 +-
.../AutoModeration/DiscordRuleEventType.cs | 26 +-
.../DiscordRuleKeywordPresetType.cs | 46 +-
.../DiscordRuleTriggerMetadata.cs | 86 +-
.../DiscordRuleTriggerMetadataBuilder.cs | 270 +-
.../AutoModeration/DiscordRuleTriggerType.cs | 56 +-
.../Entities/BaseDiscordMessageBuilder.cs | 2126 +-
.../Channel/DiscordAutoArchiveDuration.cs | 56 +-
DSharpPlus/Entities/Channel/DiscordChannel.cs | 2574 +-
.../Entities/Channel/DiscordChannelFlags.cs | 34 +-
.../Entities/Channel/DiscordChannelType.cs | 156 +-
.../Entities/Channel/DiscordDmChannel.cs | 132 +-
.../Channel/DiscordFollowedChannel.cs | 42 +-
.../Entities/Channel/DiscordOverwriteType.cs | 46 +-
.../Entities/Channel/DiscordPartialChannel.cs | 250 +-
.../Channel/DiscordSystemChannelFlags.cs | 82 +-
.../Channel/DiscordVideoQualityMode.cs | 36 +-
.../Channel/Message/DiscordAttachment.cs | 118 +-
.../Channel/Message/DiscordMentions.cs | 242 +-
.../Channel/Message/DiscordMessage.cs | 2018 +-
.../Channel/Message/DiscordMessageActivity.cs | 46 +-
.../Message/DiscordMessageActivityType.cs | 56 +-
.../Message/DiscordMessageApplication.cs | 70 +-
.../Channel/Message/DiscordMessageBuilder.cs | 584 +-
.../Channel/Message/DiscordMessageFile.cs | 94 +-
.../Channel/Message/DiscordMessageFlags.cs | 152 +-
.../Message/DiscordMessageInteraction.cs | 54 +-
.../Message/DiscordMessageReference.cs | 104 +-
.../Channel/Message/DiscordMessageSticker.cs | 282 +-
.../Message/DiscordMessageStickerPack.cs | 100 +-
.../Channel/Message/DiscordMessageType.cs | 272 +-
.../Channel/Message/DiscordReaction.cs | 58 +-
.../Channel/Message/Embed/DiscordEmbed.cs | 192 +-
.../Message/Embed/DiscordEmbedAuthor.cs | 74 +-
.../Message/Embed/DiscordEmbedBuilder.cs | 1212 +-
.../Message/Embed/DiscordEmbedField.cs | 58 +-
.../Message/Embed/DiscordEmbedFooter.cs | 60 +-
.../Message/Embed/DiscordEmbedImage.cs | 72 +-
.../Message/Embed/DiscordEmbedProvider.cs | 48 +-
.../Message/Embed/DiscordEmbedThumbnail.cs | 72 +-
.../Message/Embed/DiscordEmbedVideo.cs | 60 +-
.../Entities/Channel/Message/MentionType.cs | 66 +-
.../Entities/Channel/Message/Mentions.cs | 208 +-
.../Channel/Message/Poll/DiscordPoll.cs | 80 +-
.../Channel/Message/Poll/DiscordPollAnswer.cs | 42 +-
.../Message/Poll/DiscordPollAnswerCount.cs | 70 +-
.../Message/Poll/DiscordPollBuilder.cs | 230 +-
.../Message/Poll/DiscordPollLayoutType.cs | 26 +-
.../Channel/Message/Poll/DiscordPollMedia.cs | 56 +-
.../Channel/Message/Poll/DiscordPollResult.cs | 38 +-
.../Message/Poll/DiscordPollVoteUpdate.cs | 128 +-
.../Channel/Overwrite/DiscordOverwrite.cs | 180 +-
.../Overwrite/DiscordOverwriteBuilder.cs | 262 +-
.../Channel/Stage/DiscordStageInstance.cs | 158 +-
.../Channel/Stage/DiscordStagePrivacyLevel.cs | 36 +-
.../Channel/Thread/DiscordThreadChannel.cs | 392 +-
.../Thread/DiscordThreadChannelMember.cs | 208 +-
.../Thread/DiscordThreadChannelMetadata.cs | 100 +-
.../Channel/Thread/Forum/DefaultReaction.cs | 60 +-
.../Channel/Thread/Forum/DefaultSortOrder.cs | 34 +-
.../Thread/Forum/DiscordDefaultForumLayout.cs | 46 +-
.../Thread/Forum/DiscordForumChannel.cs | 132 +-
.../Thread/Forum/DiscordForumPostStarter.cs | 50 +-
.../Channel/Thread/Forum/DiscordForumTag.cs | 224 +-
.../Channel/Thread/Forum/ForumPostBuilder.cs | 256 +-
.../Channel/Thread/ThreadQueryResult.cs | 46 +-
.../Entities/Color/DiscordColor.Colors.cs | 504 +-
DSharpPlus/Entities/Color/DiscordColor.cs | 242 +-
DSharpPlus/Entities/DiscordConnection.cs | 144 +-
DSharpPlus/Entities/DiscordPermissions.cs | 712 +-
DSharpPlus/Entities/DiscordTeam.cs | 328 +-
DSharpPlus/Entities/DiscordUri.cs | 158 +-
.../Entities/Emoji/DiscordEmoji.EmojiUtils.cs | 23432 ++++++++--------
DSharpPlus/Entities/Emoji/DiscordEmoji.cs | 732 +-
DSharpPlus/Entities/Guild/DiscordBan.cs | 52 +-
DSharpPlus/Entities/Guild/DiscordBulkBan.cs | 64 +-
DSharpPlus/Entities/Guild/DiscordGuild.cs | 5534 ++--
.../Entities/Guild/DiscordGuildEmbed.cs | 42 +-
.../Entities/Guild/DiscordGuildEmoji.cs | 96 +-
.../Guild/DiscordGuildMembershipScreening.cs | 58 +-
.../DiscordGuildMembershipScreeningField.cs | 90 +-
.../Entities/Guild/DiscordGuildPreview.cs | 144 +-
.../Entities/Guild/DiscordGuildTemplate.cs | 146 +-
.../Guild/DiscordGuildWelcomeScreen.cs | 44 +-
.../Guild/DiscordGuildWelcomeScreenChannel.cs | 100 +-
DSharpPlus/Entities/Guild/DiscordMember.cs | 1278 +-
.../DiscordMembershipScreeningFieldType.cs | 36 +-
DSharpPlus/Entities/Guild/DiscordNsfwLevel.cs | 58 +-
.../Entities/Guild/DiscordPremiumTier.cs | 66 +-
DSharpPlus/Entities/Guild/DiscordRole.cs | 452 +-
DSharpPlus/Entities/Guild/DiscordRoleTags.cs | 64 +-
.../DiscordScheduledGuildEvent.cs | 228 +-
.../DiscordScheduledGuildEventMetadata.cs | 38 +-
.../DiscordScheduledGuildEventPrivacyLevel.cs | 34 +-
.../DiscordScheduledGuildEventStatus.cs | 54 +-
.../DiscordScheduledGuildEventType.cs | 44 +-
.../Entities/Guild/Widget/DiscordWidget.cs | 86 +-
.../Guild/Widget/DiscordWidgetMember.cs | 90 +-
.../Guild/Widget/DiscordWidgetSettings.cs | 58 +-
.../DiscordApplicationIntegrationType.cs | 36 +-
.../Integration/DiscordIntegration.cs | 144 +-
.../Integration/DiscordIntegrationAccount.cs | 46 +-
.../DiscordInteractionContextType.cs | 46 +-
.../Application/DiscordApplicationCommand.cs | 454 +-
.../DiscordApplicationCommandOption.cs | 312 +-
.../DiscordApplicationCommandOptionChoice.cs | 266 +-
.../DiscordApplicationCommandOptionType.cs | 126 +-
...DiscordApplicationCommandPermissionType.cs | 16 +-
.../DiscordApplicationCommandType.cs | 56 +-
.../Application/DiscordAutoCompleteChoice.cs | 138 +-
.../Components/DiscordActionRowComponent.cs | 40 +-
.../Components/DiscordButtonComponent.cs | 180 +-
.../Components/DiscordButtonStyle.cs | 56 +-
.../Components/DiscordComponent.cs | 66 +-
.../Components/DiscordComponentType.cs | 168 +-
.../Components/DiscordEmojiComponent.cs | 114 +-
.../Components/DiscordLinkButtonComponent.cs | 108 +-
.../DiscordSelectDefaultValueType.cs | 24 +-
.../Components/DiscordTextInputComponent.cs | 176 +-
.../Components/DiscordTextInputStyle.cs | 34 +-
.../Selects/BaseDiscordSelectComponent.cs | 144 +-
.../Selects/DiscordChannelSelectComponent.cs | 228 +-
.../DiscordMentionableSelectComponent.cs | 174 +-
.../Selects/DiscordRoleSelectComponent.cs | 200 +-
.../Selects/DiscordSelectComponent.cs | 88 +-
.../Selects/DiscordSelectComponentOption.cs | 96 +-
.../Selects/DiscordSelectDefaultValue.cs | 50 +-
.../Selects/DiscordUserSelectComponent.cs | 208 +-
.../DiscordFollowupMessageBuilder.cs | 142 +-
.../Interaction/DiscordInteraction.cs | 570 +-
...teractionApplicationCommandCallbackData.cs | 76 +-
.../Interaction/DiscordInteractionData.cs | 204 +-
.../DiscordInteractionDataOption.cs | 134 +-
.../DiscordInteractionResolvedCollection.cs | 92 +-
.../DiscordInteractionResponseBuilder.cs | 300 +-
.../DiscordInteractionResponseState.cs | 46 +-
.../DiscordInteractionResponseType.cs | 86 +-
.../Interaction/DiscordInteractionType.cs | 66 +-
.../Metadata/DiscordInteractionMetadata.cs | 114 +-
.../DiscordApplicationCommandPermission.cs | 106 +-
...scordGuildApplicationCommandPermissions.cs | 98 +-
DSharpPlus/Entities/Invite/DiscordInvite.cs | 302 +-
.../Entities/Invite/DiscordInviteChannel.cs | 46 +-
.../Entities/Invite/DiscordInviteGuild.cs | 176 +-
.../Invite/DiscordInviteTargetType.cs | 34 +-
.../Entities/Invite/DiscordStageInvite.cs | 68 +-
DSharpPlus/Entities/Optional.cs | 502 +-
DSharpPlus/Entities/SnowflakeObject.cs | 62 +-
DSharpPlus/Entities/User/DiscordActivity.cs | 894 +-
.../Entities/User/DiscordPremiumType.cs | 34 +-
DSharpPlus/Entities/User/DiscordPresence.cs | 218 +-
DSharpPlus/Entities/User/DiscordUser.cs | 846 +-
DSharpPlus/Entities/User/DiscordUserFlags.cs | 190 +-
.../Entities/Voice/DiscordVoiceRegion.cs | 188 +-
.../Entities/Voice/DiscordVoiceState.cs | 432 +-
DSharpPlus/Entities/Webhook/DiscordWebhook.cs | 462 +-
.../Entities/Webhook/DiscordWebhookBuilder.cs | 322 +-
.../Guild/GuildAuditLogCreatedEventArgs.cs | 38 +-
.../Guild/GuildDownloadCompletedEventArgs.cs | 36 +-
.../ScheduledGuildEventCompletedEventArgs.cs | 32 +-
.../EventArgs/Socket/SocketEventArgs.cs | 24 +-
.../Socket/WebSocketMessageEventArgs.cs | 86 +-
.../EventArgs/User/UserSpeakingEventArgs.cs | 52 +-
DSharpPlus/Exceptions/BadRequestException.cs | 122 +-
.../Exceptions/BulkDeleteFailedException.cs | 30 +-
DSharpPlus/Exceptions/DiscordException.cs | 52 +-
DSharpPlus/Exceptions/NotFoundException.cs | 66 +-
DSharpPlus/Exceptions/RateLimitException.cs | 66 +-
DSharpPlus/Exceptions/RequestSizeException.cs | 66 +-
DSharpPlus/Exceptions/ServerErrorException.cs | 66 +-
.../Exceptions/UnauthorizedException.cs | 66 +-
.../Extensions/ServiceCollectionExtensions.cs | 470 +-
DSharpPlus/Formatter.cs | 450 +-
DSharpPlus/IMessageCacheProvider.cs | 54 +-
DSharpPlus/Logging/CompositeDefaultLogger.cs | 80 +-
DSharpPlus/Logging/DefaultLogger.cs | 178 +-
DSharpPlus/Logging/DefaultLoggerFactory.cs | 74 +-
DSharpPlus/Logging/DefaultLoggerProvider.cs | 82 +-
DSharpPlus/Logging/LoggerEvents.cs | 256 +-
DSharpPlus/MessageCache.cs | 66 +-
DSharpPlus/Metrics/LiveRequestMetrics.cs | 34 +-
.../Metrics/RequestMetricsCollection.cs | 356 +-
DSharpPlus/Metrics/RequestMetricsContainer.cs | 264 +-
.../Net/Abstractions/AuditLogAbstractions.cs | 300 +-
.../Net/Abstractions/ClientProperties.cs | 142 +-
.../Abstractions/FollowedChannelAddPayload.cs | 18 +-
.../Net/Abstractions/Gateway/GatewayHello.cs | 44 +-
.../Gateway/GatewayIdentifyResume.cs | 150 +-
.../Net/Abstractions/Gateway/GatewayInfo.cs | 54 +-
.../Net/Abstractions/Gateway/GatewayOpCode.cs | 146 +-
.../Abstractions/Gateway/GatewayPayload.cs | 66 +-
.../Gateway/GatewayRequestGuildMembers.cs | 56 +-
DSharpPlus/Net/Abstractions/IOAuth2Payload.cs | 14 +-
.../Net/Abstractions/PollCreatePayload.cs | 114 +-
DSharpPlus/Net/Abstractions/ReadyPayload.cs | 118 +-
.../Rest/RestApplicationCommandPayloads.cs | 260 +-
.../Abstractions/Rest/RestChannelPayloads.cs | 662 +-
.../Abstractions/Rest/RestGuildPayloads.cs | 788 +-
.../Abstractions/Rest/RestStickerPayloads.cs | 56 +-
.../Abstractions/Rest/RestThreadPayloads.cs | 70 +-
.../Net/Abstractions/Rest/RestUserPayloads.cs | 148 +-
.../Abstractions/Rest/RestWebhookPayloads.cs | 148 +-
DSharpPlus/Net/Abstractions/ShardInfo.cs | 110 +-
DSharpPlus/Net/Abstractions/StatusUpdate.cs | 92 +-
.../Transport/TransportActivity.cs | 550 +-
.../Transport/TransportApplication.cs | 188 +-
.../Abstractions/Transport/TransportMember.cs | 84 +-
.../Abstractions/Transport/TransportTeam.cs | 82 +-
.../Abstractions/Transport/TransportUser.cs | 146 +-
.../Net/Abstractions/VoiceStateUpdate.cs | 66 +-
DSharpPlus/Net/ConnectionEndpoint.cs | 120 +-
.../Net/Models/ApplicationCommandEditModel.cs | 182 +-
.../Net/Models/AutoModerationRuleEditModel.cs | 84 +-
DSharpPlus/Net/Models/BaseEditModel.cs | 20 +-
DSharpPlus/Net/Models/ChannelEditModel.cs | 220 +-
DSharpPlus/Net/Models/GuildEditModel.cs | 226 +-
DSharpPlus/Net/Models/MemberEditModel.cs | 88 +-
.../Models/MembershipScreeningEditModel.cs | 46 +-
DSharpPlus/Net/Models/RoleEditModel.cs | 94 +-
.../Models/ScheduledGuildEventEditModel.cs | 120 +-
.../Net/Models/StageInstanceEditModel.cs | 32 +-
DSharpPlus/Net/Models/StickerEditModel.cs | 24 +-
.../Net/Models/ThreadChannelEditModel.cs | 78 +-
.../Net/Models/WelcomeScreenEditModel.cs | 44 +-
DSharpPlus/Net/Rest/DiscordApiClient.cs | 14094 +++++-----
DSharpPlus/Net/Rest/Endpoints.cs | 160 +-
DSharpPlus/Net/Rest/IRestRequest.cs | 70 +-
DSharpPlus/Net/Rest/IpEndpoint.cs | 60 +-
DSharpPlus/Net/Rest/MultipartRestRequest.cs | 308 +-
.../Net/Rest/PreemptiveRatelimitException.cs | 36 +-
DSharpPlus/Net/Rest/RateLimitBucket.cs | 372 +-
.../Net/Rest/RateLimitCandidateBucket.cs | 24 +-
DSharpPlus/Net/Rest/RateLimitOptions.cs | 10 +-
DSharpPlus/Net/Rest/RateLimitStrategy.cs | 664 +-
DSharpPlus/Net/Rest/RequestStreamWrapper.cs | 154 +-
DSharpPlus/Net/Rest/RestClient.cs | 566 +-
DSharpPlus/Net/Rest/RestRequest.cs | 130 +-
DSharpPlus/Net/Rest/RestResponse.cs | 38 +-
DSharpPlus/Net/Rest/SessionBucket.cs | 82 +-
.../DiscordComponentJsonConverter.cs | 100 +-
.../DiscordForumChannelJsonConverter.cs | 116 +-
DSharpPlus/Net/Serialization/DiscordJson.cs | 106 +-
...DiscordPermissionsAsStringJsonConverter.cs | 76 +-
.../ISO8601DateTimeOffsetJsonConverter.cs | 44 +-
...SnowflakeArrayAsDictionaryJsonConverter.cs | 152 +-
DSharpPlus/Net/Udp/BaseUdpClient.cs | 80 +-
DSharpPlus/Net/Udp/DspUdpClient.cs | 182 +-
DSharpPlus/Net/WebSocket/SocketLock.cs | 140 +-
DSharpPlus/Properties/AssemblyProperties.cs | 22 +-
DSharpPlus/QueryUriBuilder.cs | 94 +-
DSharpPlus/ReadOnlyConcurrentDictionary.cs | 86 +-
DSharpPlus/ReadOnlySet.cs | 94 +-
DSharpPlus/RingBuffer.cs | 460 +-
DSharpPlus/TimestampFormat.cs | 86 +-
DSharpPlus/TokenType.cs | 36 +-
DSharpPlus/Utilities.cs | 604 +-
Directory.Build.props | 106 +-
docs/articles/beyond_basics/permissions.md | 108 +-
.../commands/custom_context_checks.md | 318 +-
.../choice_provider_vs_autocomplete.md | 288 +-
.../slash_commands/missing_commands.md | 30 +-
docs/articles/toc.yml | 286 +-
docs/faq.md | 268 +-
.../SlashCommandsExtension.cs | 4088 +--
.../Program.cs | 120 +-
601 files changed, 82187 insertions(+), 82178 deletions(-)
create mode 100644 .gitattributes
diff --git a/.editorconfig b/.editorconfig
index d82e66309..6c5221951 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,240 +1,240 @@
-###############################
-# Core EditorConfig Options #
-###############################
-root = true
-
-[obsolete/*]
-generated_code = true
-
-# All files
-[*]
-tab_width = 4
-end_of_line = crlf
-indent_style = space
-
-# XML project files
-[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
-indent_size = 2
-
-# XML config files
-[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
-indent_size = 2
-
-###############################
-# .NET Coding Conventions #
-###############################
-[*.{cs,vb}]
-
-# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1826#exclude-firstordefault-and-lastordefault-methods
-dotnet_code_quality.CA1826.exclude_ordefault_methods = true
-
-# Collection Expressions - int[] numbers = []
-dotnet_style_collection_initializer = true:error
-dotnet_style_prefer_collection_expression = true:error
-
-# Nullable reference types
-dotnet_diagnostic.CS8600.severity = suggestion
-dotnet_diagnostic.CS8601.severity = suggestion
-dotnet_diagnostic.CS8602.severity = suggestion
-dotnet_diagnostic.CS8603.severity = suggestion
-dotnet_diagnostic.CS8604.severity = suggestion
-dotnet_diagnostic.CS8618.severity = suggestion
-dotnet_diagnostic.CS8619.severity = suggestion
-dotnet_diagnostic.CS8620.severity = suggestion
-dotnet_diagnostic.CS8625.severity = suggestion
-dotnet_diagnostic.CS8629.severity = suggestion
-dotnet_diagnostic.CS8632.severity = suggestion
-dotnet_diagnostic.CS8764.severity = suggestion
-dotnet_diagnostic.CS8765.severity = suggestion
-dotnet_diagnostic.CS8767.severity = suggestion
-
-dotnet_diagnostic.IDE0021.severity = error
-dotnet_diagnostic.IDE0022.severity = error
-dotnet_diagnostic.IDE0023.severity = error
-dotnet_diagnostic.IDE0024.severity = error
-dotnet_diagnostic.IDE0025.severity = error
-dotnet_diagnostic.IDE0026.severity = error
-dotnet_diagnostic.IDE0027.severity = error
-
-# Suppress CS1591: Missing XML comment for publicly visible type or member
-dotnet_diagnostic.CS1591.severity = suggestion
-
-# Suppress IDE0058: Expression value is never used
-dotnet_diagnostic.IDE0058.severity = none
-
-# Organize usings
-dotnet_sort_system_directives_first = true:error
-dotnet_separate_import_directive_groups = true:error
-
-# Unfortunately, no java-style usings
-dotnet_diagnostic.IDE0064.severity = none
-
-# Using directive is unnecessary.
-dotnet_diagnostic.IDE0005.severity = error
-
-# this. preferences
-dotnet_style_qualification_for_field = true:error
-dotnet_style_qualification_for_property = true:error
-dotnet_style_qualification_for_method = false:error
-dotnet_style_qualification_for_event = true:error
-
-# Language keywords vs BCL types preferences
-dotnet_style_predefined_type_for_locals_parameters_members = true:error
-dotnet_style_predefined_type_for_member_access = true:error
-
-# Parentheses preferences
-dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:error
-dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:error
-dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:error
-dotnet_style_parentheses_in_other_operators = never_if_unnecessary:error
-
-# Modifier preferences
-dotnet_style_require_accessibility_modifiers = always:error
-dotnet_style_readonly_field = true:error
-
-# Expression-level preferences
-dotnet_style_object_initializer = true:error
-dotnet_style_collection_initializer = true:error
-dotnet_style_explicit_tuple_names = true:error
-dotnet_style_null_propagation = true:error
-dotnet_style_coalesce_expression = true:error
-dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error
-dotnet_style_prefer_inferred_tuple_names = true:error
-dotnet_style_prefer_inferred_anonymous_type_member_names = true:error
-dotnet_style_prefer_auto_properties = true:error
-dotnet_style_prefer_conditional_expression_over_assignment = true:error
-dotnet_style_prefer_conditional_expression_over_return = true:error
-
-# Don't force namespaces to match their folder names (DSharpPlus.Entities)
-dotnet_diagnostic.IDE0130.severity = none
-dotnet_style_namespace_match_folder = false
-
-dotnet_style_prefer_simplified_boolean_expressions = true:error
-dotnet_style_operator_placement_when_wrapping = beginning_of_line:error
-
-###############################
-# Naming Conventions #
-###############################
-
-# Style Definitions
-dotnet_naming_style.pascal_case_style.capitalization = pascal_case
-
-# Use PascalCase for constant fields
-dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = error
-dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
-dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
-dotnet_naming_symbols.constant_fields.applicable_kinds = field
-dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
-dotnet_naming_symbols.constant_fields.required_modifiers = const
-
-# Interfaces should start with I
-dotnet_naming_rule.interface_should_be_begins_with_i.severity = error
-dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
-dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
-dotnet_naming_style.begins_with_i.required_prefix = I
-dotnet_naming_style.begins_with_i.capitalization = pascal_case
-dotnet_naming_symbols.interface.applicable_kinds = interface
-
-# Async Methods should end with Async
-dotnet_naming_rule.async_methods_should_be_async_suffix.severity = error
-dotnet_naming_rule.async_methods_should_be_async_suffix.symbols = async_methods
-dotnet_naming_rule.async_methods_should_be_async_suffix.style = async_suffix
-dotnet_naming_style.async_suffix.required_suffix = Async
-dotnet_naming_style.async_suffix.capitalization = pascal_case
-dotnet_naming_symbols.async_methods.applicable_kinds = method
-dotnet_naming_symbols.async_methods.required_modifiers = async
-
-# Fields should not begin with underscores, should be camelCase and should not use word separators
-dotnet_naming_rule.all_fields_notunderscored.symbols = all_fields
-dotnet_naming_rule.all_fields_notunderscored.style = notunderscored
-dotnet_naming_rule.all_fields_notunderscored.severity = error
-dotnet_naming_style.notunderscored.capitalization = camel_case
-dotnet_naming_style.notunderscored.required_prefix =
-dotnet_naming_style.notunderscored.word_separator =
-dotnet_naming_symbols.all_fields.applicable_kinds = field
-dotnet_naming_symbols.all_fields.applicable_accessibilities = *
-
-###############################
-# C# Coding Conventions #
-###############################
-[*.cs]
-
-# File-scoped namespaces
-csharp_style_namespace_declarations = file_scoped:error
-
-# var preferences
-csharp_style_var_for_built_in_types = false:error
-csharp_style_var_when_type_is_apparent = false:error
-csharp_style_var_elsewhere = false:error
-
-# Pattern matching preferences
-csharp_style_pattern_matching_over_is_with_cast_check = true:error
-csharp_style_pattern_matching_over_as_with_null_check = true:error
-
-# Null-checking preferences
-csharp_style_throw_expression = true:error
-csharp_style_conditional_delegate_call = true:error
-
-# Modifier preferences
-csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:error
-
-# Expression-level preferences
-csharp_prefer_braces = true:error
-csharp_style_deconstructed_variable_declaration = true:error
-csharp_prefer_simple_default_expression = true:error
-csharp_style_pattern_local_over_anonymous_function = true:error
-csharp_style_inlined_variable_declaration = true:error
-
-###############################
-# C# Formatting Rules #
-###############################
-
-# https://stackoverflow.com/q/63369382/10942966
-csharp_qualified_using_at_nested_scope = true:error
-csharp_using_directive_placement = outside_namespace:error
-
-# New line preferences
-csharp_new_line_before_open_brace = all
-csharp_new_line_before_else = true
-csharp_new_line_before_catch = true
-csharp_new_line_before_finally = true
-csharp_new_line_before_members_in_object_initializers = true
-csharp_new_line_before_members_in_anonymous_types = true
-csharp_new_line_between_query_expression_clauses = true
-
-# Indentation preferences
-charset = utf-8
-indent_size = 4
-insert_final_newline = true
-csharp_indent_case_contents = true
-csharp_indent_switch_labels = true
-csharp_indent_labels = flush_left
-
-# Space preferences
-csharp_space_after_cast = false
-csharp_space_after_keywords_in_control_flow_statements = true
-csharp_space_between_method_call_parameter_list_parentheses = false
-csharp_space_between_method_declaration_parameter_list_parentheses = false
-csharp_space_between_parentheses = false
-csharp_space_before_colon_in_inheritance_clause = true
-csharp_space_after_colon_in_inheritance_clause = true
-csharp_space_around_binary_operators = before_and_after
-csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
-csharp_space_between_method_call_name_and_opening_parenthesis = false
-csharp_space_between_method_call_empty_parameter_list_parentheses = false
-
-# Wrapping preferences
-csharp_preserve_single_line_statements = false
-csharp_preserve_single_line_blocks = true
-
-# Use regular constructors over primary constructors
-csharp_style_prefer_primary_constructors = false:error
-
-# Expression-bodied members
-csharp_style_expression_bodied_methods = when_on_single_line:error
-csharp_style_expression_bodied_constructors = when_on_single_line:error
-csharp_style_expression_bodied_operators = when_on_single_line:error
-csharp_style_expression_bodied_properties = when_on_single_line:error
-csharp_style_expression_bodied_indexers = when_on_single_line:error
-csharp_style_expression_bodied_accessors = when_on_single_line:error
+###############################
+# Core EditorConfig Options #
+###############################
+root = true
+
+[obsolete/*]
+generated_code = true
+
+# All files
+[*]
+tab_width = 4
+end_of_line = crlf
+indent_style = space
+
+# XML project files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
+indent_size = 2
+
+# XML config files
+[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
+indent_size = 2
+
+###############################
+# .NET Coding Conventions #
+###############################
+[*.{cs,vb}]
+
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1826#exclude-firstordefault-and-lastordefault-methods
+dotnet_code_quality.CA1826.exclude_ordefault_methods = true
+
+# Collection Expressions - int[] numbers = []
+dotnet_style_collection_initializer = true:error
+dotnet_style_prefer_collection_expression = true:error
+
+# Nullable reference types
+dotnet_diagnostic.CS8600.severity = suggestion
+dotnet_diagnostic.CS8601.severity = suggestion
+dotnet_diagnostic.CS8602.severity = suggestion
+dotnet_diagnostic.CS8603.severity = suggestion
+dotnet_diagnostic.CS8604.severity = suggestion
+dotnet_diagnostic.CS8618.severity = suggestion
+dotnet_diagnostic.CS8619.severity = suggestion
+dotnet_diagnostic.CS8620.severity = suggestion
+dotnet_diagnostic.CS8625.severity = suggestion
+dotnet_diagnostic.CS8629.severity = suggestion
+dotnet_diagnostic.CS8632.severity = suggestion
+dotnet_diagnostic.CS8764.severity = suggestion
+dotnet_diagnostic.CS8765.severity = suggestion
+dotnet_diagnostic.CS8767.severity = suggestion
+
+dotnet_diagnostic.IDE0021.severity = error
+dotnet_diagnostic.IDE0022.severity = error
+dotnet_diagnostic.IDE0023.severity = error
+dotnet_diagnostic.IDE0024.severity = error
+dotnet_diagnostic.IDE0025.severity = error
+dotnet_diagnostic.IDE0026.severity = error
+dotnet_diagnostic.IDE0027.severity = error
+
+# Suppress CS1591: Missing XML comment for publicly visible type or member
+dotnet_diagnostic.CS1591.severity = suggestion
+
+# Suppress IDE0058: Expression value is never used
+dotnet_diagnostic.IDE0058.severity = none
+
+# Organize usings
+dotnet_sort_system_directives_first = true:error
+dotnet_separate_import_directive_groups = true:error
+
+# Unfortunately, no java-style usings
+dotnet_diagnostic.IDE0064.severity = none
+
+# Using directive is unnecessary.
+dotnet_diagnostic.IDE0005.severity = error
+
+# this. preferences
+dotnet_style_qualification_for_field = true:error
+dotnet_style_qualification_for_property = true:error
+dotnet_style_qualification_for_method = false:error
+dotnet_style_qualification_for_event = true:error
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true:error
+dotnet_style_predefined_type_for_member_access = true:error
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:error
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:error
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:error
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:error
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = always:error
+dotnet_style_readonly_field = true:error
+
+# Expression-level preferences
+dotnet_style_object_initializer = true:error
+dotnet_style_collection_initializer = true:error
+dotnet_style_explicit_tuple_names = true:error
+dotnet_style_null_propagation = true:error
+dotnet_style_coalesce_expression = true:error
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error
+dotnet_style_prefer_inferred_tuple_names = true:error
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:error
+dotnet_style_prefer_auto_properties = true:error
+dotnet_style_prefer_conditional_expression_over_assignment = true:error
+dotnet_style_prefer_conditional_expression_over_return = true:error
+
+# Don't force namespaces to match their folder names (DSharpPlus.Entities)
+dotnet_diagnostic.IDE0130.severity = none
+dotnet_style_namespace_match_folder = false
+
+dotnet_style_prefer_simplified_boolean_expressions = true:error
+dotnet_style_operator_placement_when_wrapping = beginning_of_line:error
+
+###############################
+# Naming Conventions #
+###############################
+
+# Style Definitions
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+
+# Use PascalCase for constant fields
+dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = error
+dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
+dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
+dotnet_naming_symbols.constant_fields.applicable_kinds = field
+dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
+dotnet_naming_symbols.constant_fields.required_modifiers = const
+
+# Interfaces should start with I
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = error
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+dotnet_naming_symbols.interface.applicable_kinds = interface
+
+# Async Methods should end with Async
+dotnet_naming_rule.async_methods_should_be_async_suffix.severity = error
+dotnet_naming_rule.async_methods_should_be_async_suffix.symbols = async_methods
+dotnet_naming_rule.async_methods_should_be_async_suffix.style = async_suffix
+dotnet_naming_style.async_suffix.required_suffix = Async
+dotnet_naming_style.async_suffix.capitalization = pascal_case
+dotnet_naming_symbols.async_methods.applicable_kinds = method
+dotnet_naming_symbols.async_methods.required_modifiers = async
+
+# Fields should not begin with underscores, should be camelCase and should not use word separators
+dotnet_naming_rule.all_fields_notunderscored.symbols = all_fields
+dotnet_naming_rule.all_fields_notunderscored.style = notunderscored
+dotnet_naming_rule.all_fields_notunderscored.severity = error
+dotnet_naming_style.notunderscored.capitalization = camel_case
+dotnet_naming_style.notunderscored.required_prefix =
+dotnet_naming_style.notunderscored.word_separator =
+dotnet_naming_symbols.all_fields.applicable_kinds = field
+dotnet_naming_symbols.all_fields.applicable_accessibilities = *
+
+###############################
+# C# Coding Conventions #
+###############################
+[*.cs]
+
+# File-scoped namespaces
+csharp_style_namespace_declarations = file_scoped:error
+
+# var preferences
+csharp_style_var_for_built_in_types = false:error
+csharp_style_var_when_type_is_apparent = false:error
+csharp_style_var_elsewhere = false:error
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_is_with_cast_check = true:error
+csharp_style_pattern_matching_over_as_with_null_check = true:error
+
+# Null-checking preferences
+csharp_style_throw_expression = true:error
+csharp_style_conditional_delegate_call = true:error
+
+# Modifier preferences
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:error
+
+# Expression-level preferences
+csharp_prefer_braces = true:error
+csharp_style_deconstructed_variable_declaration = true:error
+csharp_prefer_simple_default_expression = true:error
+csharp_style_pattern_local_over_anonymous_function = true:error
+csharp_style_inlined_variable_declaration = true:error
+
+###############################
+# C# Formatting Rules #
+###############################
+
+# https://stackoverflow.com/q/63369382/10942966
+csharp_qualified_using_at_nested_scope = true:error
+csharp_using_directive_placement = outside_namespace:error
+
+# New line preferences
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+charset = utf-8
+indent_size = 4
+insert_final_newline = true
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = true
+csharp_indent_labels = flush_left
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+
+# Wrapping preferences
+csharp_preserve_single_line_statements = false
+csharp_preserve_single_line_blocks = true
+
+# Use regular constructors over primary constructors
+csharp_style_prefer_primary_constructors = false:error
+
+# Expression-bodied members
+csharp_style_expression_bodied_methods = when_on_single_line:error
+csharp_style_expression_bodied_constructors = when_on_single_line:error
+csharp_style_expression_bodied_operators = when_on_single_line:error
+csharp_style_expression_bodied_properties = when_on_single_line:error
+csharp_style_expression_bodied_indexers = when_on_single_line:error
+csharp_style_expression_bodied_accessors = when_on_single_line:error
csharp_style_expression_bodied_lambdas = when_on_single_line:error
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..3332e4e4a
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,9 @@
+*.cs text eol=crlf
+*.json text eol=crlf
+*.txt text eol=crlf
+*.md text eol=crlf
+*.yml text eol=crlf
+*.sln text eol=crlf
+*.csproj text eol=crlf
+*.sh text eol=lf
+.* text eol=crlf
\ No newline at end of file
diff --git a/.github/workflows/build-commit.yml b/.github/workflows/build-commit.yml
index 2f09025cf..bec933b9d 100644
--- a/.github/workflows/build-commit.yml
+++ b/.github/workflows/build-commit.yml
@@ -1,112 +1,112 @@
-name: Build Commit
-
-on:
- push:
- branches: [master]
- paths:
- - ".github/workflows/build-commit.yml"
- - "DSharpPlus*/**"
- - "tools/**"
- - "docs/**"
- - "*.sln"
- - "obsolete/DSharpPlus*/**"
- workflow_dispatch:
-
-env:
- DOTNET_NOLOGO: 1
- DOTNET_CLI_TELEMETRY_OPTOUT: 1
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
- DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
-
-jobs:
- package-commit:
- name: Package Commit
- runs-on: ubuntu-latest
- if: "!contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[ci-skip]')"
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- submodules: recursive
- fetch-depth: 0
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 9
- - name: Build Project
- run: dotnet build
- - name: Test Changes
- run: dotnet test --blame-crash --blame-hang --blame-hang-timeout "30s"
- - name: Get Nightly Version
- id: nightly
- run: printf "version=%0*d" 5 $(( 1195 + 691 + ${{ github.run_number }} )) >> "$GITHUB_OUTPUT"
- - name: Package Project
- run: |
- # We add 1195 since it's the last build number AppVeyor used.
- # We add 686 since it's the last build number GitHub Actions used before the workflow was renamed.
- dotnet pack -c Release -o build -p:Nightly=${{ steps.nightly.outputs.version }}
- dotnet nuget push "build/*" --skip-duplicate -k ${{ secrets.NUGET_ORG_API_KEY }} -s https://api.nuget.org/v3/index.json
- env:
- DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
- DISCORD_GUILD_ID: ${{ secrets.DISCORD_GUILD_ID }}
- DISCORD_CHANNEL_ID: ${{ secrets.DISCORD_CHANNEL_ID }}
- DISCORD_CHANNEL_TOPIC: ${{ secrets.DISCORD_CHANNEL_TOPIC }}
- DISCORD_DOC_BOT_USER_ID: ${{ secrets.DISCORD_DOC_BOT_USER_ID }}
- DISCORD_BOT_USAGE_CHANNEL_ID: ${{ secrets.DISCORD_BOT_USAGE_CHANNEL_ID }}
- NUGET_URL: ${{ secrets.NUGET_URL }}
- GITHUB_URL : ${{ github.server_url }}/${{ github.repository }}
- - name: Upload Artifact
- uses: actions/upload-artifact@v4
- with:
- name: DSharpPlus-Nightly-${{ steps.nightly.outputs.version }}.zip
- path: ./build/*
- - name: Get commit message header
- id: get_commit_message
- run: |
- commit_message_header=$(git log -1 --pretty=%B | head -n 1)
- echo "message_header=${commit_message_header}" >> $GITHUB_OUTPUT
- - name: Discord Webhook
- uses: tsickert/discord-webhook@v4.0.0
- with:
- webhook-url: ${{ secrets.BUILD_WEBHOOK }}
- embed-title: DSharpPlus 5.0.0-nightly-${{ steps.nightly.outputs.version }}
- embed-description: |
- NuGet Link: [`5.0.0-nightly-${{ steps.nightly.outputs.version }}`](https://www.nuget.org/packages/DSharpPlus/5.0.0-nightly-${{ steps.nightly.outputs.version }})
- Commit hash: [`${{ github.sha }}`](https://github.com/${{github.repository}}/commit/${{github.sha}})
- Commit message: ``${{ steps.get_commit_message.outputs.message_header }}``
- embed-color: 7506394
- document-commit:
- name: Document Commit
- runs-on: ubuntu-latest
- needs: package-commit
- permissions:
- pages: write
- id-token: write
- environment:
- name: github-pages
- url: ${{ steps.deployment.outputs.page_url }}
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- submodules: recursive
- fetch-depth: 0
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 9
- - name: Build Project
- run: |
- dotnet build
- dotnet tool update -g docfx --prerelease
- docfx docs/docfx.json
- - name: Upload GitHub Pages artifact
- uses: actions/upload-pages-artifact@v3
- with:
- path: ./docs/_site/
- - name: Deploy to GitHub Pages
- id: deployment
- uses: actions/deploy-pages@v4
- - name: Reload Discord documentation
- run: |
- curl -X POST -H 'Authorization: Bot ${{ secrets.DISCORD_TOKEN }}' -H 'Content-Type: application/json' -d '{"content":"<@341606460720939008> reload"}' 'https://discord.com/api/v10/channels/379379415475552276/messages'
+name: Build Commit
+
+on:
+ push:
+ branches: [master]
+ paths:
+ - ".github/workflows/build-commit.yml"
+ - "DSharpPlus*/**"
+ - "tools/**"
+ - "docs/**"
+ - "*.sln"
+ - "obsolete/DSharpPlus*/**"
+ workflow_dispatch:
+
+env:
+ DOTNET_NOLOGO: 1
+ DOTNET_CLI_TELEMETRY_OPTOUT: 1
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
+ DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
+
+jobs:
+ package-commit:
+ name: Package Commit
+ runs-on: ubuntu-latest
+ if: "!contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[ci-skip]')"
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ fetch-depth: 0
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9
+ - name: Build Project
+ run: dotnet build
+ - name: Test Changes
+ run: dotnet test --blame-crash --blame-hang --blame-hang-timeout "30s"
+ - name: Get Nightly Version
+ id: nightly
+ run: printf "version=%0*d" 5 $(( 1195 + 691 + ${{ github.run_number }} )) >> "$GITHUB_OUTPUT"
+ - name: Package Project
+ run: |
+ # We add 1195 since it's the last build number AppVeyor used.
+ # We add 686 since it's the last build number GitHub Actions used before the workflow was renamed.
+ dotnet pack -c Release -o build -p:Nightly=${{ steps.nightly.outputs.version }}
+ dotnet nuget push "build/*" --skip-duplicate -k ${{ secrets.NUGET_ORG_API_KEY }} -s https://api.nuget.org/v3/index.json
+ env:
+ DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
+ DISCORD_GUILD_ID: ${{ secrets.DISCORD_GUILD_ID }}
+ DISCORD_CHANNEL_ID: ${{ secrets.DISCORD_CHANNEL_ID }}
+ DISCORD_CHANNEL_TOPIC: ${{ secrets.DISCORD_CHANNEL_TOPIC }}
+ DISCORD_DOC_BOT_USER_ID: ${{ secrets.DISCORD_DOC_BOT_USER_ID }}
+ DISCORD_BOT_USAGE_CHANNEL_ID: ${{ secrets.DISCORD_BOT_USAGE_CHANNEL_ID }}
+ NUGET_URL: ${{ secrets.NUGET_URL }}
+ GITHUB_URL : ${{ github.server_url }}/${{ github.repository }}
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: DSharpPlus-Nightly-${{ steps.nightly.outputs.version }}.zip
+ path: ./build/*
+ - name: Get commit message header
+ id: get_commit_message
+ run: |
+ commit_message_header=$(git log -1 --pretty=%B | head -n 1)
+ echo "message_header=${commit_message_header}" >> $GITHUB_OUTPUT
+ - name: Discord Webhook
+ uses: tsickert/discord-webhook@v4.0.0
+ with:
+ webhook-url: ${{ secrets.BUILD_WEBHOOK }}
+ embed-title: DSharpPlus 5.0.0-nightly-${{ steps.nightly.outputs.version }}
+ embed-description: |
+ NuGet Link: [`5.0.0-nightly-${{ steps.nightly.outputs.version }}`](https://www.nuget.org/packages/DSharpPlus/5.0.0-nightly-${{ steps.nightly.outputs.version }})
+ Commit hash: [`${{ github.sha }}`](https://github.com/${{github.repository}}/commit/${{github.sha}})
+ Commit message: ``${{ steps.get_commit_message.outputs.message_header }}``
+ embed-color: 7506394
+ document-commit:
+ name: Document Commit
+ runs-on: ubuntu-latest
+ needs: package-commit
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ fetch-depth: 0
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9
+ - name: Build Project
+ run: |
+ dotnet build
+ dotnet tool update -g docfx --prerelease
+ docfx docs/docfx.json
+ - name: Upload GitHub Pages artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./docs/_site/
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
+ - name: Reload Discord documentation
+ run: |
+ curl -X POST -H 'Authorization: Bot ${{ secrets.DISCORD_TOKEN }}' -H 'Content-Type: application/json' -d '{"content":"<@341606460720939008> reload"}' 'https://discord.com/api/v10/channels/379379415475552276/messages'
diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml
index ca91c500e..493f124b1 100644
--- a/.github/workflows/build-pr.yml
+++ b/.github/workflows/build-pr.yml
@@ -1,48 +1,48 @@
-name: Build PR
-
-on:
- pull_request:
- types:
- - opened
- - synchronize
- - reopened
- - ready_for_review
- paths:
- - ".github/workflows/build-pr.yml"
- - "DSharpPlus*/**"
- - "tools/**"
- - "*.sln"
-env:
- DOTNET_NOLOGO: 1
- DOTNET_CLI_TELEMETRY_OPTOUT: 1
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
- DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
-
-jobs:
- build-commit:
- name: "Build PR #${{ github.event.pull_request.number }}"
- runs-on: ubuntu-latest
- if: "!contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[ci-skip]')"
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- submodules: recursive
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 9
- - name: Build Project
- run: dotnet build
- - name: Test Changes
- run: dotnet test --blame-crash --blame-hang --blame-hang-timeout "30s"
- - name: Get PR Version
- id: pr
- run: printf "version=%0*d" 5 ${{ github.run_number }} >> "$GITHUB_OUTPUT"
- - name: Build and Package Project
- run: dotnet pack --include-symbols --include-source -o build -p:PR="${{ github.event.pull_request.number }}-${{ steps.pr.outputs.version }}"
- - name: Upload artifacts
- uses: actions/upload-artifact@v4
- with:
- name: DSharpPlus-PR-${{ github.event.pull_request.number }}-${{ steps.pr.outputs.version }}
+name: Build PR
+
+on:
+ pull_request:
+ types:
+ - opened
+ - synchronize
+ - reopened
+ - ready_for_review
+ paths:
+ - ".github/workflows/build-pr.yml"
+ - "DSharpPlus*/**"
+ - "tools/**"
+ - "*.sln"
+env:
+ DOTNET_NOLOGO: 1
+ DOTNET_CLI_TELEMETRY_OPTOUT: 1
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
+ DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
+
+jobs:
+ build-commit:
+ name: "Build PR #${{ github.event.pull_request.number }}"
+ runs-on: ubuntu-latest
+ if: "!contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[ci-skip]')"
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9
+ - name: Build Project
+ run: dotnet build
+ - name: Test Changes
+ run: dotnet test --blame-crash --blame-hang --blame-hang-timeout "30s"
+ - name: Get PR Version
+ id: pr
+ run: printf "version=%0*d" 5 ${{ github.run_number }} >> "$GITHUB_OUTPUT"
+ - name: Build and Package Project
+ run: dotnet pack --include-symbols --include-source -o build -p:PR="${{ github.event.pull_request.number }}-${{ steps.pr.outputs.version }}"
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: DSharpPlus-PR-${{ github.event.pull_request.number }}-${{ steps.pr.outputs.version }}
path: ./build/*
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b56a758d1..792bbd9cf 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,92 +1,92 @@
-name: Release
-on:
- release:
- types: ["published"]
-
-env:
- DOTNET_NOLOGO: 1
- DOTNET_CLI_TELEMETRY_OPTOUT: 1
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
- DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
-
-jobs:
- build-commit:
- name: Build Commit
- runs-on: ubuntu-latest
- if: "!contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[ci-skip]')"
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- submodules: recursive
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 9
- - name: Build Project
- run: dotnet build
- package-commit:
- name: Package Commit
- runs-on: ubuntu-latest
- needs: build-commit
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- submodules: recursive
- fetch-depth: 0
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 8
- - name: Package Project
- run: |
- dotnet pack -c Release -o build
- dotnet nuget push "build/*" --skip-duplicate -k ${{ secrets.NUGET_ORG_API_KEY }} -s https://api.nuget.org/v3/index.json
- LATEST_STABLE_VERSION=$(git describe --abbrev=0 --tags 2>/dev/null || echo '') dotnet run --project ./tools/AutoUpdateChannelDescription
- env:
- DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
- DISCORD_GUILD_ID: ${{ secrets.DISCORD_GUILD_ID }}
- DISCORD_CHANNEL_ID: ${{ secrets.DISCORD_CHANNEL_ID }}
- DISCORD_CHANNEL_TOPIC: ${{ secrets.DISCORD_CHANNEL_TOPIC }}
- DISCORD_DOC_BOT_USER_ID: ${{ secrets.DISCORD_DOC_BOT_USER_ID }}
- DISCORD_BOT_USAGE_CHANNEL_ID: ${{ secrets.DISCORD_BOT_USAGE_CHANNEL_ID }}
- NUGET_URL: ${{ secrets.NUGET_URL }}
- GITHUB_URL : ${{ github.server_url }}/${{ github.repository }}
- - name: Upload Artifact
- uses: actions/upload-artifact@v4
- with:
- name: DSharpPlus.zip
- path: ./build/*
- document-commit:
- name: Document Commit
- runs-on: ubuntu-latest
- needs: package-commit
- permissions:
- pages: write
- id-token: write
- environment:
- name: github-pages
- url: ${{ steps.deployment.outputs.page_url }}
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- submodules: recursive
- fetch-depth: 0
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 8
- - name: Build Project
- run: |
- dotnet build
- dotnet tool update -g docfx --prerelease
- docfx docs/docfx.json
- - name: Upload GitHub Pages artifact
- uses: actions/upload-pages-artifact@v3
- with:
- path: ./docs/_site/
- - name: Deploy to GitHub Pages
- id: deployment
+name: Release
+on:
+ release:
+ types: ["published"]
+
+env:
+ DOTNET_NOLOGO: 1
+ DOTNET_CLI_TELEMETRY_OPTOUT: 1
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
+ DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
+
+jobs:
+ build-commit:
+ name: Build Commit
+ runs-on: ubuntu-latest
+ if: "!contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[ci-skip]')"
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9
+ - name: Build Project
+ run: dotnet build
+ package-commit:
+ name: Package Commit
+ runs-on: ubuntu-latest
+ needs: build-commit
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ fetch-depth: 0
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8
+ - name: Package Project
+ run: |
+ dotnet pack -c Release -o build
+ dotnet nuget push "build/*" --skip-duplicate -k ${{ secrets.NUGET_ORG_API_KEY }} -s https://api.nuget.org/v3/index.json
+ LATEST_STABLE_VERSION=$(git describe --abbrev=0 --tags 2>/dev/null || echo '') dotnet run --project ./tools/AutoUpdateChannelDescription
+ env:
+ DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
+ DISCORD_GUILD_ID: ${{ secrets.DISCORD_GUILD_ID }}
+ DISCORD_CHANNEL_ID: ${{ secrets.DISCORD_CHANNEL_ID }}
+ DISCORD_CHANNEL_TOPIC: ${{ secrets.DISCORD_CHANNEL_TOPIC }}
+ DISCORD_DOC_BOT_USER_ID: ${{ secrets.DISCORD_DOC_BOT_USER_ID }}
+ DISCORD_BOT_USAGE_CHANNEL_ID: ${{ secrets.DISCORD_BOT_USAGE_CHANNEL_ID }}
+ NUGET_URL: ${{ secrets.NUGET_URL }}
+ GITHUB_URL : ${{ github.server_url }}/${{ github.repository }}
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: DSharpPlus.zip
+ path: ./build/*
+ document-commit:
+ name: Document Commit
+ runs-on: ubuntu-latest
+ needs: package-commit
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ fetch-depth: 0
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8
+ - name: Build Project
+ run: |
+ dotnet build
+ dotnet tool update -g docfx --prerelease
+ docfx docs/docfx.json
+ - name: Upload GitHub Pages artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./docs/_site/
+ - name: Deploy to GitHub Pages
+ id: deployment
uses: actions/deploy-pages@v4
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index d224448a0..ecebeb4bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,490 +1,490 @@
-## Ignore Visual Studio temporary files, build results, and
-## files generated by popular Visual Studio add-ons.
-##
-## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
-
-# User-specific files
-*.rsuser
-*.suo
-*.user
-*.userosscache
-*.sln.docstates
-
-# User-specific files (MonoDevelop/Xamarin Studio)
-*.userprefs
-
-# Mono auto generated files
-mono_crash.*
-
-# Build results
-[Dd]ebug/
-[Dd]ebugPublic/
-[Rr]elease/
-[Rr]eleases/
-x64/
-x86/
-[Ww][Ii][Nn]32/
-[Aa][Rr][Mm]/
-[Aa][Rr][Mm]64/
-bld/
-[Bb]in/
-[Oo]bj/
-[Ll]og/
-[Ll]ogs/
-
-# Visual Studio 2015/2017 cache/options directory
-.vs/
-# Uncomment if you have tasks that create the project's static files in wwwroot
-#wwwroot/
-
-# Visual Studio 2017 auto generated files
-Generated\ Files/
-
-# MSTest test Results
-[Tt]est[Rr]esult*/
-[Bb]uild[Ll]og.*
-
-# NUnit
-*.VisualState.xml
-TestResult.xml
-nunit-*.xml
-
-# Build Results of an ATL Project
-[Dd]ebugPS/
-[Rr]eleasePS/
-dlldata.c
-
-# Benchmark Results
-BenchmarkDotNet.Artifacts/
-
-# .NET
-project.lock.json
-project.fragment.lock.json
-artifacts/
-
-# Tye
-.tye/
-
-# ASP.NET Scaffolding
-ScaffoldingReadMe.txt
-
-# StyleCop
-StyleCopReport.xml
-
-# Files built by Visual Studio
-*_i.c
-*_p.c
-*_h.h
-*.ilk
-*.meta
-*.obj
-*.iobj
-*.pch
-*.pdb
-*.ipdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.tmp_proj
-*_wpftmp.csproj
-*.log
-*.tlog
-*.vspscc
-*.vssscc
-.builds
-*.pidb
-*.svclog
-*.scc
-
-# Chutzpah Test files
-_Chutzpah*
-
-# Visual C++ cache files
-ipch/
-*.aps
-*.ncb
-*.opendb
-*.opensdf
-*.sdf
-*.cachefile
-*.VC.db
-*.VC.VC.opendb
-
-# Visual Studio profiler
-*.psess
-*.vsp
-*.vspx
-*.sap
-
-# Visual Studio Trace Files
-*.e2e
-
-# TFS 2012 Local Workspace
-$tf/
-
-# Guidance Automation Toolkit
-*.gpState
-
-# ReSharper is a .NET coding add-in
-_ReSharper*/
-*.[Rr]e[Ss]harper
-*.DotSettings.user
-
-# TeamCity is a build add-in
-_TeamCity*
-
-# DotCover is a Code Coverage Tool
-*.dotCover
-
-# AxoCover is a Code Coverage Tool
-.axoCover/*
-!.axoCover/settings.json
-
-# Coverlet is a free, cross platform Code Coverage Tool
-coverage*.json
-coverage*.xml
-coverage*.info
-
-# Visual Studio code coverage results
-*.coverage
-*.coveragexml
-
-# NCrunch
-_NCrunch_*
-.*crunch*.local.xml
-nCrunchTemp_*
-
-# MightyMoose
-*.mm.*
-AutoTest.Net/
-
-# Web workbench (sass)
-.sass-cache/
-
-# Installshield output folder
-[Ee]xpress/
-
-# DocProject is a documentation generator add-in
-DocProject/buildhelp/
-DocProject/Help/*.HxT
-DocProject/Help/*.HxC
-DocProject/Help/*.hhc
-DocProject/Help/*.hhk
-DocProject/Help/*.hhp
-DocProject/Help/Html2
-DocProject/Help/html
-
-# Click-Once directory
-publish/
-
-# Publish Web Output
-*.[Pp]ublish.xml
-*.azurePubxml
-# Note: Comment the next line if you want to checkin your web deploy settings,
-# but database connection strings (with potential passwords) will be unencrypted
-*.pubxml
-*.publishproj
-
-# Microsoft Azure Web App publish settings. Comment the next line if you want to
-# checkin your Azure Web App publish settings, but sensitive information contained
-# in these scripts will be unencrypted
-PublishScripts/
-
-# NuGet Packages
-*.nupkg
-# NuGet Symbol Packages
-*.snupkg
-# The packages folder can be ignored because of Package Restore
-**/[Pp]ackages/*
-# except build/, which is used as an MSBuild target.
-!**/[Pp]ackages/build/
-# Uncomment if necessary however generally it will be regenerated when needed
-#!**/[Pp]ackages/repositories.config
-# NuGet v3's project.json files produces more ignorable files
-*.nuget.props
-*.nuget.targets
-
-# Microsoft Azure Build Output
-csx/
-*.build.csdef
-
-# Microsoft Azure Emulator
-ecf/
-rcf/
-
-# Windows Store app package directories and files
-AppPackages/
-BundleArtifacts/
-Package.StoreAssociation.xml
-_pkginfo.txt
-*.appx
-*.appxbundle
-*.appxupload
-
-# Visual Studio cache files
-# files ending in .cache can be ignored
-*.[Cc]ache
-# but keep track of directories ending in .cache
-!?*.[Cc]ache/
-
-# Others
-ClientBin/
-~$*
-*~
-*.dbmdl
-*.dbproj.schemaview
-*.jfm
-*.pfx
-*.publishsettings
-orleans.codegen.cs
-
-# Including strong name files can present a security risk
-# (https://github.com/github/gitignore/pull/2483#issue-259490424)
-#*.snk
-
-# Since there are multiple workflows, uncomment next line to ignore bower_components
-# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
-#bower_components/
-
-# RIA/Silverlight projects
-Generated_Code/
-
-# Backup & report files from converting an old project file
-# to a newer Visual Studio version. Backup files are not needed,
-# because we have git ;-)
-_UpgradeReport_Files/
-Backup*/
-UpgradeLog*.XML
-UpgradeLog*.htm
-ServiceFabricBackup/
-*.rptproj.bak
-
-# SQL Server files
-*.mdf
-*.ldf
-*.ndf
-
-# Business Intelligence projects
-*.rdl.data
-*.bim.layout
-*.bim_*.settings
-*.rptproj.rsuser
-*- [Bb]ackup.rdl
-*- [Bb]ackup ([0-9]).rdl
-*- [Bb]ackup ([0-9][0-9]).rdl
-
-# Microsoft Fakes
-FakesAssemblies/
-
-# GhostDoc plugin setting file
-*.GhostDoc.xml
-
-# Node.js Tools for Visual Studio
-.ntvs_analysis.dat
-node_modules/
-
-# Visual Studio 6 build log
-*.plg
-
-# Visual Studio 6 workspace options file
-*.opt
-
-# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
-*.vbw
-
-# Visual Studio 6 auto-generated project file (contains which files were open etc.)
-*.vbp
-
-# Visual Studio 6 workspace and project file (working project files containing files to include in project)
-*.dsw
-*.dsp
-
-# Visual Studio 6 technical files
-*.ncb
-*.aps
-
-# Visual Studio LightSwitch build output
-**/*.HTMLClient/GeneratedArtifacts
-**/*.DesktopClient/GeneratedArtifacts
-**/*.DesktopClient/ModelManifest.xml
-**/*.Server/GeneratedArtifacts
-**/*.Server/ModelManifest.xml
-_Pvt_Extensions
-
-# Paket dependency manager
-.paket/paket.exe
-paket-files/
-
-# FAKE - F# Make
-.fake/
-
-# CodeRush personal settings
-.cr/personal
-
-# Python Tools for Visual Studio (PTVS)
-__pycache__/
-*.pyc
-
-# Cake - Uncomment if you are using it
-# tools/**
-# !tools/packages.config
-
-# Tabs Studio
-*.tss
-
-# Telerik's JustMock configuration file
-*.jmconfig
-
-# BizTalk build output
-*.btp.cs
-*.btm.cs
-*.odx.cs
-*.xsd.cs
-
-# OpenCover UI analysis results
-OpenCover/
-
-# Azure Stream Analytics local run output
-ASALocalRun/
-
-# MSBuild Binary and Structured Log
-*.binlog
-
-# NVidia Nsight GPU debugger configuration file
-*.nvuser
-
-# MFractors (Xamarin productivity tool) working folder
-.mfractor/
-
-# Local History for Visual Studio
-.localhistory/
-
-# Visual Studio History (VSHistory) files
-.vshistory/
-
-# BeatPulse healthcheck temp database
-healthchecksdb
-
-# Backup folder for Package Reference Convert tool in Visual Studio 2017
-MigrationBackup/
-
-# Ionide (cross platform F# VS Code tools) working folder
-.ionide/
-
-# Fody - auto-generated XML schema
-FodyWeavers.xsd
-
-# VS Code files for those working on multiple tools
-.vscode/*
-!.vscode/settings.json
-!.vscode/tasks.json
-!.vscode/launch.json
-!.vscode/extensions.json
-*.code-workspace
-
-# Local History for Visual Studio Code
-.history/
-
-# Windows Installer files from build outputs
-*.cab
-*.msi
-*.msix
-*.msm
-*.msp
-
-# JetBrains Rider
-*.sln.iml
-
-##
-## Visual studio for Mac
-##
-
-
-# globs
-Makefile.in
-*.userprefs
-*.usertasks
-config.make
-config.status
-aclocal.m4
-install-sh
-autom4te.cache/
-*.tar.gz
-tarballs/
-test-results/
-
-# Mac bundle stuff
-*.dmg
-*.app
-
-# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
-# General
-.DS_Store
-.AppleDouble
-.LSOverride
-
-# Icon must end with two \r
-Icon
-
-
-# Thumbnails
-._*
-
-# Files that might appear in the root of a volume
-.DocumentRevisions-V100
-.fseventsd
-.Spotlight-V100
-.TemporaryItems
-.Trashes
-.VolumeIcon.icns
-.com.apple.timemachine.donotpresent
-
-# Directories potentially created on remote AFP share
-.AppleDB
-.AppleDesktop
-Network Trash Folder
-Temporary Items
-.apdisk
-
-# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
-# Windows thumbnail cache files
-Thumbs.db
-ehthumbs.db
-ehthumbs_vista.db
-
-# Dump file
-*.stackdump
-
-# Folder config file
-[Dd]esktop.ini
-
-# Recycle Bin used on file shares
-$RECYCLE.BIN/
-
-# Windows Installer files
-*.cab
-*.msi
-*.msix
-*.msm
-*.msp
-
-# Windows shortcuts
-*.lnk
-
-# Vim temporary swap files
-*.swp
-
-# DSharpPlus changes
-.idea
-.vscode
-*.DotSettings
-*.patch
-docs/_site
-DSharpPlus.Test/config.json
-/DSharpPlus.HttpInteractions.AspNetCore/Properties/launchSettings.json
-/DSharpPlus.Http.AspNetCore/Properties/launchSettings.json
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# Tye
+.tye/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.tlog
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio 6 auto-generated project file (contains which files were open etc.)
+*.vbp
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# Visual Studio History (VSHistory) files
+.vshistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+*.sln.iml
+
+##
+## Visual studio for Mac
+##
+
+
+# globs
+Makefile.in
+*.userprefs
+*.usertasks
+config.make
+config.status
+aclocal.m4
+install-sh
+autom4te.cache/
+*.tar.gz
+tarballs/
+test-results/
+
+# Mac bundle stuff
+*.dmg
+*.app
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# Vim temporary swap files
+*.swp
+
+# DSharpPlus changes
+.idea
+.vscode
+*.DotSettings
+*.patch
+docs/_site
+DSharpPlus.Test/config.json
+/DSharpPlus.HttpInteractions.AspNetCore/Properties/launchSettings.json
+/DSharpPlus.Http.AspNetCore/Properties/launchSettings.json
diff --git a/DSharpPlus.Commands/AbstractContext.cs b/DSharpPlus.Commands/AbstractContext.cs
index 7913ea7f2..cb7835fb7 100644
--- a/DSharpPlus.Commands/AbstractContext.cs
+++ b/DSharpPlus.Commands/AbstractContext.cs
@@ -1,20 +1,20 @@
-using System;
-using DSharpPlus.Commands.Trees;
-using DSharpPlus.Entities;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace DSharpPlus.Commands;
-
-public abstract record AbstractContext
-{
- public required DiscordUser User { get; init; }
- public required DiscordChannel Channel { get; init; }
- public required CommandsExtension Extension { get; init; }
- public required Command Command { get; init; }
- public required IServiceScope ServiceScope { internal get; init; }
-
- public DiscordGuild? Guild => this.Channel.Guild;
- public DiscordMember? Member => this.User as DiscordMember;
- public DiscordClient Client => this.Extension.Client;
- public IServiceProvider ServiceProvider => this.ServiceScope.ServiceProvider;
-}
+using System;
+using DSharpPlus.Commands.Trees;
+using DSharpPlus.Entities;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace DSharpPlus.Commands;
+
+public abstract record AbstractContext
+{
+ public required DiscordUser User { get; init; }
+ public required DiscordChannel Channel { get; init; }
+ public required CommandsExtension Extension { get; init; }
+ public required Command Command { get; init; }
+ public required IServiceScope ServiceScope { internal get; init; }
+
+ public DiscordGuild? Guild => this.Channel.Guild;
+ public DiscordMember? Member => this.User as DiscordMember;
+ public DiscordClient Client => this.Extension.Client;
+ public IServiceProvider ServiceProvider => this.ServiceScope.ServiceProvider;
+}
diff --git a/DSharpPlus.Commands/ArgumentModifiers/ChannelTypesAttribute.cs b/DSharpPlus.Commands/ArgumentModifiers/ChannelTypesAttribute.cs
index b1e14a8fe..91a3e9e2b 100644
--- a/DSharpPlus.Commands/ArgumentModifiers/ChannelTypesAttribute.cs
+++ b/DSharpPlus.Commands/ArgumentModifiers/ChannelTypesAttribute.cs
@@ -1,18 +1,18 @@
-using System;
-using DSharpPlus.Commands.ContextChecks.ParameterChecks;
-using DSharpPlus.Entities;
-
-namespace DSharpPlus.Commands.ArgumentModifiers;
-
-///
-/// Specifies what channel types the parameter supports.
-///
-/// The required types of channels.
-[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
-public sealed class ChannelTypesAttribute(params DiscordChannelType[] channelTypes) : ParameterCheckAttribute
-{
- ///
- /// Gets the channel types allowed for this parameter.
- ///
- public DiscordChannelType[] ChannelTypes { get; init; } = channelTypes;
-}
+using System;
+using DSharpPlus.Commands.ContextChecks.ParameterChecks;
+using DSharpPlus.Entities;
+
+namespace DSharpPlus.Commands.ArgumentModifiers;
+
+///
+/// Specifies what channel types the parameter supports.
+///
+/// The required types of channels.
+[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
+public sealed class ChannelTypesAttribute(params DiscordChannelType[] channelTypes) : ParameterCheckAttribute
+{
+ ///
+ /// Gets the channel types allowed for this parameter.
+ ///
+ public DiscordChannelType[] ChannelTypes { get; init; } = channelTypes;
+}
diff --git a/DSharpPlus.Commands/ArgumentModifiers/FromCode/CodeType.cs b/DSharpPlus.Commands/ArgumentModifiers/FromCode/CodeType.cs
index 0932f5f8b..e8d33790c 100644
--- a/DSharpPlus.Commands/ArgumentModifiers/FromCode/CodeType.cs
+++ b/DSharpPlus.Commands/ArgumentModifiers/FromCode/CodeType.cs
@@ -1,25 +1,25 @@
-using System;
-
-namespace DSharpPlus.Commands.ArgumentModifiers;
-
-///
-/// The types of code-formatted text to accept.
-///
-[Flags]
-public enum CodeType
-{
- ///
- /// Accept inline code blocks - codeblocks that will not contain any newlines.
- ///
- Inline = 1 << 0,
-
- ///
- /// Accept codeblocks - codeblocks that will contain possibly multiple newlines.
- ///
- Codeblock = 1 << 1,
-
- ///
- /// Accept any type of code block.
- ///
- All = Inline | Codeblock
-}
+using System;
+
+namespace DSharpPlus.Commands.ArgumentModifiers;
+
+///
+/// The types of code-formatted text to accept.
+///
+[Flags]
+public enum CodeType
+{
+ ///
+ /// Accept inline code blocks - codeblocks that will not contain any newlines.
+ ///
+ Inline = 1 << 0,
+
+ ///
+ /// Accept codeblocks - codeblocks that will contain possibly multiple newlines.
+ ///
+ Codeblock = 1 << 1,
+
+ ///
+ /// Accept any type of code block.
+ ///
+ All = Inline | Codeblock
+}
diff --git a/DSharpPlus.Commands/ArgumentModifiers/FromCode/FromCodeAttribute.cs b/DSharpPlus.Commands/ArgumentModifiers/FromCode/FromCodeAttribute.cs
index a330a13ca..fefaab1c4 100644
--- a/DSharpPlus.Commands/ArgumentModifiers/FromCode/FromCodeAttribute.cs
+++ b/DSharpPlus.Commands/ArgumentModifiers/FromCode/FromCodeAttribute.cs
@@ -1,21 +1,21 @@
-using System;
-
-namespace DSharpPlus.Commands.ArgumentModifiers;
-
-///
-/// Removes the need to manually parse code blocks from a string.
-///
-[AttributeUsage(AttributeTargets.Parameter)]
-public sealed partial class FromCodeAttribute : Attribute
-{
- ///
- /// The type of code block to accept.
- ///
- public CodeType CodeType { get; init; }
-
- ///
- /// Creates a new with the specified .
- ///
- /// The type of code block to accept.
- public FromCodeAttribute(CodeType codeType = CodeType.All) => this.CodeType = codeType;
-}
+using System;
+
+namespace DSharpPlus.Commands.ArgumentModifiers;
+
+///
+/// Removes the need to manually parse code blocks from a string.
+///
+[AttributeUsage(AttributeTargets.Parameter)]
+public sealed partial class FromCodeAttribute : Attribute
+{
+ ///
+ /// The type of code block to accept.
+ ///
+ public CodeType CodeType { get; init; }
+
+ ///
+ /// Creates a new with the specified .
+ ///
+ /// The type of code block to accept.
+ public FromCodeAttribute(CodeType codeType = CodeType.All) => this.CodeType = codeType;
+}
diff --git a/DSharpPlus.Commands/ArgumentModifiers/MinMaxLengthAttribute.cs b/DSharpPlus.Commands/ArgumentModifiers/MinMaxLengthAttribute.cs
index df6a4bcd0..53e9eb682 100644
--- a/DSharpPlus.Commands/ArgumentModifiers/MinMaxLengthAttribute.cs
+++ b/DSharpPlus.Commands/ArgumentModifiers/MinMaxLengthAttribute.cs
@@ -1,42 +1,42 @@
-using System;
-using DSharpPlus.Commands.ContextChecks.ParameterChecks;
-
-namespace DSharpPlus.Commands.ArgumentModifiers;
-
-///
-/// Determines the minimum and maximum length that a parameter can accept.
-///
-[AttributeUsage(AttributeTargets.Parameter)]
-public sealed class MinMaxLengthAttribute : ParameterCheckAttribute
-{
- // on text commands, we interpret 6000 as unlimited - it exceeds the message limit anyway
- private const int MinLengthMinimum = 0;
- private const int MinLengthMaximum = 6000;
- private const int MaxLengthMinimum = 1;
- private const int MaxLengthMaximum = 6000;
-
- ///
- /// The minimum length that this parameter can accept.
- ///
- public int MinLength { get; private init; }
-
- ///
- /// The maximum length that this parameter can accept.
- ///
- public int MaxLength { get; private init; }
-
- ///
- /// Determines the minimum and maximum length that a parameter can accept.
- ///
- public MinMaxLengthAttribute(int minLength = MinLengthMinimum, int maxLength = MaxLengthMaximum)
- {
- ArgumentOutOfRangeException.ThrowIfLessThan(minLength, MinLengthMinimum, nameof(minLength));
- ArgumentOutOfRangeException.ThrowIfGreaterThan(minLength, MinLengthMaximum, nameof(minLength));
- ArgumentOutOfRangeException.ThrowIfLessThan(maxLength, MaxLengthMinimum, nameof(maxLength));
- ArgumentOutOfRangeException.ThrowIfGreaterThan(maxLength, MaxLengthMaximum, nameof(maxLength));
- ArgumentOutOfRangeException.ThrowIfGreaterThan(minLength, maxLength, nameof(minLength));
-
- this.MinLength = minLength;
- this.MaxLength = maxLength;
- }
-}
+using System;
+using DSharpPlus.Commands.ContextChecks.ParameterChecks;
+
+namespace DSharpPlus.Commands.ArgumentModifiers;
+
+///
+/// Determines the minimum and maximum length that a parameter can accept.
+///
+[AttributeUsage(AttributeTargets.Parameter)]
+public sealed class MinMaxLengthAttribute : ParameterCheckAttribute
+{
+ // on text commands, we interpret 6000 as unlimited - it exceeds the message limit anyway
+ private const int MinLengthMinimum = 0;
+ private const int MinLengthMaximum = 6000;
+ private const int MaxLengthMinimum = 1;
+ private const int MaxLengthMaximum = 6000;
+
+ ///
+ /// The minimum length that this parameter can accept.
+ ///
+ public int MinLength { get; private init; }
+
+ ///
+ /// The maximum length that this parameter can accept.
+ ///
+ public int MaxLength { get; private init; }
+
+ ///
+ /// Determines the minimum and maximum length that a parameter can accept.
+ ///
+ public MinMaxLengthAttribute(int minLength = MinLengthMinimum, int maxLength = MaxLengthMaximum)
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(minLength, MinLengthMinimum, nameof(minLength));
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(minLength, MinLengthMaximum, nameof(minLength));
+ ArgumentOutOfRangeException.ThrowIfLessThan(maxLength, MaxLengthMinimum, nameof(maxLength));
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(maxLength, MaxLengthMaximum, nameof(maxLength));
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(minLength, maxLength, nameof(minLength));
+
+ this.MinLength = minLength;
+ this.MaxLength = maxLength;
+ }
+}
diff --git a/DSharpPlus.Commands/ArgumentModifiers/MinMaxValueAttribute.cs b/DSharpPlus.Commands/ArgumentModifiers/MinMaxValueAttribute.cs
index 587655512..450bed7f6 100644
--- a/DSharpPlus.Commands/ArgumentModifiers/MinMaxValueAttribute.cs
+++ b/DSharpPlus.Commands/ArgumentModifiers/MinMaxValueAttribute.cs
@@ -1,60 +1,60 @@
-using System;
-using DSharpPlus.Commands.ContextChecks.ParameterChecks;
-
-namespace DSharpPlus.Commands.ArgumentModifiers;
-
-///
-/// Determines the minimum and maximum values that a parameter can accept.
-///
-[AttributeUsage(AttributeTargets.Parameter)]
-public sealed class MinMaxValueAttribute : ParameterCheckAttribute
-{
- ///
- /// The minimum value that this parameter can accept.
- ///
- public object? MinValue { get; private init; }
-
- ///
- /// The maximum value that this parameter can accept.
- ///
- public object? MaxValue { get; private init; }
-
- ///
- /// Determines the minimum and maximum values that a parameter can accept.
- ///
- public MinMaxValueAttribute(object? minValue = null, object? maxValue = null)
- {
- this.MinValue = minValue;
- this.MaxValue = maxValue;
-
- if (minValue is not null && maxValue is not null && minValue.GetType() != maxValue.GetType())
- {
- throw new ArgumentException("The minimum and maximum values must be of the same type.");
- }
-
- if (minValue is null || maxValue is null)
- {
- return;
- }
-
- bool correctlyOrdered = minValue switch
- {
- byte => (byte)minValue <= (byte)maxValue,
- sbyte => (sbyte)minValue <= (sbyte)maxValue,
- short => (short)minValue <= (short)maxValue,
- ushort => (ushort)minValue <= (ushort)maxValue,
- int => (int)minValue <= (int)maxValue,
- uint => (uint)minValue <= (uint)maxValue,
- long => (long)minValue <= (long)maxValue,
- ulong => (ulong)minValue <= (ulong)maxValue,
- float => (float)minValue <= (float)maxValue,
- double => (double)minValue <= (double)maxValue,
- _ => throw new ArgumentException("The type of the minimum/maximum values is not supported."),
- };
-
- if (!correctlyOrdered)
- {
- throw new ArgumentException("The minimum value cannot be greater than the maximum value.");
- }
- }
-}
+using System;
+using DSharpPlus.Commands.ContextChecks.ParameterChecks;
+
+namespace DSharpPlus.Commands.ArgumentModifiers;
+
+///
+/// Determines the minimum and maximum values that a parameter can accept.
+///
+[AttributeUsage(AttributeTargets.Parameter)]
+public sealed class MinMaxValueAttribute : ParameterCheckAttribute
+{
+ ///
+ /// The minimum value that this parameter can accept.
+ ///
+ public object? MinValue { get; private init; }
+
+ ///
+ /// The maximum value that this parameter can accept.
+ ///
+ public object? MaxValue { get; private init; }
+
+ ///
+ /// Determines the minimum and maximum values that a parameter can accept.
+ ///
+ public MinMaxValueAttribute(object? minValue = null, object? maxValue = null)
+ {
+ this.MinValue = minValue;
+ this.MaxValue = maxValue;
+
+ if (minValue is not null && maxValue is not null && minValue.GetType() != maxValue.GetType())
+ {
+ throw new ArgumentException("The minimum and maximum values must be of the same type.");
+ }
+
+ if (minValue is null || maxValue is null)
+ {
+ return;
+ }
+
+ bool correctlyOrdered = minValue switch
+ {
+ byte => (byte)minValue <= (byte)maxValue,
+ sbyte => (sbyte)minValue <= (sbyte)maxValue,
+ short => (short)minValue <= (short)maxValue,
+ ushort => (ushort)minValue <= (ushort)maxValue,
+ int => (int)minValue <= (int)maxValue,
+ uint => (uint)minValue <= (uint)maxValue,
+ long => (long)minValue <= (long)maxValue,
+ ulong => (ulong)minValue <= (ulong)maxValue,
+ float => (float)minValue <= (float)maxValue,
+ double => (double)minValue <= (double)maxValue,
+ _ => throw new ArgumentException("The type of the minimum/maximum values is not supported."),
+ };
+
+ if (!correctlyOrdered)
+ {
+ throw new ArgumentException("The minimum value cannot be greater than the maximum value.");
+ }
+ }
+}
diff --git a/DSharpPlus.Commands/ArgumentModifiers/RemainingTextAttribute.cs b/DSharpPlus.Commands/ArgumentModifiers/RemainingTextAttribute.cs
index 5c901ce30..4b8ce68fe 100644
--- a/DSharpPlus.Commands/ArgumentModifiers/RemainingTextAttribute.cs
+++ b/DSharpPlus.Commands/ArgumentModifiers/RemainingTextAttribute.cs
@@ -1,6 +1,6 @@
-using System;
-
-namespace DSharpPlus.Commands.ArgumentModifiers;
-
-[AttributeUsage(AttributeTargets.Parameter)]
-public sealed class RemainingTextAttribute : Attribute;
+using System;
+
+namespace DSharpPlus.Commands.ArgumentModifiers;
+
+[AttributeUsage(AttributeTargets.Parameter)]
+public sealed class RemainingTextAttribute : Attribute;
diff --git a/DSharpPlus.Commands/CommandAttribute.cs b/DSharpPlus.Commands/CommandAttribute.cs
index a51ba0a56..f4bc617d9 100644
--- a/DSharpPlus.Commands/CommandAttribute.cs
+++ b/DSharpPlus.Commands/CommandAttribute.cs
@@ -1,30 +1,30 @@
-using System;
-
-namespace DSharpPlus.Commands;
-
-[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Delegate)]
-public sealed class CommandAttribute : Attribute
-{
- ///
- /// The name of the command.
- ///
- public string Name { get; init; }
-
- ///
- /// Creates a new instance of the class.
- ///
- /// The name of the command.
- public CommandAttribute(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException(nameof(name), "The name of the command cannot be null or whitespace.");
- }
- else if (name.Length is < 1 or > 32)
- {
- throw new ArgumentOutOfRangeException(nameof(name), "The name of the command must be between 1 and 32 characters.");
- }
-
- this.Name = name;
- }
-}
+using System;
+
+namespace DSharpPlus.Commands;
+
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Delegate)]
+public sealed class CommandAttribute : Attribute
+{
+ ///
+ /// The name of the command.
+ ///
+ public string Name { get; init; }
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The name of the command.
+ public CommandAttribute(string name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ throw new ArgumentNullException(nameof(name), "The name of the command cannot be null or whitespace.");
+ }
+ else if (name.Length is < 1 or > 32)
+ {
+ throw new ArgumentOutOfRangeException(nameof(name), "The name of the command must be between 1 and 32 characters.");
+ }
+
+ this.Name = name;
+ }
+}
diff --git a/DSharpPlus.Commands/CommandContext.cs b/DSharpPlus.Commands/CommandContext.cs
index 249e728a9..256f11b2e 100644
--- a/DSharpPlus.Commands/CommandContext.cs
+++ b/DSharpPlus.Commands/CommandContext.cs
@@ -1,138 +1,138 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using DSharpPlus.Commands.Trees;
-using DSharpPlus.Entities;
-
-namespace DSharpPlus.Commands;
-
-///
-/// Represents a base context for application command contexts.
-///
-public abstract record CommandContext : AbstractContext
-{
- ///
- /// The command arguments.
- ///
- public required IReadOnlyDictionary Arguments { get; init; }
-
- ///
- /// The followup messages sent from this interaction.
- ///
- public IReadOnlyDictionary FollowupMessages => this.followupMessages;
- protected Dictionary followupMessages = [];
-
- ///
- public virtual ValueTask RespondAsync(string content) => RespondAsync(new DiscordMessageBuilder().WithContent(content));
-
- ///
- public virtual ValueTask RespondAsync(DiscordEmbed embed) => RespondAsync(new DiscordMessageBuilder().AddEmbed(embed));
-
- ///
- /// Creates a response to this interaction.
- /// You must create a response within 3 seconds of this interaction being executed; if the command has the potential to take more than 3 seconds, use at the start, and edit the response later.
- ///
- /// Content to send in the response.
- /// Embed to send in the response.
- public virtual ValueTask RespondAsync(string content, DiscordEmbed embed) => RespondAsync(new DiscordMessageBuilder().WithContent(content).AddEmbed(embed));
-
- ///
- /// The message builder.
- public abstract ValueTask RespondAsync(IDiscordMessageBuilder builder);
-
- ///
- public virtual ValueTask EditResponseAsync(string content) => EditResponseAsync(new DiscordMessageBuilder().WithContent(content));
-
- ///
- public virtual ValueTask EditResponseAsync(DiscordEmbed embed) => EditResponseAsync(new DiscordMessageBuilder().AddEmbed(embed));
-
- ///
- /// Edits the response.
- ///
- /// Content to send in the response.
- /// Embed to send in the response.
- public virtual ValueTask EditResponseAsync(string content, DiscordEmbed embed)
- => EditResponseAsync(new DiscordMessageBuilder().WithContent(content).AddEmbed(embed));
-
- ///
- /// The message builder.
- public abstract ValueTask EditResponseAsync(IDiscordMessageBuilder builder);
-
- ///
- /// Gets the sent response.
- ///
- /// The sent response.
- public abstract ValueTask GetResponseAsync();
-
- ///
- /// Creates a deferred response to this interaction.
- ///
- public abstract ValueTask DeferResponseAsync();
-
- ///
- /// Deletes the sent response.
- ///
- public abstract ValueTask DeleteResponseAsync();
-
- ///
- public virtual ValueTask FollowupAsync(string content) => FollowupAsync(new DiscordMessageBuilder().WithContent(content));
-
- ///
- public virtual ValueTask FollowupAsync(DiscordEmbed embed) => FollowupAsync(new DiscordMessageBuilder().AddEmbed(embed));
-
- ///
- /// Creates a followup message to the interaction.
- ///
- /// Content to send in the followup message.
- /// Embed to send in the followup message.
- /// The created message.
- public virtual ValueTask FollowupAsync(string content, DiscordEmbed embed)
- => FollowupAsync(new DiscordMessageBuilder().WithContent(content).AddEmbed(embed));
-
- ///
- /// The followup message to be sent.
- public abstract ValueTask FollowupAsync(IDiscordMessageBuilder builder);
-
- ///
- public virtual ValueTask EditFollowupAsync(ulong messageId, string content)
- => EditFollowupAsync(messageId, new DiscordMessageBuilder().WithContent(content));
-
- ///
- public virtual ValueTask EditFollowupAsync(ulong messageId, DiscordEmbed embed)
- => EditFollowupAsync(messageId, new DiscordMessageBuilder().AddEmbed(embed));
-
- ///
- /// Edits a followup message.
- ///
- /// The id of the followup message to edit.
- /// Content to send in the followup message.
- /// Embed to send in the followup message.
- /// The edited message.
- public virtual ValueTask EditFollowupAsync(ulong messageId, string content, DiscordEmbed embed)
- => EditFollowupAsync(messageId, new DiscordMessageBuilder().WithContent(content).AddEmbed(embed));
-
- ///
- /// The id of the followup message to edit.
- /// The message builder.
- public abstract ValueTask EditFollowupAsync(ulong messageId, IDiscordMessageBuilder builder);
-
- ///
- /// Gets a sent followup message from this interaction.
- ///
- /// The id of the followup message to edit.
- /// Whether to ignore the cache and fetch the message from Discord.
- /// The message.
- public abstract ValueTask GetFollowupAsync(ulong messageId, bool ignoreCache = false);
-
- ///
- /// Deletes a followup message sent from this interaction.
- ///
- /// The id of the followup message to delete.
- public abstract ValueTask DeleteFollowupAsync(ulong messageId);
-
- ///
- /// Cast this context to a different one.
- ///
- /// The type to cast to.
- /// This context as T.
- public T As() where T : CommandContext => (T)this;
-}
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using DSharpPlus.Commands.Trees;
+using DSharpPlus.Entities;
+
+namespace DSharpPlus.Commands;
+
+///
+/// Represents a base context for application command contexts.
+///
+public abstract record CommandContext : AbstractContext
+{
+ ///
+ /// The command arguments.
+ ///
+ public required IReadOnlyDictionary Arguments { get; init; }
+
+ ///
+ /// The followup messages sent from this interaction.
+ ///
+ public IReadOnlyDictionary FollowupMessages => this.followupMessages;
+ protected Dictionary followupMessages = [];
+
+ ///
+ public virtual ValueTask RespondAsync(string content) => RespondAsync(new DiscordMessageBuilder().WithContent(content));
+
+ ///
+ public virtual ValueTask RespondAsync(DiscordEmbed embed) => RespondAsync(new DiscordMessageBuilder().AddEmbed(embed));
+
+ ///
+ /// Creates a response to this interaction.
+ /// You must create a response within 3 seconds of this interaction being executed; if the command has the potential to take more than 3 seconds, use at the start, and edit the response later.
+ ///
+ /// Content to send in the response.
+ /// Embed to send in the response.
+ public virtual ValueTask RespondAsync(string content, DiscordEmbed embed) => RespondAsync(new DiscordMessageBuilder().WithContent(content).AddEmbed(embed));
+
+ ///
+ /// The message builder.
+ public abstract ValueTask RespondAsync(IDiscordMessageBuilder builder);
+
+ ///
+ public virtual ValueTask EditResponseAsync(string content) => EditResponseAsync(new DiscordMessageBuilder().WithContent(content));
+
+ ///
+ public virtual ValueTask EditResponseAsync(DiscordEmbed embed) => EditResponseAsync(new DiscordMessageBuilder().AddEmbed(embed));
+
+ ///
+ /// Edits the response.
+ ///
+ /// Content to send in the response.
+ /// Embed to send in the response.
+ public virtual ValueTask EditResponseAsync(string content, DiscordEmbed embed)
+ => EditResponseAsync(new DiscordMessageBuilder().WithContent(content).AddEmbed(embed));
+
+ ///
+ /// The message builder.
+ public abstract ValueTask EditResponseAsync(IDiscordMessageBuilder builder);
+
+ ///
+ /// Gets the sent response.
+ ///
+ /// The sent response.
+ public abstract ValueTask GetResponseAsync();
+
+ ///
+ /// Creates a deferred response to this interaction.
+ ///
+ public abstract ValueTask DeferResponseAsync();
+
+ ///
+ /// Deletes the sent response.
+ ///
+ public abstract ValueTask DeleteResponseAsync();
+
+ ///
+ public virtual ValueTask FollowupAsync(string content) => FollowupAsync(new DiscordMessageBuilder().WithContent(content));
+
+ ///
+ public virtual ValueTask FollowupAsync(DiscordEmbed embed) => FollowupAsync(new DiscordMessageBuilder().AddEmbed(embed));
+
+ ///
+ /// Creates a followup message to the interaction.
+ ///
+ /// Content to send in the followup message.
+ /// Embed to send in the followup message.
+ /// The created message.
+ public virtual ValueTask FollowupAsync(string content, DiscordEmbed embed)
+ => FollowupAsync(new DiscordMessageBuilder().WithContent(content).AddEmbed(embed));
+
+ ///
+ /// The followup message to be sent.
+ public abstract ValueTask FollowupAsync(IDiscordMessageBuilder builder);
+
+ ///
+ public virtual ValueTask EditFollowupAsync(ulong messageId, string content)
+ => EditFollowupAsync(messageId, new DiscordMessageBuilder().WithContent(content));
+
+ ///
+ public virtual ValueTask EditFollowupAsync(ulong messageId, DiscordEmbed embed)
+ => EditFollowupAsync(messageId, new DiscordMessageBuilder().AddEmbed(embed));
+
+ ///
+ /// Edits a followup message.
+ ///
+ /// The id of the followup message to edit.
+ /// Content to send in the followup message.
+ /// Embed to send in the followup message.
+ /// The edited message.
+ public virtual ValueTask EditFollowupAsync(ulong messageId, string content, DiscordEmbed embed)
+ => EditFollowupAsync(messageId, new DiscordMessageBuilder().WithContent(content).AddEmbed(embed));
+
+ ///
+ /// The id of the followup message to edit.
+ /// The message builder.
+ public abstract ValueTask EditFollowupAsync(ulong messageId, IDiscordMessageBuilder builder);
+
+ ///
+ /// Gets a sent followup message from this interaction.
+ ///
+ /// The id of the followup message to edit.
+ /// Whether to ignore the cache and fetch the message from Discord.
+ /// The message.
+ public abstract ValueTask GetFollowupAsync(ulong messageId, bool ignoreCache = false);
+
+ ///
+ /// Deletes a followup message sent from this interaction.
+ ///
+ /// The id of the followup message to delete.
+ public abstract ValueTask DeleteFollowupAsync(ulong messageId);
+
+ ///
+ /// Cast this context to a different one.
+ ///
+ /// The type to cast to.
+ /// This context as T.
+ public T As() where T : CommandContext => (T)this;
+}
diff --git a/DSharpPlus.Commands/CommandsConfiguration.cs b/DSharpPlus.Commands/CommandsConfiguration.cs
index c019abb57..62b1867fd 100644
--- a/DSharpPlus.Commands/CommandsConfiguration.cs
+++ b/DSharpPlus.Commands/CommandsConfiguration.cs
@@ -1,34 +1,34 @@
-namespace DSharpPlus.Commands;
-
-///
-/// The configuration copied to an instance of .
-///
-public sealed record CommandsConfiguration
-{
- ///
- /// The guild id to use for debugging. Leave as 0 to disable.
- ///
- public ulong DebugGuildId { get; set; }
-
- ///
- /// Whether to enable the default command error handler.
- ///
- public bool UseDefaultCommandErrorHandler { get; set; } = true;
-
- ///
- /// Whether to register default command processors when they're not found in the processor list.
- ///
- ///
- /// You may still provide your own custom processors via ,
- /// as this configuration option will only add the default processors if they're not found in the list.
- ///
- public bool RegisterDefaultCommandProcessors { get; set; } = true;
-
- ///
- /// The command executor to use for command execution.
- ///
- ///
- /// The command executor is responsible for executing context checks, making full use of the dependency injection system, executing the command method itself, and handling errors.
- ///
- public ICommandExecutor CommandExecutor { get; set; } = new DefaultCommandExecutor();
-}
+namespace DSharpPlus.Commands;
+
+///
+/// The configuration copied to an instance of .
+///
+public sealed record CommandsConfiguration
+{
+ ///
+ /// The guild id to use for debugging. Leave as 0 to disable.
+ ///
+ public ulong DebugGuildId { get; set; }
+
+ ///
+ /// Whether to enable the default command error handler.
+ ///
+ public bool UseDefaultCommandErrorHandler { get; set; } = true;
+
+ ///
+ /// Whether to register default command processors when they're not found in the processor list.
+ ///
+ ///
+ /// You may still provide your own custom processors via ,
+ /// as this configuration option will only add the default processors if they're not found in the list.
+ ///
+ public bool RegisterDefaultCommandProcessors { get; set; } = true;
+
+ ///
+ /// The command executor to use for command execution.
+ ///
+ ///
+ /// The command executor is responsible for executing context checks, making full use of the dependency injection system, executing the command method itself, and handling errors.
+ ///
+ public ICommandExecutor CommandExecutor { get; set; } = new DefaultCommandExecutor();
+}
diff --git a/DSharpPlus.Commands/CommandsExtension.cs b/DSharpPlus.Commands/CommandsExtension.cs
index e4838a0e7..867b6bdc6 100644
--- a/DSharpPlus.Commands/CommandsExtension.cs
+++ b/DSharpPlus.Commands/CommandsExtension.cs
@@ -1,593 +1,593 @@
-using System;
-using System.Collections.Frozen;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading.Tasks;
-
-using DSharpPlus.AsyncEvents;
-using DSharpPlus.Commands.ContextChecks;
-using DSharpPlus.Commands.ContextChecks.ParameterChecks;
-using DSharpPlus.Commands.EventArgs;
-using DSharpPlus.Commands.Exceptions;
-using DSharpPlus.Commands.Processors;
-using DSharpPlus.Commands.Processors.MessageCommands;
-using DSharpPlus.Commands.Processors.SlashCommands;
-using DSharpPlus.Commands.Processors.TextCommands;
-using DSharpPlus.Commands.Processors.TextCommands.ContextChecks;
-using DSharpPlus.Commands.Processors.UserCommands;
-using DSharpPlus.Commands.Trees;
-using DSharpPlus.Commands.Trees.Metadata;
-using DSharpPlus.Entities;
-using DSharpPlus.Exceptions;
-
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-
-using CheckFunc = System.Func
-<
- object,
- DSharpPlus.Commands.ContextChecks.ContextCheckAttribute,
- DSharpPlus.Commands.CommandContext,
- System.Threading.Tasks.ValueTask
->;
-
-using ParameterCheckFunc = System.Func
-<
- object,
- DSharpPlus.Commands.ContextChecks.ParameterChecks.ParameterCheckAttribute,
- DSharpPlus.Commands.ContextChecks.ParameterChecks.ParameterCheckInfo,
- DSharpPlus.Commands.CommandContext,
- System.Threading.Tasks.ValueTask
->;
-
-namespace DSharpPlus.Commands;
-
-///
-/// An all in one extension for managing commands.
-///
-public sealed class CommandsExtension
-{
- public DiscordClient Client { get; private set; }
-
- ///
- public IServiceProvider ServiceProvider { get; private set; }
-
- ///
- public ulong DebugGuildId { get; init; }
-
- ///
- public bool UseDefaultCommandErrorHandler { get; init; }
-
- ///
- public bool RegisterDefaultCommandProcessors { get; init; }
-
- public ICommandExecutor CommandExecutor { get; init; }
-
- ///
- /// The registered commands that the users can execute.
- ///
- public IReadOnlyDictionary Commands { get; private set; } = new Dictionary();
- private readonly List commandBuilders = [];
-
- ///
- /// All registered command processors.
- ///
- public IReadOnlyDictionary Processors => this.processors;
- private readonly Dictionary processors = [];
-
- public IReadOnlyList Checks => this.checks;
- private readonly List checks = [];
-
- public IReadOnlyList ParameterChecks => this.parameterChecks;
- private readonly List parameterChecks = [];
-
- ///
- /// Executed everytime a command is finished executing.
- ///
- public event AsyncEventHandler CommandExecuted
- {
- add => this.commandExecuted.Register(value);
- remove => this.commandExecuted.Unregister(value);
- }
-
- internal AsyncEvent commandExecuted;
-
- ///
- /// Executed everytime a command has errored.
- ///
- public event AsyncEventHandler CommandErrored
- {
- add => this.commandErrored.Register(value);
- remove => this.commandErrored.Unregister(value);
- }
-
- internal AsyncEvent commandErrored;
-
- ///
- /// Executed before commands are finalized into a read-only state.
- ///
- ///
- /// Apply any mass-mutations to the commands or command parameters here.
- ///
- public event AsyncEventHandler ConfiguringCommands
- {
- add => this.configuringCommands.Register(value);
- remove => this.configuringCommands.Unregister(value);
- }
-
- private AsyncEvent configuringCommands;
-
- ///
- /// Used to log messages from this extension.
- ///
- private ILogger logger;
-
- ///
- /// Creates a new instance of the class.
- ///
- /// The configuration to use.
- internal CommandsExtension(CommandsConfiguration configuration)
- {
- ArgumentNullException.ThrowIfNull(configuration);
-
- this.DebugGuildId = configuration.DebugGuildId;
- this.UseDefaultCommandErrorHandler = configuration.UseDefaultCommandErrorHandler;
- this.RegisterDefaultCommandProcessors = configuration.RegisterDefaultCommandProcessors;
- this.CommandExecutor = configuration.CommandExecutor;
- }
-
- ///
- /// Sets up the extension to use the specified .
- ///
- /// The client to register our event handlers too.
- public void Setup(DiscordClient client)
- {
- if (client is null)
- {
- throw new ArgumentNullException(nameof(client));
- }
- else if (this.Client is not null)
- {
- throw new InvalidOperationException("Commands Extension is already initialized.");
- }
-
- this.Client = client;
- this.ServiceProvider = client.ServiceProvider;
- this.logger = client.ServiceProvider.GetRequiredService>();
-
- DefaultClientErrorHandler errorHandler = new(client.Logger);
- this.commandErrored = new(errorHandler);
- this.commandExecuted = new(errorHandler);
- this.configuringCommands = new(errorHandler);
-
- // TODO: Move this to the IEventHandler system so the Commands namespace
- // will have zero awareness of built-in command processors.
- this.configuringCommands.Register(SlashCommandProcessor.ConfigureCommands);
- if (this.UseDefaultCommandErrorHandler)
- {
- this.CommandErrored += DefaultCommandErrorHandlerAsync;
- }
-
- AddCheck();
- AddCheck();
- AddCheck();
- AddCheck();
- AddCheck();
- AddCheck();
-
- AddParameterCheck();
- AddParameterCheck();
- AddParameterCheck();
- AddParameterCheck();
- }
-
- public void AddCommand(CommandBuilder command) => this.commandBuilders.Add(command);
- public void AddCommand(Delegate commandDelegate, params ulong[] guildIds) => this.commandBuilders.Add(CommandBuilder.From(commandDelegate, guildIds));
- public void AddCommand(Delegate commandDelegate) => this.commandBuilders.Add(CommandBuilder.From(commandDelegate));
- public void AddCommand(Type type, params ulong[] guildIds) => this.commandBuilders.Add(CommandBuilder.From(type, guildIds));
- public void AddCommand(Type type) => this.commandBuilders.Add(CommandBuilder.From(type));
-
- // !type.IsNested || type.DeclaringType?.GetCustomAttribute() is null
- // This is done to prevent nested classes from being added as commands, while still allowing non-command classes containing commands to be added.
- // See https://github.com/DSharpPlus/DSharpPlus/pull/2273#discussion_r2009114568 for more information.
- public void AddCommands(Assembly assembly, params ulong[] guildIds) => AddCommands(assembly.GetTypes().Where(type =>
- !type.IsNested || type.DeclaringType?.GetCustomAttribute() is null), guildIds);
-
- public void AddCommands(Assembly assembly) => AddCommands(assembly.GetTypes().Where(type =>
- !type.IsNested || type.DeclaringType?.GetCustomAttribute() is null));
-
- public void AddCommands(IEnumerable commands) => this.commandBuilders.AddRange(commands);
- public void AddCommands(IEnumerable types) => AddCommands(types, []);
- public void AddCommands(params CommandBuilder[] commands) => this.commandBuilders.AddRange(commands);
- public void AddCommands(Type type, params ulong[] guildIds) => AddCommands([type], guildIds);
- public void AddCommands(Type type) => AddCommands([type]);
- public void AddCommands() => AddCommands([typeof(T)]);
- public void AddCommands(params ulong[] guildIds) => AddCommands([typeof(T)], guildIds);
- public void AddCommands(IEnumerable types, params ulong[] guildIds)
- {
- foreach (Type type in types)
- {
- if (type.GetCustomAttribute() is not null)
- {
- this.Client.Logger.LogDebug("Adding command from type {Type}", type.FullName ?? type.Name);
- this.commandBuilders.Add(CommandBuilder.From(type, guildIds));
- continue;
- }
-
- foreach (MethodInfo method in type.GetMethods())
- {
- if (method.GetCustomAttribute() is not null)
- {
- this.Client.Logger.LogDebug("Adding command from type {Type}", type.FullName ?? type.Name);
- this.commandBuilders.Add(CommandBuilder.From(method, guildIds: guildIds));
- }
- }
- }
- }
-
- ///
- /// Gets a list of commands filtered for a specific command processor
- ///
- /// Processor which is calling this method
- /// Returns a list of valid commands. This list can be empty if no commands are valid for this processor type
- public IReadOnlyList GetCommandsForProcessor(ICommandProcessor processor)
- {
- // Those processors use a different attribute to filter and filter themself
- if (processor is MessageCommandProcessor or UserCommandProcessor)
- {
- return this.Commands.Values.ToList();
- }
-
- Type contextType = processor.ContextType;
- Type processorType = processor.GetType();
- List commands = new(this.Commands.Values.Count());
- foreach (Command command in this.Commands.Values)
- {
- Command? filteredCommand = FilterCommand(command, processorType, contextType);
- if (filteredCommand is not null)
- {
- commands.Add(filteredCommand);
- }
- }
-
- return commands;
- }
-
- private Command? FilterCommand(Command command, Type processorType, Type contextType)
- {
- AllowedProcessorsAttribute? allowedProcessorsAttribute = command.Attributes.OfType().FirstOrDefault();
- if (allowedProcessorsAttribute is not null && !allowedProcessorsAttribute.Processors.Contains(processorType))
- {
- return null;
- }
- else if (command.Method is not null)
- {
- Type methodContextType = command.Method.GetParameters().First().ParameterType;
- if (!methodContextType.IsAssignableTo(contextType) && methodContextType != typeof(CommandContext))
- {
- return null;
- }
- }
-
- List subcommands = new(command.Subcommands.Count);
- foreach (Command subcommand in command.Subcommands)
- {
- Command? filteredSubcommand = FilterCommand(subcommand, processorType, contextType);
- if (filteredSubcommand is not null)
- {
- subcommands.Add(filteredSubcommand);
- }
- }
-
- return command with
- {
- Subcommands = subcommands,
- };
- }
-
- public void AddProcessor(ICommandProcessor processor) => this.processors.Add(processor.GetType(), processor);
- public void AddProcessor() where TProcessor : ICommandProcessor, new() => AddProcessor(new TProcessor());
- public void AddProcessors(params ICommandProcessor[] processors) => AddProcessors((IEnumerable)processors);
- public void AddProcessors(IEnumerable processors)
- {
- foreach (ICommandProcessor processor in processors)
- {
- AddProcessor(processor);
- }
- }
-
- public TProcessor GetProcessor() where TProcessor : ICommandProcessor => (TProcessor)this.processors[typeof(TProcessor)];
- public bool TryGetProcessor([NotNullWhen(true)] out TProcessor? processor) where TProcessor : ICommandProcessor
- {
- if (this.processors.TryGetValue(typeof(TProcessor), out ICommandProcessor? baseProcessor))
- {
- processor = (TProcessor)baseProcessor;
- return true;
- }
-
- processor = default;
- return false;
- }
-
- ///
- /// Adds all public checks from the provided assembly to the extension.
- ///
- public void AddChecks(Assembly assembly)
- {
- foreach (Type t in assembly.GetTypes())
- {
- if (t.GetInterface("DSharpPlus.Commands.ContextChecks.IContextCheck`1") is not null)
- {
- AddCheck(t);
- }
- }
- }
-
- ///
- /// Adds a new check to the extension.
- ///
- public void AddCheck() where T : IContextCheck => AddCheck(typeof(T));
-
- ///
- /// Adds a new check to the extension.
- ///
- public void AddCheck(Type checkType)
- {
- // get all implemented check interfaces, we can pretty easily handle having multiple checks in one type
- foreach (Type t in checkType.GetInterfaces())
- {
- if (t.Namespace != "DSharpPlus.Commands.ContextChecks" || t.Name != "IContextCheck`1")
- {
- continue;
- }
-
- Type attributeType = t.GetGenericArguments()[0];
- MethodInfo method = checkType
- .GetMethods(BindingFlags.Public | BindingFlags.Instance)
- .First(x => x.Name == "ExecuteCheckAsync" && x.GetParameters()[0].ParameterType == attributeType);
-
- // create the func for invoking the check here, during startup
- ParameterExpression check = Expression.Parameter(checkType);
- ParameterExpression attribute = Expression.Parameter(attributeType);
- ParameterExpression context = Expression.Parameter(typeof(CommandContext));
- MethodCallExpression call = Expression.Call(
- instance: check,
- method: method,
- arg0: attribute,
- arg1: context
- );
-
- Type delegateType = typeof(Func<,,,>).MakeGenericType(
- checkType,
- attributeType,
- typeof(CommandContext),
- typeof(ValueTask)
- );
-
- CheckFunc func = Unsafe.As(Expression.Lambda(delegateType, call, check, attribute, context).Compile());
- this.checks.Add(new()
- {
- AttributeType = attributeType,
- CheckType = checkType,
- ExecuteCheckAsync = func,
- });
- }
- }
-
- ///
- /// Adds all parameter checks from the provided assembly to the extension.
- ///
- public void AddParameterChecks(Assembly assembly)
- {
- foreach (Type t in assembly.GetTypes())
- {
- if (t.GetInterface("DSharpPlus.Commands.ContextChecks.ParameterChecks.IParameterCheck`1") is not null)
- {
- AddParameterCheck(t);
- }
- }
- }
-
- ///
- /// Adds a new check to the extension.
- ///
- public void AddParameterCheck() where T : IParameterCheck => AddParameterCheck(typeof(T));
-
- ///
- /// Adds a new check to the extension.
- ///
- public void AddParameterCheck(Type checkType)
- {
- // get all implemented check interfaces, we can pretty easily handle having multiple checks in one type
- foreach (Type t in checkType.GetInterfaces())
- {
- if (t.Namespace != "DSharpPlus.Commands.ContextChecks.ParameterChecks" || t.Name != "IParameterCheck`1")
- {
- continue;
- }
-
- Type attributeType = t.GetGenericArguments()[0];
- MethodInfo method = checkType
- .GetMethods(BindingFlags.Public | BindingFlags.Instance)
- .First(x => x.Name == "ExecuteCheckAsync" && x.GetParameters()[0].ParameterType == attributeType);
-
- // create the func for invoking the check here, during startup
- ParameterExpression check = Expression.Parameter(checkType);
- ParameterExpression attribute = Expression.Parameter(attributeType);
- ParameterExpression info = Expression.Parameter(typeof(ParameterCheckInfo));
- ParameterExpression context = Expression.Parameter(typeof(CommandContext));
- MethodCallExpression call = Expression.Call(
- instance: check,
- method: method,
- arg0: attribute,
- arg1: info,
- arg2: context
- );
-
- Type delegateType = typeof(Func<,,,,>).MakeGenericType(
- checkType,
- attributeType,
- typeof(ParameterCheckInfo),
- typeof(CommandContext),
- typeof(ValueTask)
- );
-
- ParameterCheckFunc func = Unsafe.As(Expression.Lambda(delegateType, call, check, attribute, info, context).Compile());
- this.parameterChecks.Add(
- new()
- {
- AttributeType = attributeType,
- CheckType = checkType,
- ExecuteCheckAsync = func,
- }
- );
- }
- }
-
- public async Task RefreshAsync()
- {
- await BuildCommandsAsync();
-
- if (this.RegisterDefaultCommandProcessors)
- {
- this.processors.TryAdd(typeof(TextCommandProcessor), new TextCommandProcessor());
- this.processors.TryAdd(typeof(SlashCommandProcessor), new SlashCommandProcessor());
- this.processors.TryAdd(typeof(MessageCommandProcessor), new MessageCommandProcessor());
- this.processors.TryAdd(typeof(UserCommandProcessor), new UserCommandProcessor());
- }
-
- if (this.processors.TryGetValue(typeof(UserCommandProcessor), out ICommandProcessor? userProcessor))
- {
- await userProcessor.ConfigureAsync(this);
- }
-
- if (this.processors.TryGetValue(typeof(MessageCommandProcessor), out ICommandProcessor? messageProcessor))
- {
- await messageProcessor.ConfigureAsync(this);
- }
-
- foreach (ICommandProcessor processor in this.processors.Values)
- {
- Type type = processor.GetType();
- if (type == typeof(UserCommandProcessor) || type == typeof(MessageCommandProcessor))
- {
- continue;
- }
-
- await processor.ConfigureAsync(this);
- }
- }
-
- internal async ValueTask BuildCommandsAsync()
- {
- await this.configuringCommands.InvokeAsync(this, new ConfigureCommandsEventArgs() { CommandTrees = this.commandBuilders });
-
- Dictionary commands = [];
- foreach (CommandBuilder commandBuilder in this.commandBuilders)
- {
- try
- {
- Command command = commandBuilder.Build();
- commands.Add(command.Name, command);
- }
- catch (Exception error)
- {
- this.logger.LogError(error, "Failed to build command '{CommandBuilder}'", commandBuilder.FullName);
- }
- }
-
- this.Commands = commands.ToFrozenDictionary();
- }
-
- ///
- /// The default command error handler. Only used if is set to true.
- ///
- /// The extension.
- /// The event arguments containing the exception.
- private static async Task DefaultCommandErrorHandlerAsync(CommandsExtension extension, CommandErroredEventArgs eventArgs)
- {
- StringBuilder stringBuilder = new();
- DiscordMessageBuilder messageBuilder = new();
-
- // Error message
- stringBuilder.Append(eventArgs.Exception switch
- {
- CommandNotFoundException commandNotFoundException => $"Command ``{commandNotFoundException.CommandName}`` was not found.",
- CommandRegistrationFailedException => $"Application commands failed to register.",
- ArgumentParseException argumentParseException when argumentParseException.ConversionResult?.Value is not null =>
- $"Failed to parse argument ``{argumentParseException.Parameter.Name}``: ``{argumentParseException.ConversionResult.Value.ToString() ?? ""}`` is not a valid value. {argumentParseException.Message}",
- ArgumentParseException argumentParseException =>
- $"Failed to parse argument ``{argumentParseException.Parameter.Name}``: {argumentParseException.Message}",
- ChecksFailedException checksFailedException when checksFailedException.Errors.Count == 1 =>
- $"The following error occurred: ``{checksFailedException.Errors[0].ErrorMessage}``",
- ChecksFailedException checksFailedException =>
- $"The following context checks failed: ```\n{string.Join("\n- ", checksFailedException.Errors.Select(x => x.ErrorMessage)).Trim()}\n```.",
- ParameterChecksFailedException checksFailedException when checksFailedException.Errors.Count == 1 =>
- $"The following error occurred: ``{checksFailedException.Errors[0].ErrorMessage}``",
- ParameterChecksFailedException checksFailedException =>
- $"The following context checks failed: ```\n{string.Join("\n- ", checksFailedException.Errors.Select(x => x.ErrorMessage)).Trim()}\n```.",
- DiscordException discordException when discordException.Response is not null && (int)discordException.Response.StatusCode >= 500 && (int)discordException.Response.StatusCode < 600 =>
- $"Discord API error {discordException.Response.StatusCode} occurred: {discordException.JsonMessage ?? "No further information was provided."}",
- DiscordException discordException when discordException.Response is not null =>
- $"Discord API error {discordException.Response.StatusCode} occurred: {discordException.JsonMessage ?? discordException.Message}",
- _ => $"An unexpected error occurred: {eventArgs.Exception.Message}",
- });
-
- // Stack trace
- if (!string.IsNullOrWhiteSpace(eventArgs.Exception.StackTrace))
- {
- // If the stack trace can fit inside a codeblock
- if (8 + eventArgs.Exception.StackTrace.Length + stringBuilder.Length <= 2000)
- {
- stringBuilder.Append($"```\n{eventArgs.Exception.StackTrace}\n```");
- messageBuilder.WithContent(stringBuilder.ToString());
- }
- // If the exception message exceeds the message character limit, cram it all into an attatched file with a simple message in the content.
- else if (stringBuilder.Length >= 2000)
- {
- messageBuilder.WithContent(
- "Exception Message exceeds character limit, see attached file."
- );
- string formattedFile =
- $"{stringBuilder}{Environment.NewLine}{Environment.NewLine}Stack Trace:{Environment.NewLine}{eventArgs.Exception.StackTrace}";
- messageBuilder.AddFile(
- "MessageAndStackTrace.txt",
- new MemoryStream(Encoding.UTF8.GetBytes(formattedFile)),
- AddFileOptions.CloseStream
- );
- }
- // Otherwise, display the exception message in the content and the trace in an attached file
- else
- {
- messageBuilder.WithContent(stringBuilder.ToString());
- messageBuilder.AddFile("StackTrace.txt", new MemoryStream(Encoding.UTF8.GetBytes(eventArgs.Exception.StackTrace)), AddFileOptions.CloseStream);
- }
- }
- // If no stack trace, and the message is still too long, attatch a file with the message and use a simple message in the content.
- else if (stringBuilder.Length >= 2000)
- {
- messageBuilder.WithContent("Exception Message exceeds character limit, see attached file.");
- messageBuilder.AddFile("Message.txt", new MemoryStream(Encoding.UTF8.GetBytes(stringBuilder.ToString())), AddFileOptions.CloseStream);
- }
- // Otherwise, if no stack trace and the Exception message will fit, send the message as content
- else
- {
- messageBuilder.WithContent(stringBuilder.ToString());
- }
-
- if (eventArgs.Context is SlashCommandContext { Interaction.ResponseState: not DiscordInteractionResponseState.Unacknowledged })
- {
- await eventArgs.Context.FollowupAsync(messageBuilder);
- }
- else
- {
- await eventArgs.Context.RespondAsync(messageBuilder);
- }
- }
-}
+using System;
+using System.Collections.Frozen;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+
+using DSharpPlus.AsyncEvents;
+using DSharpPlus.Commands.ContextChecks;
+using DSharpPlus.Commands.ContextChecks.ParameterChecks;
+using DSharpPlus.Commands.EventArgs;
+using DSharpPlus.Commands.Exceptions;
+using DSharpPlus.Commands.Processors;
+using DSharpPlus.Commands.Processors.MessageCommands;
+using DSharpPlus.Commands.Processors.SlashCommands;
+using DSharpPlus.Commands.Processors.TextCommands;
+using DSharpPlus.Commands.Processors.TextCommands.ContextChecks;
+using DSharpPlus.Commands.Processors.UserCommands;
+using DSharpPlus.Commands.Trees;
+using DSharpPlus.Commands.Trees.Metadata;
+using DSharpPlus.Entities;
+using DSharpPlus.Exceptions;
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+using CheckFunc = System.Func
+<
+ object,
+ DSharpPlus.Commands.ContextChecks.ContextCheckAttribute,
+ DSharpPlus.Commands.CommandContext,
+ System.Threading.Tasks.ValueTask
+>;
+
+using ParameterCheckFunc = System.Func
+<
+ object,
+ DSharpPlus.Commands.ContextChecks.ParameterChecks.ParameterCheckAttribute,
+ DSharpPlus.Commands.ContextChecks.ParameterChecks.ParameterCheckInfo,
+ DSharpPlus.Commands.CommandContext,
+ System.Threading.Tasks.ValueTask
+>;
+
+namespace DSharpPlus.Commands;
+
+///
+/// An all in one extension for managing commands.
+///
+public sealed class CommandsExtension
+{
+ public DiscordClient Client { get; private set; }
+
+ ///
+ public IServiceProvider ServiceProvider { get; private set; }
+
+ ///
+ public ulong DebugGuildId { get; init; }
+
+ ///
+ public bool UseDefaultCommandErrorHandler { get; init; }
+
+ ///
+ public bool RegisterDefaultCommandProcessors { get; init; }
+
+ public ICommandExecutor CommandExecutor { get; init; }
+
+ ///
+ /// The registered commands that the users can execute.
+ ///
+ public IReadOnlyDictionary Commands { get; private set; } = new Dictionary();
+ private readonly List commandBuilders = [];
+
+ ///
+ /// All registered command processors.
+ ///
+ public IReadOnlyDictionary Processors => this.processors;
+ private readonly Dictionary processors = [];
+
+ public IReadOnlyList Checks => this.checks;
+ private readonly List checks = [];
+
+ public IReadOnlyList ParameterChecks => this.parameterChecks;
+ private readonly List parameterChecks = [];
+
+ ///
+ /// Executed everytime a command is finished executing.
+ ///
+ public event AsyncEventHandler CommandExecuted
+ {
+ add => this.commandExecuted.Register(value);
+ remove => this.commandExecuted.Unregister(value);
+ }
+
+ internal AsyncEvent commandExecuted;
+
+ ///
+ /// Executed everytime a command has errored.
+ ///
+ public event AsyncEventHandler CommandErrored
+ {
+ add => this.commandErrored.Register(value);
+ remove => this.commandErrored.Unregister(value);
+ }
+
+ internal AsyncEvent commandErrored;
+
+ ///
+ /// Executed before commands are finalized into a read-only state.
+ ///
+ ///
+ /// Apply any mass-mutations to the commands or command parameters here.
+ ///
+ public event AsyncEventHandler ConfiguringCommands
+ {
+ add => this.configuringCommands.Register(value);
+ remove => this.configuringCommands.Unregister(value);
+ }
+
+ private AsyncEvent configuringCommands;
+
+ ///
+ /// Used to log messages from this extension.
+ ///
+ private ILogger logger;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The configuration to use.
+ internal CommandsExtension(CommandsConfiguration configuration)
+ {
+ ArgumentNullException.ThrowIfNull(configuration);
+
+ this.DebugGuildId = configuration.DebugGuildId;
+ this.UseDefaultCommandErrorHandler = configuration.UseDefaultCommandErrorHandler;
+ this.RegisterDefaultCommandProcessors = configuration.RegisterDefaultCommandProcessors;
+ this.CommandExecutor = configuration.CommandExecutor;
+ }
+
+ ///
+ /// Sets up the extension to use the specified .
+ ///
+ /// The client to register our event handlers too.
+ public void Setup(DiscordClient client)
+ {
+ if (client is null)
+ {
+ throw new ArgumentNullException(nameof(client));
+ }
+ else if (this.Client is not null)
+ {
+ throw new InvalidOperationException("Commands Extension is already initialized.");
+ }
+
+ this.Client = client;
+ this.ServiceProvider = client.ServiceProvider;
+ this.logger = client.ServiceProvider.GetRequiredService>();
+
+ DefaultClientErrorHandler errorHandler = new(client.Logger);
+ this.commandErrored = new(errorHandler);
+ this.commandExecuted = new(errorHandler);
+ this.configuringCommands = new(errorHandler);
+
+ // TODO: Move this to the IEventHandler system so the Commands namespace
+ // will have zero awareness of built-in command processors.
+ this.configuringCommands.Register(SlashCommandProcessor.ConfigureCommands);
+ if (this.UseDefaultCommandErrorHandler)
+ {
+ this.CommandErrored += DefaultCommandErrorHandlerAsync;
+ }
+
+ AddCheck();
+ AddCheck();
+ AddCheck();
+ AddCheck();
+ AddCheck();
+ AddCheck();
+
+ AddParameterCheck();
+ AddParameterCheck();
+ AddParameterCheck();
+ AddParameterCheck();
+ }
+
+ public void AddCommand(CommandBuilder command) => this.commandBuilders.Add(command);
+ public void AddCommand(Delegate commandDelegate, params ulong[] guildIds) => this.commandBuilders.Add(CommandBuilder.From(commandDelegate, guildIds));
+ public void AddCommand(Delegate commandDelegate) => this.commandBuilders.Add(CommandBuilder.From(commandDelegate));
+ public void AddCommand(Type type, params ulong[] guildIds) => this.commandBuilders.Add(CommandBuilder.From(type, guildIds));
+ public void AddCommand(Type type) => this.commandBuilders.Add(CommandBuilder.From(type));
+
+ // !type.IsNested || type.DeclaringType?.GetCustomAttribute() is null
+ // This is done to prevent nested classes from being added as commands, while still allowing non-command classes containing commands to be added.
+ // See https://github.com/DSharpPlus/DSharpPlus/pull/2273#discussion_r2009114568 for more information.
+ public void AddCommands(Assembly assembly, params ulong[] guildIds) => AddCommands(assembly.GetTypes().Where(type =>
+ !type.IsNested || type.DeclaringType?.GetCustomAttribute() is null), guildIds);
+
+ public void AddCommands(Assembly assembly) => AddCommands(assembly.GetTypes().Where(type =>
+ !type.IsNested || type.DeclaringType?.GetCustomAttribute() is null));
+
+ public void AddCommands(IEnumerable commands) => this.commandBuilders.AddRange(commands);
+ public void AddCommands(IEnumerable types) => AddCommands(types, []);
+ public void AddCommands(params CommandBuilder[] commands) => this.commandBuilders.AddRange(commands);
+ public void AddCommands(Type type, params ulong[] guildIds) => AddCommands([type], guildIds);
+ public void AddCommands(Type type) => AddCommands([type]);
+ public void AddCommands() => AddCommands([typeof(T)]);
+ public void AddCommands(params ulong[] guildIds) => AddCommands([typeof(T)], guildIds);
+ public void AddCommands(IEnumerable types, params ulong[] guildIds)
+ {
+ foreach (Type type in types)
+ {
+ if (type.GetCustomAttribute() is not null)
+ {
+ this.Client.Logger.LogDebug("Adding command from type {Type}", type.FullName ?? type.Name);
+ this.commandBuilders.Add(CommandBuilder.From(type, guildIds));
+ continue;
+ }
+
+ foreach (MethodInfo method in type.GetMethods())
+ {
+ if (method.GetCustomAttribute() is not null)
+ {
+ this.Client.Logger.LogDebug("Adding command from type {Type}", type.FullName ?? type.Name);
+ this.commandBuilders.Add(CommandBuilder.From(method, guildIds: guildIds));
+ }
+ }
+ }
+ }
+
+ ///
+ /// Gets a list of commands filtered for a specific command processor
+ ///
+ /// Processor which is calling this method
+ /// Returns a list of valid commands. This list can be empty if no commands are valid for this processor type
+ public IReadOnlyList GetCommandsForProcessor(ICommandProcessor processor)
+ {
+ // Those processors use a different attribute to filter and filter themself
+ if (processor is MessageCommandProcessor or UserCommandProcessor)
+ {
+ return this.Commands.Values.ToList();
+ }
+
+ Type contextType = processor.ContextType;
+ Type processorType = processor.GetType();
+ List commands = new(this.Commands.Values.Count());
+ foreach (Command command in this.Commands.Values)
+ {
+ Command? filteredCommand = FilterCommand(command, processorType, contextType);
+ if (filteredCommand is not null)
+ {
+ commands.Add(filteredCommand);
+ }
+ }
+
+ return commands;
+ }
+
+ private Command? FilterCommand(Command command, Type processorType, Type contextType)
+ {
+ AllowedProcessorsAttribute? allowedProcessorsAttribute = command.Attributes.OfType().FirstOrDefault();
+ if (allowedProcessorsAttribute is not null && !allowedProcessorsAttribute.Processors.Contains(processorType))
+ {
+ return null;
+ }
+ else if (command.Method is not null)
+ {
+ Type methodContextType = command.Method.GetParameters().First().ParameterType;
+ if (!methodContextType.IsAssignableTo(contextType) && methodContextType != typeof(CommandContext))
+ {
+ return null;
+ }
+ }
+
+ List subcommands = new(command.Subcommands.Count);
+ foreach (Command subcommand in command.Subcommands)
+ {
+ Command? filteredSubcommand = FilterCommand(subcommand, processorType, contextType);
+ if (filteredSubcommand is not null)
+ {
+ subcommands.Add(filteredSubcommand);
+ }
+ }
+
+ return command with
+ {
+ Subcommands = subcommands,
+ };
+ }
+
+ public void AddProcessor(ICommandProcessor processor) => this.processors.Add(processor.GetType(), processor);
+ public void AddProcessor() where TProcessor : ICommandProcessor, new() => AddProcessor(new TProcessor());
+ public void AddProcessors(params ICommandProcessor[] processors) => AddProcessors((IEnumerable)processors);
+ public void AddProcessors(IEnumerable processors)
+ {
+ foreach (ICommandProcessor processor in processors)
+ {
+ AddProcessor(processor);
+ }
+ }
+
+ public TProcessor GetProcessor() where TProcessor : ICommandProcessor => (TProcessor)this.processors[typeof(TProcessor)];
+ public bool TryGetProcessor([NotNullWhen(true)] out TProcessor? processor) where TProcessor : ICommandProcessor
+ {
+ if (this.processors.TryGetValue(typeof(TProcessor), out ICommandProcessor? baseProcessor))
+ {
+ processor = (TProcessor)baseProcessor;
+ return true;
+ }
+
+ processor = default;
+ return false;
+ }
+
+ ///
+ /// Adds all public checks from the provided assembly to the extension.
+ ///
+ public void AddChecks(Assembly assembly)
+ {
+ foreach (Type t in assembly.GetTypes())
+ {
+ if (t.GetInterface("DSharpPlus.Commands.ContextChecks.IContextCheck`1") is not null)
+ {
+ AddCheck(t);
+ }
+ }
+ }
+
+ ///
+ /// Adds a new check to the extension.
+ ///
+ public void AddCheck() where T : IContextCheck => AddCheck(typeof(T));
+
+ ///
+ /// Adds a new check to the extension.
+ ///
+ public void AddCheck(Type checkType)
+ {
+ // get all implemented check interfaces, we can pretty easily handle having multiple checks in one type
+ foreach (Type t in checkType.GetInterfaces())
+ {
+ if (t.Namespace != "DSharpPlus.Commands.ContextChecks" || t.Name != "IContextCheck`1")
+ {
+ continue;
+ }
+
+ Type attributeType = t.GetGenericArguments()[0];
+ MethodInfo method = checkType
+ .GetMethods(BindingFlags.Public | BindingFlags.Instance)
+ .First(x => x.Name == "ExecuteCheckAsync" && x.GetParameters()[0].ParameterType == attributeType);
+
+ // create the func for invoking the check here, during startup
+ ParameterExpression check = Expression.Parameter(checkType);
+ ParameterExpression attribute = Expression.Parameter(attributeType);
+ ParameterExpression context = Expression.Parameter(typeof(CommandContext));
+ MethodCallExpression call = Expression.Call(
+ instance: check,
+ method: method,
+ arg0: attribute,
+ arg1: context
+ );
+
+ Type delegateType = typeof(Func<,,,>).MakeGenericType(
+ checkType,
+ attributeType,
+ typeof(CommandContext),
+ typeof(ValueTask)
+ );
+
+ CheckFunc func = Unsafe.As(Expression.Lambda(delegateType, call, check, attribute, context).Compile());
+ this.checks.Add(new()
+ {
+ AttributeType = attributeType,
+ CheckType = checkType,
+ ExecuteCheckAsync = func,
+ });
+ }
+ }
+
+ ///
+ /// Adds all parameter checks from the provided assembly to the extension.
+ ///
+ public void AddParameterChecks(Assembly assembly)
+ {
+ foreach (Type t in assembly.GetTypes())
+ {
+ if (t.GetInterface("DSharpPlus.Commands.ContextChecks.ParameterChecks.IParameterCheck`1") is not null)
+ {
+ AddParameterCheck(t);
+ }
+ }
+ }
+
+ ///
+ /// Adds a new check to the extension.
+ ///
+ public void AddParameterCheck() where T : IParameterCheck => AddParameterCheck(typeof(T));
+
+ ///
+ /// Adds a new check to the extension.
+ ///
+ public void AddParameterCheck(Type checkType)
+ {
+ // get all implemented check interfaces, we can pretty easily handle having multiple checks in one type
+ foreach (Type t in checkType.GetInterfaces())
+ {
+ if (t.Namespace != "DSharpPlus.Commands.ContextChecks.ParameterChecks" || t.Name != "IParameterCheck`1")
+ {
+ continue;
+ }
+
+ Type attributeType = t.GetGenericArguments()[0];
+ MethodInfo method = checkType
+ .GetMethods(BindingFlags.Public | BindingFlags.Instance)
+ .First(x => x.Name == "ExecuteCheckAsync" && x.GetParameters()[0].ParameterType == attributeType);
+
+ // create the func for invoking the check here, during startup
+ ParameterExpression check = Expression.Parameter(checkType);
+ ParameterExpression attribute = Expression.Parameter(attributeType);
+ ParameterExpression info = Expression.Parameter(typeof(ParameterCheckInfo));
+ ParameterExpression context = Expression.Parameter(typeof(CommandContext));
+ MethodCallExpression call = Expression.Call(
+ instance: check,
+ method: method,
+ arg0: attribute,
+ arg1: info,
+ arg2: context
+ );
+
+ Type delegateType = typeof(Func<,,,,>).MakeGenericType(
+ checkType,
+ attributeType,
+ typeof(ParameterCheckInfo),
+ typeof(CommandContext),
+ typeof(ValueTask)
+ );
+
+ ParameterCheckFunc func = Unsafe.As(Expression.Lambda(delegateType, call, check, attribute, info, context).Compile());
+ this.parameterChecks.Add(
+ new()
+ {
+ AttributeType = attributeType,
+ CheckType = checkType,
+ ExecuteCheckAsync = func,
+ }
+ );
+ }
+ }
+
+ public async Task RefreshAsync()
+ {
+ await BuildCommandsAsync();
+
+ if (this.RegisterDefaultCommandProcessors)
+ {
+ this.processors.TryAdd(typeof(TextCommandProcessor), new TextCommandProcessor());
+ this.processors.TryAdd(typeof(SlashCommandProcessor), new SlashCommandProcessor());
+ this.processors.TryAdd(typeof(MessageCommandProcessor), new MessageCommandProcessor());
+ this.processors.TryAdd(typeof(UserCommandProcessor), new UserCommandProcessor());
+ }
+
+ if (this.processors.TryGetValue(typeof(UserCommandProcessor), out ICommandProcessor? userProcessor))
+ {
+ await userProcessor.ConfigureAsync(this);
+ }
+
+ if (this.processors.TryGetValue(typeof(MessageCommandProcessor), out ICommandProcessor? messageProcessor))
+ {
+ await messageProcessor.ConfigureAsync(this);
+ }
+
+ foreach (ICommandProcessor processor in this.processors.Values)
+ {
+ Type type = processor.GetType();
+ if (type == typeof(UserCommandProcessor) || type == typeof(MessageCommandProcessor))
+ {
+ continue;
+ }
+
+ await processor.ConfigureAsync(this);
+ }
+ }
+
+ internal async ValueTask BuildCommandsAsync()
+ {
+ await this.configuringCommands.InvokeAsync(this, new ConfigureCommandsEventArgs() { CommandTrees = this.commandBuilders });
+
+ Dictionary commands = [];
+ foreach (CommandBuilder commandBuilder in this.commandBuilders)
+ {
+ try
+ {
+ Command command = commandBuilder.Build();
+ commands.Add(command.Name, command);
+ }
+ catch (Exception error)
+ {
+ this.logger.LogError(error, "Failed to build command '{CommandBuilder}'", commandBuilder.FullName);
+ }
+ }
+
+ this.Commands = commands.ToFrozenDictionary();
+ }
+
+ ///
+ /// The default command error handler. Only used if is set to true.
+ ///
+ /// The extension.
+ /// The event arguments containing the exception.
+ private static async Task DefaultCommandErrorHandlerAsync(CommandsExtension extension, CommandErroredEventArgs eventArgs)
+ {
+ StringBuilder stringBuilder = new();
+ DiscordMessageBuilder messageBuilder = new();
+
+ // Error message
+ stringBuilder.Append(eventArgs.Exception switch
+ {
+ CommandNotFoundException commandNotFoundException => $"Command ``{commandNotFoundException.CommandName}`` was not found.",
+ CommandRegistrationFailedException => $"Application commands failed to register.",
+ ArgumentParseException argumentParseException when argumentParseException.ConversionResult?.Value is not null =>
+ $"Failed to parse argument ``{argumentParseException.Parameter.Name}``: ``{argumentParseException.ConversionResult.Value.ToString() ?? ""}`` is not a valid value. {argumentParseException.Message}",
+ ArgumentParseException argumentParseException =>
+ $"Failed to parse argument ``{argumentParseException.Parameter.Name}``: {argumentParseException.Message}",
+ ChecksFailedException checksFailedException when checksFailedException.Errors.Count == 1 =>
+ $"The following error occurred: ``{checksFailedException.Errors[0].ErrorMessage}``",
+ ChecksFailedException checksFailedException =>
+ $"The following context checks failed: ```\n{string.Join("\n- ", checksFailedException.Errors.Select(x => x.ErrorMessage)).Trim()}\n```.",
+ ParameterChecksFailedException checksFailedException when checksFailedException.Errors.Count == 1 =>
+ $"The following error occurred: ``{checksFailedException.Errors[0].ErrorMessage}``",
+ ParameterChecksFailedException checksFailedException =>
+ $"The following context checks failed: ```\n{string.Join("\n- ", checksFailedException.Errors.Select(x => x.ErrorMessage)).Trim()}\n```.",
+ DiscordException discordException when discordException.Response is not null && (int)discordException.Response.StatusCode >= 500 && (int)discordException.Response.StatusCode < 600 =>
+ $"Discord API error {discordException.Response.StatusCode} occurred: {discordException.JsonMessage ?? "No further information was provided."}",
+ DiscordException discordException when discordException.Response is not null =>
+ $"Discord API error {discordException.Response.StatusCode} occurred: {discordException.JsonMessage ?? discordException.Message}",
+ _ => $"An unexpected error occurred: {eventArgs.Exception.Message}",
+ });
+
+ // Stack trace
+ if (!string.IsNullOrWhiteSpace(eventArgs.Exception.StackTrace))
+ {
+ // If the stack trace can fit inside a codeblock
+ if (8 + eventArgs.Exception.StackTrace.Length + stringBuilder.Length <= 2000)
+ {
+ stringBuilder.Append($"```\n{eventArgs.Exception.StackTrace}\n```");
+ messageBuilder.WithContent(stringBuilder.ToString());
+ }
+ // If the exception message exceeds the message character limit, cram it all into an attatched file with a simple message in the content.
+ else if (stringBuilder.Length >= 2000)
+ {
+ messageBuilder.WithContent(
+ "Exception Message exceeds character limit, see attached file."
+ );
+ string formattedFile =
+ $"{stringBuilder}{Environment.NewLine}{Environment.NewLine}Stack Trace:{Environment.NewLine}{eventArgs.Exception.StackTrace}";
+ messageBuilder.AddFile(
+ "MessageAndStackTrace.txt",
+ new MemoryStream(Encoding.UTF8.GetBytes(formattedFile)),
+ AddFileOptions.CloseStream
+ );
+ }
+ // Otherwise, display the exception message in the content and the trace in an attached file
+ else
+ {
+ messageBuilder.WithContent(stringBuilder.ToString());
+ messageBuilder.AddFile("StackTrace.txt", new MemoryStream(Encoding.UTF8.GetBytes(eventArgs.Exception.StackTrace)), AddFileOptions.CloseStream);
+ }
+ }
+ // If no stack trace, and the message is still too long, attatch a file with the message and use a simple message in the content.
+ else if (stringBuilder.Length >= 2000)
+ {
+ messageBuilder.WithContent("Exception Message exceeds character limit, see attached file.");
+ messageBuilder.AddFile("Message.txt", new MemoryStream(Encoding.UTF8.GetBytes(stringBuilder.ToString())), AddFileOptions.CloseStream);
+ }
+ // Otherwise, if no stack trace and the Exception message will fit, send the message as content
+ else
+ {
+ messageBuilder.WithContent(stringBuilder.ToString());
+ }
+
+ if (eventArgs.Context is SlashCommandContext { Interaction.ResponseState: not DiscordInteractionResponseState.Unacknowledged })
+ {
+ await eventArgs.Context.FollowupAsync(messageBuilder);
+ }
+ else
+ {
+ await eventArgs.Context.RespondAsync(messageBuilder);
+ }
+ }
+}
diff --git a/DSharpPlus.Commands/ContextChecks/ContextCheckAttribute.cs b/DSharpPlus.Commands/ContextChecks/ContextCheckAttribute.cs
index befc6002c..92055ae91 100644
--- a/DSharpPlus.Commands/ContextChecks/ContextCheckAttribute.cs
+++ b/DSharpPlus.Commands/ContextChecks/ContextCheckAttribute.cs
@@ -1,6 +1,6 @@
-using System;
-
-namespace DSharpPlus.Commands.ContextChecks;
-
-[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
-public abstract class ContextCheckAttribute : Attribute;
+using System;
+
+namespace DSharpPlus.Commands.ContextChecks;
+
+[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
+public abstract class ContextCheckAttribute : Attribute;
diff --git a/DSharpPlus.Commands/ContextChecks/ContextCheckFailedData.cs b/DSharpPlus.Commands/ContextChecks/ContextCheckFailedData.cs
index 369eb3d34..a973b0970 100644
--- a/DSharpPlus.Commands/ContextChecks/ContextCheckFailedData.cs
+++ b/DSharpPlus.Commands/ContextChecks/ContextCheckFailedData.cs
@@ -1,13 +1,13 @@
-using System;
-
-namespace DSharpPlus.Commands.ContextChecks;
-
-///
-/// Represents data for when a context check fails execution.
-///
-public sealed class ContextCheckFailedData
-{
- public required ContextCheckAttribute ContextCheckAttribute { get; init; }
- public required string ErrorMessage { get; init; }
- public Exception? Exception { get; init; }
-}
+using System;
+
+namespace DSharpPlus.Commands.ContextChecks;
+
+///
+/// Represents data for when a context check fails execution.
+///
+public sealed class ContextCheckFailedData
+{
+ public required ContextCheckAttribute ContextCheckAttribute { get; init; }
+ public required string ErrorMessage { get; init; }
+ public Exception? Exception { get; init; }
+}
diff --git a/DSharpPlus.Commands/ContextChecks/ContextCheckMapEntry.cs b/DSharpPlus.Commands/ContextChecks/ContextCheckMapEntry.cs
index c1bb815c4..9259c8252 100644
--- a/DSharpPlus.Commands/ContextChecks/ContextCheckMapEntry.cs
+++ b/DSharpPlus.Commands/ContextChecks/ContextCheckMapEntry.cs
@@ -1,18 +1,18 @@
-using System;
-using System.Threading.Tasks;
-
-namespace DSharpPlus.Commands.ContextChecks;
-
-///
-/// Represents an entry in a map of attributes to check types. we can't just do this as a dictionary because one attribute may
-/// key multiple different checks.
-///
-public readonly record struct ContextCheckMapEntry
-{
- public required Type AttributeType { get; init; }
-
- public required Type CheckType { get; init; }
-
- // we cache this here so that we don't have to deal with it every invocation.
- public required Func