From 11d56186de73bd834a892968b8044d5cd9aa2e66 Mon Sep 17 00:00:00 2001 From: KubaZ2 Date: Sun, 25 Aug 2024 22:33:36 +0200 Subject: [PATCH] Merge alpha --- .github/workflows/build.yml | 5 + Documentation/guides/advanced/sharding.md | 6 +- Documentation/guides/events/first-events.md | 6 +- Documentation/guides/events/intents.md | 4 +- .../guides/getting-started/coding.md | 6 +- .../Introduction/MessageCommandModule.cs | 1 - .../Introduction/UserCommandModule.cs | 1 - .../application-commands/introduction.md | 6 +- .../application-commands/localizations.md | 4 +- .../component-interactions/introduction.md | 6 +- .../services/text-commands/introduction.md | 6 +- .../NetCord.Hosting.AspNetCore.csproj | 2 +- .../ApplicationCommandInteractionHandler.cs | 7 +- .../ApplicationCommandResultHandler.cs | 34 ++ .../ApplicationCommandServiceOptions.cs | 36 +- .../AutocompleteInteractionHandler.cs | 7 +- .../AutocompleteInteractionResultHandler.cs | 25 ++ .../IApplicationCommandResultHandler.cs | 12 + .../IAutocompleteInteractionResultHandler.cs | 12 + .../Commands/CommandHandler.cs | 7 +- .../Commands/CommandResultHandler.cs | 32 ++ .../Commands/CommandServiceOptions.cs | 24 +- .../Commands/ICommandResultHandler.cs | 12 + .../ComponentInteractionHandler.cs | 7 +- .../ComponentInteractionResultHandler.cs | 34 ++ .../ComponentInteractionServiceOptions.cs | 21 +- .../IComponentInteractionResultHandler.cs | 12 + .../NetCord.Hosting.Services.csproj | 2 +- .../NetCord.Hosting/NetCord.Hosting.csproj | 2 +- NetCord.Services/Commands/CommandService.cs | 12 +- NetCord.Services/NetCord.Services.csproj | 2 +- NetCord/ApplicationEmoji.cs | 9 + NetCord/Attachment.cs | 5 + NetCord/AuditLogEvent.cs | 35 ++ NetCord/AutoModerationActionType.cs | 1 + NetCord/AutoModerationRuleEventType.cs | 1 + NetCord/AutoModerationRuleTriggerType.cs | 1 + NetCord/AvatarDecorationData.cs | 12 + NetCord/CodeBlock.cs | 2 +- NetCord/Components/Button.cs | 2 +- NetCord/Components/IButton.cs | 3 +- NetCord/Components/ICustomizableButton.cs | 7 + NetCord/Components/LinkButton.cs | 2 +- NetCord/Components/PremiumButton.cs | 10 + NetCord/CustomEmoji.cs | 67 ++++ NetCord/EmbedType.cs | 2 +- NetCord/Gateway/AuditLogEntry.cs | 10 +- .../EventArgs/GuildInviteDeleteEventArgs.cs | 12 - .../EventArgs/InviteDeleteEventArgs.cs | 12 + .../EventArgs/MessageReactionAddEventArgs.cs | 6 + .../MessageReactionRemoveEventArgs.cs | 4 + NetCord/Gateway/GatewayClient.cs | 16 +- NetCord/Gateway/GatewayIntents.cs | 2 +- .../GuildJoinRequestFormResponseFieldType.cs | 2 +- NetCord/Gateway/GuildJoinRequestStatus.cs | 2 +- NetCord/Gateway/IPartialMessage.cs | 339 ------------------ NetCord/Gateway/{GuildInvite.cs => Invite.cs} | 24 +- ...ntArgs.cs => JsonInviteDeleteEventArgs.cs} | 2 +- .../JsonMessageReactionAddEventArgs.cs | 9 + .../JsonMessageReactionRemoveEventArgs.cs | 6 + .../{JsonGuildInvite.cs => JsonInvite.cs} | 7 +- NetCord/Gateway/Message.cs | 48 ++- NetCord/Gateway/Platform.cs | 2 +- NetCord/Gateway/ShardedGatewayClient.cs | 32 +- NetCord/GuildEmoji.cs | 66 +--- NetCord/GuildUser.cs | 6 + NetCord/{IGuildInvite.cs => IInvite.cs} | 5 +- NetCord/ImageUrl.cs | 4 +- NetCord/IntegrationType.cs | 2 +- NetCord/Interaction.cs | 6 + NetCord/InteractionGuildReference.cs | 14 + ...nviteTargetType.cs => InviteTargetType.cs} | 2 +- NetCord/InviteType.cs | 8 + ...ttachmentPropertiesIEnumerableConverter.cs | 5 + ...Handling.cs => SafeStringEnumConverter.cs} | 33 +- NetCord/JsonModels/JsonAttachment.cs | 3 + .../JsonModels/JsonAvatarDecorationData.cs | 12 + NetCord/JsonModels/JsonComponent.cs | 3 + NetCord/JsonModels/JsonGuildUser.cs | 3 + NetCord/JsonModels/JsonInteraction.cs | 3 + .../JsonInteractionGuildReference.cs | 12 + NetCord/JsonModels/JsonMessage.cs | 10 +- NetCord/JsonModels/JsonMessageCall.cs | 12 + NetCord/JsonModels/JsonMessageSnapshot.cs | 9 + .../JsonModels/JsonMessageSnapshotMessage.cs | 33 ++ .../JsonSelectMenuDefaultValueType.cs | 2 +- NetCord/JsonModels/JsonUser.cs | 4 +- NetCord/MessagePollProperties.cs | 28 +- NetCord/MessageReferenceType.cs | 7 + NetCord/MessageSnapshot.cs | 11 + NetCord/MessageSnapshotMessage.cs | 57 +++ NetCord/NetCord.csproj | 2 +- NetCord/PartialGuildUser.cs | 23 +- NetCord/Permissions.cs | 5 + NetCord/ReactionType.cs | 7 + NetCord/Rest/ApplicationEmojiOptions.cs | 14 + NetCord/Rest/ApplicationEmojiProperties.cs | 12 + NetCord/Rest/AttachmentProperties.cs | 5 + .../ComponentProperties/ButtonProperties.cs | 2 +- .../ComponentProperties/IButtonProperties.cs | 23 +- .../ICustomizableButtonProperties.cs | 14 + .../LinkButtonProperties.cs | 7 +- .../PremiumButtonProperties.cs | 19 + NetCord/Rest/ConnectionType.cs | 11 +- NetCord/Rest/EmbedImageProperties.cs | 9 - .../Rest/ForumGuildThreadMessageProperties.cs | 2 +- NetCord/Rest/IMessageProperties.cs | 16 + NetCord/Rest/InteractionCallback.cs | 6 - NetCord/Rest/InteractionCallbackType.cs | 5 - NetCord/Rest/InteractionMessageProperties.cs | 2 +- ...nviteProperties.cs => InviteProperties.cs} | 4 +- ...onRestGuildInvite.cs => JsonRestInvite.cs} | 7 +- NetCord/Rest/MentionableValueProperties.cs | 2 +- NetCord/Rest/MessageCall.cs | 12 + NetCord/Rest/MessageProperties.cs | 2 +- .../MessageReactionsPaginationProperties.cs | 9 + NetCord/Rest/MessageReferenceProperties.cs | 38 +- NetCord/Rest/ReplyMessageProperties.cs | 6 +- NetCord/Rest/RestAuditLogEntry.cs | 2 +- NetCord/Rest/RestClient.AuditLog.cs | 6 +- NetCord/Rest/RestClient.Channel.cs | 48 +-- NetCord/Rest/RestClient.Emoji.cs | 31 ++ NetCord/Rest/RestClient.Guild.cs | 4 +- NetCord/Rest/RestClient.Invite.cs | 14 +- NetCord/Rest/RestClient.Poll.cs | 4 +- NetCord/Rest/RestGuildInvite.cs | 16 +- NetCord/Rest/RestMessage.cs | 21 +- NetCord/Rest/WebhookMessageProperties.cs | 2 +- NetCord/Serialization.cs | 13 +- NetCord/TeamRole.cs | 2 +- NetCord/User.cs | 23 +- NetCord/UserStatusType.cs | 2 +- .../RestClientMethodAliasesGenerator.cs | 2 +- .../CustomSlashCommandResultHandler.cs | 20 ++ Tests/NetCord.Test.Hosting/Program.cs | 5 +- .../Commands/Administrative/BanCommands.cs | 2 +- .../Commands/Administrative/MuteCommands.cs | 2 +- Tests/NetCord.Test/Commands/NormalCommands.cs | 4 +- .../NetCord.Test/Commands/StrangeCommands.cs | 10 +- .../localizations/localization.pl.pl.pl.json | 71 ---- 140 files changed, 1139 insertions(+), 836 deletions(-) create mode 100644 Hosting/NetCord.Hosting.Services/ApplicationCommands/ApplicationCommandResultHandler.cs create mode 100644 Hosting/NetCord.Hosting.Services/ApplicationCommands/AutocompleteInteractionResultHandler.cs create mode 100644 Hosting/NetCord.Hosting.Services/ApplicationCommands/IApplicationCommandResultHandler.cs create mode 100644 Hosting/NetCord.Hosting.Services/ApplicationCommands/IAutocompleteInteractionResultHandler.cs create mode 100644 Hosting/NetCord.Hosting.Services/Commands/CommandResultHandler.cs create mode 100644 Hosting/NetCord.Hosting.Services/Commands/ICommandResultHandler.cs create mode 100644 Hosting/NetCord.Hosting.Services/ComponentInteractions/ComponentInteractionResultHandler.cs create mode 100644 Hosting/NetCord.Hosting.Services/ComponentInteractions/IComponentInteractionResultHandler.cs create mode 100644 NetCord/ApplicationEmoji.cs create mode 100644 NetCord/AvatarDecorationData.cs create mode 100644 NetCord/Components/ICustomizableButton.cs create mode 100644 NetCord/Components/PremiumButton.cs create mode 100644 NetCord/CustomEmoji.cs delete mode 100644 NetCord/Gateway/EventArgs/GuildInviteDeleteEventArgs.cs create mode 100644 NetCord/Gateway/EventArgs/InviteDeleteEventArgs.cs delete mode 100644 NetCord/Gateway/IPartialMessage.cs rename NetCord/Gateway/{GuildInvite.cs => Invite.cs} (61%) rename NetCord/Gateway/JsonModels/EventArgs/{JsonGuildInviteDeleteEventArgs.cs => JsonInviteDeleteEventArgs.cs} (88%) rename NetCord/Gateway/JsonModels/{JsonGuildInvite.cs => JsonInvite.cs} (87%) rename NetCord/{IGuildInvite.cs => IInvite.cs} (79%) create mode 100644 NetCord/InteractionGuildReference.cs rename NetCord/{GuildInviteTargetType.cs => InviteTargetType.cs} (67%) create mode 100644 NetCord/InviteType.cs rename NetCord/JsonConverters/{StringEnumConverterWithErrorHandling.cs => SafeStringEnumConverter.cs} (70%) create mode 100644 NetCord/JsonModels/JsonAvatarDecorationData.cs create mode 100644 NetCord/JsonModels/JsonInteractionGuildReference.cs create mode 100644 NetCord/JsonModels/JsonMessageCall.cs create mode 100644 NetCord/JsonModels/JsonMessageSnapshot.cs create mode 100644 NetCord/JsonModels/JsonMessageSnapshotMessage.cs create mode 100644 NetCord/MessageReferenceType.cs create mode 100644 NetCord/MessageSnapshot.cs create mode 100644 NetCord/MessageSnapshotMessage.cs create mode 100644 NetCord/ReactionType.cs create mode 100644 NetCord/Rest/ApplicationEmojiOptions.cs create mode 100644 NetCord/Rest/ApplicationEmojiProperties.cs create mode 100644 NetCord/Rest/ComponentProperties/ICustomizableButtonProperties.cs create mode 100644 NetCord/Rest/ComponentProperties/PremiumButtonProperties.cs create mode 100644 NetCord/Rest/IMessageProperties.cs rename NetCord/Rest/{GuildInviteProperties.cs => InviteProperties.cs} (85%) rename NetCord/Rest/JsonModels/{JsonRestGuildInvite.cs => JsonRestInvite.cs} (90%) create mode 100644 NetCord/Rest/MessageCall.cs create mode 100644 NetCord/Rest/MessageReactionsPaginationProperties.cs create mode 100644 Tests/NetCord.Test.Hosting/CustomSlashCommandResultHandler.cs delete mode 100644 Tests/NetCord.Test/localizations/localization.pl.pl.pl.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a9db2811..40f83e57 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,6 +5,11 @@ on: branches-ignore: - stable - alpha + pull_request: + types: [opened, synchronize] + branches: + - stable + - alpha jobs: build: diff --git a/Documentation/guides/advanced/sharding.md b/Documentation/guides/advanced/sharding.md index 7d505518..c1bdfdc8 100644 --- a/Documentation/guides/advanced/sharding.md +++ b/Documentation/guides/advanced/sharding.md @@ -10,14 +10,14 @@ Sharding is splitting your bot into multiple @"NetCord.Gateway.GatewayClient"s. ## How to shard? -## [Hosting](#tab/hosting) +## [Generic Host](#tab/generic-host) -With hosting, to start sharding, instead of calling @NetCord.Hosting.Gateway.GatewayClientHostBuilderExtensions.UseDiscordGateway(Microsoft.Extensions.Hosting.IHostBuilder), you need to call @NetCord.Hosting.Gateway.ShardedGatewayClientHostBuilderExtensions.UseDiscordShardedGateway(Microsoft.Extensions.Hosting.IHostBuilder). Example: +To start sharding with the generic host, instead of calling @NetCord.Hosting.Gateway.GatewayClientHostBuilderExtensions.UseDiscordGateway(Microsoft.Extensions.Hosting.IHostBuilder), you need to call @NetCord.Hosting.Gateway.ShardedGatewayClientHostBuilderExtensions.UseDiscordShardedGateway(Microsoft.Extensions.Hosting.IHostBuilder). Example: [!code-cs[Program.cs](ShardingHosting/Program.cs)] Also note that you need to use @NetCord.Hosting.Gateway.IShardedGatewayEventHandler or @NetCord.Hosting.Gateway.IShardedGatewayEventHandler`1 instead of @NetCord.Hosting.Gateway.IGatewayEventHandler or @NetCord.Hosting.Gateway.IGatewayEventHandler`1 for event handlers. You also need to use @NetCord.Hosting.Gateway.GatewayEventHandlerServiceCollectionExtensions.AddShardedGatewayEventHandlers(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Reflection.Assembly) to add event handlers. -## [Without Hosting](#tab/without-hosting) +## [Bare Bones](#tab/bare-bones) To start sharding, you need to create an instance of @NetCord.Gateway.ShardedGatewayClient. Its usage is very similar to @NetCord.Gateway.GatewayClient. Example: [!code-cs[Program.cs](Sharding/Program.cs)] diff --git a/Documentation/guides/events/first-events.md b/Documentation/guides/events/first-events.md index c6ee916a..f658ccee 100644 --- a/Documentation/guides/events/first-events.md +++ b/Documentation/guides/events/first-events.md @@ -1,8 +1,8 @@ # First Events -## [Hosting](#tab/hosting) +## [Generic Host](#tab/generic-host) -With hosting, the preferred way to receive events is by implementing @NetCord.Hosting.Gateway.IGatewayEventHandler or @NetCord.Hosting.Gateway.IGatewayEventHandler`1. +The preferred way to receive events with the generic host is by implementing @NetCord.Hosting.Gateway.IGatewayEventHandler or @NetCord.Hosting.Gateway.IGatewayEventHandler`1. First, use @NetCord.Hosting.Gateway.GatewayEventHandlerServiceCollectionExtensions.AddGatewayEventHandlers(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Reflection.Assembly) to add all event handlers in an assembly. You also need to call @NetCord.Hosting.Gateway.GatewayEventHandlerHostExtensions.UseGatewayEventHandlers(Microsoft.Extensions.Hosting.IHost) to bind the handlers to the client. [!code-cs[Program.cs](FirstEventsHosting/Program.cs?highlight=18,21)] @@ -28,7 +28,7 @@ Other events work similar to these. You can play with them if you want! > [!NOTE] > When using @NetCord.Gateway.ShardedGatewayClient, you need to implement @NetCord.Hosting.Gateway.IShardedGatewayEventHandler or @NetCord.Hosting.Gateway.IShardedGatewayEventHandler`1 instead. You also need to use @NetCord.Hosting.Gateway.GatewayEventHandlerServiceCollectionExtensions.AddShardedGatewayEventHandlers(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Reflection.Assembly) to add event handlers instead. -## [Without Hosting](#tab/without-hosting) +## [Bare Bones](#tab/bare-bones) ### MessageCreate Event To listen to the event, add the following lines before `client.StartAsync()`! diff --git a/Documentation/guides/events/intents.md b/Documentation/guides/events/intents.md index 0f60ddd5..058ceeb9 100644 --- a/Documentation/guides/events/intents.md +++ b/Documentation/guides/events/intents.md @@ -12,10 +12,10 @@ Privileged intents are intents that you need to enable in [Discord Developer Por Intents in NetCord are handled by @NetCord.Gateway.GatewayIntents. You specify them like this: -## [Hosting](#tab/hosting) +## [Generic Host](#tab/generic-host) [!code-cs[Program.cs](IntentsHosting/Program.cs?highlight=6#L6-L13)] -## [Without Hosting](#tab/without-hosting) +## [Bare Bones](#tab/bare-bones) [!code-cs[Program.cs](Intents/Program.cs?highlight=3#L4-L7)] *** diff --git a/Documentation/guides/getting-started/coding.md b/Documentation/guides/getting-started/coding.md index 803fc756..dee3770f 100644 --- a/Documentation/guides/getting-started/coding.md +++ b/Documentation/guides/getting-started/coding.md @@ -10,9 +10,9 @@ Before we start, you need a token of your bot... so you need to go to the [Disco > [!IMPORTANT] > You should never give your token to anybody. -## [Hosting](#tab/hosting) +## [Generic Host](#tab/generic-host) -With hosting, you can just use @NetCord.Hosting.Gateway.GatewayClientHostBuilderExtensions.UseDiscordGateway(Microsoft.Extensions.Hosting.IHostBuilder) to add your bot to the host. Quite easy, right? +With the generic host, you can just use @NetCord.Hosting.Gateway.GatewayClientHostBuilderExtensions.UseDiscordGateway(Microsoft.Extensions.Hosting.IHostBuilder) to add your bot to the host. Quite easy, right? [!code-cs[Program.cs](CodingHosting/Program.cs)] Also note that the token needs to be stored in the configuration. You can for example use `appsettings.json` file. It should look like this: @@ -21,7 +21,7 @@ Also note that the token needs to be stored in the configuration. You can for ex Now, when you run the code, your bot should be online! ![](../../images/coding_BotOnline.png) -## [Without Hosting](#tab/without-hosting) +## [Bare Bones](#tab/bare-bones) Add the following lines to file `Program.cs`. [!code-cs[Program.cs](Coding/Program.cs#L1-L4)] diff --git a/Documentation/guides/services/application-commands/Introduction/MessageCommandModule.cs b/Documentation/guides/services/application-commands/Introduction/MessageCommandModule.cs index a6e9058d..0a0ac78e 100644 --- a/Documentation/guides/services/application-commands/Introduction/MessageCommandModule.cs +++ b/Documentation/guides/services/application-commands/Introduction/MessageCommandModule.cs @@ -7,4 +7,3 @@ public class MessageCommandModule : ApplicationCommandModule Context.Target.CreatedAt.ToString(); } - diff --git a/Documentation/guides/services/application-commands/Introduction/UserCommandModule.cs b/Documentation/guides/services/application-commands/Introduction/UserCommandModule.cs index 67ca082b..28d2c8a5 100644 --- a/Documentation/guides/services/application-commands/Introduction/UserCommandModule.cs +++ b/Documentation/guides/services/application-commands/Introduction/UserCommandModule.cs @@ -7,4 +7,3 @@ public class UserCommandModule : ApplicationCommandModule [UserCommand("ID")] public string Id() => Context.Target.Id.ToString(); } - diff --git a/Documentation/guides/services/application-commands/introduction.md b/Documentation/guides/services/application-commands/introduction.md index 75c64060..de5a98cd 100644 --- a/Documentation/guides/services/application-commands/introduction.md +++ b/Documentation/guides/services/application-commands/introduction.md @@ -8,12 +8,12 @@ > > **must** be lowercase. -## [Hosting](#tab/hosting) +## [Generic Host](#tab/generic-host) -With hosting, adding application commands is very easy. Use @NetCord.Hosting.Services.ApplicationCommands.ApplicationCommandServiceHostBuilderExtensions.UseApplicationCommands``2(Microsoft.Extensions.Hosting.IHostBuilder) to add an application command service to your host builder. Then, use @NetCord.Hosting.Services.ApplicationCommands.ApplicationCommandServiceHostExtensions.AddSlashCommand*, @NetCord.Hosting.Services.ApplicationCommands.ApplicationCommandServiceHostExtensions.AddUserCommand* or @NetCord.Hosting.Services.ApplicationCommands.ApplicationCommandServiceHostExtensions.AddMessageCommand* to add an application command using the ASP.NET Core minimal APIs way and/or use @NetCord.Hosting.Services.ServicesHostExtensions.AddModules(Microsoft.Extensions.Hosting.IHost,System.Reflection.Assembly) to add modules from an assembly. You also need to use @NetCord.Hosting.Gateway.GatewayEventHandlerHostExtensions.UseGatewayEventHandlers(Microsoft.Extensions.Hosting.IHost) to bind the service event handlers. +Adding application commands with the generic host is very easy. Use @NetCord.Hosting.Services.ApplicationCommands.ApplicationCommandServiceHostBuilderExtensions.UseApplicationCommands``2(Microsoft.Extensions.Hosting.IHostBuilder) to add an application command service to your host builder. Then, use @NetCord.Hosting.Services.ApplicationCommands.ApplicationCommandServiceHostExtensions.AddSlashCommand*, @NetCord.Hosting.Services.ApplicationCommands.ApplicationCommandServiceHostExtensions.AddUserCommand* or @NetCord.Hosting.Services.ApplicationCommands.ApplicationCommandServiceHostExtensions.AddMessageCommand* to add an application command using the ASP.NET Core minimal APIs way and/or use @NetCord.Hosting.Services.ServicesHostExtensions.AddModules(Microsoft.Extensions.Hosting.IHost,System.Reflection.Assembly) to add modules from an assembly. You also need to use @NetCord.Hosting.Gateway.GatewayEventHandlerHostExtensions.UseGatewayEventHandlers(Microsoft.Extensions.Hosting.IHost) to bind the service event handlers. [!code-cs[Program.cs](IntroductionHosting/Program.cs?highlight=11-13,16-20)] -## [Without Hosting](#tab/without-hosting) +## [Bare Bones](#tab/bare-bones) First, add the following lines to using the section. [!code-cs[Program.cs](Introduction/Program.cs#L4-L5)] diff --git a/Documentation/guides/services/application-commands/localizations.md b/Documentation/guides/services/application-commands/localizations.md index d741fbb2..072f81b6 100644 --- a/Documentation/guides/services/application-commands/localizations.md +++ b/Documentation/guides/services/application-commands/localizations.md @@ -6,10 +6,10 @@ To localize application commands, you need to use @NetCord.Services.ApplicationC The samples below show how to specify the @NetCord.Services.ApplicationCommands.JsonLocalizationsProvider. -## [Hosting](#tab/hosting) +## [Generic Host](#tab/generic-host) [!code-cs[Program.cs](LocalizationsHosting/Program.cs?highlight=6#L9-L16)] -## [Without Hosting](#tab/without-hosting) +## [Bare Bones](#tab/bare-bones) [!code-cs[Program.cs](Localizations/Program.cs?highlight=3#L12-L15)] *** diff --git a/Documentation/guides/services/component-interactions/introduction.md b/Documentation/guides/services/component-interactions/introduction.md index a0e3ddb9..709df467 100644 --- a/Documentation/guides/services/component-interactions/introduction.md +++ b/Documentation/guides/services/component-interactions/introduction.md @@ -1,11 +1,11 @@ # Introduction -## [Hosting](#tab/hosting) +## [Generic Host](#tab/generic-host) -With hosting, adding component interactions is very easy. Use @NetCord.Hosting.Services.ComponentInteractions.ComponentInteractionServiceHostBuilderExtensions.UseComponentInteractions``2(Microsoft.Extensions.Hosting.IHostBuilder) to add a component interaction service to your host builder. Then, use @NetCord.Hosting.Services.ComponentInteractions.ComponentInteractionServiceHostExtensions.AddComponentInteraction* to add a component interaction using the ASP.NET Core minimal APIs way and/or use @NetCord.Hosting.Services.ServicesHostExtensions.AddModules(Microsoft.Extensions.Hosting.IHost,System.Reflection.Assembly) to add modules from an assembly. You also need to use @NetCord.Hosting.Gateway.GatewayEventHandlerHostExtensions.UseGatewayEventHandlers(Microsoft.Extensions.Hosting.IHost) to bind the service event handlers. +Adding component interactions with the generic host is very easy. Use @NetCord.Hosting.Services.ComponentInteractions.ComponentInteractionServiceHostBuilderExtensions.UseComponentInteractions``2(Microsoft.Extensions.Hosting.IHostBuilder) to add a component interaction service to your host builder. Then, use @NetCord.Hosting.Services.ComponentInteractions.ComponentInteractionServiceHostExtensions.AddComponentInteraction* to add a component interaction using the ASP.NET Core minimal APIs way and/or use @NetCord.Hosting.Services.ServicesHostExtensions.AddModules(Microsoft.Extensions.Hosting.IHost,System.Reflection.Assembly) to add modules from an assembly. You also need to use @NetCord.Hosting.Gateway.GatewayEventHandlerHostExtensions.UseGatewayEventHandlers(Microsoft.Extensions.Hosting.IHost) to bind the service event handlers. [!code-cs[Program.cs](IntroductionHosting/Program.cs?highlight=11-17,20-28)] -## [Without Hosting](#tab/without-hosting) +## [Bare Bones](#tab/bare-bones) First, add the following lines to the using section. [!code-cs[Program.cs](Introduction/Program.cs#L4-L5)] diff --git a/Documentation/guides/services/text-commands/introduction.md b/Documentation/guides/services/text-commands/introduction.md index 5e5ee20d..8e34cd55 100644 --- a/Documentation/guides/services/text-commands/introduction.md +++ b/Documentation/guides/services/text-commands/introduction.md @@ -1,8 +1,8 @@ # Introduction -## [Hosting](#tab/hosting) +## [Generic Host](#tab/generic-host) -With hosting, adding commands is very easy. Use @NetCord.Hosting.Services.Commands.CommandServiceHostBuilderExtensions.UseCommands``1(Microsoft.Extensions.Hosting.IHostBuilder) to add a command service to your host builder. Then, use @NetCord.Hosting.Services.Commands.CommandServiceHostExtensions.AddCommand* to add a command using the ASP.NET Core minimal APIs way and/or use @NetCord.Hosting.Services.ServicesHostExtensions.AddModules(Microsoft.Extensions.Hosting.IHost,System.Reflection.Assembly) to add modules from an assembly. You also need to use @NetCord.Hosting.Gateway.GatewayEventHandlerHostExtensions.UseGatewayEventHandlers(Microsoft.Extensions.Hosting.IHost) to bind the service event handlers. +Adding commands with the generic host is very easy. Use @NetCord.Hosting.Services.Commands.CommandServiceHostBuilderExtensions.UseCommands``1(Microsoft.Extensions.Hosting.IHostBuilder) to add a command service to your host builder. Then, use @NetCord.Hosting.Services.Commands.CommandServiceHostExtensions.AddCommand* to add a command using the ASP.NET Core minimal APIs way and/or use @NetCord.Hosting.Services.ServicesHostExtensions.AddModules(Microsoft.Extensions.Hosting.IHost,System.Reflection.Assembly) to add modules from an assembly. You also need to use @NetCord.Hosting.Gateway.GatewayEventHandlerHostExtensions.UseGatewayEventHandlers(Microsoft.Extensions.Hosting.IHost) to bind the service event handlers. [!code-cs[Program.cs](IntroductionHosting/Program.cs?highlight=10,13-15)] ### Specifying a prefix @@ -10,7 +10,7 @@ With hosting, adding commands is very easy. Use @NetCord.Hosting.Services.Comman You can specify a prefix in the configuration. You can for example use `appsettings.json` file. It should look like this: [!code-json[appsettings.json](IntroductionHosting/appsettings.json)] -## [Without Hosting](#tab/without-hosting) +## [Bare Bones](#tab/bare-bones) First, add the following lines to the using section. [!code-cs[Program.cs](Introduction/Program.cs#L3-L4)] diff --git a/Hosting/NetCord.Hosting.AspNetCore/NetCord.Hosting.AspNetCore.csproj b/Hosting/NetCord.Hosting.AspNetCore/NetCord.Hosting.AspNetCore.csproj index 347e4419..7cc919a7 100644 --- a/Hosting/NetCord.Hosting.AspNetCore/NetCord.Hosting.AspNetCore.csproj +++ b/Hosting/NetCord.Hosting.AspNetCore/NetCord.Hosting.AspNetCore.csproj @@ -13,7 +13,7 @@ SmallSquare.png MIT $(VersionPrefix) - alpha.73 + alpha.82 The modern and fully customizable C# Discord library. true diff --git a/Hosting/NetCord.Hosting.Services/ApplicationCommands/ApplicationCommandInteractionHandler.cs b/Hosting/NetCord.Hosting.Services/ApplicationCommands/ApplicationCommandInteractionHandler.cs index fa0e2e6b..54d49683 100644 --- a/Hosting/NetCord.Hosting.Services/ApplicationCommands/ApplicationCommandInteractionHandler.cs +++ b/Hosting/NetCord.Hosting.Services/ApplicationCommands/ApplicationCommandInteractionHandler.cs @@ -4,7 +4,6 @@ using NetCord.Gateway; using NetCord.Hosting.Gateway; -using NetCord.Services; using NetCord.Services.ApplicationCommands; namespace NetCord.Hosting.Services.ApplicationCommands; @@ -19,7 +18,7 @@ internal unsafe partial class ApplicationCommandInteractionHandler, Interaction, GatewayClient?, ValueTask> _handleAsync; private readonly Func _createContext; - private readonly Func _handleResultAsync; + private readonly IApplicationCommandResultHandler _resultHandler; private readonly GatewayClient? _client; public ApplicationCommandInteractionHandler(IServiceProvider services, @@ -44,7 +43,7 @@ public ApplicationCommandInteractionHandler(IServiceProvider services, _handleAsync = &HandleInteractionAsync; _createContext = optionsValue.CreateContext ?? ContextHelper.CreateContextDelegate(); - _handleResultAsync = optionsValue.HandleResultAsync; + _resultHandler = optionsValue.ResultHandler; _client = client; } @@ -89,7 +88,7 @@ private async ValueTask HandleInteractionAsyncCore(TInteraction interaction, Gat try { - await _handleResultAsync(result, interaction, client, _logger, services).ConfigureAwait(false); + await _resultHandler.HandleResultAsync(result, context, client, _logger, services).ConfigureAwait(false); } catch (Exception exceptionHandlerException) { diff --git a/Hosting/NetCord.Hosting.Services/ApplicationCommands/ApplicationCommandResultHandler.cs b/Hosting/NetCord.Hosting.Services/ApplicationCommands/ApplicationCommandResultHandler.cs new file mode 100644 index 00000000..ee815b7c --- /dev/null +++ b/Hosting/NetCord.Hosting.Services/ApplicationCommands/ApplicationCommandResultHandler.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.Logging; + +using NetCord.Gateway; +using NetCord.Rest; +using NetCord.Services; +using NetCord.Services.ApplicationCommands; + +namespace NetCord.Hosting.Services.ApplicationCommands; + +public class ApplicationCommandResultHandler(MessageFlags? messageFlags = null) : IApplicationCommandResultHandler where TContext : IApplicationCommandContext +{ + public ValueTask HandleResultAsync(IExecutionResult result, TContext context, GatewayClient? client, ILogger logger, IServiceProvider services) + { + if (result is not IFailResult failResult) + return default; + + var resultMessage = failResult.Message; + + var interaction = context.Interaction; + + if (failResult is IExceptionResult exceptionResult) + logger.LogError(exceptionResult.Exception, "Execution of an application command of name '{Name}' failed with an exception", interaction.Data.Name); + else + logger.LogDebug("Execution of an application command of name '{Name}' failed with '{Message}'", interaction.Data.Name, resultMessage); + + InteractionMessageProperties message = new() + { + Content = resultMessage, + Flags = messageFlags, + }; + + return new(interaction.SendResponseAsync(InteractionCallback.Message(message))); + } +} diff --git a/Hosting/NetCord.Hosting.Services/ApplicationCommands/ApplicationCommandServiceOptions.cs b/Hosting/NetCord.Hosting.Services/ApplicationCommands/ApplicationCommandServiceOptions.cs index 559c37fe..29af31e2 100644 --- a/Hosting/NetCord.Hosting.Services/ApplicationCommands/ApplicationCommandServiceOptions.cs +++ b/Hosting/NetCord.Hosting.Services/ApplicationCommands/ApplicationCommandServiceOptions.cs @@ -1,8 +1,4 @@ -using Microsoft.Extensions.Logging; - -using NetCord.Gateway; -using NetCord.Rest; -using NetCord.Services; +using NetCord.Gateway; using NetCord.Services.ApplicationCommands; namespace NetCord.Hosting.Services.ApplicationCommands; @@ -15,38 +11,12 @@ public class ApplicationCommandServiceOptions where TInt public Func? CreateContext { get; set; } - public Func HandleResultAsync { get; set; } = (result, interaction, client, logger, services) => - { - if (result is not IFailResult failResult) - return default; - - var message = failResult.Message; - - if (failResult is IExceptionResult exceptionResult) - logger.LogError(exceptionResult.Exception, "Execution of an application command of name '{Name}' failed with an exception", interaction.Data.Name); - else - logger.LogDebug("Execution of an application command of name '{Name}' failed with '{Message}'", interaction.Data.Name, message); - - return new(interaction.SendResponseAsync(InteractionCallback.Message(message))); - }; + public IApplicationCommandResultHandler ResultHandler { get; set; } = new ApplicationCommandResultHandler(); } public class ApplicationCommandServiceOptions : ApplicationCommandServiceOptions where TInteraction : ApplicationCommandInteraction where TContext : IApplicationCommandContext where TAutocompleteContext : IAutocompleteInteractionContext { public Func? CreateAutocompleteContext { get; set; } - public Func HandleAutocompleteResultAsync { get; set; } = (result, interaction, client, logger, services) => - { - if (result is not IFailResult failResult) - return default; - - var commandName = interaction.Data.Name; - - if (failResult is IExceptionResult exceptionResult) - logger.LogError(exceptionResult.Exception, "Execution of an autocomplete for application command of name '{Name}' failed with an exception", commandName); - else - logger.LogDebug("Execution of an autocomplete for application command of name '{Name}' failed with '{Message}'", commandName, failResult.Message); - - return default; - }; + public IAutocompleteInteractionResultHandler AutocompleteResultHandler { get; set; } = new AutocompleteInteractionResultHandler(); } diff --git a/Hosting/NetCord.Hosting.Services/ApplicationCommands/AutocompleteInteractionHandler.cs b/Hosting/NetCord.Hosting.Services/ApplicationCommands/AutocompleteInteractionHandler.cs index 36a80d1f..4b72bba2 100644 --- a/Hosting/NetCord.Hosting.Services/ApplicationCommands/AutocompleteInteractionHandler.cs +++ b/Hosting/NetCord.Hosting.Services/ApplicationCommands/AutocompleteInteractionHandler.cs @@ -4,7 +4,6 @@ using NetCord.Gateway; using NetCord.Hosting.Gateway; -using NetCord.Services; using NetCord.Services.ApplicationCommands; namespace NetCord.Hosting.Services.ApplicationCommands; @@ -19,7 +18,7 @@ internal unsafe partial class AutocompleteInteractionHandler, Interaction, GatewayClient?, ValueTask> _handleAsync; private readonly Func _createContext; - private readonly Func _handleResultAsync; + private readonly IAutocompleteInteractionResultHandler _resultHandler; private readonly GatewayClient? _client; public AutocompleteInteractionHandler(IServiceProvider services, @@ -44,7 +43,7 @@ public AutocompleteInteractionHandler(IServiceProvider services, _handleAsync = &HandleInteractionAsync; _createContext = optionsValue.CreateAutocompleteContext ?? ContextHelper.CreateContextDelegate(); - _handleResultAsync = optionsValue.HandleAutocompleteResultAsync; + _resultHandler = optionsValue.AutocompleteResultHandler; _client = client; } @@ -89,7 +88,7 @@ private async ValueTask HandleInteractionAsyncCore(AutocompleteInteraction inter try { - await _handleResultAsync(result, interaction, client, _logger, services).ConfigureAwait(false); + await _resultHandler.HandleResultAsync(result, context, client, _logger, services).ConfigureAwait(false); } catch (Exception exceptionHandlerException) { diff --git a/Hosting/NetCord.Hosting.Services/ApplicationCommands/AutocompleteInteractionResultHandler.cs b/Hosting/NetCord.Hosting.Services/ApplicationCommands/AutocompleteInteractionResultHandler.cs new file mode 100644 index 00000000..ae436fe7 --- /dev/null +++ b/Hosting/NetCord.Hosting.Services/ApplicationCommands/AutocompleteInteractionResultHandler.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.Logging; + +using NetCord.Gateway; +using NetCord.Services; +using NetCord.Services.ApplicationCommands; + +namespace NetCord.Hosting.Services.ApplicationCommands; + +public class AutocompleteInteractionResultHandler : IAutocompleteInteractionResultHandler where TAutocompleteContext : IAutocompleteInteractionContext +{ + public ValueTask HandleResultAsync(IExecutionResult result, TAutocompleteContext context, GatewayClient? client, ILogger logger, IServiceProvider services) + { + if (result is not IFailResult failResult) + return default; + + var commandName = context.Interaction.Data.Name; + + if (failResult is IExceptionResult exceptionResult) + logger.LogError(exceptionResult.Exception, "Execution of an autocomplete for application command of name '{Name}' failed with an exception", commandName); + else + logger.LogDebug("Execution of an autocomplete for application command of name '{Name}' failed with '{Message}'", commandName, failResult.Message); + + return default; + } +} diff --git a/Hosting/NetCord.Hosting.Services/ApplicationCommands/IApplicationCommandResultHandler.cs b/Hosting/NetCord.Hosting.Services/ApplicationCommands/IApplicationCommandResultHandler.cs new file mode 100644 index 00000000..61eaf576 --- /dev/null +++ b/Hosting/NetCord.Hosting.Services/ApplicationCommands/IApplicationCommandResultHandler.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.Logging; + +using NetCord.Gateway; +using NetCord.Services; +using NetCord.Services.ApplicationCommands; + +namespace NetCord.Hosting.Services.ApplicationCommands; + +public interface IApplicationCommandResultHandler where TContext : IApplicationCommandContext +{ + public ValueTask HandleResultAsync(IExecutionResult result, TContext context, GatewayClient? client, ILogger logger, IServiceProvider services); +} diff --git a/Hosting/NetCord.Hosting.Services/ApplicationCommands/IAutocompleteInteractionResultHandler.cs b/Hosting/NetCord.Hosting.Services/ApplicationCommands/IAutocompleteInteractionResultHandler.cs new file mode 100644 index 00000000..50a0cb7c --- /dev/null +++ b/Hosting/NetCord.Hosting.Services/ApplicationCommands/IAutocompleteInteractionResultHandler.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.Logging; + +using NetCord.Gateway; +using NetCord.Services; +using NetCord.Services.ApplicationCommands; + +namespace NetCord.Hosting.Services.ApplicationCommands; + +public interface IAutocompleteInteractionResultHandler where TAutocompleteContext : IAutocompleteInteractionContext +{ + public ValueTask HandleResultAsync(IExecutionResult result, TAutocompleteContext context, GatewayClient? client, ILogger logger, IServiceProvider services); +} diff --git a/Hosting/NetCord.Hosting.Services/Commands/CommandHandler.cs b/Hosting/NetCord.Hosting.Services/Commands/CommandHandler.cs index d0577dea..258a6d99 100644 --- a/Hosting/NetCord.Hosting.Services/Commands/CommandHandler.cs +++ b/Hosting/NetCord.Hosting.Services/Commands/CommandHandler.cs @@ -4,7 +4,6 @@ using NetCord.Gateway; using NetCord.Hosting.Gateway; -using NetCord.Services; using NetCord.Services.Commands; namespace NetCord.Hosting.Services.Commands; @@ -20,7 +19,7 @@ internal unsafe partial class CommandHandler : IGatewayEventHandler, Message, GatewayClient, ValueTask> _handleAsync; private readonly Func> _getPrefixLengthAsync; private readonly Func _createContext; - private readonly Func _handleResultAsync; + private readonly ICommandResultHandler _resultHandler; private readonly GatewayClient? _client; public CommandHandler(IServiceProvider services, @@ -46,7 +45,7 @@ public CommandHandler(IServiceProvider services, _getPrefixLengthAsync = GetGetPrefixLengthAsyncDelegate(optionsValue); _createContext = optionsValue.CreateContext ?? ContextHelper.CreateContextDelegate(); - _handleResultAsync = optionsValue.HandleResultAsync; + _resultHandler = optionsValue.ResultHandler; _client = client; } @@ -134,7 +133,7 @@ private async ValueTask HandleMessageAsyncCore(Message message, GatewayClient cl try { - await _handleResultAsync(result, message, client, _logger, services).ConfigureAwait(false); + await _resultHandler.HandleResultAsync(result, context, client, _logger, services).ConfigureAwait(false); } catch (Exception exceptionHandlerException) { diff --git a/Hosting/NetCord.Hosting.Services/Commands/CommandResultHandler.cs b/Hosting/NetCord.Hosting.Services/Commands/CommandResultHandler.cs new file mode 100644 index 00000000..d8d009ba --- /dev/null +++ b/Hosting/NetCord.Hosting.Services/Commands/CommandResultHandler.cs @@ -0,0 +1,32 @@ +using Microsoft.Extensions.Logging; + +using NetCord.Gateway; +using NetCord.Services; +using NetCord.Services.Commands; + +namespace NetCord.Hosting.Services.Commands; + +public class CommandResultHandler(MessageFlags? messageFlags = null) : ICommandResultHandler where TContext : ICommandContext +{ + public ValueTask HandleResultAsync(IExecutionResult result, TContext context, GatewayClient client, ILogger logger, IServiceProvider services) + { + if (result is not IFailResult failResult) + return default; + + var resultMessage = failResult.Message; + + var message = context.Message; + + if (failResult is IExceptionResult exceptionResult) + logger.LogError(exceptionResult.Exception, "Execution of a command with content '{Content}' failed with an exception", message.Content); + else + logger.LogDebug("Execution of a command with content '{Content}' failed with '{Message}'", message.Content, resultMessage); + + return new(message.ReplyAsync(new() + { + Content = resultMessage, + FailIfNotExists = false, + Flags = messageFlags, + })); + } +} diff --git a/Hosting/NetCord.Hosting.Services/Commands/CommandServiceOptions.cs b/Hosting/NetCord.Hosting.Services/Commands/CommandServiceOptions.cs index b59f27d2..de132b81 100644 --- a/Hosting/NetCord.Hosting.Services/Commands/CommandServiceOptions.cs +++ b/Hosting/NetCord.Hosting.Services/Commands/CommandServiceOptions.cs @@ -1,7 +1,4 @@ -using Microsoft.Extensions.Logging; - -using NetCord.Gateway; -using NetCord.Services; +using NetCord.Gateway; using NetCord.Services.Commands; namespace NetCord.Hosting.Services.Commands; @@ -20,22 +17,5 @@ public class CommandServiceOptions where TContext : ICommandContext public Func? CreateContext { get; set; } - public Func HandleResultAsync { get; set; } = (result, message, client, logger, services) => - { - if (result is not IFailResult failResult) - return default; - - string resultMessage = failResult.Message; - - if (failResult is IExceptionResult exceptionResult) - logger.LogError(exceptionResult.Exception, "Execution of a command with content '{Content}' failed with an exception", message.Content); - else - logger.LogDebug("Execution of a command with content '{Content}' failed with '{Message}'", message.Content, resultMessage); - - return new(message.ReplyAsync(new() - { - Content = resultMessage, - FailIfNotExists = false, - })); - }; + public ICommandResultHandler ResultHandler { get; set; } = new CommandResultHandler(); } diff --git a/Hosting/NetCord.Hosting.Services/Commands/ICommandResultHandler.cs b/Hosting/NetCord.Hosting.Services/Commands/ICommandResultHandler.cs new file mode 100644 index 00000000..bd3a4133 --- /dev/null +++ b/Hosting/NetCord.Hosting.Services/Commands/ICommandResultHandler.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.Logging; + +using NetCord.Gateway; +using NetCord.Services; +using NetCord.Services.Commands; + +namespace NetCord.Hosting.Services.Commands; + +public interface ICommandResultHandler where TContext : ICommandContext +{ + public ValueTask HandleResultAsync(IExecutionResult result, TContext context, GatewayClient client, ILogger logger, IServiceProvider services); +} diff --git a/Hosting/NetCord.Hosting.Services/ComponentInteractions/ComponentInteractionHandler.cs b/Hosting/NetCord.Hosting.Services/ComponentInteractions/ComponentInteractionHandler.cs index 12ff83dc..7a841413 100644 --- a/Hosting/NetCord.Hosting.Services/ComponentInteractions/ComponentInteractionHandler.cs +++ b/Hosting/NetCord.Hosting.Services/ComponentInteractions/ComponentInteractionHandler.cs @@ -4,7 +4,6 @@ using NetCord.Gateway; using NetCord.Hosting.Gateway; -using NetCord.Services; using NetCord.Services.ComponentInteractions; namespace NetCord.Hosting.Services.ComponentInteractions; @@ -19,7 +18,7 @@ internal unsafe partial class ComponentInteractionHandler, Interaction, GatewayClient?, ValueTask> _handleAsync; private readonly Func _createContext; - private readonly Func _handleResultAsync; + private readonly IComponentInteractionResultHandler _resultHandler; private readonly GatewayClient? _client; public ComponentInteractionHandler(IServiceProvider services, @@ -44,7 +43,7 @@ public ComponentInteractionHandler(IServiceProvider services, _handleAsync = &HandleInteractionAsync; _createContext = optionsValue.CreateContext ?? ContextHelper.CreateContextDelegate(); - _handleResultAsync = optionsValue.HandleResultAsync; + _resultHandler = optionsValue.ResultHandler; _client = client; } @@ -89,7 +88,7 @@ private async ValueTask HandleInteractionAsyncCore(TInteraction interaction, Gat try { - await _handleResultAsync(result, interaction, client, _logger, services).ConfigureAwait(false); + await _resultHandler.HandleResultAsync(result, context, client, _logger, services).ConfigureAwait(false); } catch (Exception exceptionHandlerException) { diff --git a/Hosting/NetCord.Hosting.Services/ComponentInteractions/ComponentInteractionResultHandler.cs b/Hosting/NetCord.Hosting.Services/ComponentInteractions/ComponentInteractionResultHandler.cs new file mode 100644 index 00000000..e8c8d01c --- /dev/null +++ b/Hosting/NetCord.Hosting.Services/ComponentInteractions/ComponentInteractionResultHandler.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.Logging; + +using NetCord.Gateway; +using NetCord.Rest; +using NetCord.Services; +using NetCord.Services.ComponentInteractions; + +namespace NetCord.Hosting.Services.ComponentInteractions; + +public class ComponentInteractionResultHandler(MessageFlags? messageFlags = null) : IComponentInteractionResultHandler where TContext : IComponentInteractionContext +{ + public ValueTask HandleResultAsync(IExecutionResult result, TContext context, GatewayClient? client, ILogger logger, IServiceProvider services) + { + if (result is not IFailResult failResult) + return default; + + var resultMessage = failResult.Message; + + var interaction = context.Interaction; + + if (failResult is IExceptionResult exceptionResult) + logger.LogError(exceptionResult.Exception, "Execution of an interaction of custom ID '{Id}' failed with an exception", interaction.Id); + else + logger.LogDebug("Execution of an interaction of custom ID '{Id}' failed with '{Message}'", interaction.Id, resultMessage); + + InteractionMessageProperties message = new() + { + Content = resultMessage, + Flags = messageFlags, + }; + + return new(interaction.SendResponseAsync(InteractionCallback.Message(message))); + } +} diff --git a/Hosting/NetCord.Hosting.Services/ComponentInteractions/ComponentInteractionServiceOptions.cs b/Hosting/NetCord.Hosting.Services/ComponentInteractions/ComponentInteractionServiceOptions.cs index f77427b3..21d5d51f 100644 --- a/Hosting/NetCord.Hosting.Services/ComponentInteractions/ComponentInteractionServiceOptions.cs +++ b/Hosting/NetCord.Hosting.Services/ComponentInteractions/ComponentInteractionServiceOptions.cs @@ -1,8 +1,4 @@ -using Microsoft.Extensions.Logging; - -using NetCord.Gateway; -using NetCord.Rest; -using NetCord.Services; +using NetCord.Gateway; using NetCord.Services.ComponentInteractions; namespace NetCord.Hosting.Services.ComponentInteractions; @@ -15,18 +11,5 @@ public class ComponentInteractionServiceOptions where TI public Func? CreateContext { get; set; } - public Func HandleResultAsync { get; set; } = (result, interaction, client, logger, services) => - { - if (result is not IFailResult failResult) - return default; - - var message = failResult.Message; - - if (failResult is IExceptionResult exceptionResult) - logger.LogError(exceptionResult.Exception, "Execution of an interaction of custom ID '{Id}' failed with an exception", interaction.Id); - else - logger.LogDebug("Execution of an interaction of custom ID '{Id}' failed with '{Message}'", interaction.Id, message); - - return new(interaction.SendResponseAsync(InteractionCallback.Message(message))); - }; + public IComponentInteractionResultHandler ResultHandler { get; set; } = new ComponentInteractionResultHandler(); } diff --git a/Hosting/NetCord.Hosting.Services/ComponentInteractions/IComponentInteractionResultHandler.cs b/Hosting/NetCord.Hosting.Services/ComponentInteractions/IComponentInteractionResultHandler.cs new file mode 100644 index 00000000..ea5b6a07 --- /dev/null +++ b/Hosting/NetCord.Hosting.Services/ComponentInteractions/IComponentInteractionResultHandler.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.Logging; + +using NetCord.Gateway; +using NetCord.Services; +using NetCord.Services.ComponentInteractions; + +namespace NetCord.Hosting.Services.ComponentInteractions; + +public interface IComponentInteractionResultHandler where TContext : IComponentInteractionContext +{ + public ValueTask HandleResultAsync(IExecutionResult result, TContext context, GatewayClient? client, ILogger logger, IServiceProvider services); +} diff --git a/Hosting/NetCord.Hosting.Services/NetCord.Hosting.Services.csproj b/Hosting/NetCord.Hosting.Services/NetCord.Hosting.Services.csproj index 96d65e32..bbd8d8f0 100644 --- a/Hosting/NetCord.Hosting.Services/NetCord.Hosting.Services.csproj +++ b/Hosting/NetCord.Hosting.Services/NetCord.Hosting.Services.csproj @@ -14,7 +14,7 @@ SmallSquare.png MIT $(VersionPrefix) - alpha.81 + alpha.90 The modern and fully customizable C# Discord library. true diff --git a/Hosting/NetCord.Hosting/NetCord.Hosting.csproj b/Hosting/NetCord.Hosting/NetCord.Hosting.csproj index 32e79f22..b70ac12c 100644 --- a/Hosting/NetCord.Hosting/NetCord.Hosting.csproj +++ b/Hosting/NetCord.Hosting/NetCord.Hosting.csproj @@ -13,7 +13,7 @@ SmallSquare.png MIT $(VersionPrefix) - alpha.72 + alpha.81 The modern and fully customizable C# Discord library. true diff --git a/NetCord.Services/Commands/CommandService.cs b/NetCord.Services/Commands/CommandService.cs index f09d9844..9f39cb8b 100644 --- a/NetCord.Services/Commands/CommandService.cs +++ b/NetCord.Services/Commands/CommandService.cs @@ -108,26 +108,26 @@ private async ValueTask ExecuteAsyncCore(int prefixLength, TCo var index = fullCommand.Span.IndexOfAny(separators); SortedList>? commandInfos; ReadOnlyMemory baseArguments; - if (index == -1) + if (index >= 0) { - var command = fullCommand; + var command = fullCommand[..index]; if (!TryGetCommandInfos(command, out commandInfos)) return new NotFoundResult("Command not found."); - baseArguments = default; + baseArguments = fullCommand[(index + 1)..].TrimStart(separators); } else { - var command = fullCommand[..index]; + var command = fullCommand; if (!TryGetCommandInfos(command, out commandInfos)) return new NotFoundResult("Command not found."); - baseArguments = fullCommand[(index + 1)..]; + baseArguments = default; } - var configuration = _configuration; + var configuration = _configuration; var maxIndex = commandInfos.Count - 1; for (var i = 0; i <= maxIndex; i++) diff --git a/NetCord.Services/NetCord.Services.csproj b/NetCord.Services/NetCord.Services.csproj index e7a80b88..e99d12c2 100644 --- a/NetCord.Services/NetCord.Services.csproj +++ b/NetCord.Services/NetCord.Services.csproj @@ -14,7 +14,7 @@ SmallSquare.png MIT $(VersionPrefix) - alpha.203 + alpha.212 The modern and fully customizable C# Discord library. true diff --git a/NetCord/ApplicationEmoji.cs b/NetCord/ApplicationEmoji.cs new file mode 100644 index 00000000..934c882b --- /dev/null +++ b/NetCord/ApplicationEmoji.cs @@ -0,0 +1,9 @@ +using NetCord.JsonModels; +using NetCord.Rest; + +namespace NetCord; + +public partial class ApplicationEmoji(JsonEmoji jsonModel, ulong applicationId, RestClient client) : CustomEmoji(jsonModel, client) +{ + public ulong ApplicationId { get; } = applicationId; +} diff --git a/NetCord/Attachment.cs b/NetCord/Attachment.cs index f25208ba..63157fdc 100644 --- a/NetCord/Attachment.cs +++ b/NetCord/Attachment.cs @@ -14,6 +14,11 @@ public class Attachment(JsonModels.JsonAttachment jsonModel) : Entity, IJsonMode /// public string FileName => _jsonModel.FileName; + /// + /// Title of the attachment. + /// + public string? Title => _jsonModel.Title; + /// /// Description for the attachment (max 1024 characters). /// diff --git a/NetCord/AuditLogEvent.cs b/NetCord/AuditLogEvent.cs index 195ec977..90ca94dd 100644 --- a/NetCord/AuditLogEvent.cs +++ b/NetCord/AuditLogEvent.cs @@ -281,4 +281,39 @@ public enum AuditLogEvent /// Creator monetization terms were accepted. /// CreatorMonetizationTermsAccepted = 151, + + /// + /// Guild onboarding question was created. + /// + OnboardingPromptCreate = 163, + + /// + /// Guild onboarding question was updated. + /// + OnboardingPromptUpdate = 164, + + /// + /// Guild onboarding question was deleted. + /// + OnboardingPromptDelete = 165, + + /// + /// Guild onboarding was created. + /// + OnboardingCreate = 166, + + /// + /// Guild onboarding was updated. + /// + OnboardingUpdate = 167, + + /// + /// Server guide was created. + /// + HomeSettingsCreate = 190, + + /// + /// Server guide was updated. + /// + HomeSettingsUpdate = 191, } diff --git a/NetCord/AutoModerationActionType.cs b/NetCord/AutoModerationActionType.cs index 0c4552c9..871e06e4 100644 --- a/NetCord/AutoModerationActionType.cs +++ b/NetCord/AutoModerationActionType.cs @@ -5,4 +5,5 @@ public enum AutoModerationActionType BlockMessage = 1, SendAlertMessage = 2, Timeout = 3, + BlockUserInteraction = 4, } diff --git a/NetCord/AutoModerationRuleEventType.cs b/NetCord/AutoModerationRuleEventType.cs index 19def3fa..53bceaf1 100644 --- a/NetCord/AutoModerationRuleEventType.cs +++ b/NetCord/AutoModerationRuleEventType.cs @@ -3,4 +3,5 @@ public enum AutoModerationRuleEventType { MessageSend = 1, + UserUpdate = 2, } diff --git a/NetCord/AutoModerationRuleTriggerType.cs b/NetCord/AutoModerationRuleTriggerType.cs index f493143a..5e7da728 100644 --- a/NetCord/AutoModerationRuleTriggerType.cs +++ b/NetCord/AutoModerationRuleTriggerType.cs @@ -6,4 +6,5 @@ public enum AutoModerationRuleTriggerType Spam = 3, KeywordPreset = 4, MentionSpam = 5, + UserProfile = 6, } diff --git a/NetCord/AvatarDecorationData.cs b/NetCord/AvatarDecorationData.cs new file mode 100644 index 00000000..8281b0bd --- /dev/null +++ b/NetCord/AvatarDecorationData.cs @@ -0,0 +1,12 @@ +using NetCord.JsonModels; + +namespace NetCord; + +public class AvatarDecorationData(JsonAvatarDecorationData jsonModel) : IJsonModel +{ + JsonAvatarDecorationData IJsonModel.JsonModel => jsonModel; + + public string Hash => jsonModel.Hash; + + public ulong SkuId => jsonModel.SkuId; +} diff --git a/NetCord/CodeBlock.cs b/NetCord/CodeBlock.cs index ada3f51c..f6b12de1 100644 --- a/NetCord/CodeBlock.cs +++ b/NetCord/CodeBlock.cs @@ -69,7 +69,7 @@ public static bool TryParse(ReadOnlySpan s, bool strictMode, [MaybeNullWhe foreach (var c in formatterSpan) { - if (char.IsAsciiLetterOrDigit(c) || c == '+' || c == '-') + if (char.IsAsciiLetterOrDigit(c) || c is '+' or '-' or '#' or '_') continue; goto Success; diff --git a/NetCord/Components/Button.cs b/NetCord/Components/Button.cs index a0785b62..98409dda 100644 --- a/NetCord/Components/Button.cs +++ b/NetCord/Components/Button.cs @@ -1,6 +1,6 @@ namespace NetCord; -public class Button : IButton, IJsonModel +public class Button : ICustomizableButton, IJsonModel { JsonModels.JsonComponent IJsonModel.JsonModel => _jsonModel; private readonly JsonModels.JsonComponent _jsonModel; diff --git a/NetCord/Components/IButton.cs b/NetCord/Components/IButton.cs index ce12a6e2..dc4852ed 100644 --- a/NetCord/Components/IButton.cs +++ b/NetCord/Components/IButton.cs @@ -2,8 +2,6 @@ public interface IButton { - public string? Label { get; } - public EmojiReference? Emoji { get; } public bool Disabled { get; } public static IButton CreateFromJson(JsonModels.JsonComponent jsonModel) @@ -11,6 +9,7 @@ public static IButton CreateFromJson(JsonModels.JsonComponent jsonModel) return jsonModel.Style.GetValueOrDefault() switch { (ButtonStyle)5 => new LinkButton(jsonModel), + (ButtonStyle)6 => new PremiumButton(jsonModel), _ => new Button(jsonModel), }; } diff --git a/NetCord/Components/ICustomizableButton.cs b/NetCord/Components/ICustomizableButton.cs new file mode 100644 index 00000000..0bf263d9 --- /dev/null +++ b/NetCord/Components/ICustomizableButton.cs @@ -0,0 +1,7 @@ +namespace NetCord; + +public interface ICustomizableButton : IButton +{ + public string? Label { get; } + public EmojiReference? Emoji { get; } +} diff --git a/NetCord/Components/LinkButton.cs b/NetCord/Components/LinkButton.cs index 7739e6e7..3825c7ed 100644 --- a/NetCord/Components/LinkButton.cs +++ b/NetCord/Components/LinkButton.cs @@ -2,7 +2,7 @@ namespace NetCord; -public class LinkButton : IButton, IJsonModel +public class LinkButton : ICustomizableButton, IJsonModel { JsonComponent IJsonModel.JsonModel => _jsonModel; private readonly JsonComponent _jsonModel; diff --git a/NetCord/Components/PremiumButton.cs b/NetCord/Components/PremiumButton.cs new file mode 100644 index 00000000..a89391be --- /dev/null +++ b/NetCord/Components/PremiumButton.cs @@ -0,0 +1,10 @@ +using NetCord.JsonModels; + +namespace NetCord; +public class PremiumButton(JsonComponent jsonModel) : IButton, IJsonModel +{ + JsonComponent IJsonModel.JsonModel => jsonModel; + + public ulong SkuId => jsonModel.SkuId.GetValueOrDefault(); + public bool Disabled => jsonModel.Disabled.GetValueOrDefault(); +} diff --git a/NetCord/CustomEmoji.cs b/NetCord/CustomEmoji.cs new file mode 100644 index 00000000..6bd4c620 --- /dev/null +++ b/NetCord/CustomEmoji.cs @@ -0,0 +1,67 @@ +using NetCord.JsonModels; +using NetCord.Rest; + +namespace NetCord; + +public abstract class CustomEmoji : Emoji, ISpanFormattable +{ + private protected RestClient _client; + + public CustomEmoji(JsonEmoji jsonModel, RestClient client) : base(jsonModel) + { + _client = client; + + var creator = jsonModel.Creator; + if (creator is not null) + Creator = new(creator, client); + } + + public ulong Id => _jsonModel.Id.GetValueOrDefault(); + + public User? Creator { get; } + + public bool? RequireColons => _jsonModel.RequireColons; + + public bool? Managed => _jsonModel.Managed; + + public bool? Available => _jsonModel.Available; + + public override string ToString() => Animated ? $"" : $"<:{Name}:{Id}>"; + + public string ToString(string? format, IFormatProvider? formatProvider) => ToString(); + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider? provider = null) + { + var name = Name; + if (Animated) + { + if (destination.Length < 6 + name.Length || !Id.TryFormat(destination[(4 + name.Length)..^1], out int length)) + { + charsWritten = 0; + return false; + } + + "))] +[JsonConverter(typeof(JsonConverters.SafeStringEnumConverter))] public enum EmbedType { [JsonPropertyName("rich")] diff --git a/NetCord/Gateway/AuditLogEntry.cs b/NetCord/Gateway/AuditLogEntry.cs index 49b6ccc3..549c96be 100644 --- a/NetCord/Gateway/AuditLogEntry.cs +++ b/NetCord/Gateway/AuditLogEntry.cs @@ -12,14 +12,17 @@ public class AuditLogEntry : Entity, IJsonModel JsonAuditLogEntry IJsonModel.JsonModel => _jsonModel; private protected readonly JsonAuditLogEntry _jsonModel; - public AuditLogEntry(JsonAuditLogEntry jsonModel) + public AuditLogEntry(JsonAuditLogEntry jsonModel, ulong guildId) { _jsonModel = jsonModel; + Changes = _jsonModel.Changes.ToDictionaryOrEmpty(c => c.Key, c => new AuditLogChange(c)); var options = _jsonModel.Options; if (options is not null) Options = new(options); + + GuildId = guildId; } public override ulong Id => _jsonModel.Id; @@ -54,6 +57,11 @@ public AuditLogEntry(JsonAuditLogEntry jsonModel) /// public string? Reason => _jsonModel.Reason; + /// + /// The ID of the guild this audit log entry belongs to. + /// + public ulong GuildId { get; } + private bool TryGetChangeModel(Expression> expression, [NotNullWhen(true)] out JsonAuditLogChange model) { var member = GetMemberAccess(expression); diff --git a/NetCord/Gateway/EventArgs/GuildInviteDeleteEventArgs.cs b/NetCord/Gateway/EventArgs/GuildInviteDeleteEventArgs.cs deleted file mode 100644 index 630019e6..00000000 --- a/NetCord/Gateway/EventArgs/GuildInviteDeleteEventArgs.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace NetCord.Gateway; - -public class GuildInviteDeleteEventArgs(JsonModels.EventArgs.JsonGuildInviteDeleteEventArgs jsonModel) : IJsonModel -{ - JsonModels.EventArgs.JsonGuildInviteDeleteEventArgs IJsonModel.JsonModel => jsonModel; - - public ulong InviteChannelId => jsonModel.InviteChannelId; - - public ulong? GuildId => jsonModel.GuildId; - - public string InviteCode => jsonModel.InviteCode; -} diff --git a/NetCord/Gateway/EventArgs/InviteDeleteEventArgs.cs b/NetCord/Gateway/EventArgs/InviteDeleteEventArgs.cs new file mode 100644 index 00000000..82e7b3e9 --- /dev/null +++ b/NetCord/Gateway/EventArgs/InviteDeleteEventArgs.cs @@ -0,0 +1,12 @@ +namespace NetCord.Gateway; + +public class InviteDeleteEventArgs(JsonModels.EventArgs.JsonInviteDeleteEventArgs jsonModel) : IJsonModel +{ + JsonModels.EventArgs.JsonInviteDeleteEventArgs IJsonModel.JsonModel => jsonModel; + + public ulong InviteChannelId => jsonModel.InviteChannelId; + + public ulong? GuildId => jsonModel.GuildId; + + public string InviteCode => jsonModel.InviteCode; +} diff --git a/NetCord/Gateway/EventArgs/MessageReactionAddEventArgs.cs b/NetCord/Gateway/EventArgs/MessageReactionAddEventArgs.cs index e3b2522a..74793716 100644 --- a/NetCord/Gateway/EventArgs/MessageReactionAddEventArgs.cs +++ b/NetCord/Gateway/EventArgs/MessageReactionAddEventArgs.cs @@ -31,4 +31,10 @@ public MessageReactionAddEventArgs(JsonModels.EventArgs.JsonMessageReactionAddEv public MessageReactionEmoji Emoji { get; } public ulong? MessageAuthorId => _jsonModel.MessageAuthorId; + + public bool Burst => _jsonModel.Burst; + + public IReadOnlyList BurstColors => _jsonModel.BurstColors; + + public ReactionType Type => _jsonModel.Type; } diff --git a/NetCord/Gateway/EventArgs/MessageReactionRemoveEventArgs.cs b/NetCord/Gateway/EventArgs/MessageReactionRemoveEventArgs.cs index e887f8cd..689c958f 100644 --- a/NetCord/Gateway/EventArgs/MessageReactionRemoveEventArgs.cs +++ b/NetCord/Gateway/EventArgs/MessageReactionRemoveEventArgs.cs @@ -13,4 +13,8 @@ public class MessageReactionRemoveEventArgs(JsonModels.EventArgs.JsonMessageReac public ulong? GuildId => jsonModel.GuildId; public MessageReactionEmoji Emoji { get; } = new(jsonModel.Emoji); + + public bool Burst => jsonModel.Burst; + + public ReactionType Type => jsonModel.Type; } diff --git a/NetCord/Gateway/GatewayClient.cs b/NetCord/Gateway/GatewayClient.cs index 6cd367ff..77b379fb 100644 --- a/NetCord/Gateway/GatewayClient.cs +++ b/NetCord/Gateway/GatewayClient.cs @@ -489,7 +489,7 @@ public partial class GatewayClient : WebSocketClient, IEntity ///
Required Intents: ///
Optional Intents: None /// - public event Func? GuildInviteCreate; + public event Func? InviteCreate; /// /// Sent when an invite is deleted. Only sent if the bot has the permission for the relevant channel.
@@ -498,7 +498,7 @@ public partial class GatewayClient : WebSocketClient, IEntity ///
Required Intents: ///
Optional Intents: None /// - public event Func? GuildInviteDelete; + public event Func? InviteDelete; /// /// Sent when a message is created. @@ -547,7 +547,7 @@ public partial class GatewayClient : WebSocketClient, IEntity /// /// Sent when a message is updated. - /// The inner payload is a partial message object, with only the message's ID and Guild ID being guaranteed present, all other fields can be null.
+ /// The inner payload is a message object with set , and fields.
///
/// ///
Required Intents: , * @@ -588,7 +588,7 @@ public partial class GatewayClient : WebSocketClient, IEntity ///

