| @@ -47,6 +47,12 @@ enough. Here is a list of recommended VPS provider. | |||||
| * Location(s): | * Location(s): | ||||
| * Europe: Lithuania | * Europe: Lithuania | ||||
| * Based in: Europe | * Based in: Europe | ||||
| * [ServerStarter.Host](https://serverstarter.host/clients/store/discord-bots) | |||||
| * Description: Bot hosting with a panel for quick deployment and | |||||
| no Linux knowledge required. | |||||
| * Location(s): | |||||
| * America: United States | |||||
| * Based in: United States | |||||
| ## .NET Core Deployment | ## .NET Core Deployment | ||||
| @@ -100,4 +106,4 @@ Windows 10 x64 based machine: | |||||
| * `dotnet publish -c Release -r win10-x64` | * `dotnet publish -c Release -r win10-x64` | ||||
| [.NET Core application deployment]: https://docs.microsoft.com/en-us/dotnet/core/deploying/ | [.NET Core application deployment]: https://docs.microsoft.com/en-us/dotnet/core/deploying/ | ||||
| [Runtime ID]: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog | |||||
| [Runtime ID]: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog | |||||
| @@ -279,8 +279,8 @@ Meaning, the constructor parameters and public settable properties of a module w | |||||
| For more information on dependency injection, read the [DependencyInjection] guides. | For more information on dependency injection, read the [DependencyInjection] guides. | ||||
| > [!NOTE] | > [!NOTE] | ||||
| > On every command execution, module dependencies are resolved using a new service scope which allows you to utilize scoped service instances, just like in Asp.Net. | |||||
| > Including the precondition checks, every module method is executed using the same service scope and service scopes are disposed right after the `AfterExecute` method returns. | |||||
| > On every command execution, if the 'AutoServiceScopes' option is enabled in the config , module dependencies are resolved using a new service scope which allows you to utilize scoped service instances, just like in Asp.Net. | |||||
| > Including the precondition checks, every module method is executed using the same service scope and service scopes are disposed right after the `AfterExecute` method returns. This doesn't apply to methods other than `ExecuteAsync()`. | |||||
| ## Module Groups | ## Module Groups | ||||
| @@ -291,6 +291,11 @@ By nesting commands inside a module that is tagged with [GroupAttribute] you can | |||||
| > Although creating nested module stuctures are allowed, | > Although creating nested module stuctures are allowed, | ||||
| > you are not permitted to use more than 2 [GroupAttribute]'s in module hierarchy. | > you are not permitted to use more than 2 [GroupAttribute]'s in module hierarchy. | ||||
| > [!NOTE] | |||||
| > To not use the command group's name as a prefix for component or modal interaction's custom id set `ignoreGroupNames` parameter to `true` in classes with [GroupAttribute] | |||||
| > | |||||
| > However, you have to be careful to prevent overlapping ids of buttons and modals | |||||
| [!code-csharp[Command Group Example](samples/intro/groupmodule.cs)] | [!code-csharp[Command Group Example](samples/intro/groupmodule.cs)] | ||||
| ## Executing Commands | ## Executing Commands | ||||
| @@ -303,8 +308,19 @@ Any of the following socket events can be used to execute commands: | |||||
| - [AutocompleteExecuted] | - [AutocompleteExecuted] | ||||
| - [UserCommandExecuted] | - [UserCommandExecuted] | ||||
| - [MessageCommandExecuted] | - [MessageCommandExecuted] | ||||
| - [ModalExecuted] | |||||
| These events will trigger for the specific type of interaction they inherit their name from. The [InteractionCreated] event will trigger for all. | |||||
| An example of executing a command from an event can be seen here: | |||||
| Commands can be either executed on the gateway thread or on a seperate thread from the thread pool. This behaviour can be configured by changing the *RunMode* property of `InteractionServiceConfig` or by setting the *runMode* parameter of a command attribute. | |||||
| [!code-csharp[Command Event Example](samples/intro/event.cs)] | |||||
| Commands can be either executed on the gateway thread or on a seperate thread from the thread pool. | |||||
| This behaviour can be configured by changing the `RunMode` property of `InteractionServiceConfig` or by setting the *runMode* parameter of a command attribute. | |||||
| > [!WARNING] | |||||
| > In the example above, no form of post-execution is presented. | |||||
| > Please carefully read the [Post Execution Documentation] for the best approach in resolving the result based on your `RunMode`. | |||||
| You can also configure the way [InteractionService] executes the commands. | You can also configure the way [InteractionService] executes the commands. | ||||
| By default, commands are executed using `ConstructorInfo.Invoke()` to create module instances and | By default, commands are executed using `ConstructorInfo.Invoke()` to create module instances and | ||||
| @@ -371,6 +387,7 @@ delegate can be used to create HTTP responses from a deserialized json object st | |||||
| [AutocompleteExecuted]: xref:Discord.WebSocket.BaseSocketClient | [AutocompleteExecuted]: xref:Discord.WebSocket.BaseSocketClient | ||||
| [UserCommandExecuted]: xref:Discord.WebSocket.BaseSocketClient | [UserCommandExecuted]: xref:Discord.WebSocket.BaseSocketClient | ||||
| [MessageCommandExecuted]: xref:Discord.WebSocket.BaseSocketClient | [MessageCommandExecuted]: xref:Discord.WebSocket.BaseSocketClient | ||||
| [ModalExecuted]: xref:Discord.WebSocket.BaseSocketClient | |||||
| [DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient | [DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient | ||||
| [DiscordRestClient]: xref:Discord.Rest.DiscordRestClient | [DiscordRestClient]: xref:Discord.Rest.DiscordRestClient | ||||
| [SocketInteractionContext]: xref:Discord.Interactions.SocketInteractionContext | [SocketInteractionContext]: xref:Discord.Interactions.SocketInteractionContext | ||||
| @@ -0,0 +1,14 @@ | |||||
| // Theres multiple ways to subscribe to the event, depending on your application. Please use the approach fit to your type of client. | |||||
| // DiscordSocketClient: | |||||
| _socketClient.InteractionCreated += async (x) => | |||||
| { | |||||
| var ctx = new SocketInteractionContext(_socketClient, x); | |||||
| await _interactionService.ExecuteCommandAsync(ctx, _serviceProvider); | |||||
| } | |||||
| // DiscordShardedClient: | |||||
| _shardedClient.InteractionCreated += async (x) => | |||||
| { | |||||
| var ctx = new ShardedInteractionContext(_shardedClient, x); | |||||
| await _interactionService.ExecuteCommandAsync(ctx, _serviceProvider); | |||||
| } | |||||
| @@ -16,6 +16,11 @@ public class CommandGroupModule : InteractionModuleBase<SocketInteractionContext | |||||
| // group-name subcommand-group-name echo | // group-name subcommand-group-name echo | ||||
| [SlashCommand("echo", "Echo an input")] | [SlashCommand("echo", "Echo an input")] | ||||
| public async Task EchoSubcommand(string input) | public async Task EchoSubcommand(string input) | ||||
| => await RespondAsync(input); | |||||
| => await RespondAsync(input, components: new ComponentBuilder().WithButton("Echo", $"echoButton_{input}").Build()); | |||||
| // Component interaction with ignoreGroupNames set to true | |||||
| [ComponentInteraction("echoButton_*", true)] | |||||
| public async Task EchoButton(string input) | |||||
| => await RespondAsync(input); | |||||
| } | } | ||||
| } | } | ||||
| @@ -12,7 +12,9 @@ public class FoodModal : IModal | |||||
| [ModalTextInput("food_name", placeholder: "Pizza", maxLength: 20)] | [ModalTextInput("food_name", placeholder: "Pizza", maxLength: 20)] | ||||
| public string Food { get; set; } | public string Food { get; set; } | ||||
| // Additional paremeters can be specified to further customize the input. | |||||
| // Additional paremeters can be specified to further customize the input. | |||||
| // Parameters can be optional | |||||
| [RequiredInput(false)] | |||||
| [InputLabel("Why??")] | [InputLabel("Why??")] | ||||
| [ModalTextInput("food_reason", TextInputStyle.Paragraph, "Kuz it's tasty", maxLength: 500)] | [ModalTextInput("food_reason", TextInputStyle.Paragraph, "Kuz it's tasty", maxLength: 500)] | ||||
| public string Reason { get; set; } | public string Reason { get; set; } | ||||
| @@ -22,10 +24,15 @@ public class FoodModal : IModal | |||||
| [ModalInteraction("food_menu")] | [ModalInteraction("food_menu")] | ||||
| public async Task ModalResponse(FoodModal modal) | public async Task ModalResponse(FoodModal modal) | ||||
| { | { | ||||
| // Check if "Why??" field is populated | |||||
| string reason = string.IsNullOrWhiteSpace(modal.Reason) | |||||
| ? "." | |||||
| : $" because {modal.Reason}"; | |||||
| // Build the message to send. | // Build the message to send. | ||||
| string message = "hey @everyone, I just learned " + | string message = "hey @everyone, I just learned " + | ||||
| $"{Context.User.Mention}'s favorite food is " + | $"{Context.User.Mention}'s favorite food is " + | ||||
| $"{modal.Food} because {modal.Reason}."; | |||||
| $"{modal.Food}{reason}"; | |||||
| // Specify the AllowedMentions so we don't actually ping everyone. | // Specify the AllowedMentions so we don't actually ping everyone. | ||||
| AllowedMentions mentions = new(); | AllowedMentions mentions = new(); | ||||
| @@ -17,11 +17,9 @@ bot. (When developing on .NET Framework, this would be `bin/debug`, | |||||
| when developing on .NET Core, this is where you execute `dotnet run` | when developing on .NET Core, this is where you execute `dotnet run` | ||||
| from; typically the same directory as your csproj). | from; typically the same directory as your csproj). | ||||
| For Windows Users, precompiled binaries are available for your | |||||
| convienence [here](https://github.com/discord-net/Discord.Net/tree/dev/voice-natives). | |||||
| **For Windows users, precompiled binaries are available for your convienence [here](https://github.com/discord-net/Discord.Net/tree/dev/voice-natives).** | |||||
| For Linux Users, you will need to compile [Sodium] and [Opus] from | |||||
| source, or install them from your package manager. | |||||
| **For Linux users, you will need to compile [Sodium] and [Opus] from source, or install them from your package manager.** | |||||
| [Sodium]: https://download.libsodium.org/libsodium/releases/ | [Sodium]: https://download.libsodium.org/libsodium/releases/ | ||||
| [Opus]: http://downloads.xiph.org/releases/opus/ | [Opus]: http://downloads.xiph.org/releases/opus/ | ||||
| @@ -251,7 +251,7 @@ namespace Discord | |||||
| private static Assembly _overrideDomain_Resolving(AssemblyLoadContext arg1, AssemblyName arg2) | private static Assembly _overrideDomain_Resolving(AssemblyLoadContext arg1, AssemblyName arg2) | ||||
| { | { | ||||
| // resolve the override id | // resolve the override id | ||||
| var v = _loadedOverrides.FirstOrDefault(x => x.Value.Any(x => x.Assembly.FullName == arg1.Assemblies.FirstOrDefault().FullName)); | |||||
| var v = _loadedOverrides.FirstOrDefault(x => x.Value.Any(x => x.Assembly.FullName == arg1.Assemblies.First().FullName)); | |||||
| return GetDependencyAsync(v.Key.Id, $"{arg2}").GetAwaiter().GetResult(); | return GetDependencyAsync(v.Key.Id, $"{arg2}").GetAwaiter().GetResult(); | ||||
| } | } | ||||
| @@ -22,6 +22,7 @@ namespace ShardedClient.Services | |||||
| _service.Log += LogAsync; | _service.Log += LogAsync; | ||||
| _client.InteractionCreated += OnInteractionAsync; | _client.InteractionCreated += OnInteractionAsync; | ||||
| _client.ShardReady += ReadyAsync; | |||||
| // For examples on how to handle post execution, | // For examples on how to handle post execution, | ||||
| // see the InteractionFramework samples. | // see the InteractionFramework samples. | ||||
| } | } | ||||
| @@ -30,11 +31,6 @@ namespace ShardedClient.Services | |||||
| public async Task InitializeAsync() | public async Task InitializeAsync() | ||||
| { | { | ||||
| await _service.AddModulesAsync(typeof(InteractionHandlingService).Assembly, _provider); | await _service.AddModulesAsync(typeof(InteractionHandlingService).Assembly, _provider); | ||||
| #if DEBUG | |||||
| await _service.RegisterCommandsToGuildAsync(1 /* implement */); | |||||
| #else | |||||
| await _service.RegisterCommandsGloballyAsync(); | |||||
| #endif | |||||
| } | } | ||||
| private async Task OnInteractionAsync(SocketInteraction interaction) | private async Task OnInteractionAsync(SocketInteraction interaction) | ||||
| @@ -53,5 +49,14 @@ namespace ShardedClient.Services | |||||
| return Task.CompletedTask; | return Task.CompletedTask; | ||||
| } | } | ||||
| private async Task ReadyAsync(DiscordSocketClient _) | |||||
| { | |||||
| #if DEBUG | |||||
| await _service.RegisterCommandsToGuildAsync(1 /* implement */); | |||||
| #else | |||||
| await _service.RegisterCommandsGloballyAsync(); | |||||
| #endif | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -206,6 +206,7 @@ namespace Discord.Commands | |||||
| try | try | ||||
| { | { | ||||
| await instance.BeforeExecuteAsync(cmd).ConfigureAwait(false); | |||||
| instance.BeforeExecute(cmd); | instance.BeforeExecute(cmd); | ||||
| var task = method.Invoke(instance, args) as Task ?? Task.Delay(0); | var task = method.Invoke(instance, args) as Task ?? Task.Delay(0); | ||||
| @@ -221,6 +222,7 @@ namespace Discord.Commands | |||||
| } | } | ||||
| finally | finally | ||||
| { | { | ||||
| await instance.AfterExecuteAsync(cmd).ConfigureAwait(false); | |||||
| instance.AfterExecute(cmd); | instance.AfterExecute(cmd); | ||||
| (instance as IDisposable)?.Dispose(); | (instance as IDisposable)?.Dispose(); | ||||
| } | } | ||||
| @@ -1,4 +1,5 @@ | |||||
| using Discord.Commands.Builders; | using Discord.Commands.Builders; | ||||
| using System.Threading.Tasks; | |||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| @@ -13,12 +14,24 @@ namespace Discord.Commands | |||||
| /// <param name="context">The context to set.</param> | /// <param name="context">The context to set.</param> | ||||
| void SetContext(ICommandContext context); | void SetContext(ICommandContext context); | ||||
| /// <summary> | |||||
| /// Executed asynchronously before a command is run in this module base. | |||||
| /// </summary> | |||||
| /// <param name="command">The command thats about to run.</param> | |||||
| Task BeforeExecuteAsync(CommandInfo command); | |||||
| /// <summary> | /// <summary> | ||||
| /// Executed before a command is run in this module base. | /// Executed before a command is run in this module base. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="command">The command thats about to run.</param> | /// <param name="command">The command thats about to run.</param> | ||||
| void BeforeExecute(CommandInfo command); | void BeforeExecute(CommandInfo command); | ||||
| /// <summary> | |||||
| /// Executed asynchronously after a command is run in this module base. | |||||
| /// </summary> | |||||
| /// <param name="command">The command thats about to run.</param> | |||||
| Task AfterExecuteAsync(CommandInfo command); | |||||
| /// <summary> | /// <summary> | ||||
| /// Executed after a command is ran in this module base. | /// Executed after a command is ran in this module base. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -46,6 +46,11 @@ namespace Discord.Commands | |||||
| return await Context.Channel.SendMessageAsync(message, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); | return await Context.Channel.SendMessageAsync(message, isTTS, embed, options, allowedMentions, messageReference, components, stickers, embeds).ConfigureAwait(false); | ||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// The method to execute asynchronously before executing the command. | |||||
| /// </summary> | |||||
| /// <param name="command">The <see cref="CommandInfo"/> of the command to be executed.</param> | |||||
| protected virtual Task BeforeExecuteAsync(CommandInfo command) => Task.CompletedTask; | |||||
| /// <summary> | |||||
| /// The method to execute before executing the command. | /// The method to execute before executing the command. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="command">The <see cref="CommandInfo"/> of the command to be executed.</param> | /// <param name="command">The <see cref="CommandInfo"/> of the command to be executed.</param> | ||||
| @@ -53,6 +58,11 @@ namespace Discord.Commands | |||||
| { | { | ||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// The method to execute asynchronously after executing the command. | |||||
| /// </summary> | |||||
| /// <param name="command">The <see cref="CommandInfo"/> of the command to be executed.</param> | |||||
| protected virtual Task AfterExecuteAsync(CommandInfo command) => Task.CompletedTask; | |||||
| /// <summary> | |||||
| /// The method to execute after executing the command. | /// The method to execute after executing the command. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="command">The <see cref="CommandInfo"/> of the command to be executed.</param> | /// <param name="command">The <see cref="CommandInfo"/> of the command to be executed.</param> | ||||
| @@ -76,7 +86,9 @@ namespace Discord.Commands | |||||
| var newValue = context as T; | var newValue = context as T; | ||||
| Context = newValue ?? throw new InvalidOperationException($"Invalid context type. Expected {typeof(T).Name}, got {context.GetType().Name}."); | Context = newValue ?? throw new InvalidOperationException($"Invalid context type. Expected {typeof(T).Name}, got {context.GetType().Name}."); | ||||
| } | } | ||||
| Task IModuleBase.BeforeExecuteAsync(CommandInfo command) => BeforeExecuteAsync(command); | |||||
| void IModuleBase.BeforeExecute(CommandInfo command) => BeforeExecute(command); | void IModuleBase.BeforeExecute(CommandInfo command) => BeforeExecute(command); | ||||
| Task IModuleBase.AfterExecuteAsync(CommandInfo command) => AfterExecuteAsync(command); | |||||
| void IModuleBase.AfterExecute(CommandInfo command) => AfterExecute(command); | void IModuleBase.AfterExecute(CommandInfo command) => AfterExecute(command); | ||||
| void IModuleBase.OnModuleBuilding(CommandService commandService, ModuleBuilder builder) => OnModuleBuilding(commandService, builder); | void IModuleBase.OnModuleBuilding(CommandService commandService, ModuleBuilder builder) => OnModuleBuilding(commandService, builder); | ||||
| #endregion | #endregion | ||||
| @@ -12,174 +12,174 @@ namespace Discord | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has no features. | /// The guild has no features. | ||||
| /// </summary> | /// </summary> | ||||
| None = 0, | |||||
| None = 0L, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has access to animated banners. | /// The guild has access to animated banners. | ||||
| /// </summary> | /// </summary> | ||||
| AnimatedBanner = 1 << 0, | |||||
| AnimatedBanner = 1L << 0, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has access to set an animated guild icon. | /// The guild has access to set an animated guild icon. | ||||
| /// </summary> | /// </summary> | ||||
| AnimatedIcon = 1 << 1, | |||||
| AnimatedIcon = 1L << 1, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has access to set a guild banner image. | /// The guild has access to set a guild banner image. | ||||
| /// </summary> | /// </summary> | ||||
| Banner = 1 << 2, | |||||
| Banner = 1L << 2, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has access to channel banners. | /// The guild has access to channel banners. | ||||
| /// </summary> | /// </summary> | ||||
| ChannelBanner = 1 << 3, | |||||
| ChannelBanner = 1L << 3, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has access to use commerce features (i.e. create store channels). | /// The guild has access to use commerce features (i.e. create store channels). | ||||
| /// </summary> | /// </summary> | ||||
| Commerce = 1 << 4, | |||||
| Commerce = 1L << 4, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild can enable welcome screen, Membership Screening, stage channels and discovery, and receives community updates. | /// The guild can enable welcome screen, Membership Screening, stage channels and discovery, and receives community updates. | ||||
| /// </summary> | /// </summary> | ||||
| Community = 1 << 5, | |||||
| Community = 1L << 5, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild is able to be discovered in the directory. | /// The guild is able to be discovered in the directory. | ||||
| /// </summary> | /// </summary> | ||||
| Discoverable = 1 << 6, | |||||
| Discoverable = 1L << 6, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has discoverable disabled. | /// The guild has discoverable disabled. | ||||
| /// </summary> | /// </summary> | ||||
| DiscoverableDisabled = 1 << 7, | |||||
| DiscoverableDisabled = 1L << 7, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has enabled discoverable before. | /// The guild has enabled discoverable before. | ||||
| /// </summary> | /// </summary> | ||||
| EnabledDiscoverableBefore = 1 << 8, | |||||
| EnabledDiscoverableBefore = 1L << 8, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild is able to be featured in the directory. | /// The guild is able to be featured in the directory. | ||||
| /// </summary> | /// </summary> | ||||
| Featureable = 1 << 9, | |||||
| Featureable = 1L << 9, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has a force relay. | /// The guild has a force relay. | ||||
| /// </summary> | /// </summary> | ||||
| ForceRelay = 1 << 10, | |||||
| ForceRelay = 1L << 10, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has a directory entry. | /// The guild has a directory entry. | ||||
| /// </summary> | /// </summary> | ||||
| HasDirectoryEntry = 1 << 11, | |||||
| HasDirectoryEntry = 1L << 11, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild is a hub. | /// The guild is a hub. | ||||
| /// </summary> | /// </summary> | ||||
| Hub = 1 << 12, | |||||
| Hub = 1L << 12, | |||||
| /// <summary> | /// <summary> | ||||
| /// You shouldn't be here... | /// You shouldn't be here... | ||||
| /// </summary> | /// </summary> | ||||
| InternalEmployeeOnly = 1 << 13, | |||||
| InternalEmployeeOnly = 1L << 13, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has access to set an invite splash background. | /// The guild has access to set an invite splash background. | ||||
| /// </summary> | /// </summary> | ||||
| InviteSplash = 1 << 14, | |||||
| InviteSplash = 1L << 14, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild is linked to a hub. | /// The guild is linked to a hub. | ||||
| /// </summary> | /// </summary> | ||||
| LinkedToHub = 1 << 15, | |||||
| LinkedToHub = 1L << 15, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has member profiles. | /// The guild has member profiles. | ||||
| /// </summary> | /// </summary> | ||||
| MemberProfiles = 1 << 16, | |||||
| MemberProfiles = 1L << 16, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has enabled <seealso href="https://discord.com/developers/docs/resources/guild#membership-screening-object">Membership Screening</seealso>. | /// The guild has enabled <seealso href="https://discord.com/developers/docs/resources/guild#membership-screening-object">Membership Screening</seealso>. | ||||
| /// </summary> | /// </summary> | ||||
| MemberVerificationGateEnabled = 1 << 17, | |||||
| MemberVerificationGateEnabled = 1L << 17, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has enabled monetization. | /// The guild has enabled monetization. | ||||
| /// </summary> | /// </summary> | ||||
| MonetizationEnabled = 1 << 18, | |||||
| MonetizationEnabled = 1L << 18, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has more emojis. | /// The guild has more emojis. | ||||
| /// </summary> | /// </summary> | ||||
| MoreEmoji = 1 << 19, | |||||
| MoreEmoji = 1L << 19, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has increased custom sticker slots. | /// The guild has increased custom sticker slots. | ||||
| /// </summary> | /// </summary> | ||||
| MoreStickers = 1 << 20, | |||||
| MoreStickers = 1L << 20, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has access to create news channels. | /// The guild has access to create news channels. | ||||
| /// </summary> | /// </summary> | ||||
| News = 1 << 21, | |||||
| News = 1L << 21, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has new thread permissions. | /// The guild has new thread permissions. | ||||
| /// </summary> | /// </summary> | ||||
| NewThreadPermissions = 1 << 22, | |||||
| NewThreadPermissions = 1L << 22, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild is partnered. | /// The guild is partnered. | ||||
| /// </summary> | /// </summary> | ||||
| Partnered = 1 << 23, | |||||
| Partnered = 1L << 23, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has a premium tier three override; guilds made by Discord usually have this. | /// The guild has a premium tier three override; guilds made by Discord usually have this. | ||||
| /// </summary> | /// </summary> | ||||
| PremiumTier3Override = 1 << 24, | |||||
| PremiumTier3Override = 1L << 24, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild can be previewed before joining via Membership Screening or the directory. | /// The guild can be previewed before joining via Membership Screening or the directory. | ||||
| /// </summary> | /// </summary> | ||||
| PreviewEnabled = 1 << 25, | |||||
| PreviewEnabled = 1L << 25, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has access to create private threads. | /// The guild has access to create private threads. | ||||
| /// </summary> | /// </summary> | ||||
| PrivateThreads = 1 << 26, | |||||
| PrivateThreads = 1L << 26, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has relay enabled. | /// The guild has relay enabled. | ||||
| /// </summary> | /// </summary> | ||||
| RelayEnabled = 1 << 27, | |||||
| RelayEnabled = 1L << 27, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild is able to set role icons. | /// The guild is able to set role icons. | ||||
| /// </summary> | /// </summary> | ||||
| RoleIcons = 1 << 28, | |||||
| RoleIcons = 1L << 28, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has role subscriptions available for purchase. | /// The guild has role subscriptions available for purchase. | ||||
| /// </summary> | /// </summary> | ||||
| RoleSubscriptionsAvailableForPurchase = 1 << 29, | |||||
| RoleSubscriptionsAvailableForPurchase = 1L << 29, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has role subscriptions enabled. | /// The guild has role subscriptions enabled. | ||||
| /// </summary> | /// </summary> | ||||
| RoleSubscriptionsEnabled = 1 << 30, | |||||
| RoleSubscriptionsEnabled = 1L << 30, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has access to the seven day archive time for threads. | /// The guild has access to the seven day archive time for threads. | ||||
| /// </summary> | /// </summary> | ||||
| SevenDayThreadArchive = 1 << 31, | |||||
| SevenDayThreadArchive = 1L << 31, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has text in voice enabled. | /// The guild has text in voice enabled. | ||||
| /// </summary> | /// </summary> | ||||
| TextInVoiceEnabled = 1 << 32, | |||||
| TextInVoiceEnabled = 1L << 32, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has threads enabled. | /// The guild has threads enabled. | ||||
| /// </summary> | /// </summary> | ||||
| ThreadsEnabled = 1 << 33, | |||||
| ThreadsEnabled = 1L << 33, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has testing threads enabled. | /// The guild has testing threads enabled. | ||||
| /// </summary> | /// </summary> | ||||
| ThreadsEnabledTesting = 1 << 34, | |||||
| ThreadsEnabledTesting = 1L << 34, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has the default thread auto archive. | /// The guild has the default thread auto archive. | ||||
| /// </summary> | /// </summary> | ||||
| ThreadsDefaultAutoArchiveDuration = 1 << 35, | |||||
| ThreadsDefaultAutoArchiveDuration = 1L << 35, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has access to the three day archive time for threads. | /// The guild has access to the three day archive time for threads. | ||||
| /// </summary> | /// </summary> | ||||
| ThreeDayThreadArchive = 1 << 36, | |||||
| ThreeDayThreadArchive = 1L << 36, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has enabled ticketed events. | /// The guild has enabled ticketed events. | ||||
| /// </summary> | /// </summary> | ||||
| TicketedEventsEnabled = 1 << 37, | |||||
| TicketedEventsEnabled = 1L << 37, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has access to set a vanity URL. | /// The guild has access to set a vanity URL. | ||||
| /// </summary> | /// </summary> | ||||
| VanityUrl = 1 << 38, | |||||
| VanityUrl = 1L << 38, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild is verified. | /// The guild is verified. | ||||
| /// </summary> | /// </summary> | ||||
| Verified = 1 << 39, | |||||
| Verified = 1L << 39, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has access to set 384kbps bitrate in voice (previously VIP voice servers). | /// The guild has access to set 384kbps bitrate in voice (previously VIP voice servers). | ||||
| /// </summary> | /// </summary> | ||||
| VIPRegions = 1 << 40, | |||||
| VIPRegions = 1L << 40, | |||||
| /// <summary> | /// <summary> | ||||
| /// The guild has enabled the welcome screen. | /// The guild has enabled the welcome screen. | ||||
| /// </summary> | /// </summary> | ||||
| WelcomeScreenEnabled = 1 << 41, | |||||
| WelcomeScreenEnabled = 1L << 41, | |||||
| } | } | ||||
| } | } | ||||
| @@ -9,10 +9,11 @@ namespace Discord.Interactions | |||||
| public NullableConverter(InteractionService interactionService, IServiceProvider services) | public NullableConverter(InteractionService interactionService, IServiceProvider services) | ||||
| { | { | ||||
| var type = Nullable.GetUnderlyingType(typeof(T)); | |||||
| var nullableType = typeof(T); | |||||
| var type = Nullable.GetUnderlyingType(nullableType); | |||||
| if (type is null) | if (type is null) | ||||
| throw new ArgumentException($"No type {nameof(TypeConverter)} is defined for this {type.FullName}", "type"); | |||||
| throw new ArgumentException($"No type {nameof(TypeConverter)} is defined for this {nullableType.FullName}", nameof(type)); | |||||
| _typeConverter = interactionService.GetTypeConverter(type, services); | _typeConverter = interactionService.GetTypeConverter(type, services); | ||||
| } | } | ||||
| @@ -1756,7 +1756,7 @@ namespace Discord.API | |||||
| if (args.TargetType.Value == TargetUserType.Stream) | if (args.TargetType.Value == TargetUserType.Stream) | ||||
| Preconditions.GreaterThan(args.TargetUserId, 0, nameof(args.TargetUserId)); | Preconditions.GreaterThan(args.TargetUserId, 0, nameof(args.TargetUserId)); | ||||
| if (args.TargetType.Value == TargetUserType.EmbeddedApplication) | if (args.TargetType.Value == TargetUserType.EmbeddedApplication) | ||||
| Preconditions.GreaterThan(args.TargetApplicationId, 0, nameof(args.TargetUserId)); | |||||
| Preconditions.GreaterThan(args.TargetApplicationId, 0, nameof(args.TargetApplicationId)); | |||||
| } | } | ||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| @@ -15,7 +15,7 @@ namespace Discord.Rest | |||||
| Thread = thread; | Thread = thread; | ||||
| ThreadType = type; | ThreadType = type; | ||||
| Before = before; | Before = before; | ||||
| After = After; | |||||
| After = after; | |||||
| } | } | ||||
| internal static ThreadUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) | internal static ThreadUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) | ||||
| @@ -428,7 +428,7 @@ namespace Discord.Rest | |||||
| var ids = args.Roles.Value.Select(r => r.Id); | var ids = args.Roles.Value.Select(r => r.Id); | ||||
| if (args.RoleIds.IsSpecified) | if (args.RoleIds.IsSpecified) | ||||
| args.RoleIds.Value.Concat(ids); | |||||
| args.RoleIds = Optional.Create(args.RoleIds.Value.Concat(ids)); | |||||
| else | else | ||||
| args.RoleIds = Optional.Create(ids); | args.RoleIds = Optional.Create(ids); | ||||
| } | } | ||||
| @@ -426,7 +426,7 @@ namespace Discord.Rest | |||||
| AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | ||||
| => await FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | => await FollowupWithFileAsync(fileStream, fileName, text, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | ||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(string filePath, string text, string fileName, Embed[] embeds, bool isTTS, bool ephemeral, | |||||
| async Task<IUserMessage> IDiscordInteraction.FollowupWithFileAsync(string filePath, string fileName, string text, Embed[] embeds, bool isTTS, bool ephemeral, | |||||
| AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | AllowedMentions allowedMentions, MessageComponent components, Embed embed, RequestOptions options) | ||||
| => await FollowupWithFileAsync(filePath, text, fileName, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | => await FollowupWithFileAsync(filePath, text, fileName, embeds, isTTS, ephemeral, allowedMentions, components, embed, options).ConfigureAwait(false); | ||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| @@ -0,0 +1,13 @@ | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API.Gateway | |||||
| { | |||||
| internal class WebhooksUpdatedEvent | |||||
| { | |||||
| [JsonProperty("guild_id")] | |||||
| public ulong GuildId { get; set; } | |||||
| [JsonProperty("channel_id")] | |||||
| public ulong ChannelId { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -55,7 +55,7 @@ namespace Discord.WebSocket | |||||
| /// <summary> Fired when a channel is updated. </summary> | /// <summary> Fired when a channel is updated. </summary> | ||||
| /// <remarks> | /// <remarks> | ||||
| /// <para> | /// <para> | ||||
| /// This event is fired when a generic channel has been destroyed. The event handler must return a | |||||
| /// This event is fired when a generic channel has been updated. The event handler must return a | |||||
| /// <see cref="Task"/> and accept 2 <see cref="SocketChannel"/> as its parameters. | /// <see cref="Task"/> and accept 2 <see cref="SocketChannel"/> as its parameters. | ||||
| /// </para> | /// </para> | ||||
| /// <para> | /// <para> | ||||
| @@ -106,7 +106,7 @@ namespace Discord.WebSocket | |||||
| /// <remarks> | /// <remarks> | ||||
| /// <para> | /// <para> | ||||
| /// This event is fired when a message is deleted. The event handler must return a | /// This event is fired when a message is deleted. The event handler must return a | ||||
| /// <see cref="Task"/> and accept a <see cref="Cacheable{TEntity,TId}"/> and | |||||
| /// <see cref="Task"/> and accept a <see cref="Cacheable{TEntity,TId}"/> and | |||||
| /// <see cref="ISocketMessageChannel"/> as its parameters. | /// <see cref="ISocketMessageChannel"/> as its parameters. | ||||
| /// </para> | /// </para> | ||||
| /// <para> | /// <para> | ||||
| @@ -117,11 +117,11 @@ namespace Discord.WebSocket | |||||
| /// </note> | /// </note> | ||||
| /// If caching is enabled via <see cref="DiscordSocketConfig"/>, the | /// If caching is enabled via <see cref="DiscordSocketConfig"/>, the | ||||
| /// <see cref="Cacheable{TEntity,TId}"/> entity will contain the deleted message; otherwise, in event | /// <see cref="Cacheable{TEntity,TId}"/> entity will contain the deleted message; otherwise, in event | ||||
| /// that the message cannot be retrieved, the snowflake ID of the message is preserved in the | |||||
| /// that the message cannot be retrieved, the snowflake ID of the message is preserved in the | |||||
| /// <see cref="ulong"/>. | /// <see cref="ulong"/>. | ||||
| /// </para> | /// </para> | ||||
| /// <para> | /// <para> | ||||
| /// The source channel of the removed message will be passed into the | |||||
| /// The source channel of the removed message will be passed into the | |||||
| /// <see cref="ISocketMessageChannel"/> parameter. | /// <see cref="ISocketMessageChannel"/> parameter. | ||||
| /// </para> | /// </para> | ||||
| /// </remarks> | /// </remarks> | ||||
| @@ -143,7 +143,7 @@ namespace Discord.WebSocket | |||||
| /// </note> | /// </note> | ||||
| /// <para> | /// <para> | ||||
| /// This event is fired when multiple messages are bulk deleted. The event handler must return a | /// This event is fired when multiple messages are bulk deleted. The event handler must return a | ||||
| /// <see cref="Task"/> and accept an <see cref="IReadOnlyCollection{Cacheable}"/> and | |||||
| /// <see cref="Task"/> and accept an <see cref="IReadOnlyCollection{Cacheable}"/> and | |||||
| /// <see cref="ISocketMessageChannel"/> as its parameters. | /// <see cref="ISocketMessageChannel"/> as its parameters. | ||||
| /// </para> | /// </para> | ||||
| /// <para> | /// <para> | ||||
| @@ -154,11 +154,11 @@ namespace Discord.WebSocket | |||||
| /// </note> | /// </note> | ||||
| /// If caching is enabled via <see cref="DiscordSocketConfig"/>, the | /// If caching is enabled via <see cref="DiscordSocketConfig"/>, the | ||||
| /// <see cref="Cacheable{TEntity,TId}"/> entity will contain the deleted message; otherwise, in event | /// <see cref="Cacheable{TEntity,TId}"/> entity will contain the deleted message; otherwise, in event | ||||
| /// that the message cannot be retrieved, the snowflake ID of the message is preserved in the | |||||
| /// that the message cannot be retrieved, the snowflake ID of the message is preserved in the | |||||
| /// <see cref="ulong"/>. | /// <see cref="ulong"/>. | ||||
| /// </para> | /// </para> | ||||
| /// <para> | /// <para> | ||||
| /// The source channel of the removed message will be passed into the | |||||
| /// The source channel of the removed message will be passed into the | |||||
| /// <see cref="ISocketMessageChannel"/> parameter. | /// <see cref="ISocketMessageChannel"/> parameter. | ||||
| /// </para> | /// </para> | ||||
| /// </remarks> | /// </remarks> | ||||
| @@ -178,14 +178,14 @@ namespace Discord.WebSocket | |||||
| /// <para> | /// <para> | ||||
| /// If caching is enabled via <see cref="DiscordSocketConfig"/>, the | /// If caching is enabled via <see cref="DiscordSocketConfig"/>, the | ||||
| /// <see cref="Cacheable{TEntity,TId}"/> entity will contain the original message; otherwise, in event | /// <see cref="Cacheable{TEntity,TId}"/> entity will contain the original message; otherwise, in event | ||||
| /// that the message cannot be retrieved, the snowflake ID of the message is preserved in the | |||||
| /// that the message cannot be retrieved, the snowflake ID of the message is preserved in the | |||||
| /// <see cref="ulong"/>. | /// <see cref="ulong"/>. | ||||
| /// </para> | /// </para> | ||||
| /// <para> | /// <para> | ||||
| /// The updated message will be passed into the <see cref="SocketMessage"/> parameter. | /// The updated message will be passed into the <see cref="SocketMessage"/> parameter. | ||||
| /// </para> | /// </para> | ||||
| /// <para> | /// <para> | ||||
| /// The source channel of the updated message will be passed into the | |||||
| /// The source channel of the updated message will be passed into the | |||||
| /// <see cref="ISocketMessageChannel"/> parameter. | /// <see cref="ISocketMessageChannel"/> parameter. | ||||
| /// </para> | /// </para> | ||||
| /// </remarks> | /// </remarks> | ||||
| @@ -199,24 +199,24 @@ namespace Discord.WebSocket | |||||
| /// <remarks> | /// <remarks> | ||||
| /// <para> | /// <para> | ||||
| /// This event is fired when a reaction is added to a user message. The event handler must return a | /// This event is fired when a reaction is added to a user message. The event handler must return a | ||||
| /// <see cref="Task"/> and accept a <see cref="Cacheable{TEntity,TId}"/>, an | |||||
| /// <see cref="Task"/> and accept a <see cref="Cacheable{TEntity,TId}"/>, an | |||||
| /// <see cref="ISocketMessageChannel"/>, and a <see cref="SocketReaction"/> as its parameter. | /// <see cref="ISocketMessageChannel"/>, and a <see cref="SocketReaction"/> as its parameter. | ||||
| /// </para> | /// </para> | ||||
| /// <para> | /// <para> | ||||
| /// If caching is enabled via <see cref="DiscordSocketConfig"/>, the | /// If caching is enabled via <see cref="DiscordSocketConfig"/>, the | ||||
| /// <see cref="Cacheable{TEntity,TId}"/> entity will contain the original message; otherwise, in event | /// <see cref="Cacheable{TEntity,TId}"/> entity will contain the original message; otherwise, in event | ||||
| /// that the message cannot be retrieved, the snowflake ID of the message is preserved in the | |||||
| /// that the message cannot be retrieved, the snowflake ID of the message is preserved in the | |||||
| /// <see cref="ulong"/>. | /// <see cref="ulong"/>. | ||||
| /// </para> | /// </para> | ||||
| /// <para> | /// <para> | ||||
| /// The source channel of the reaction addition will be passed into the | |||||
| /// The source channel of the reaction addition will be passed into the | |||||
| /// <see cref="ISocketMessageChannel"/> parameter. | /// <see cref="ISocketMessageChannel"/> parameter. | ||||
| /// </para> | /// </para> | ||||
| /// <para> | /// <para> | ||||
| /// The reaction that was added will be passed into the <see cref="SocketReaction"/> parameter. | /// The reaction that was added will be passed into the <see cref="SocketReaction"/> parameter. | ||||
| /// </para> | /// </para> | ||||
| /// <note> | /// <note> | ||||
| /// When fetching the reaction from this event, a user may not be provided under | |||||
| /// When fetching the reaction from this event, a user may not be provided under | |||||
| /// <see cref="SocketReaction.User"/>. Please see the documentation of the property for more | /// <see cref="SocketReaction.User"/>. Please see the documentation of the property for more | ||||
| /// information. | /// information. | ||||
| /// </note> | /// </note> | ||||
| @@ -367,7 +367,7 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| internal readonly AsyncEvent<Func<Cacheable<SocketGuildEvent, ulong>, SocketGuildEvent, Task>> _guildScheduledEventUpdated = new AsyncEvent<Func<Cacheable<SocketGuildEvent, ulong>, SocketGuildEvent, Task>>(); | internal readonly AsyncEvent<Func<Cacheable<SocketGuildEvent, ulong>, SocketGuildEvent, Task>> _guildScheduledEventUpdated = new AsyncEvent<Func<Cacheable<SocketGuildEvent, ulong>, SocketGuildEvent, Task>>(); | ||||
| /// <summary> | /// <summary> | ||||
| /// Fired when a guild event is cancelled. | /// Fired when a guild event is cancelled. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -877,5 +877,20 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| internal readonly AsyncEvent<Func<SocketCustomSticker, Task>> _guildStickerDeleted = new AsyncEvent<Func<SocketCustomSticker, Task>>(); | internal readonly AsyncEvent<Func<SocketCustomSticker, Task>> _guildStickerDeleted = new AsyncEvent<Func<SocketCustomSticker, Task>>(); | ||||
| #endregion | #endregion | ||||
| #region Webhooks | |||||
| /// <summary> | |||||
| /// Fired when a webhook is modified, moved, or deleted. If the webhook was | |||||
| /// moved the channel represents the destination channel, not the source. | |||||
| /// </summary> | |||||
| public event Func<SocketGuild, SocketChannel, Task> WebhooksUpdated | |||||
| { | |||||
| add { _webhooksUpdated.Add(value); } | |||||
| remove { _webhooksUpdated.Remove(value); } | |||||
| } | |||||
| internal readonly AsyncEvent<Func<SocketGuild, SocketChannel, Task>> _webhooksUpdated = new AsyncEvent<Func<SocketGuild, SocketChannel, Task>>(); | |||||
| #endregion | |||||
| } | } | ||||
| } | } | ||||
| @@ -496,6 +496,8 @@ namespace Discord.WebSocket | |||||
| client.GuildScheduledEventStarted += (arg) => _guildScheduledEventStarted.InvokeAsync(arg); | client.GuildScheduledEventStarted += (arg) => _guildScheduledEventStarted.InvokeAsync(arg); | ||||
| client.GuildScheduledEventUserAdd += (arg1, arg2) => _guildScheduledEventUserAdd.InvokeAsync(arg1, arg2); | client.GuildScheduledEventUserAdd += (arg1, arg2) => _guildScheduledEventUserAdd.InvokeAsync(arg1, arg2); | ||||
| client.GuildScheduledEventUserRemove += (arg1, arg2) => _guildScheduledEventUserRemove.InvokeAsync(arg1, arg2); | client.GuildScheduledEventUserRemove += (arg1, arg2) => _guildScheduledEventUserRemove.InvokeAsync(arg1, arg2); | ||||
| client.WebhooksUpdated += (arg1, arg2) => _webhooksUpdated.InvokeAsync(arg1, arg2); | |||||
| } | } | ||||
| #endregion | #endregion | ||||
| @@ -2839,6 +2839,23 @@ namespace Discord.WebSocket | |||||
| #endregion | #endregion | ||||
| #region Webhooks | |||||
| case "WEBHOOKS_UPDATE": | |||||
| { | |||||
| var data = (payload as JToken).ToObject<WebhooksUpdatedEvent>(_serializer); | |||||
| type = "WEBHOOKS_UPDATE"; | |||||
| await _gatewayLogger.DebugAsync("Received Dispatch (WEBHOOKS_UPDATE)").ConfigureAwait(false); | |||||
| var guild = State.GetGuild(data.GuildId); | |||||
| var channel = State.GetChannel(data.ChannelId); | |||||
| await TimedInvokeAsync(_webhooksUpdated, nameof(WebhooksUpdated), guild, channel); | |||||
| } | |||||
| break; | |||||
| #endregion | |||||
| #region Ignored (User only) | #region Ignored (User only) | ||||
| case "CHANNEL_PINS_ACK": | case "CHANNEL_PINS_ACK": | ||||
| await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_ACK)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_ACK)").ConfigureAwait(false); | ||||
| @@ -2858,9 +2875,6 @@ namespace Discord.WebSocket | |||||
| case "USER_SETTINGS_UPDATE": | case "USER_SETTINGS_UPDATE": | ||||
| await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false); | ||||
| break; | break; | ||||
| case "WEBHOOKS_UPDATE": | |||||
| await _gatewayLogger.DebugAsync("Ignored Dispatch (WEBHOOKS_UPDATE)").ConfigureAwait(false); | |||||
| break; | |||||
| #endregion | #endregion | ||||
| #region Others | #region Others | ||||
| @@ -532,13 +532,10 @@ namespace Discord.WebSocket | |||||
| Features = model.Features; | Features = model.Features; | ||||
| var roles = new ConcurrentDictionary<ulong, SocketRole>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.Roles.Length * 1.05)); | var roles = new ConcurrentDictionary<ulong, SocketRole>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.Roles.Length * 1.05)); | ||||
| if (model.Roles != null) | |||||
| for (int i = 0; i < model.Roles.Length; i++) | |||||
| { | { | ||||
| for (int i = 0; i < model.Roles.Length; i++) | |||||
| { | |||||
| var role = SocketRole.Create(this, state, model.Roles[i]); | |||||
| roles.TryAdd(role.Id, role); | |||||
| } | |||||
| var role = SocketRole.Create(this, state, model.Roles[i]); | |||||
| roles.TryAdd(role.Id, role); | |||||
| } | } | ||||
| _roles = roles; | _roles = roles; | ||||
| @@ -39,7 +39,7 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| foreach (var channel in resolved.Channels.Value) | foreach (var channel in resolved.Channels.Value) | ||||
| { | { | ||||
| SocketChannel socketChannel = guild != null | |||||
| var socketChannel = guild != null | |||||
| ? guild.GetChannel(channel.Value.Id) | ? guild.GetChannel(channel.Value.Id) | ||||
| : discord.GetChannel(channel.Value.Id); | : discord.GetChannel(channel.Value.Id); | ||||
| @@ -69,7 +69,7 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| } | } | ||||
| if (resolved.Roles.IsSpecified) | |||||
| if (resolved.Roles.IsSpecified && guild != null) | |||||
| { | { | ||||
| foreach (var role in resolved.Roles.Value) | foreach (var role in resolved.Roles.Value) | ||||
| { | { | ||||