/// *Ephemeral messages do not use the guild channel. Because of this, they are tied to the intent, and the message object won't include a or . ///
- public event Func? MessageUpdate; + public event Func? MessageUpdate; /// /// Sent when a message is deleted.
@@ -1130,7 +1130,7 @@ await InvokeEventAsync(Ready, args, data => break; case "GUILD_AUDIT_LOG_ENTRY_CREATE": { - await InvokeEventAsync(GuildAuditLogEntryCreate, () => new(data.ToObject(Serialization.Default.JsonAuditLogEntry))).ConfigureAwait(false); + await InvokeEventAsync(GuildAuditLogEntryCreate, () => new(data.ToObject(Serialization.Default.JsonAuditLogEntry), GetGuildId())).ConfigureAwait(false); } break; case "GUILD_BAN_ADD": @@ -1260,12 +1260,12 @@ await InvokeEventAsync(Ready, args, data => break; case "INVITE_CREATE": { - await InvokeEventAsync(GuildInviteCreate, () => new(data.ToObject(Serialization.Default.JsonGuildInvite), Rest)).ConfigureAwait(false); + await InvokeEventAsync(InviteCreate, () => new(data.ToObject(Serialization.Default.JsonInvite), Rest)).ConfigureAwait(false); } break; case "INVITE_DELETE": { - await InvokeEventAsync(GuildInviteDelete, () => new(data.ToObject(Serialization.Default.JsonGuildInviteDeleteEventArgs))).ConfigureAwait(false); + await InvokeEventAsync(InviteDelete, () => new(data.ToObject(Serialization.Default.JsonInviteDeleteEventArgs))).ConfigureAwait(false); } break; case "MESSAGE_CREATE": @@ -1290,7 +1290,7 @@ await InvokeEventAsync( await InvokeEventAsync( MessageUpdate, () => data.ToObject(Serialization.Default.JsonMessage), - json => IPartialMessage.CreateFromJson(json, Cache, Rest), + json => Message.CreateFromJson(json, Cache, Rest), json => _configuration.CacheDMChannels && !json.GuildId.HasValue && !json.Flags.GetValueOrDefault().HasFlag(MessageFlags.Ephemeral), json => { diff --git a/NetCord/Gateway/GatewayIntents.cs b/NetCord/Gateway/GatewayIntents.cs index 28bc5281..8eec41b5 100644 --- a/NetCord/Gateway/GatewayIntents.cs +++ b/NetCord/Gateway/GatewayIntents.cs @@ -75,7 +75,7 @@ public enum GatewayIntents : uint /// /// Associated with the following events:
- /// , + /// , ///
GuildInvites = 1 << 6, diff --git a/NetCord/Gateway/GuildJoinRequestFormResponseFieldType.cs b/NetCord/Gateway/GuildJoinRequestFormResponseFieldType.cs index 0cf645ae..308a0f19 100644 --- a/NetCord/Gateway/GuildJoinRequestFormResponseFieldType.cs +++ b/NetCord/Gateway/GuildJoinRequestFormResponseFieldType.cs @@ -2,7 +2,7 @@ namespace NetCord.Gateway; -[JsonConverter(typeof(JsonConverters.StringEnumConverterWithErrorHandling))] +[JsonConverter(typeof(JsonConverters.SafeStringEnumConverter))] public enum GuildJoinRequestFormResponseFieldType { [JsonPropertyName("TERMS")] diff --git a/NetCord/Gateway/GuildJoinRequestStatus.cs b/NetCord/Gateway/GuildJoinRequestStatus.cs index 39e8e3bf..f62f2941 100644 --- a/NetCord/Gateway/GuildJoinRequestStatus.cs +++ b/NetCord/Gateway/GuildJoinRequestStatus.cs @@ -2,7 +2,7 @@ namespace NetCord.Gateway; -[JsonConverter(typeof(JsonConverters.StringEnumConverterWithErrorHandling))] +[JsonConverter(typeof(JsonConverters.SafeStringEnumConverter))] public enum GuildJoinRequestStatus { [JsonPropertyName("STARTED")] diff --git a/NetCord/Gateway/IPartialMessage.cs b/NetCord/Gateway/IPartialMessage.cs deleted file mode 100644 index 1ed7ff11..00000000 --- a/NetCord/Gateway/IPartialMessage.cs +++ /dev/null @@ -1,339 +0,0 @@ -using NetCord.JsonModels; -using NetCord.Rest; - -namespace NetCord.Gateway; - -/// -/// Represents an incomplete object, with missing fields. Sent during events. -/// -public partial interface IPartialMessage : IEntity -{ - public static IPartialMessage CreateFromJson(JsonMessage jsonModel, IGatewayClientCache cache, RestClient client) - { - if (jsonModel.Content is null || jsonModel.Author is null) - { - var (guild, channel) = GetCacheData(jsonModel, cache); - return new PartialMessage(jsonModel, guild, channel, client); - } - - return Message.CreateFromJson(jsonModel, cache, client); - } - - internal static (Guild?, TextChannel?) GetCacheData(JsonMessage jsonModel, IGatewayClientCache cache) - { - Guild? guild; - TextChannel? channel; - var guildId = jsonModel.GuildId; - if (guildId.HasValue) - { - if (cache.Guilds.TryGetValue(guildId.GetValueOrDefault(), out guild)) - { - var channelId = jsonModel.ChannelId; - if (guild.Channels.TryGetValue(channelId, out var guildChannel)) - channel = (TextChannel)guildChannel; - else if (guild.ActiveThreads.TryGetValue(channelId, out var thread)) - channel = thread; - else - channel = null; - } - else - channel = null; - } - else - { - guild = null; - channel = cache.DMChannels.GetValueOrDefault(jsonModel.ChannelId); - } - - return (guild, channel); - } - - /// - /// The ID of the the message belongs to. - /// - public ulong? GuildId { get; } - - /// - /// The the message belongs to. - /// - public Guild? Guild { get; } - - /// - /// The the message was sent in. - /// - public TextChannel? Channel { get; } - - /// - public ulong ChannelId { get; } - - /// - public User? Author { get; } - - /// - public string? Content { get; } - - /// - public DateTimeOffset? EditedAt { get; } - - /// - public bool? IsTts { get; } - - /// - public bool? MentionEveryone { get; } - - /// - public IReadOnlyDictionary? MentionedUsers { get; } - - /// - public IReadOnlyList? MentionedRoleIds { get; } - - /// - public IReadOnlyDictionary? MentionedChannels { get; } - - /// - public IReadOnlyDictionary? Attachments { get; } - - /// - public IReadOnlyList? Embeds { get; } - - /// - public IReadOnlyList? Reactions { get; } - - /// - public string? Nonce { get; } - - /// - public bool? IsPinned { get; } - - /// - public ulong? WebhookId { get; } - - /// - public MessageType? Type { get; } - - /// - public MessageActivity? Activity { get; } - - /// - public Application? Application { get; } - - /// - public ulong? ApplicationId { get; } - - /// - public MessageReference? MessageReference { get; } - - /// - public MessageFlags? Flags { get; } - - /// - public RestMessage? ReferencedMessage { get; } - - /// - public MessageInteractionMetadata? InteractionMetadata { get; } - - /// - [Obsolete($"Replaced by '{nameof(InteractionMetadata)}'")] - public MessageInteraction? Interaction { get; } - - /// - public GuildThread? StartedThread { get; } - - /// - public IReadOnlyList? Components { get; } - - /// - public IReadOnlyDictionary? Stickers { get; } - - /// - public int? Position { get; } - - /// - public RoleSubscriptionData? RoleSubscriptionData { get; } - - /// - public InteractionResolvedData? ResolvedData { get; } - - public MessagePoll? Poll { get; } - - /// - public Task ReplyAsync(ReplyMessageProperties replyMessage, RestRequestProperties? properties = null); -} - -internal partial class PartialMessage : ClientEntity, IPartialMessage, IJsonModel -{ - private readonly JsonMessage _jsonModel; - JsonMessage IJsonModel.JsonModel => _jsonModel; - - public PartialMessage(JsonMessage jsonModel, Guild? guild, TextChannel? channel, RestClient client) : base(client) - { - _jsonModel = jsonModel; - - Guild = guild; - Channel = channel; - - var author = jsonModel.Author; - if (author is not null) - { - var guildUser = jsonModel.GuildUser; - if (guildUser is null) - Author = new(jsonModel.Author!, client); - else - { - guildUser.User = jsonModel.Author!; - Author = new GuildUser(guildUser, jsonModel.GuildId.GetValueOrDefault(), client); - } - } - - var mentionedUsers = jsonModel.MentionedUsers; - if (mentionedUsers is not null) - MentionedUsers = mentionedUsers.ToDictionary(u => u.Id, u => - { - var guildUser = u.GuildUser; - if (guildUser is null) - return new User(u, client); - - guildUser.User = u; - return new GuildUser(guildUser, jsonModel.GuildId.GetValueOrDefault(), client); - }); - - var mentionedChannels = jsonModel.MentionedChannels; - if (mentionedChannels is not null) - MentionedChannels = mentionedChannels.ToDictionary(c => c.Id, c => new GuildChannelMention(c)); - - var attachments = jsonModel.Attachments; - if (attachments is not null) - Attachments = attachments.ToDictionary(a => a.Id, Attachment.CreateFromJson); - - var embeds = jsonModel.Embeds; - if (embeds is not null) - Embeds = embeds.Select(e => new Embed(e)).ToArray(); - - var reactions = jsonModel.Reactions; - if (reactions is not null) - Reactions = reactions.Select(r => new MessageReaction(r)).ToArray(); - - var activity = jsonModel.Activity; - if (activity is not null) - Activity = new(activity); - - var application = jsonModel.Application; - if (application is not null) - Application = new(application, client); - - var messageReference = jsonModel.MessageReference; - if (messageReference is not null) - MessageReference = new(messageReference); - - var referencedMessage = jsonModel.ReferencedMessage; - if (referencedMessage is not null) - ReferencedMessage = new(referencedMessage, client); - - var interactionMetadata = jsonModel.InteractionMetadata; - if (interactionMetadata is not null) - InteractionMetadata = new(interactionMetadata, client); - -#pragma warning disable CS0618 // Type or member is obsolete - var interaction = jsonModel.Interaction; - if (interaction is not null) - Interaction = new(interaction, client); -#pragma warning restore CS0618 // Type or member is obsolete - - var startedThread = jsonModel.StartedThread; - if (startedThread is not null) - StartedThread = GuildThread.CreateFromJson(startedThread, client); - - var components = jsonModel.Components; - if (components is not null) - Components = components.Select(IMessageComponent.CreateFromJson).ToArray(); - - var stickers = jsonModel.Stickers; - if (stickers is not null) - Stickers = stickers.ToDictionary(s => s.Id, s => new MessageSticker(s, client)); - - var roleSubscriptionData = jsonModel.RoleSubscriptionData; - if (roleSubscriptionData is not null) - RoleSubscriptionData = new(roleSubscriptionData); - - var resolvedData = jsonModel.ResolvedData; - if (resolvedData is not null) - ResolvedData = new(resolvedData, jsonModel.GuildId, client); - - var poll = jsonModel.Poll; - if (poll is not null) - Poll = new(poll); - } - - public override ulong Id => _jsonModel.Id; - - public ulong? GuildId => _jsonModel.GuildId; - - public Guild? Guild { get; } - - public ulong ChannelId => _jsonModel.ChannelId; - - public TextChannel? Channel { get; } - - public User? Author { get; } - - public string? Content => _jsonModel.Content; - - public DateTimeOffset? EditedAt => _jsonModel.EditedAt; - - public bool? IsTts => _jsonModel.IsTts; - - public bool? MentionEveryone => _jsonModel.MentionEveryone; - - public IReadOnlyDictionary? MentionedUsers { get; } - - public IReadOnlyList? MentionedRoleIds => _jsonModel.MentionedRoleIds; - - public IReadOnlyDictionary? MentionedChannels { get; } - - public IReadOnlyDictionary? Attachments { get; } - - public IReadOnlyList? Embeds { get; } - - public IReadOnlyList? Reactions { get; } - - public string? Nonce => _jsonModel.Nonce; - - public bool? IsPinned => _jsonModel.IsPinned; - - public ulong? WebhookId => _jsonModel.WebhookId; - - public MessageType? Type => _jsonModel.Type; - - public MessageActivity? Activity { get; } - - public Application? Application { get; } - - public ulong? ApplicationId => _jsonModel.ApplicationId; - - public MessageReference? MessageReference { get; } - - public MessageFlags? Flags => _jsonModel.Flags; - - public RestMessage? ReferencedMessage { get; } - - public MessageInteractionMetadata? InteractionMetadata { get; } - - public MessageInteraction? Interaction { get; } - - public GuildThread? StartedThread { get; } - - public IReadOnlyList? Components { get; } - - public IReadOnlyDictionary? Stickers { get; } - - public int? Position => _jsonModel.Position; - - public RoleSubscriptionData? RoleSubscriptionData { get; } - - public InteractionResolvedData? ResolvedData { get; } - - public MessagePoll? Poll { get; } - - public Task ReplyAsync(ReplyMessageProperties replyMessage, RestRequestProperties? properties = null) - => SendAsync(replyMessage.ToMessageProperties(Id), properties); -} diff --git a/NetCord/Gateway/GuildInvite.cs b/NetCord/Gateway/Invite.cs similarity index 61% rename from NetCord/Gateway/GuildInvite.cs rename to NetCord/Gateway/Invite.cs index eb3b553b..c3da9d67 100644 --- a/NetCord/Gateway/GuildInvite.cs +++ b/NetCord/Gateway/Invite.cs @@ -2,12 +2,12 @@ namespace NetCord.Gateway; -public class GuildInvite : IGuildInvite, IJsonModel +public class Invite : IInvite, IJsonModel { - JsonModels.JsonGuildInvite IJsonModel.JsonModel => _jsonModel; - private readonly JsonModels.JsonGuildInvite _jsonModel; + JsonModels.JsonInvite IJsonModel.JsonModel => _jsonModel; + private readonly JsonModels.JsonInvite _jsonModel; - public GuildInvite(JsonModels.JsonGuildInvite jsonModel, RestClient client) + public Invite(JsonModels.JsonInvite jsonModel, RestClient client) { _jsonModel = jsonModel; @@ -24,6 +24,8 @@ public GuildInvite(JsonModels.JsonGuildInvite jsonModel, RestClient client) TargetApplication = new(targetApplication, client); } + public InviteType Type => _jsonModel.Type; + public ulong ChannelId => _jsonModel.ChannelId; public string Code => _jsonModel.Code; @@ -38,7 +40,7 @@ public GuildInvite(JsonModels.JsonGuildInvite jsonModel, RestClient client) public int MaxUses => _jsonModel.MaxUses; - public GuildInviteTargetType? TargetType => _jsonModel.TargetType; + public InviteTargetType? TargetType => _jsonModel.TargetType; public User? TargetUser { get; } @@ -48,15 +50,15 @@ public GuildInvite(JsonModels.JsonGuildInvite jsonModel, RestClient client) public int Uses => _jsonModel.Uses; - ulong? IGuildInvite.ChannelId => ChannelId; + ulong? IInvite.ChannelId => ChannelId; - int? IGuildInvite.MaxAge => MaxAge; + int? IInvite.MaxAge => MaxAge; - int? IGuildInvite.MaxUses => MaxUses; + int? IInvite.MaxUses => MaxUses; - bool? IGuildInvite.Temporary => Temporary; + bool? IInvite.Temporary => Temporary; - int? IGuildInvite.Uses => Uses; + int? IInvite.Uses => Uses; - DateTimeOffset? IGuildInvite.CreatedAt => CreatedAt; + DateTimeOffset? IInvite.CreatedAt => CreatedAt; } diff --git a/NetCord/Gateway/JsonModels/EventArgs/JsonGuildInviteDeleteEventArgs.cs b/NetCord/Gateway/JsonModels/EventArgs/JsonInviteDeleteEventArgs.cs similarity index 88% rename from NetCord/Gateway/JsonModels/EventArgs/JsonGuildInviteDeleteEventArgs.cs rename to NetCord/Gateway/JsonModels/EventArgs/JsonInviteDeleteEventArgs.cs index 5a762d33..d3c6b4a7 100644 --- a/NetCord/Gateway/JsonModels/EventArgs/JsonGuildInviteDeleteEventArgs.cs +++ b/NetCord/Gateway/JsonModels/EventArgs/JsonInviteDeleteEventArgs.cs @@ -2,7 +2,7 @@ namespace NetCord.Gateway.JsonModels.EventArgs; -public class JsonGuildInviteDeleteEventArgs +public class JsonInviteDeleteEventArgs { [JsonPropertyName("channel_id")] public ulong InviteChannelId { get; set; } diff --git a/NetCord/Gateway/JsonModels/EventArgs/JsonMessageReactionAddEventArgs.cs b/NetCord/Gateway/JsonModels/EventArgs/JsonMessageReactionAddEventArgs.cs index 6f4ca1b8..204c3f57 100644 --- a/NetCord/Gateway/JsonModels/EventArgs/JsonMessageReactionAddEventArgs.cs +++ b/NetCord/Gateway/JsonModels/EventArgs/JsonMessageReactionAddEventArgs.cs @@ -26,4 +26,13 @@ public class JsonMessageReactionAddEventArgs [JsonPropertyName("message_author_id")] public ulong? MessageAuthorId { get; set; } + + [JsonPropertyName("burst")] + public bool Burst { get; set; } + + [JsonPropertyName("burst_colors")] + public Color[] BurstColors { get; set; } + + [JsonPropertyName("type")] + public ReactionType Type { get; set; } } diff --git a/NetCord/Gateway/JsonModels/EventArgs/JsonMessageReactionRemoveEventArgs.cs b/NetCord/Gateway/JsonModels/EventArgs/JsonMessageReactionRemoveEventArgs.cs index a5693081..9351a1bc 100644 --- a/NetCord/Gateway/JsonModels/EventArgs/JsonMessageReactionRemoveEventArgs.cs +++ b/NetCord/Gateway/JsonModels/EventArgs/JsonMessageReactionRemoveEventArgs.cs @@ -20,4 +20,10 @@ public class JsonMessageReactionRemoveEventArgs [JsonPropertyName("emoji")] public JsonEmoji Emoji { get; set; } + + [JsonPropertyName("burst")] + public bool Burst { get; set; } + + [JsonPropertyName("type")] + public ReactionType Type { get; set; } } diff --git a/NetCord/Gateway/JsonModels/JsonGuildInvite.cs b/NetCord/Gateway/JsonModels/JsonInvite.cs similarity index 87% rename from NetCord/Gateway/JsonModels/JsonGuildInvite.cs rename to NetCord/Gateway/JsonModels/JsonInvite.cs index b6baaad0..ec00d630 100644 --- a/NetCord/Gateway/JsonModels/JsonGuildInvite.cs +++ b/NetCord/Gateway/JsonModels/JsonInvite.cs @@ -4,8 +4,11 @@ namespace NetCord.Gateway.JsonModels; -public class JsonGuildInvite +public class JsonInvite { + [JsonPropertyName("type")] + public InviteType Type { get; set; } + [JsonPropertyName("channel_id")] public ulong ChannelId { get; set; } @@ -28,7 +31,7 @@ public class JsonGuildInvite public int MaxUses { get; set; } [JsonPropertyName("target_type")] - public GuildInviteTargetType? TargetType { get; set; } + public InviteTargetType? TargetType { get; set; } [JsonPropertyName("target_user")] public JsonUser? TargetUser { get; set; } diff --git a/NetCord/Gateway/Message.cs b/NetCord/Gateway/Message.cs index 5652970d..6006a3a7 100644 --- a/NetCord/Gateway/Message.cs +++ b/NetCord/Gateway/Message.cs @@ -6,14 +6,43 @@ namespace NetCord.Gateway; /// /// Represents a complete object, with all required fields present. /// -public class Message(JsonMessage jsonModel, Guild? guild, TextChannel? channel, RestClient client) : RestMessage(jsonModel, client), IPartialMessage +public class Message(JsonMessage jsonModel, Guild? guild, TextChannel? channel, RestClient client) : RestMessage(jsonModel, client) { public static Message CreateFromJson(JsonMessage jsonModel, IGatewayClientCache cache, RestClient client) { - var (guild, channel) = IPartialMessage.GetCacheData(jsonModel, cache); + var (guild, channel) = GetCacheData(jsonModel, cache); return new(jsonModel, guild, channel, client); } + internal static (Guild?, TextChannel?) GetCacheData(JsonMessage jsonModel, IGatewayClientCache cache) + { + Guild? guild; + TextChannel? channel; + var guildId = jsonModel.GuildId; + if (guildId.HasValue) + { + if (cache.Guilds.TryGetValue(guildId.GetValueOrDefault(), out guild)) + { + var channelId = jsonModel.ChannelId; + if (guild.Channels.TryGetValue(channelId, out var guildChannel)) + channel = (TextChannel)guildChannel; + else if (guild.ActiveThreads.TryGetValue(channelId, out var thread)) + channel = thread; + else + channel = null; + } + else + channel = null; + } + else + { + guild = null; + channel = cache.DMChannels.GetValueOrDefault(jsonModel.ChannelId); + } + + return (guild, channel); + } + /// public ulong? GuildId => _jsonModel.GuildId; @@ -22,19 +51,4 @@ public static Message CreateFromJson(JsonMessage jsonModel, IGatewayClientCache /// public TextChannel? Channel { get; } = channel; - - /// - bool? IPartialMessage.IsTts => IsTts; - - /// - bool? IPartialMessage.MentionEveryone => MentionEveryone; - - /// - bool? IPartialMessage.IsPinned => IsPinned; - - /// - MessageType? IPartialMessage.Type => Type; - - /// - MessageFlags? IPartialMessage.Flags => Flags; } diff --git a/NetCord/Gateway/Platform.cs b/NetCord/Gateway/Platform.cs index d3d5dd97..13aef7c1 100644 --- a/NetCord/Gateway/Platform.cs +++ b/NetCord/Gateway/Platform.cs @@ -2,7 +2,7 @@ namespace NetCord.Gateway; -[JsonConverter(typeof(JsonConverters.StringEnumConverterWithErrorHandling))] +[JsonConverter(typeof(JsonConverters.SafeStringEnumConverter))] public enum Platform { [JsonPropertyName("desktop")] diff --git a/NetCord/Gateway/ShardedGatewayClient.cs b/NetCord/Gateway/ShardedGatewayClient.cs index e06cca89..087c3244 100644 --- a/NetCord/Gateway/ShardedGatewayClient.cs +++ b/NetCord/Gateway/ShardedGatewayClient.cs @@ -305,8 +305,8 @@ private void HookEvents(GatewayClient client) HookEvent(client, _guildIntegrationCreateLock, ref _guildIntegrationCreate, a => _guildIntegrationCreate!(client, a), (c, e) => c.GuildIntegrationCreate += e); HookEvent(client, _guildIntegrationUpdateLock, ref _guildIntegrationUpdate, a => _guildIntegrationUpdate!(client, a), (c, e) => c.GuildIntegrationUpdate += e); HookEvent(client, _guildIntegrationDeleteLock, ref _guildIntegrationDelete, a => _guildIntegrationDelete!(client, a), (c, e) => c.GuildIntegrationDelete += e); - HookEvent(client, _guildInviteCreateLock, ref _guildInviteCreate, a => _guildInviteCreate!(client, a), (c, e) => c.GuildInviteCreate += e); - HookEvent(client, _guildInviteDeleteLock, ref _guildInviteDelete, a => _guildInviteDelete!(client, a), (c, e) => c.GuildInviteDelete += e); + HookEvent(client, _inviteCreateLock, ref _inviteCreate, a => _inviteCreate!(client, a), (c, e) => c.InviteCreate += e); + HookEvent(client, _inviteDeleteLock, ref _inviteDelete, a => _inviteDelete!(client, a), (c, e) => c.InviteDelete += e); HookEvent(client, _messageCreateLock, ref _messageCreate, a => _messageCreate!(client, a), (c, e) => c.MessageCreate += e); HookEvent(client, _messageUpdateLock, ref _messageUpdate, a => _messageUpdate!(client, a), (c, e) => c.MessageUpdate += e); HookEvent(client, _messageDeleteLock, ref _messageDelete, a => _messageDelete!(client, a), (c, e) => c.MessageDelete += e); @@ -1035,35 +1035,35 @@ public event Func? Gu private Func? _guildIntegrationDelete; private readonly object _guildIntegrationDeleteLock = new(); - /// - public event Func? GuildInviteCreate + /// + public event Func? InviteCreate { add { - HookEvent(_guildInviteCreateLock, value, ref _guildInviteCreate, client => a => _guildInviteCreate!(client, a), (c, e) => c.GuildInviteCreate += e); + HookEvent(_inviteCreateLock, value, ref _inviteCreate, client => a => _inviteCreate!(client, a), (c, e) => c.InviteCreate += e); } remove { - UnhookEvent(_guildInviteCreateLock, value, ref _guildInviteCreate, (c, e) => c.GuildInviteCreate -= e); + UnhookEvent(_inviteCreateLock, value, ref _inviteCreate, (c, e) => c.InviteCreate -= e); } } - private Func? _guildInviteCreate; - private readonly object _guildInviteCreateLock = new(); + private Func? _inviteCreate; + private readonly object _inviteCreateLock = new(); - /// - public event Func? GuildInviteDelete + /// + public event Func? InviteDelete { add { - HookEvent(_guildInviteDeleteLock, value, ref _guildInviteDelete, client => a => _guildInviteDelete!(client, a), (c, e) => c.GuildInviteDelete += e); + HookEvent(_inviteDeleteLock, value, ref _inviteDelete, client => a => _inviteDelete!(client, a), (c, e) => c.InviteDelete += e); } remove { - UnhookEvent(_guildInviteDeleteLock, value, ref _guildInviteDelete, (c, e) => c.GuildInviteDelete -= e); + UnhookEvent(_inviteDeleteLock, value, ref _inviteDelete, (c, e) => c.InviteDelete -= e); } } - private Func? _guildInviteDelete; - private readonly object _guildInviteDeleteLock = new(); + private Func? _inviteDelete; + private readonly object _inviteDeleteLock = new(); /// public event Func? MessageCreate @@ -1081,7 +1081,7 @@ public event Func? MessageCreate private readonly object _messageCreateLock = new(); /// - public event Func? MessageUpdate + public event Func? MessageUpdate { add { @@ -1092,7 +1092,7 @@ public event Func? MessageUpdate UnhookEvent(_messageUpdateLock, value, ref _messageUpdate, (c, e) => c.MessageUpdate -= e); } } - private Func? _messageUpdate; + private Func? _messageUpdate; private readonly object _messageUpdateLock = new(); /// diff --git a/NetCord/GuildEmoji.cs b/NetCord/GuildEmoji.cs index ae4f9b5a..a613a701 100644 --- a/NetCord/GuildEmoji.cs +++ b/NetCord/GuildEmoji.cs @@ -3,71 +3,9 @@ namespace NetCord; -public partial class GuildEmoji : Emoji, ISpanFormattable +public partial class GuildEmoji(JsonEmoji jsonModel, ulong guildId, RestClient client) : CustomEmoji(jsonModel, client) { - private readonly RestClient _client; - - public GuildEmoji(JsonEmoji jsonModel, ulong guildId, RestClient client) : base(jsonModel) - { - _client = client; - - var creator = jsonModel.Creator; - if (creator is not null) - Creator = new(creator, client); - - GuildId = guildId; - } - - public ulong Id => _jsonModel.Id.GetValueOrDefault(); - public IReadOnlyList? AllowedRoles => _jsonModel.AllowedRoles; - public User? Creator { get; } - - public bool? RequireColons => _jsonModel.RequireColons; - - public bool? Managed => _jsonModel.Managed; - - public bool? Available => _jsonModel.Available; - - public ulong GuildId { get; } - - public override string ToString() => Animated ? $"" : $"<:{Name}:{Id}>"; - - public string ToString(string? format, IFormatProvider? formatProvider) => ToString(); - - public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider? provider = null) - { - var name = Name; - if (Animated) - { - if (destination.Length < 6 + name.Length || !Id.TryFormat(destination[(4 + name.Length)..^1], out int length)) - { - charsWritten = 0; - return false; - } - - "An pointing to the user's guild avatar. If the user does not have one set, returns . public ImageUrl? GetGuildAvatarUrl(ImageFormat? format = null) => GuildAvatarHash is string hash ? ImageUrl.GuildUserAvatar(GuildId, Id, hash, format) : null; + /// + /// Gets the of the user's guild avatar decoration. + /// + /// An pointing to the user's guild avatar decoration. If the user does not have one set, returns . + public ImageUrl? GetGuildAvatarDecorationUrl() => GuildAvatarDecorationData is { Hash: var hash } ? ImageUrl.AvatarDecoration(hash) : null; + /// /// Applies a timeout to the for a specified . /// diff --git a/NetCord/IGuildInvite.cs b/NetCord/IInvite.cs similarity index 79% rename from NetCord/IGuildInvite.cs rename to NetCord/IInvite.cs index b13862c0..41b91a6f 100644 --- a/NetCord/IGuildInvite.cs +++ b/NetCord/IInvite.cs @@ -1,7 +1,8 @@ namespace NetCord; -public interface IGuildInvite +public interface IInvite { + public InviteType Type { get; } public ulong? GuildId { get; } public ulong? ChannelId { get; } public string Code { get; } @@ -10,7 +11,7 @@ public interface IGuildInvite public Application? TargetApplication { get; } public int? MaxAge { get; } public int? MaxUses { get; } - public GuildInviteTargetType? TargetType { get; } + public InviteTargetType? TargetType { get; } public bool? Temporary { get; } public int? Uses { get; } public DateTimeOffset? CreatedAt { get; } diff --git a/NetCord/ImageUrl.cs b/NetCord/ImageUrl.cs index 3ad5a00e..77f0542a 100644 --- a/NetCord/ImageUrl.cs +++ b/NetCord/ImageUrl.cs @@ -157,9 +157,9 @@ public static ImageUrl GuildUserAvatar(ulong guildId, ulong userId, string avata return new($"/guilds/{guildId}/users/{userId}/avatars/{avatarHash}", GetExtension(avatarHash, format)); } - public static ImageUrl UserAvatarDecoration(ulong userId, string avatarDecorationHash) + public static ImageUrl AvatarDecoration(string avatarDecorationHash) { - return new($"/avatar-decorations/{userId}/{avatarDecorationHash}", "png"); + return new($"/avatar-decoration-presets/{avatarDecorationHash}", "png"); } public static ImageUrl ApplicationIcon(ulong applicationId, string iconHash, ImageFormat format) diff --git a/NetCord/IntegrationType.cs b/NetCord/IntegrationType.cs index 6d635f34..ddbde53f 100644 --- a/NetCord/IntegrationType.cs +++ b/NetCord/IntegrationType.cs @@ -2,7 +2,7 @@ namespace NetCord; -[JsonConverter(typeof(JsonConverters.StringEnumConverterWithErrorHandling))] +[JsonConverter(typeof(JsonConverters.SafeStringEnumConverter))] public enum IntegrationType { [JsonPropertyName("twitch")] diff --git a/NetCord/Interaction.cs b/NetCord/Interaction.cs index 01ebdd4e..d2bb8440 100644 --- a/NetCord/Interaction.cs +++ b/NetCord/Interaction.cs @@ -20,6 +20,10 @@ private protected Interaction(JsonModels.JsonInteraction jsonModel, Guild? guild else User = new(jsonModel.User!, client); + var guildReference = jsonModel.GuildReference; + if (guildReference is not null) + GuildReference = new(guildReference); + Guild = guild; Channel = TextChannel.CreateFromJson(jsonModel.Channel!, client); Entitlements = jsonModel.Entitlements.Select(e => new Entitlement(e)).ToArray(); @@ -33,6 +37,8 @@ private protected Interaction(JsonModels.JsonInteraction jsonModel, Guild? guild public ulong? GuildId => _jsonModel.GuildId; + public InteractionGuildReference? GuildReference { get; } + public Guild? Guild { get; } public TextChannel Channel { get; } diff --git a/NetCord/InteractionGuildReference.cs b/NetCord/InteractionGuildReference.cs new file mode 100644 index 00000000..f76c8d64 --- /dev/null +++ b/NetCord/InteractionGuildReference.cs @@ -0,0 +1,14 @@ +using NetCord.JsonModels; + +namespace NetCord; + +public class InteractionGuildReference(JsonInteractionGuildReference jsonModel) : Entity, IJsonModel +{ + JsonInteractionGuildReference IJsonModel.JsonModel => jsonModel; + + public override ulong Id => jsonModel.Id; + + public string[] Features => jsonModel.Features; + + public string Locale => jsonModel.Locale; +} diff --git a/NetCord/GuildInviteTargetType.cs b/NetCord/InviteTargetType.cs similarity index 67% rename from NetCord/GuildInviteTargetType.cs rename to NetCord/InviteTargetType.cs index c53465ef..f1d6fb60 100644 --- a/NetCord/GuildInviteTargetType.cs +++ b/NetCord/InviteTargetType.cs @@ -1,6 +1,6 @@ namespace NetCord; -public enum GuildInviteTargetType +public enum InviteTargetType { Stream = 1, EmbeddedApplication = 2, diff --git a/NetCord/InviteType.cs b/NetCord/InviteType.cs new file mode 100644 index 00000000..c4365f46 --- /dev/null +++ b/NetCord/InviteType.cs @@ -0,0 +1,8 @@ +namespace NetCord; + +public enum InviteType : byte +{ + Guild = 0, + GroupDMChannel = 1, + Friend = 2, +} diff --git a/NetCord/JsonConverters/AttachmentPropertiesIEnumerableConverter.cs b/NetCord/JsonConverters/AttachmentPropertiesIEnumerableConverter.cs index f75040e4..342df3be 100644 --- a/NetCord/JsonConverters/AttachmentPropertiesIEnumerableConverter.cs +++ b/NetCord/JsonConverters/AttachmentPropertiesIEnumerableConverter.cs @@ -9,6 +9,7 @@ public class AttachmentPropertiesIEnumerableConverter : JsonConverter : JsonConverter where T : struct, Enum +public class SafeStringEnumConverter : JsonConverter where T : struct, Enum { - private static readonly JsonEncodedText _unknownName = JsonEncodedText.Encode(default(ReadOnlySpan)); private static readonly T _unknownValue = (T)(object)-1; - private readonly Dictionary, T> _namesDictionary; - private readonly Dictionary _valuesDictionary; + private readonly FrozenDictionary, T> _namesDictionary; + private readonly FrozenDictionary _valuesDictionary; [UnconditionalSuppressMessage("Trimming", "IL2090:'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The generic parameter of the source method or type does not have matching annotations.", Justification = "Literal fields on enums can never be trimmed")] - public StringEnumConverterWithErrorHandling() + public SafeStringEnumConverter() { var enumType = typeof(T); var fields = enumType.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); int length = fields.Length; - Dictionary, T> namesDictionary = new(length, OrdinalReadOnlyMemoryByteComparer.Instance); - Dictionary valuesDictionary = new(length); + var names = new KeyValuePair, T>[length]; + var values = new KeyValuePair[length]; for (var i = 0; i < length; i++) { @@ -37,12 +37,12 @@ public StringEnumConverterWithErrorHandling() var rawValue = field.GetRawConstantValue()!; var value = (T)rawValue; - namesDictionary.Add(nameBytes, value); - valuesDictionary.Add(value, JsonEncodedText.Encode(nameBytes)); + names[i] = new(nameBytes, value); + values[i] = new(value, JsonEncodedText.Encode(nameBytes)); } - _namesDictionary = namesDictionary; - _valuesDictionary = valuesDictionary; + _namesDictionary = names.ToFrozenDictionary(SafeStringEnumConverter.OrdinalReadOnlyMemoryByteComparer.Instance); + _valuesDictionary = values.ToFrozenDictionary(); } public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) @@ -61,15 +61,20 @@ public override T ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConve public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { - writer.WriteStringValue(_valuesDictionary.TryGetValue(value, out var name) ? name : _unknownName); + writer.WriteStringValue(_valuesDictionary.TryGetValue(value, out var name) ? name : SafeStringEnumConverter._unknownName); } public override void WriteAsPropertyName(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { - writer.WritePropertyName(_valuesDictionary.TryGetValue(value, out var name) ? name : _unknownName); + writer.WritePropertyName(_valuesDictionary.TryGetValue(value, out var name) ? name : SafeStringEnumConverter._unknownName); } +} + +static file class SafeStringEnumConverter +{ + internal static readonly JsonEncodedText _unknownName = JsonEncodedText.Encode(default(ReadOnlySpan)); - private class OrdinalReadOnlyMemoryByteComparer : IComparer>, IEqualityComparer> + internal class OrdinalReadOnlyMemoryByteComparer : IComparer>, IEqualityComparer> { public static OrdinalReadOnlyMemoryByteComparer Instance { get; } = new(); diff --git a/NetCord/JsonModels/JsonAttachment.cs b/NetCord/JsonModels/JsonAttachment.cs index 68a7ce74..c689dfce 100644 --- a/NetCord/JsonModels/JsonAttachment.cs +++ b/NetCord/JsonModels/JsonAttachment.cs @@ -8,6 +8,9 @@ public class JsonAttachment : JsonEntity [JsonPropertyName("filename")] public string FileName { get; set; } + [JsonPropertyName("title")] + public string? Title { get; set; } + [JsonPropertyName("description")] public string? Description { get; set; } diff --git a/NetCord/JsonModels/JsonAvatarDecorationData.cs b/NetCord/JsonModels/JsonAvatarDecorationData.cs new file mode 100644 index 00000000..043a2141 --- /dev/null +++ b/NetCord/JsonModels/JsonAvatarDecorationData.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace NetCord.JsonModels; + +public class JsonAvatarDecorationData +{ + [JsonPropertyName("asset")] + public string Hash { get; set; } + + [JsonPropertyName("sku_id")] + public ulong SkuId { get; set; } +} diff --git a/NetCord/JsonModels/JsonComponent.cs b/NetCord/JsonModels/JsonComponent.cs index 515c2b49..670e7643 100644 --- a/NetCord/JsonModels/JsonComponent.cs +++ b/NetCord/JsonModels/JsonComponent.cs @@ -25,6 +25,9 @@ public class JsonComponent [JsonPropertyName("url")] public string? Url { get; set; } + [JsonPropertyName("sku_id")] + public ulong? SkuId { get; set; } + [JsonPropertyName("options")] public JsonMenuSelectOption[] Options { get; set; } diff --git a/NetCord/JsonModels/JsonGuildUser.cs b/NetCord/JsonModels/JsonGuildUser.cs index 07351745..ed1aa980 100644 --- a/NetCord/JsonModels/JsonGuildUser.cs +++ b/NetCord/JsonModels/JsonGuildUser.cs @@ -42,4 +42,7 @@ public class JsonGuildUser [JsonPropertyName("communication_disabled_until")] public DateTimeOffset? TimeOutUntil { get; set; } + + [JsonPropertyName("avatar_decoration_data")] + public JsonAvatarDecorationData? GuildAvatarDecorationData { get; set; } } diff --git a/NetCord/JsonModels/JsonInteraction.cs b/NetCord/JsonModels/JsonInteraction.cs index 9cd0a632..773ea0f2 100644 --- a/NetCord/JsonModels/JsonInteraction.cs +++ b/NetCord/JsonModels/JsonInteraction.cs @@ -16,6 +16,9 @@ public class JsonInteraction : JsonEntity [JsonPropertyName("guild_id")] public ulong? GuildId { get; set; } + [JsonPropertyName("guild")] + public JsonInteractionGuildReference? GuildReference { get; set; } + [JsonPropertyName("channel")] public JsonChannel? Channel { get; set; } diff --git a/NetCord/JsonModels/JsonInteractionGuildReference.cs b/NetCord/JsonModels/JsonInteractionGuildReference.cs new file mode 100644 index 00000000..f1c1b7a3 --- /dev/null +++ b/NetCord/JsonModels/JsonInteractionGuildReference.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace NetCord.JsonModels; + +public class JsonInteractionGuildReference : JsonEntity +{ + [JsonPropertyName("features")] + public string[] Features { get; set; } + + [JsonPropertyName("locale")] + public string Locale { get; set; } +} diff --git a/NetCord/JsonModels/JsonMessage.cs b/NetCord/JsonModels/JsonMessage.cs index ca1935e8..5b4d8262 100644 --- a/NetCord/JsonModels/JsonMessage.cs +++ b/NetCord/JsonModels/JsonMessage.cs @@ -65,11 +65,14 @@ public class JsonMessage : JsonEntity [JsonPropertyName("application_id")] public ulong? ApplicationId { get; set; } + [JsonPropertyName("flags")] + public MessageFlags? Flags { get; set; } + [JsonPropertyName("message_reference")] public JsonMessageReference? MessageReference { get; set; } - [JsonPropertyName("flags")] - public MessageFlags? Flags { get; set; } + [JsonPropertyName("message_snapshots")] + public JsonMessageSnapshot[]? MessageSnapshots { get; set; } [JsonPropertyName("referenced_message")] public JsonMessage? ReferencedMessage { get; set; } @@ -107,4 +110,7 @@ public class JsonMessage : JsonEntity [JsonPropertyName("poll")] public JsonMessagePoll? Poll { get; set; } + + [JsonPropertyName("call")] + public JsonMessageCall? Call { get; set; } } diff --git a/NetCord/JsonModels/JsonMessageCall.cs b/NetCord/JsonModels/JsonMessageCall.cs new file mode 100644 index 00000000..53555571 --- /dev/null +++ b/NetCord/JsonModels/JsonMessageCall.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace NetCord.JsonModels; + +public class JsonMessageCall +{ + [JsonPropertyName("participants")] + public ulong[] Participants { get; set; } + + [JsonPropertyName("ended_timestamp")] + public DateTimeOffset? EndedAt { get; set; } +} diff --git a/NetCord/JsonModels/JsonMessageSnapshot.cs b/NetCord/JsonModels/JsonMessageSnapshot.cs new file mode 100644 index 00000000..6273b843 --- /dev/null +++ b/NetCord/JsonModels/JsonMessageSnapshot.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace NetCord.JsonModels; + +public class JsonMessageSnapshot +{ + [JsonPropertyName("message")] + public JsonMessageSnapshotMessage Message { get; set; } +} diff --git a/NetCord/JsonModels/JsonMessageSnapshotMessage.cs b/NetCord/JsonModels/JsonMessageSnapshotMessage.cs new file mode 100644 index 00000000..737a25fd --- /dev/null +++ b/NetCord/JsonModels/JsonMessageSnapshotMessage.cs @@ -0,0 +1,33 @@ +using System.Text.Json.Serialization; + +namespace NetCord.JsonModels; + +public class JsonMessageSnapshotMessage +{ + [JsonPropertyName("type")] + public MessageType Type { get; set; } + + [JsonPropertyName("content")] + public string Content { get; set; } + + [JsonPropertyName("embeds")] + public JsonEmbed[] Embeds { get; set; } + + [JsonPropertyName("attachments")] + public JsonAttachment[] Attachments { get; set; } + + [JsonPropertyName("timestamp")] + public DateTimeOffset CreatedAt { get; set; } + + [JsonPropertyName("edited_timestamp")] + public DateTimeOffset? EditedAt { get; set; } + + [JsonPropertyName("flags")] + public MessageFlags? Flags { get; set; } + + [JsonPropertyName("mentions")] + public JsonUser[] MentionedUsers { get; set; } + + [JsonPropertyName("mention_roles")] + public ulong[] MentionedRoleIds { get; set; } +} diff --git a/NetCord/JsonModels/JsonSelectMenuDefaultValueType.cs b/NetCord/JsonModels/JsonSelectMenuDefaultValueType.cs index 371e1bd4..992b0835 100644 --- a/NetCord/JsonModels/JsonSelectMenuDefaultValueType.cs +++ b/NetCord/JsonModels/JsonSelectMenuDefaultValueType.cs @@ -4,7 +4,7 @@ namespace NetCord.JsonModels; -[JsonConverter(typeof(StringEnumConverterWithErrorHandling))] +[JsonConverter(typeof(SafeStringEnumConverter))] public enum JsonSelectMenuDefaultValueType { [JsonPropertyName("user")] diff --git a/NetCord/JsonModels/JsonUser.cs b/NetCord/JsonModels/JsonUser.cs index 2c96a529..f2ac4940 100644 --- a/NetCord/JsonModels/JsonUser.cs +++ b/NetCord/JsonModels/JsonUser.cs @@ -50,8 +50,8 @@ public class JsonUser : JsonEntity [JsonPropertyName("public_flags")] public UserFlags? PublicFlags { get; set; } - [JsonPropertyName("avatar_decoration")] - public string? AvatarDecorationHash { get; set; } + [JsonPropertyName("avatar_decoration_data")] + public JsonAvatarDecorationData? AvatarDecorationData { get; set; } [JsonPropertyName("member")] public JsonGuildUser? GuildUser { get; set; } diff --git a/NetCord/MessagePollProperties.cs b/NetCord/MessagePollProperties.cs index b7f19af9..f46226ff 100644 --- a/NetCord/MessagePollProperties.cs +++ b/NetCord/MessagePollProperties.cs @@ -2,21 +2,43 @@ namespace NetCord; -public partial class MessagePollProperties(MessagePollMediaProperties question, IEnumerable answers, int durationInHours) +/// +/// +/// +/// The question of the poll. +/// Each of the answers available in the poll, up to 10. +public partial class MessagePollProperties(MessagePollMediaProperties question, IEnumerable answers) { + /// + /// The question of the poll. + /// [JsonPropertyName("question")] public MessagePollMediaProperties Question { get; set; } = question; + /// + /// Each of the answers available in the poll, up to 10. + /// [JsonPropertyName("answers")] public IEnumerable Answers { get; set; } = answers; + /// + /// Number of hours the poll should be open for, up to 32 days. Defaults to 24. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonPropertyName("duration")] - public int DurationInHours { get; set; } = durationInHours; + public int? DurationInHours { get; set; } + /// + /// Whether a user can select multiple answers. Defaults to . + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonPropertyName("allow_multiselect")] public bool AllowMultiselect { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + /// + /// The layout of the poll. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonPropertyName("layout_type")] public MessagePollLayoutType? LayoutType { get; set; } } diff --git a/NetCord/MessageReferenceType.cs b/NetCord/MessageReferenceType.cs new file mode 100644 index 00000000..327bee75 --- /dev/null +++ b/NetCord/MessageReferenceType.cs @@ -0,0 +1,7 @@ +namespace NetCord; + +public enum MessageReferenceType : byte +{ + Reply = 0, + Forward = 1, +} diff --git a/NetCord/MessageSnapshot.cs b/NetCord/MessageSnapshot.cs new file mode 100644 index 00000000..83f696de --- /dev/null +++ b/NetCord/MessageSnapshot.cs @@ -0,0 +1,11 @@ +using NetCord.JsonModels; +using NetCord.Rest; + +namespace NetCord; + +public class MessageSnapshot(JsonMessageSnapshot jsonModel, ulong? guildId, RestClient client) : IJsonModel +{ + JsonMessageSnapshot IJsonModel.JsonModel => jsonModel; + + public MessageSnapshotMessage Message { get; } = new(jsonModel.Message, guildId, client); +} diff --git a/NetCord/MessageSnapshotMessage.cs b/NetCord/MessageSnapshotMessage.cs new file mode 100644 index 00000000..dc0207b8 --- /dev/null +++ b/NetCord/MessageSnapshotMessage.cs @@ -0,0 +1,57 @@ +using NetCord.JsonModels; +using NetCord.Rest; + +namespace NetCord; + +public class MessageSnapshotMessage(JsonMessageSnapshotMessage jsonModel, ulong? guildId, RestClient client) : IJsonModel +{ + JsonMessageSnapshotMessage IJsonModel.JsonModel => jsonModel; + + /// + /// The type of the message. + /// + public MessageType Type => jsonModel.Type; + + /// + /// The text contents of the message. + /// + public string Content => jsonModel.Content; + + /// + /// A list of objects containing any embedded content present in the message. + /// + public IReadOnlyList Embeds { get; } = jsonModel.Embeds.Select(e => new Embed(e)).ToArray(); + + /// + /// A dictionary of objects indexed by their IDs, containing any files attached in the message. + /// + public IReadOnlyDictionary Attachments { get; } = jsonModel.Attachments.ToDictionary(a => a.Id, Attachment.CreateFromJson); + + /// + /// When the message was edited (or null if never). + /// + public DateTimeOffset? EditedAt => jsonModel.EditedAt; + + /// + /// A object indicating the message's applied flags. + /// + public MessageFlags Flags => jsonModel.Flags.GetValueOrDefault(); + + /// + /// A dictionary of objects indexed by their IDs, containing users specifically mentioned in the message. + /// + public IReadOnlyDictionary MentionedUsers { get; } = jsonModel.MentionedUsers.ToDictionary(u => u.Id, u => + { + var guildUser = u.GuildUser; + if (guildUser is null) + return new User(u, client); + + guildUser.User = u; + return new GuildUser(guildUser, guildId.GetValueOrDefault(), client); + }); + + /// + /// A list of IDs corresponding to roles specifically mentioned in the message. + /// + public IReadOnlyList MentionedRoleIds => jsonModel.MentionedRoleIds; +} diff --git a/NetCord/NetCord.csproj b/NetCord/NetCord.csproj index 473837e7..21b525f6 100644 --- a/NetCord/NetCord.csproj +++ b/NetCord/NetCord.csproj @@ -14,7 +14,7 @@ SmallSquare.png MIT $(VersionPrefix) - alpha.302 + alpha.311 The modern and fully customizable C# Discord library. true diff --git a/NetCord/PartialGuildUser.cs b/NetCord/PartialGuildUser.cs index 4f1dac59..f9ba0aad 100644 --- a/NetCord/PartialGuildUser.cs +++ b/NetCord/PartialGuildUser.cs @@ -6,10 +6,19 @@ namespace NetCord; /// /// Represents a object that lacks a field, as well as methods relying on it. /// -public class PartialGuildUser(JsonGuildUser jsonModel, RestClient client) : User(jsonModel.User, client), IJsonModel +public class PartialGuildUser : User, IJsonModel { JsonGuildUser IJsonModel.JsonModel => _jsonModel; - private protected new readonly JsonGuildUser _jsonModel = jsonModel; + private protected new readonly JsonGuildUser _jsonModel; + + public PartialGuildUser(JsonGuildUser jsonModel, RestClient client) : base(jsonModel.User, client) + { + _jsonModel = jsonModel; + + var guildAvatarDecorationData = jsonModel.GuildAvatarDecorationData; + if (guildAvatarDecorationData is not null) + GuildAvatarDecorationData = new(guildAvatarDecorationData); + } /// /// The user's guild nickname. @@ -66,8 +75,18 @@ public class PartialGuildUser(JsonGuildUser jsonModel, RestClient client) : User /// public DateTimeOffset? TimeOutUntil => _jsonModel.TimeOutUntil; + /// + /// Data for the guild user's avatar decoration. + /// + public AvatarDecorationData? GuildAvatarDecorationData { get; } + /// /// Whether the user has a guild avatar set. /// public bool HasGuildAvatar => GuildAvatarHash is not null; + + /// + /// Whether the user has a set avatar decoration. + /// + public bool HasGuildAvatarDecoration => GuildAvatarDecorationData is not null; } diff --git a/NetCord/Permissions.cs b/NetCord/Permissions.cs index c8b23d23..869a4e9c 100644 --- a/NetCord/Permissions.cs +++ b/NetCord/Permissions.cs @@ -242,4 +242,9 @@ public enum Permissions : ulong /// Allows sending polls. ///
SendPolls = 1uL << 49, + + /// + /// Allows user-installed apps to send public responses. When disabled, users will still be allowed to use their apps but the responses will be ephemeral. This only applies to apps not also installed to the server. + /// + UseExternalApplications = 1uL << 50, } diff --git a/NetCord/ReactionType.cs b/NetCord/ReactionType.cs new file mode 100644 index 00000000..eab5af15 --- /dev/null +++ b/NetCord/ReactionType.cs @@ -0,0 +1,7 @@ +namespace NetCord; + +public enum ReactionType : byte +{ + Normal = 0, + Burst = 1, +} diff --git a/NetCord/Rest/ApplicationEmojiOptions.cs b/NetCord/Rest/ApplicationEmojiOptions.cs new file mode 100644 index 00000000..6f5653da --- /dev/null +++ b/NetCord/Rest/ApplicationEmojiOptions.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace NetCord.Rest; + +public partial class ApplicationEmojiOptions +{ + internal ApplicationEmojiOptions() + { + } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("name")] + public string? Name { get; set; } +} diff --git a/NetCord/Rest/ApplicationEmojiProperties.cs b/NetCord/Rest/ApplicationEmojiProperties.cs new file mode 100644 index 00000000..201cbd5f --- /dev/null +++ b/NetCord/Rest/ApplicationEmojiProperties.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace NetCord.Rest; + +public partial class ApplicationEmojiProperties(string name, ImageProperties image) +{ + [JsonPropertyName("name")] + public string Name { get; set; } = name; + + [JsonPropertyName("image")] + public ImageProperties Image { get; set; } = image; +} diff --git a/NetCord/Rest/AttachmentProperties.cs b/NetCord/Rest/AttachmentProperties.cs index 524acabe..f7cd394c 100644 --- a/NetCord/Rest/AttachmentProperties.cs +++ b/NetCord/Rest/AttachmentProperties.cs @@ -26,6 +26,11 @@ protected AttachmentProperties(string fileName) ///
public string FileName { get; set; } + /// + /// Title of the attachment. + /// + public string? Title { get; set; } + /// /// Description for the file (max 1024 characters for attachments sent by message, max 200 characters for attachments used for sticker creation). /// diff --git a/NetCord/Rest/ComponentProperties/ButtonProperties.cs b/NetCord/Rest/ComponentProperties/ButtonProperties.cs index 83991c3c..07e22870 100644 --- a/NetCord/Rest/ComponentProperties/ButtonProperties.cs +++ b/NetCord/Rest/ComponentProperties/ButtonProperties.cs @@ -2,7 +2,7 @@ namespace NetCord.Rest; -public partial class ButtonProperties : IButtonProperties +public partial class ButtonProperties : ICustomizableButtonProperties { /// /// Developer-defined identifier for the button (max 100 characters). diff --git a/NetCord/Rest/ComponentProperties/IButtonProperties.cs b/NetCord/Rest/ComponentProperties/IButtonProperties.cs index b26c7158..be89a564 100644 --- a/NetCord/Rest/ComponentProperties/IButtonProperties.cs +++ b/NetCord/Rest/ComponentProperties/IButtonProperties.cs @@ -9,23 +9,13 @@ public partial interface IButtonProperties /// /// Style of the button. /// - public ButtonStyle Style { get; set; } + public ButtonStyle Style { get; } /// /// Type of the component. /// public ComponentType ComponentType { get; } - /// - /// Text that appears on the button (max 80 characters). - /// - public string? Label { get; set; } - - /// - /// Emoji that appears on the button. - /// - public EmojiProperties? Emoji { get; set; } - /// /// Whether the button is disabled. /// @@ -39,11 +29,14 @@ public override void Write(Utf8JsonWriter writer, IButtonProperties button, Json { switch (button) { - case ButtonProperties actionButton: - JsonSerializer.Serialize(writer, actionButton, Serialization.Default.ButtonProperties); + case ButtonProperties buttonProperties: + JsonSerializer.Serialize(writer, buttonProperties, Serialization.Default.ButtonProperties); + break; + case LinkButtonProperties linkButtonProperties: + JsonSerializer.Serialize(writer, linkButtonProperties, Serialization.Default.LinkButtonProperties); break; - case LinkButtonProperties linkButton: - JsonSerializer.Serialize(writer, linkButton, Serialization.Default.LinkButtonProperties); + case PremiumButtonProperties premiumButtonProperties: + JsonSerializer.Serialize(writer, premiumButtonProperties, Serialization.Default.PremiumButtonProperties); break; default: throw new InvalidOperationException($"Invalid {nameof(IButtonProperties)} value."); diff --git a/NetCord/Rest/ComponentProperties/ICustomizableButtonProperties.cs b/NetCord/Rest/ComponentProperties/ICustomizableButtonProperties.cs new file mode 100644 index 00000000..96d7a346 --- /dev/null +++ b/NetCord/Rest/ComponentProperties/ICustomizableButtonProperties.cs @@ -0,0 +1,14 @@ +namespace NetCord.Rest; + +public partial interface ICustomizableButtonProperties : IButtonProperties +{ + /// + /// Text that appears on the button (max 80 characters). + /// + public string? Label { get; set; } + + /// + /// Emoji that appears on the button. + /// + public EmojiProperties? Emoji { get; set; } +} diff --git a/NetCord/Rest/ComponentProperties/LinkButtonProperties.cs b/NetCord/Rest/ComponentProperties/LinkButtonProperties.cs index 76d2ec2b..03bdc6c6 100644 --- a/NetCord/Rest/ComponentProperties/LinkButtonProperties.cs +++ b/NetCord/Rest/ComponentProperties/LinkButtonProperties.cs @@ -2,7 +2,7 @@ namespace NetCord.Rest; -public partial class LinkButtonProperties : IButtonProperties +public partial class LinkButtonProperties : ICustomizableButtonProperties { /// /// Url of the button. @@ -11,7 +11,7 @@ public partial class LinkButtonProperties : IButtonProperties public string Url { get; set; } [JsonPropertyName("style")] - public ButtonStyle Style { get; set; } + public ButtonStyle Style => (ButtonStyle)5; [JsonPropertyName("type")] public ComponentType ComponentType => ComponentType.Button; @@ -37,7 +37,6 @@ public LinkButtonProperties(string url, string label) { Url = url; Label = label; - Style = (ButtonStyle)5; } /// @@ -49,7 +48,6 @@ public LinkButtonProperties(string url, EmojiProperties emoji) { Url = url; Emoji = emoji; - Style = (ButtonStyle)5; } /// @@ -63,6 +61,5 @@ public LinkButtonProperties(string url, string label, EmojiProperties emoji) Url = url; Label = label; Emoji = emoji; - Style = (ButtonStyle)5; } } diff --git a/NetCord/Rest/ComponentProperties/PremiumButtonProperties.cs b/NetCord/Rest/ComponentProperties/PremiumButtonProperties.cs new file mode 100644 index 00000000..6e5dfd9c --- /dev/null +++ b/NetCord/Rest/ComponentProperties/PremiumButtonProperties.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; + +namespace NetCord.Rest; + +public partial class PremiumButtonProperties(ulong skuId) : IButtonProperties +{ + [JsonPropertyName("sku_id")] + public ulong SkuId { get; set; } = skuId; + + [JsonPropertyName("style")] + public ButtonStyle Style => (ButtonStyle)6; + + [JsonPropertyName("type")] + public ComponentType ComponentType => ComponentType.Button; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonPropertyName("disabled")] + public bool Disabled { get; set; } +} diff --git a/NetCord/Rest/ConnectionType.cs b/NetCord/Rest/ConnectionType.cs index 5d3f27de..5fb5b678 100644 --- a/NetCord/Rest/ConnectionType.cs +++ b/NetCord/Rest/ConnectionType.cs @@ -2,12 +2,18 @@ namespace NetCord.Rest; -[JsonConverter(typeof(JsonConverters.StringEnumConverterWithErrorHandling))] +[JsonConverter(typeof(JsonConverters.SafeStringEnumConverter))] public enum ConnectionType { [JsonPropertyName("battlenet")] BattleNet, + [JsonPropertyName("bungie")] + Bungie, + + [JsonPropertyName("domain")] + Domain, + [JsonPropertyName("ebay")] Ebay, @@ -38,6 +44,9 @@ public enum ConnectionType [JsonPropertyName("riotgames")] RiotGames, + [JsonPropertyName("roblox")] + Roblox, + [JsonPropertyName("spotify")] Spotify, diff --git a/NetCord/Rest/EmbedImageProperties.cs b/NetCord/Rest/EmbedImageProperties.cs index e962284e..1b388f9d 100644 --- a/NetCord/Rest/EmbedImageProperties.cs +++ b/NetCord/Rest/EmbedImageProperties.cs @@ -16,13 +16,4 @@ public partial class EmbedImageProperties(string? url) public string? Url { get; set; } = url; public static implicit operator EmbedImageProperties(string? url) => new(url); - - public static implicit operator EmbedImageProperties(AttachmentProperties attachment) => FromAttachment(attachment.FileName); - - /// - /// Creates new based on . - /// - /// Attachment file name. - /// - public static EmbedImageProperties FromAttachment(string attachmentFileName) => new($"attachment://{attachmentFileName}"); } diff --git a/NetCord/Rest/ForumGuildThreadMessageProperties.cs b/NetCord/Rest/ForumGuildThreadMessageProperties.cs index b863b6da..f5451880 100644 --- a/NetCord/Rest/ForumGuildThreadMessageProperties.cs +++ b/NetCord/Rest/ForumGuildThreadMessageProperties.cs @@ -2,7 +2,7 @@ namespace NetCord.Rest; -public partial class ForumGuildThreadMessageProperties +public partial class ForumGuildThreadMessageProperties : IMessageProperties { [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonPropertyName("content")] diff --git a/NetCord/Rest/IMessageProperties.cs b/NetCord/Rest/IMessageProperties.cs new file mode 100644 index 00000000..c4d3947b --- /dev/null +++ b/NetCord/Rest/IMessageProperties.cs @@ -0,0 +1,16 @@ +namespace NetCord.Rest; + +public partial interface IMessageProperties +{ + public string? Content { get; set; } + + public IEnumerable? Embeds { get; set; } + + public AllowedMentionsProperties? AllowedMentions { get; set; } + + public IEnumerable? Attachments { get; set; } + + public IEnumerable? Components { get; set; } + + public MessageFlags? Flags { get; set; } +} diff --git a/NetCord/Rest/InteractionCallback.cs b/NetCord/Rest/InteractionCallback.cs index 30a354b0..aa01f252 100644 --- a/NetCord/Rest/InteractionCallback.cs +++ b/NetCord/Rest/InteractionCallback.cs @@ -66,12 +66,6 @@ public static InteractionCallback Auto public static InteractionCallback Modal(ModalProperties modal) => new(InteractionCallbackType.Modal, modal); - /// - /// Respond to an interaction with an upgrade button, only available for apps with monetization enabled. - /// - public static InteractionCallback PremiumRequired - => new(InteractionCallbackType.PremiumRequired); - public HttpContent Serialize() { switch (this) diff --git a/NetCord/Rest/InteractionCallbackType.cs b/NetCord/Rest/InteractionCallbackType.cs index db4b68e5..c2a70d2c 100644 --- a/NetCord/Rest/InteractionCallbackType.cs +++ b/NetCord/Rest/InteractionCallbackType.cs @@ -36,9 +36,4 @@ public enum InteractionCallbackType /// Respond to an interaction with a popup modal. /// Modal = 9, - - /// - /// Respond to an interaction with an upgrade button, only available for apps with monetization enabled. - /// - PremiumRequired = 10, } diff --git a/NetCord/Rest/InteractionMessageProperties.cs b/NetCord/Rest/InteractionMessageProperties.cs index 515c9753..6fa8bb55 100644 --- a/NetCord/Rest/InteractionMessageProperties.cs +++ b/NetCord/Rest/InteractionMessageProperties.cs @@ -2,7 +2,7 @@ namespace NetCord.Rest; -public partial class InteractionMessageProperties : IHttpSerializable +public partial class InteractionMessageProperties : IHttpSerializable, IMessageProperties { [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonPropertyName("tts")] diff --git a/NetCord/Rest/GuildInviteProperties.cs b/NetCord/Rest/InviteProperties.cs similarity index 85% rename from NetCord/Rest/GuildInviteProperties.cs rename to NetCord/Rest/InviteProperties.cs index 134bc42e..e9bd3e73 100644 --- a/NetCord/Rest/GuildInviteProperties.cs +++ b/NetCord/Rest/InviteProperties.cs @@ -2,7 +2,7 @@ namespace NetCord.Rest; -public partial class GuildInviteProperties +public partial class InviteProperties { [JsonPropertyName("max_age")] public int? MaxAge { get; set; } @@ -17,7 +17,7 @@ public partial class GuildInviteProperties public bool? Unique { get; set; } [JsonPropertyName("target_type")] - public GuildInviteTargetType? TargetType { get; set; } + public InviteTargetType? TargetType { get; set; } [JsonPropertyName("target_user_id")] public ulong? TargetUserId { get; set; } diff --git a/NetCord/Rest/JsonModels/JsonRestGuildInvite.cs b/NetCord/Rest/JsonModels/JsonRestInvite.cs similarity index 90% rename from NetCord/Rest/JsonModels/JsonRestGuildInvite.cs rename to NetCord/Rest/JsonModels/JsonRestInvite.cs index 890ccfab..b64cee49 100644 --- a/NetCord/Rest/JsonModels/JsonRestGuildInvite.cs +++ b/NetCord/Rest/JsonModels/JsonRestInvite.cs @@ -4,8 +4,11 @@ namespace NetCord.Rest.JsonModels; -public class JsonRestGuildInvite +public class JsonRestInvite { + [JsonPropertyName("type")] + public InviteType Type { get; set; } + [JsonPropertyName("code")] public string Code { get; set; } @@ -19,7 +22,7 @@ public class JsonRestGuildInvite public JsonUser? Inviter { get; set; } [JsonPropertyName("target_type")] - public GuildInviteTargetType? TargetType { get; set; } + public InviteTargetType? TargetType { get; set; } [JsonPropertyName("target_user")] public JsonUser? TargetUser { get; set; } diff --git a/NetCord/Rest/MentionableValueProperties.cs b/NetCord/Rest/MentionableValueProperties.cs index 58d43cb8..b5a71e09 100644 --- a/NetCord/Rest/MentionableValueProperties.cs +++ b/NetCord/Rest/MentionableValueProperties.cs @@ -9,7 +9,7 @@ public partial struct MentionableValueProperties(ulong id, MentionableValueType [JsonPropertyName("id")] public ulong Id { get; set; } = id; - [JsonConverter(typeof(StringEnumConverterWithErrorHandling))] + [JsonConverter(typeof(SafeStringEnumConverter))] [JsonPropertyName("type")] public MentionableValueType Type { get; set; } = type; } diff --git a/NetCord/Rest/MessageCall.cs b/NetCord/Rest/MessageCall.cs new file mode 100644 index 00000000..2799ded7 --- /dev/null +++ b/NetCord/Rest/MessageCall.cs @@ -0,0 +1,12 @@ +using NetCord.JsonModels; + +namespace NetCord.Rest; + +public class MessageCall(JsonMessageCall jsonModel) : IJsonModel +{ + JsonMessageCall IJsonModel.JsonModel => jsonModel; + + public IReadOnlyList Participants => jsonModel.Participants; + + public DateTimeOffset? EndedAt => jsonModel.EndedAt; +} diff --git a/NetCord/Rest/MessageProperties.cs b/NetCord/Rest/MessageProperties.cs index 15d32a35..b7773825 100644 --- a/NetCord/Rest/MessageProperties.cs +++ b/NetCord/Rest/MessageProperties.cs @@ -2,7 +2,7 @@ namespace NetCord.Rest; -public partial class MessageProperties : IHttpSerializable +public partial class MessageProperties : IHttpSerializable, IMessageProperties { [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonPropertyName("content")] diff --git a/NetCord/Rest/MessageReactionsPaginationProperties.cs b/NetCord/Rest/MessageReactionsPaginationProperties.cs new file mode 100644 index 00000000..99175e95 --- /dev/null +++ b/NetCord/Rest/MessageReactionsPaginationProperties.cs @@ -0,0 +1,9 @@ +namespace NetCord.Rest; + +public partial record MessageReactionsPaginationProperties : PaginationProperties, IPaginationProperties +{ + public ReactionType? Type { get; set; } + + static MessageReactionsPaginationProperties IPaginationProperties.Create() => new(); + static MessageReactionsPaginationProperties IPaginationProperties.Create(MessageReactionsPaginationProperties properties) => new(properties); +} diff --git a/NetCord/Rest/MessageReferenceProperties.cs b/NetCord/Rest/MessageReferenceProperties.cs index e7e5ecf3..291270cd 100644 --- a/NetCord/Rest/MessageReferenceProperties.cs +++ b/NetCord/Rest/MessageReferenceProperties.cs @@ -2,11 +2,43 @@ namespace NetCord.Rest; -public partial class MessageReferenceProperties(ulong messageId, bool failIfNotExists = true) +public partial class MessageReferenceProperties { + public static MessageReferenceProperties Reply(ulong messageId, bool failIfNotExists = true) + { + return new() + { + Type = MessageReferenceType.Reply, + MessageId = messageId, + FailIfNotExists = failIfNotExists, + }; + } + + public static MessageReferenceProperties Forward(ulong channelId, ulong messageId, bool failIfNotExists = true) + { + return new() + { + Type = MessageReferenceType.Forward, + ChannelId = channelId, + MessageId = messageId, + FailIfNotExists = failIfNotExists, + }; + } + + private MessageReferenceProperties() + { + } + + [JsonPropertyName("type")] + public MessageReferenceType Type { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonPropertyName("channel_id")] + public ulong? ChannelId { get; set; } + [JsonPropertyName("message_id")] - public ulong Id { get; set; } = messageId; + public ulong MessageId { get; set; } [JsonPropertyName("fail_if_not_exists")] - public bool FailIfNotExists { get; set; } = failIfNotExists; + public bool FailIfNotExists { get; set; } } diff --git a/NetCord/Rest/ReplyMessageProperties.cs b/NetCord/Rest/ReplyMessageProperties.cs index 0414bf2b..70951117 100644 --- a/NetCord/Rest/ReplyMessageProperties.cs +++ b/NetCord/Rest/ReplyMessageProperties.cs @@ -1,6 +1,6 @@ namespace NetCord.Rest; -public partial class ReplyMessageProperties +public partial class ReplyMessageProperties : IMessageProperties { public string? Content { get; set; } public NonceProperties? Nonce { get; set; } @@ -12,6 +12,7 @@ public partial class ReplyMessageProperties public IEnumerable? Components { get; set; } public IEnumerable? StickerIds { get; set; } public MessageFlags? Flags { get; set; } + public MessagePollProperties? Poll { get; set; } public MessageProperties ToMessageProperties(ulong messageReferenceId) { @@ -23,10 +24,11 @@ public MessageProperties ToMessageProperties(ulong messageReferenceId) Attachments = Attachments, Embeds = Embeds, AllowedMentions = AllowedMentions ?? new(), - MessageReference = new(messageReferenceId, FailIfNotExists.GetValueOrDefault(true)), + MessageReference = MessageReferenceProperties.Reply(messageReferenceId, FailIfNotExists.GetValueOrDefault(true)), Components = Components, StickerIds = StickerIds, Flags = Flags, + Poll = Poll, }; } diff --git a/NetCord/Rest/RestAuditLogEntry.cs b/NetCord/Rest/RestAuditLogEntry.cs index d2d48b48..d72929ea 100644 --- a/NetCord/Rest/RestAuditLogEntry.cs +++ b/NetCord/Rest/RestAuditLogEntry.cs @@ -3,7 +3,7 @@ namespace NetCord.Rest; -public class RestAuditLogEntry(JsonAuditLogEntry jsonModel, RestAuditLogEntryData data) : AuditLogEntry(jsonModel) +public class RestAuditLogEntry(JsonAuditLogEntry jsonModel, RestAuditLogEntryData data, ulong guildId) : AuditLogEntry(jsonModel, guildId) { /// /// Data of objects referenced in the audit log. diff --git a/NetCord/Rest/RestClient.AuditLog.cs b/NetCord/Rest/RestClient.AuditLog.cs index 9a156cb3..c4aee1e1 100644 --- a/NetCord/Rest/RestClient.AuditLog.cs +++ b/NetCord/Rest/RestClient.AuditLog.cs @@ -19,17 +19,17 @@ public IAsyncEnumerable GetGuildAuditLogAsync(ulong guildId, { var jsonAuditLog = await s.ToObjectAsync(Serialization.Default.JsonAuditLog).ConfigureAwait(false); RestAuditLogEntryData data = new(jsonAuditLog, this); - return jsonAuditLog.AuditLogEntries.Select(e => new RestAuditLogEntry(e, data)); + return jsonAuditLog.AuditLogEntries.Select(e => new RestAuditLogEntry(e, data, guildId)); }, e => e.Id, HttpMethod.Get, $"/guilds/{guildId}/audit-logs", new(paginationProperties.Limit.GetValueOrDefault(), paginationProperties.Direction.GetValueOrDefault(), id => id.ToString(), userId.HasValue ? (actionType.HasValue - ? $"?user_id={userId.GetValueOrDefault()}&action_type={actionType.GetValueOrDefault()}&" + ? $"?user_id={userId.GetValueOrDefault()}&action_type={(int)actionType.GetValueOrDefault()}&" : $"?user_id={userId.GetValueOrDefault()}&") : (actionType.HasValue - ? $"?action_type={actionType.GetValueOrDefault()}&" + ? $"?action_type={(int)actionType.GetValueOrDefault()}&" : "?")), new(guildId), properties); diff --git a/NetCord/Rest/RestClient.Channel.cs b/NetCord/Rest/RestClient.Channel.cs index 292a6d61..d56cee62 100644 --- a/NetCord/Rest/RestClient.Channel.cs +++ b/NetCord/Rest/RestClient.Channel.cs @@ -1,6 +1,4 @@ -using NetCord.Gateway; - -namespace NetCord.Rest; +namespace NetCord.Rest; public partial class RestClient { @@ -57,12 +55,12 @@ public async Task> GetMessagesAroundAsyn => (await (await SendRequestAsync(HttpMethod.Get, $"/channels/{channelId}/messages", $"?limit={limit.GetValueOrDefault(100)}&around={messageId}", new(channelId), properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonMessageArray).ConfigureAwait(false)).ToDictionary(m => m.Id, m => new RestMessage(m, this)); [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] public async Task GetMessageAsync(ulong channelId, ulong messageId, RestRequestProperties? properties = null) => new(await (await SendRequestAsync(HttpMethod.Get, $"/channels/{channelId}/messages/{messageId}", null, new(channelId), properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonMessage).ConfigureAwait(false), this); [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), TypeNameOverride = "Message")] public async Task SendMessageAsync(ulong channelId, MessageProperties message, RestRequestProperties? properties = null) { using (HttpContent content = message.Serialize()) @@ -71,31 +69,33 @@ public async Task SendMessageAsync(ulong channelId, MessageProperti [GenerateAlias([typeof(AnnouncementGuildChannel)], nameof(AnnouncementGuildChannel.Id))] [GenerateAlias([typeof(AnnouncementGuildThread)], nameof(AnnouncementGuildThread.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] public async Task CrosspostMessageAsync(ulong channelId, ulong messageId, RestRequestProperties? properties = null) => new(await (await SendRequestAsync(HttpMethod.Post, $"/channels/{channelId}/messages/{messageId}/crosspost", null, new(channelId), properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonMessage).ConfigureAwait(false), this); [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] public Task AddMessageReactionAsync(ulong channelId, ulong messageId, ReactionEmojiProperties emoji, RestRequestProperties? properties = null) => SendRequestAsync(HttpMethod.Put, $"/channels/{channelId}/messages/{messageId}/reactions/{ReactionEmojiToString(emoji)}/@me", null, new(channelId), properties); [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] public Task DeleteMessageReactionAsync(ulong channelId, ulong messageId, ReactionEmojiProperties emoji, RestRequestProperties? properties = null) => SendRequestAsync(HttpMethod.Delete, $"/channels/{channelId}/messages/{messageId}/reactions/{ReactionEmojiToString(emoji)}/@me", null, new(channelId), properties); [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] public Task DeleteMessageReactionAsync(ulong channelId, ulong messageId, ReactionEmojiProperties emoji, ulong userId, RestRequestProperties? properties = null) => SendRequestAsync(HttpMethod.Delete, $"/channels/{channelId}/messages/{messageId}/reactions/{ReactionEmojiToString(emoji)}/{userId}", null, new(channelId), properties); [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] - public IAsyncEnumerable GetMessageReactionsAsync(ulong channelId, ulong messageId, ReactionEmojiProperties emoji, PaginationProperties? paginationProperties = null, RestRequestProperties? properties = null) + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + public IAsyncEnumerable GetMessageReactionsAsync(ulong channelId, ulong messageId, ReactionEmojiProperties emoji, MessageReactionsPaginationProperties? paginationProperties = null, RestRequestProperties? properties = null) { paginationProperties = PaginationProperties.PrepareWithDirectionValidation(paginationProperties, PaginationDirection.After, 100); + var type = paginationProperties.Type; + return new QueryPaginationAsyncEnumerable( this, paginationProperties, @@ -103,25 +103,25 @@ public IAsyncEnumerable GetMessageReactionsAsync(ulong channelId, ulong me u => u.Id, HttpMethod.Get, $"/channels/{channelId}/messages/{messageId}/reactions/{ReactionEmojiToString(emoji)}", - new(paginationProperties.Limit.GetValueOrDefault(), paginationProperties.Direction.GetValueOrDefault(), id => id.ToString()), + new(paginationProperties.Limit.GetValueOrDefault(), paginationProperties.Direction.GetValueOrDefault(), id => id.ToString(), type.HasValue ? $"?type={(byte)type.GetValueOrDefault()}&" : "?"), new(channelId), properties); } [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] public Task DeleteAllMessageReactionsAsync(ulong channelId, ulong messageId, RestRequestProperties? properties = null) => SendRequestAsync(HttpMethod.Delete, $"/channels/{channelId}/messages/{messageId}/reactions", null, new(channelId), properties); [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] public Task DeleteAllMessageReactionsAsync(ulong channelId, ulong messageId, ReactionEmojiProperties emoji, RestRequestProperties? properties = null) => SendRequestAsync(HttpMethod.Delete, $"/channels/{channelId}/messages/{messageId}/reactions/{ReactionEmojiToString(emoji)}", null, new(channelId), properties); private static string ReactionEmojiToString(ReactionEmojiProperties emoji) => emoji.Id.HasValue ? $"{emoji.Name}:{emoji.Id.GetValueOrDefault()}" : emoji.Name; [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] public async Task ModifyMessageAsync(ulong channelId, ulong messageId, Action action, RestRequestProperties? properties = null) { MessageOptions messageOptions = new(); @@ -131,7 +131,7 @@ public async Task ModifyMessageAsync(ulong channelId, ulong message } [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] public Task DeleteMessageAsync(ulong channelId, ulong messageId, RestRequestProperties? properties = null) => SendRequestAsync(HttpMethod.Delete, $"/channels/{channelId}/messages/{messageId}", null, new(channelId), properties); @@ -197,15 +197,15 @@ public async Task ModifyGuildChannelPermissionsAsync(ulong channelId, Permission } [GenerateAlias([typeof(IGuildChannel)], nameof(IGuildChannel.Id))] - public async Task> GetGuildChannelInvitesAsync(ulong channelId, RestRequestProperties? properties = null) - => (await (await SendRequestAsync(HttpMethod.Get, $"/channels/{channelId}/invites", null, new(channelId), properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestGuildInviteArray).ConfigureAwait(false)).Select(r => new RestGuildInvite(r, this)); + public async Task> GetGuildChannelInvitesAsync(ulong channelId, RestRequestProperties? properties = null) + => (await (await SendRequestAsync(HttpMethod.Get, $"/channels/{channelId}/invites", null, new(channelId), properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestInviteArray).ConfigureAwait(false)).Select(r => new RestInvite(r, this)); [GenerateAlias([typeof(IGuildChannel)], nameof(IGuildChannel.Id))] - public async Task CreateGuildChannelInviteAsync(ulong channelId, GuildInviteProperties? guildInviteProperties = null, RestRequestProperties? properties = null) + public async Task CreateGuildChannelInviteAsync(ulong channelId, InviteProperties? inviteProperties = null, RestRequestProperties? properties = null) #pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types. { - using (HttpContent content = new JsonContent(guildInviteProperties, Serialization.Default.GuildInviteProperties)) - return new(await (await SendRequestAsync(HttpMethod.Post, content, $"/channels/{channelId}/invites", null, new(channelId), properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestGuildInvite).ConfigureAwait(false), this); + using (HttpContent content = new JsonContent(inviteProperties, Serialization.Default.InviteProperties)) + return new(await (await SendRequestAsync(HttpMethod.Post, content, $"/channels/{channelId}/invites", null, new(channelId), properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestInvite).ConfigureAwait(false), this); } #pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types. @@ -238,12 +238,12 @@ public async Task> GetPinnedMessagesAsyn => (await (await SendRequestAsync(HttpMethod.Get, $"/channels/{channelId}/pins", null, new(channelId), properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonMessageArray).ConfigureAwait(false)).ToDictionary(m => m.Id, m => new RestMessage(m, this)); [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] public Task PinMessageAsync(ulong channelId, ulong messageId, RestRequestProperties? properties = null) => SendRequestAsync(HttpMethod.Put, $"/channels/{channelId}/pins/{messageId}", null, new(channelId), properties); [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] public Task UnpinMessageAsync(ulong channelId, ulong messageId, RestRequestProperties? properties = null) => SendRequestAsync(HttpMethod.Delete, $"/channels/{channelId}/pins/{messageId}", null, new(channelId), properties); @@ -259,7 +259,7 @@ public Task GroupDMChannelDeleteUserAsync(ulong channelId, ulong userId, RestReq => SendRequestAsync(HttpMethod.Delete, $"/channels/{channelId}/recipients/{userId}", null, new(channelId), properties); [GenerateAlias([typeof(TextGuildChannel)], nameof(TextGuildChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = "Message")] public async Task CreateGuildThreadAsync(ulong channelId, ulong messageId, GuildThreadFromMessageProperties threadFromMessageProperties, RestRequestProperties? properties = null) { using (HttpContent content = new JsonContent(threadFromMessageProperties, Serialization.Default.GuildThreadFromMessageProperties)) diff --git a/NetCord/Rest/RestClient.Emoji.cs b/NetCord/Rest/RestClient.Emoji.cs index 6492cc67..361037aa 100644 --- a/NetCord/Rest/RestClient.Emoji.cs +++ b/NetCord/Rest/RestClient.Emoji.cs @@ -34,4 +34,35 @@ public async Task ModifyGuildEmojiAsync(ulong guildId, ulong emojiId [GenerateAlias([typeof(GuildEmoji)], nameof(GuildEmoji.GuildId), nameof(GuildEmoji.Id))] public Task DeleteGuildEmojiAsync(ulong guildId, ulong emojiId, RestRequestProperties? properties = null) => SendRequestAsync(HttpMethod.Delete, $"/guilds/{guildId}/emojis/{emojiId}", null, new(guildId), properties); + + [GenerateAlias([typeof(Application)], nameof(Application.Id))] + public async Task> GetApplicationEmojisAsync(ulong applicationId, RestRequestProperties? properties = null) + => (await (await SendRequestAsync(HttpMethod.Get, $"/applications/{applicationId}/emojis", null, null, properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonEmojiArray).ConfigureAwait(false)).ToDictionary(e => e.Id.GetValueOrDefault(), e => new ApplicationEmoji(e, applicationId, this)); + + [GenerateAlias([typeof(Application)], nameof(Application.Id))] + [GenerateAlias([typeof(ApplicationEmoji)], nameof(ApplicationEmoji.ApplicationId), nameof(ApplicationEmoji.Id))] + public async Task GetApplicationEmojiAsync(ulong applicationId, ulong emojiId, RestRequestProperties? properties = null) + => new(await (await SendRequestAsync(HttpMethod.Get, $"/applications/{applicationId}/emojis/{emojiId}", null, null, properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonEmoji).ConfigureAwait(false), applicationId, this); + + [GenerateAlias([typeof(Application)], nameof(Application.Id))] + public async Task CreateApplicationEmojiAsync(ulong applicationId, ApplicationEmojiProperties applicationEmojiProperties, RestRequestProperties? properties = null) + { + using (HttpContent content = new JsonContent(applicationEmojiProperties, Serialization.Default.ApplicationEmojiProperties)) + return new(await (await SendRequestAsync(HttpMethod.Post, content, $"/applications/{applicationId}/emojis", null, null, properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonEmoji).ConfigureAwait(false), applicationId, this); + } + + [GenerateAlias([typeof(Application)], nameof(Application.Id))] + [GenerateAlias([typeof(ApplicationEmoji)], nameof(ApplicationEmoji.ApplicationId), nameof(ApplicationEmoji.Id))] + public async Task ModifyApplicationEmojiAsync(ulong applicationId, ulong emojiId, Action action, RestRequestProperties? properties = null) + { + ApplicationEmojiOptions applicationEmojiOptions = new(); + action(applicationEmojiOptions); + using (HttpContent content = new JsonContent(applicationEmojiOptions, Serialization.Default.ApplicationEmojiOptions)) + return new(await (await SendRequestAsync(HttpMethod.Patch, content, $"/applications/{applicationId}/emojis/{emojiId}", null, null, properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonEmoji).ConfigureAwait(false), applicationId, this); + } + + [GenerateAlias([typeof(Application)], nameof(Application.Id))] + [GenerateAlias([typeof(ApplicationEmoji)], nameof(ApplicationEmoji.ApplicationId), nameof(ApplicationEmoji.Id))] + public Task DeleteApplicationEmojiAsync(ulong applicationId, ulong emojiId, RestRequestProperties? properties = null) + => SendRequestAsync(HttpMethod.Delete, $"/applications/{applicationId}/emojis/{emojiId}", null, null, properties); } diff --git a/NetCord/Rest/RestClient.Guild.cs b/NetCord/Rest/RestClient.Guild.cs index 6f1ca5b5..c729a386 100644 --- a/NetCord/Rest/RestClient.Guild.cs +++ b/NetCord/Rest/RestClient.Guild.cs @@ -240,8 +240,8 @@ public async Task> GetGuildVoiceRegionsAsync(ulong guil => (await (await SendRequestAsync(HttpMethod.Get, $"/guilds/{guildId}/regions", null, new(guildId), properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonVoiceRegionArray).ConfigureAwait(false)).Select(r => new VoiceRegion(r)); [GenerateAlias([typeof(RestGuild)], nameof(RestGuild.Id), TypeNameOverride = nameof(Guild))] - public async Task> GetGuildInvitesAsync(ulong guildId, RestRequestProperties? properties = null) - => (await (await SendRequestAsync(HttpMethod.Get, $"/guilds/{guildId}/invites", null, new(guildId), properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestGuildInviteArray).ConfigureAwait(false)).Select(i => new RestGuildInvite(i, this)); + public async Task> GetGuildInvitesAsync(ulong guildId, RestRequestProperties? properties = null) + => (await (await SendRequestAsync(HttpMethod.Get, $"/guilds/{guildId}/invites", null, new(guildId), properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestInviteArray).ConfigureAwait(false)).Select(i => new RestInvite(i, this)); [GenerateAlias([typeof(RestGuild)], nameof(RestGuild.Id), TypeNameOverride = nameof(Guild))] public async Task> GetGuildIntegrationsAsync(ulong guildId, RestRequestProperties? properties = null) diff --git a/NetCord/Rest/RestClient.Invite.cs b/NetCord/Rest/RestClient.Invite.cs index 1374084c..c152ea76 100644 --- a/NetCord/Rest/RestClient.Invite.cs +++ b/NetCord/Rest/RestClient.Invite.cs @@ -4,16 +4,16 @@ namespace NetCord.Rest; public partial class RestClient { - [GenerateAlias([typeof(RestGuildInvite)], nameof(RestGuildInvite.Code), TypeNameOverride = nameof(GuildInvite))] - public async Task GetGuildInviteAsync(string inviteCode, bool withCounts = false, bool withExpiration = false, ulong? guildScheduledEventId = null, RestRequestProperties? properties = null) + [GenerateAlias([typeof(RestInvite)], nameof(RestInvite.Code), TypeNameOverride = nameof(Invite))] + public async Task GetGuildInviteAsync(string inviteCode, bool withCounts = false, bool withExpiration = false, ulong? guildScheduledEventId = null, RestRequestProperties? properties = null) { if (guildScheduledEventId.HasValue) - return new(await (await SendRequestAsync(HttpMethod.Get, $"/invites/{inviteCode}", $"?with_counts={withCounts}&with_expiration={withExpiration}&guild_scheduled_event_id={guildScheduledEventId}", null, properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestGuildInvite).ConfigureAwait(false), this); + return new(await (await SendRequestAsync(HttpMethod.Get, $"/invites/{inviteCode}", $"?with_counts={withCounts}&with_expiration={withExpiration}&guild_scheduled_event_id={guildScheduledEventId}", null, properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestInvite).ConfigureAwait(false), this); else - return new(await (await SendRequestAsync(HttpMethod.Get, $"/invites/{inviteCode}", $"?with_counts={withCounts}&with_expiration={withExpiration}", null, properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestGuildInvite).ConfigureAwait(false), this); + return new(await (await SendRequestAsync(HttpMethod.Get, $"/invites/{inviteCode}", $"?with_counts={withCounts}&with_expiration={withExpiration}", null, properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestInvite).ConfigureAwait(false), this); } - [GenerateAlias([typeof(RestGuildInvite)], nameof(RestGuildInvite.Code), TypeNameOverride = nameof(GuildInvite))] - public async Task DeleteGuildInviteAsync(string inviteCode, RestRequestProperties? properties = null) - => new(await (await SendRequestAsync(HttpMethod.Delete, $"/invites/{inviteCode}", null, null, properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestGuildInvite).ConfigureAwait(false), this); + [GenerateAlias([typeof(RestInvite)], nameof(RestInvite.Code), TypeNameOverride = nameof(Invite))] + public async Task DeleteGuildInviteAsync(string inviteCode, RestRequestProperties? properties = null) + => new(await (await SendRequestAsync(HttpMethod.Delete, $"/invites/{inviteCode}", null, null, properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestInvite).ConfigureAwait(false), this); } diff --git a/NetCord/Rest/RestClient.Poll.cs b/NetCord/Rest/RestClient.Poll.cs index edfe1494..28133d85 100644 --- a/NetCord/Rest/RestClient.Poll.cs +++ b/NetCord/Rest/RestClient.Poll.cs @@ -5,7 +5,7 @@ namespace NetCord.Rest; public partial class RestClient { [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = nameof(Message))] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = nameof(Message))] public IAsyncEnumerable GetMessagePollAnswerVotersAsync(ulong channelId, ulong messageId, int answerId, PaginationProperties? paginationProperties = null, RestRequestProperties? properties = null) { paginationProperties = PaginationProperties.PrepareWithDirectionValidation(paginationProperties, PaginationDirection.After, 100); @@ -23,7 +23,7 @@ public IAsyncEnumerable GetMessagePollAnswerVotersAsync(ulong channelId, u } [GenerateAlias([typeof(TextChannel)], nameof(TextChannel.Id))] - [GenerateAlias([typeof(RestMessage), typeof(IPartialMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = nameof(Message))] + [GenerateAlias([typeof(RestMessage)], nameof(RestMessage.ChannelId), nameof(RestMessage.Id), TypeNameOverride = nameof(Message))] public async Task EndMessagePollAsync(ulong channelId, ulong messageId, RestRequestProperties? properties = null) => new(await (await SendRequestAsync(HttpMethod.Post, $"/channels/{channelId}/polls/{messageId}/expire", null, new(channelId), properties).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonMessage).ConfigureAwait(false), this); } diff --git a/NetCord/Rest/RestGuildInvite.cs b/NetCord/Rest/RestGuildInvite.cs index 0b858918..1f35bb5f 100644 --- a/NetCord/Rest/RestGuildInvite.cs +++ b/NetCord/Rest/RestGuildInvite.cs @@ -1,12 +1,14 @@ namespace NetCord.Rest; -public partial class RestGuildInvite : IGuildInvite, IJsonModel +public partial class RestInvite : IInvite, IJsonModel { - JsonModels.JsonRestGuildInvite IJsonModel.JsonModel => _jsonModel; - private readonly JsonModels.JsonRestGuildInvite _jsonModel; + JsonModels.JsonRestInvite IJsonModel.JsonModel => _jsonModel; + private readonly JsonModels.JsonRestInvite _jsonModel; private readonly RestClient _client; + public InviteType Type => _jsonModel.Type; + public string Code => _jsonModel.Code; public RestGuild? Guild { get; } @@ -15,7 +17,7 @@ public partial class RestGuildInvite : IGuildInvite, IJsonModel _jsonModel.TargetType; + public InviteTargetType? TargetType => _jsonModel.TargetType; public User? TargetUser { get; } @@ -41,11 +43,11 @@ public partial class RestGuildInvite : IGuildInvite, IJsonModel _jsonModel.CreatedAt; - ulong? IGuildInvite.GuildId => Guild?.Id; + ulong? IInvite.GuildId => Guild?.Id; - ulong? IGuildInvite.ChannelId => Channel?.Id; + ulong? IInvite.ChannelId => Channel?.Id; - public RestGuildInvite(JsonModels.JsonRestGuildInvite jsonModel, RestClient client) + public RestInvite(JsonModels.JsonRestInvite jsonModel, RestClient client) { _jsonModel = jsonModel; diff --git a/NetCord/Rest/RestMessage.cs b/NetCord/Rest/RestMessage.cs index 4419655f..00897add 100644 --- a/NetCord/Rest/RestMessage.cs +++ b/NetCord/Rest/RestMessage.cs @@ -46,8 +46,14 @@ public RestMessage(NetCord.JsonModels.JsonMessage jsonModel, RestClient client) var messageReference = jsonModel.MessageReference; if (messageReference is not null) + { MessageReference = new(messageReference); + MessageSnapshots = jsonModel.MessageSnapshots.SelectOrEmpty(s => new MessageSnapshot(s, messageReference.GuildId, client)).ToArray(); + } + else + MessageSnapshots = []; + var referencedMessage = jsonModel.ReferencedMessage; if (referencedMessage is not null) ReferencedMessage = new(referencedMessage, client); @@ -80,6 +86,10 @@ public RestMessage(NetCord.JsonModels.JsonMessage jsonModel, RestClient client) var poll = jsonModel.Poll; if (poll is not null) Poll = new(poll); + + var call = jsonModel.Call; + if (call is not null) + Call = new(call); } /// @@ -188,15 +198,20 @@ public RestMessage(NetCord.JsonModels.JsonMessage jsonModel, RestClient client) /// public ulong? ApplicationId => _jsonModel.ApplicationId; + /// + /// A object indicating the message's applied flags. + /// + public MessageFlags Flags => _jsonModel.Flags.GetValueOrDefault(); + /// /// Contains data showing the source of a crosspost, channel follow add, pin, or message reply. /// public MessageReference? MessageReference { get; } /// - /// A object indicating the message's applied flags. + /// A list of messages associated with the message reference. /// - public MessageFlags Flags => _jsonModel.Flags.GetValueOrDefault(); + public IReadOnlyList MessageSnapshots { get; } /// /// The message associated with the . @@ -250,6 +265,8 @@ public RestMessage(NetCord.JsonModels.JsonMessage jsonModel, RestClient client) public MessagePoll? Poll { get; } + public MessageCall? Call { get; } + public Task ReplyAsync(ReplyMessageProperties replyMessage, RestRequestProperties? properties = null) => SendAsync(replyMessage.ToMessageProperties(Id), properties); } diff --git a/NetCord/Rest/WebhookMessageProperties.cs b/NetCord/Rest/WebhookMessageProperties.cs index 9550eeef..38cc920c 100644 --- a/NetCord/Rest/WebhookMessageProperties.cs +++ b/NetCord/Rest/WebhookMessageProperties.cs @@ -2,7 +2,7 @@ namespace NetCord.Rest; -public partial class WebhookMessageProperties : IHttpSerializable +public partial class WebhookMessageProperties : IHttpSerializable, IMessageProperties { [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonPropertyName("content")] diff --git a/NetCord/Serialization.cs b/NetCord/Serialization.cs index 89c160bd..42a33f23 100644 --- a/NetCord/Serialization.cs +++ b/NetCord/Serialization.cs @@ -39,8 +39,8 @@ namespace NetCord; [JsonSerializable(typeof(JsonMessageReactionAddEventArgs))] [JsonSerializable(typeof(JsonMessageDeleteBulkEventArgs))] [JsonSerializable(typeof(JsonMessageDeleteEventArgs))] -[JsonSerializable(typeof(JsonGuildInviteDeleteEventArgs))] -[JsonSerializable(typeof(JsonGuildInvite))] +[JsonSerializable(typeof(JsonInviteDeleteEventArgs))] +[JsonSerializable(typeof(JsonInvite))] [JsonSerializable(typeof(JsonInteraction))] [JsonSerializable(typeof(JsonGuildIntegrationDeleteEventArgs))] [JsonSerializable(typeof(JsonIntegration))] @@ -96,6 +96,7 @@ namespace NetCord; [JsonSerializable(typeof(MessageCommandProperties))] [JsonSerializable(typeof(ButtonProperties))] [JsonSerializable(typeof(LinkButtonProperties))] +[JsonSerializable(typeof(PremiumButtonProperties))] [JsonSerializable(typeof(IEnumerable))] [JsonSerializable(typeof(StringMenuProperties))] [JsonSerializable(typeof(UserMenuProperties))] @@ -125,9 +126,9 @@ namespace NetCord; [JsonSerializable(typeof(JsonUser[]))] [JsonSerializable(typeof(BulkDeleteMessagesProperties))] [JsonSerializable(typeof(PermissionOverwriteProperties))] -[JsonSerializable(typeof(JsonRestGuildInvite[]))] -[JsonSerializable(typeof(GuildInviteProperties))] -[JsonSerializable(typeof(JsonRestGuildInvite))] +[JsonSerializable(typeof(JsonRestInvite[]))] +[JsonSerializable(typeof(InviteProperties))] +[JsonSerializable(typeof(JsonRestInvite))] [JsonSerializable(typeof(FollowAnnouncementGuildChannelProperties))] [JsonSerializable(typeof(JsonFollowedChannel))] [JsonSerializable(typeof(GroupDMChannelUserAddProperties))] @@ -138,7 +139,9 @@ namespace NetCord; [JsonSerializable(typeof(JsonEmoji[]))] [JsonSerializable(typeof(JsonEmoji))] [JsonSerializable(typeof(GuildEmojiProperties))] +[JsonSerializable(typeof(ApplicationEmojiProperties))] [JsonSerializable(typeof(GuildEmojiOptions))] +[JsonSerializable(typeof(ApplicationEmojiOptions))] [JsonSerializable(typeof(JsonGateway))] [JsonSerializable(typeof(JsonGatewayBot))] [JsonSerializable(typeof(GuildProperties))] diff --git a/NetCord/TeamRole.cs b/NetCord/TeamRole.cs index 1f60e08a..62a890c0 100644 --- a/NetCord/TeamRole.cs +++ b/NetCord/TeamRole.cs @@ -8,7 +8,7 @@ namespace NetCord; /// /// The Owner role is not represented in the enum, as it is not represented in 's field. Instead, owners can be identified using a 's field. They have the most permissive role, and can take destructive, irreversible actions like deleting team-owned apps or the team itself. Teams are limited to 1 owner. /// -[JsonConverter(typeof(JsonConverters.StringEnumConverterWithErrorHandling))] +[JsonConverter(typeof(JsonConverters.SafeStringEnumConverter))] public enum TeamRole { /// diff --git a/NetCord/User.cs b/NetCord/User.cs index 36870978..90c14cc4 100644 --- a/NetCord/User.cs +++ b/NetCord/User.cs @@ -8,10 +8,19 @@ namespace NetCord; /// /// Users in Discord are generally considered the base entity and can be members of guilds, participate in text and voice chat, and much more. Users are separated by a distinction of 'bot' vs 'normal'. Bot users are automated users that are 'owned' by another user. /// -public partial class User(JsonModels.JsonUser jsonModel, RestClient client) : ClientEntity(client), IJsonModel +public partial class User : ClientEntity, IJsonModel { JsonModels.JsonUser IJsonModel.JsonModel => _jsonModel; - private protected readonly JsonModels.JsonUser _jsonModel = jsonModel; + private protected readonly JsonModels.JsonUser _jsonModel; + + public User(JsonModels.JsonUser jsonModel, RestClient client) : base(client) + { + _jsonModel = jsonModel; + + var avatarDecorationData = jsonModel.AvatarDecorationData; + if (avatarDecorationData is not null) + AvatarDecorationData = new(avatarDecorationData); + } /// /// The user's ID. @@ -176,12 +185,12 @@ public partial class User(JsonModels.JsonUser jsonModel, RestClient client) : Cl public UserFlags? PublicFlags => _jsonModel.PublicFlags; /// - /// The user's avatar decoration hash. + /// Data for the user's avatar decoration. /// /// /// Requires the identify OAuth2 scope. /// - public string? AvatarDecorationHash => _jsonModel.AvatarDecorationHash; + public AvatarDecorationData? AvatarDecorationData { get; } /// /// Whether the user has a set custom avatar. @@ -210,13 +219,13 @@ public partial class User(JsonModels.JsonUser jsonModel, RestClient client) : Cl /// /// Whether the user has a set avatar decoration. /// - public bool HasAvatarDecoration => AvatarDecorationHash is not null; + public bool HasAvatarDecoration => AvatarDecorationData is not null; /// - /// Gets the of the user's avatar decoration URL. + /// Gets the of the user's avatar decoration. /// /// An pointing to the user's avatar decoration. If the user does not have one set, returns . - public ImageUrl? GetAvatarDecorationUrl() => AvatarDecorationHash is string hash ? ImageUrl.UserAvatarDecoration(Id, hash) : null; + public ImageUrl? GetAvatarDecorationUrl() => AvatarDecorationData is { Hash: var hash } ? ImageUrl.AvatarDecoration(hash) : null; /// /// Returns an object representing the user's default avatar. diff --git a/NetCord/UserStatusType.cs b/NetCord/UserStatusType.cs index 9c103a32..75a5f90b 100644 --- a/NetCord/UserStatusType.cs +++ b/NetCord/UserStatusType.cs @@ -2,7 +2,7 @@ namespace NetCord; -[JsonConverter(typeof(JsonConverters.StringEnumConverterWithErrorHandling))] +[JsonConverter(typeof(JsonConverters.SafeStringEnumConverter))] public enum UserStatusType { [JsonPropertyName("online")] diff --git a/SourceGenerators/RestClientMethodAliasesGenerator/RestClientMethodAliasesGenerator.cs b/SourceGenerators/RestClientMethodAliasesGenerator/RestClientMethodAliasesGenerator.cs index 2a9ed6b5..c3c1c845 100644 --- a/SourceGenerators/RestClientMethodAliasesGenerator/RestClientMethodAliasesGenerator.cs +++ b/SourceGenerators/RestClientMethodAliasesGenerator/RestClientMethodAliasesGenerator.cs @@ -25,7 +25,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) namespace NetCord.Rest; [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - public class GenerateAliasAttribute(Type[] types, params string?[] parameterAliases) : Attribute + internal class GenerateAliasAttribute(Type[] types, params string?[] parameterAliases) : Attribute { public Type[] Types { get; } = types; diff --git a/Tests/NetCord.Test.Hosting/CustomSlashCommandResultHandler.cs b/Tests/NetCord.Test.Hosting/CustomSlashCommandResultHandler.cs new file mode 100644 index 00000000..51b0dfce --- /dev/null +++ b/Tests/NetCord.Test.Hosting/CustomSlashCommandResultHandler.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.Logging; + +using NetCord.Gateway; +using NetCord.Hosting.Services.ApplicationCommands; +using NetCord.Services; +using NetCord.Services.ApplicationCommands; + +namespace NetCord.Test.Hosting; + +internal class CustomSlashCommandResultHandler : IApplicationCommandResultHandler +{ + private static readonly ApplicationCommandResultHandler _defaultHandler = new(MessageFlags.Ephemeral); + + public ValueTask HandleResultAsync(IExecutionResult result, SlashCommandContext context, GatewayClient? client, ILogger logger, IServiceProvider services) + { + logger.LogInformation("Handling result of slash command"); + + return _defaultHandler.HandleResultAsync(result, context, client, logger, services); + } +} diff --git a/Tests/NetCord.Test.Hosting/Program.cs b/Tests/NetCord.Test.Hosting/Program.cs index 1536c37b..f5e546de 100644 --- a/Tests/NetCord.Test.Hosting/Program.cs +++ b/Tests/NetCord.Test.Hosting/Program.cs @@ -39,7 +39,10 @@ { Intents = GatewayIntents.All, }) - .AddApplicationCommands() + .AddApplicationCommands(options => + { + options.ResultHandler = new CustomSlashCommandResultHandler(); + }) .AddApplicationCommands() .AddApplicationCommands() .AddComponentInteractions() diff --git a/Tests/NetCord.Test/Commands/Administrative/BanCommands.cs b/Tests/NetCord.Test/Commands/Administrative/BanCommands.cs index 79c71828..8b5d6589 100644 --- a/Tests/NetCord.Test/Commands/Administrative/BanCommands.cs +++ b/Tests/NetCord.Test/Commands/Administrative/BanCommands.cs @@ -25,7 +25,7 @@ public async Task Ban(UserId userId, TimeSpan deleteMessagesTime = default, [Com [ actionRow ], - MessageReference = new(Context.Message.Id), + MessageReference = MessageReferenceProperties.Reply(Context.Message.Id), AllowedMentions = AllowedMentionsProperties.None, }; await SendAsync(message); diff --git a/Tests/NetCord.Test/Commands/Administrative/MuteCommands.cs b/Tests/NetCord.Test/Commands/Administrative/MuteCommands.cs index c4e22350..16b1439e 100644 --- a/Tests/NetCord.Test/Commands/Administrative/MuteCommands.cs +++ b/Tests/NetCord.Test/Commands/Administrative/MuteCommands.cs @@ -23,7 +23,7 @@ public async Task Mute([CanManage] GuildUser user, TimeSpan time, [CommandParame [ actionRow ], - MessageReference = new(Context.Message.Id), + MessageReference = MessageReferenceProperties.Reply(Context.Message.Id), AllowedMentions = AllowedMentionsProperties.None, }; await SendAsync(message); diff --git a/Tests/NetCord.Test/Commands/NormalCommands.cs b/Tests/NetCord.Test/Commands/NormalCommands.cs index 293ef64c..6c789c89 100644 --- a/Tests/NetCord.Test/Commands/NormalCommands.cs +++ b/Tests/NetCord.Test/Commands/NormalCommands.cs @@ -21,7 +21,7 @@ public Task Say([CommandParameter(Remainder = true)] ReadOnlyMemory text) [Command("reply")] public Task Reply([CommandParameter(Remainder = true)] string text) { - return SendAsync(new MessageProperties() { Content = text, AllowedMentions = AllowedMentionsProperties.None, MessageReference = new(Context.Message.Id) }); + return SendAsync(new MessageProperties() { Content = text, AllowedMentions = AllowedMentionsProperties.None, MessageReference = MessageReferenceProperties.Reply(Context.Message.Id) }); } [Command("roles")] @@ -106,7 +106,7 @@ public Task Avatar([CommandParameter(Remainder = true)] GuildUser? user = null) [ embed ], - MessageReference = new(Context.Message.Id, false), + MessageReference = MessageReferenceProperties.Reply(Context.Message.Id, false), AllowedMentions = new() { ReplyMention = false diff --git a/Tests/NetCord.Test/Commands/StrangeCommands.cs b/Tests/NetCord.Test/Commands/StrangeCommands.cs index f1ad7cfd..28abb8c5 100644 --- a/Tests/NetCord.Test/Commands/StrangeCommands.cs +++ b/Tests/NetCord.Test/Commands/StrangeCommands.cs @@ -65,7 +65,7 @@ public Task Button() { Content = "This is button:", Components = [actionRow], - MessageReference = new(Context.Message.Id), + MessageReference = MessageReferenceProperties.Reply(Context.Message.Id), AllowedMentions = new() { ReplyMention = false @@ -89,7 +89,7 @@ public Task Link([CommandParameter(Remainder = true)] Uri url) actionRow ], Content = "This is the message with the link", - MessageReference = new(Context.Message.Id), + MessageReference = MessageReferenceProperties.Reply(Context.Message.Id), AllowedMentions = new() { ReplyMention = false @@ -114,7 +114,7 @@ public Task Dzejus() MessageProperties message = new() { Attachments = [file], - MessageReference = new(Context.Message.Id), + MessageReference = MessageReferenceProperties.Reply(Context.Message.Id), AllowedMentions = AllowedMentionsProperties.None }; return SendAsync(message); @@ -185,7 +185,7 @@ public Task Menu(params string[] values) { Content = "Here is your menu:", Components = [new StringMenuProperties("menu", values.Select(v => new StringMenuSelectOptionProperties(v, v))) { MaxValues = values.Length }], - MessageReference = new(Context.Message.Id) + MessageReference = MessageReferenceProperties.Reply(Context.Message.Id) }; return SendAsync(message); } @@ -269,7 +269,7 @@ public Task AttachmentAsync() return SendAsync(new() { Attachments = [attachment], - Embeds = [new() { Image = attachment }] + Embeds = [new() { Image = $"attachment://{attachment.FileName}" }] }); } diff --git a/Tests/NetCord.Test/localizations/localization.pl.pl.pl.json b/Tests/NetCord.Test/localizations/localization.pl.pl.pl.json deleted file mode 100644 index e9cf1b27..00000000 --- a/Tests/NetCord.Test/localizations/localization.pl.pl.pl.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "commands": { - "ping": { - "description": "Sprawdza czy bot jest aktywny", - "parameters": { - "s": { - "name": "ś", - "description": "Wartość do zwrócenia" - } - } - }, - "permission-nested": { - "name": "permisja-zagnieżdżona", - "description": "Permisja", - "subcommands": { - "add": { - "name": "dodaj", - "description": "Dodaje permisję", - "parameters": { - "i": { - "name": "io" - }, - "permission": { - "name": "permisja", - "description": "Permisja do dodania" - } - } - }, - "remove": { - "name": "usuń", - "description": "Usuwa permisję" - }, - "list": { - "name": "lista", - "description": "Lista permisji", - "subcommands": { - "user": { - "name": "użytkownik", - "description": "Lista permisji użytkownika", - "parameters": { - "i": { - "name": "io" - }, - "permission": { - "name": "permisja", - "description": "Permisja do dodania" - } - } - }, - "role": { - "name": "rola", - "description": "Lista permisji roli" - } - } - } - } - } - }, - "enums": { - "NetCord.Test.SlashCommands.DeleteMessagesDays": { - "DontRemove": "Nie usuwaj", - "Last24Hours": "Ostatnie 24 godziny", - "Last2Days": "Ostatnie 2 dni", - "Last3Days": "Ostatnie 3 dni", - "Last4Days": "Ostatnie 4 dni", - "Last5Days": "Ostatnie 5 dni", - "Last6Days": "Ostatnie 6 dni", - "LastWeek": "Ostatni tydzień" - } - } -}