Browse Source

Merge branch 'dev' into v4/state-cache-providers

v4/state-cache-providers
Quin Lynch 3 years ago
parent
commit
e14e54061e
52 changed files with 790 additions and 88 deletions
  1. +2
    -0
      .github/FUNDING.yml
  2. +23
    -0
      CHANGELOG.md
  3. +1
    -1
      Discord.Net.targets
  4. +0
    -1
      README.md
  5. +1
    -1
      docs/docfx.json
  6. +1
    -1
      docs/guides/getting_started/first-bot.md
  7. +3
    -1
      docs/guides/int_basics/application-commands/slash-commands/parameters.md
  8. +10
    -0
      docs/guides/int_framework/intro.md
  9. +59
    -0
      docs/guides/int_framework/permissions.md
  10. +21
    -0
      docs/guides/int_framework/samples/intro/groupmodule.cs
  11. +6
    -0
      docs/guides/int_framework/samples/permissions/guild-only.cs
  12. +7
    -0
      docs/guides/int_framework/samples/permissions/guild-perms.cs
  13. +16
    -0
      docs/guides/int_framework/samples/permissions/perm-nesting.cs
  14. +4
    -0
      docs/guides/int_framework/samples/permissions/perm-stacking.cs
  15. +2
    -2
      docs/guides/other_libs/samples/ModifyLogMethod.cs
  16. +2
    -0
      docs/guides/toc.yml
  17. +1
    -1
      samples/InteractionFramework/Modules/ExampleModule.cs
  18. +0
    -16
      samples/MediatRSample/MediatRSample.sln
  19. +5
    -0
      src/Discord.Net.Core/DiscordErrorCode.cs
  20. +10
    -0
      src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs
  21. +35
    -1
      src/Discord.Net.Core/Entities/Interactions/ContextMenus/MessageCommandBuilder.cs
  22. +35
    -1
      src/Discord.Net.Core/Entities/Interactions/ContextMenus/UserCommandBuilder.cs
  23. +13
    -0
      src/Discord.Net.Core/Entities/Interactions/IApplicationCommand.cs
  24. +42
    -19
      src/Discord.Net.Core/Entities/Interactions/SlashCommands/SlashCommandBuilder.cs
  25. +24
    -0
      src/Discord.Net.Core/Interactions/IRouteMatchContainer.cs
  26. +16
    -0
      src/Discord.Net.Core/Interactions/IRouteSegmentMatch.cs
  27. +16
    -0
      src/Discord.Net.Core/Interactions/RouteSegmentMatch.cs
  28. +17
    -0
      src/Discord.Net.Core/Utils/Preconditions.cs
  29. +25
    -0
      src/Discord.Net.Interactions/Attributes/DefaultMemberPermissionAttribute.cs
  30. +1
    -0
      src/Discord.Net.Interactions/Attributes/DefaultPermissionAttribute.cs
  31. +25
    -0
      src/Discord.Net.Interactions/Attributes/EnabledInDmAttribute.cs
  32. +38
    -0
      src/Discord.Net.Interactions/Builders/Commands/ContextCommandBuilder.cs
  33. +38
    -0
      src/Discord.Net.Interactions/Builders/Commands/SlashCommandBuilder.cs
  34. +38
    -0
      src/Discord.Net.Interactions/Builders/ModuleBuilder.cs
  35. +30
    -0
      src/Discord.Net.Interactions/Builders/ModuleClassBuilder.cs
  36. +8
    -0
      src/Discord.Net.Interactions/Info/Commands/ContextCommands/ContextCommandInfo.cs
  37. +8
    -0
      src/Discord.Net.Interactions/Info/Commands/SlashCommandInfo.cs
  38. +13
    -0
      src/Discord.Net.Interactions/Info/IApplicationCommandInfo.cs
  39. +28
    -0
      src/Discord.Net.Interactions/Info/ModuleInfo.cs
  40. +13
    -1
      src/Discord.Net.Interactions/InteractionContext.cs
  41. +24
    -0
      src/Discord.Net.Interactions/InteractionService.cs
  42. +18
    -3
      src/Discord.Net.Interactions/Utilities/ApplicationCommandRestUtil.cs
  43. +7
    -0
      src/Discord.Net.Rest/API/Common/ApplicationCommand.cs
  44. +6
    -0
      src/Discord.Net.Rest/API/Rest/CreateApplicationCommandParams.cs
  45. +21
    -4
      src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs
  46. +9
    -0
      src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommand.cs
  47. +13
    -1
      src/Discord.Net.Rest/Interactions/RestInteractionContext.cs
  48. +1
    -1
      src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
  49. +1
    -1
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
  50. +9
    -0
      src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommand.cs
  51. +13
    -1
      src/Discord.Net.WebSocket/Interactions/SocketInteractionContext.cs
  52. +31
    -31
      src/Discord.Net/Discord.Net.nuspec

+ 2
- 0
.github/FUNDING.yml View File

@@ -1 +1,3 @@
github: quinchs
open_collective: discordnet
custom: https://paypal.me/quinchs

+ 23
- 0
CHANGELOG.md View File

@@ -1,5 +1,28 @@
# Changelog

## [3.6.1] - 2022-04-30
### Added
- #2272 add 50080 Error code (503e720)

### Fixed
- #2267 Permissions v2 Invalid Operation Exception (a8f6075)
- #2271 null user on interaction without bot scope (f2bb55e)
- #2274 Implement fix for Custom Id Segments NRE (0d74c5c)

### Misc
- 3.6.0 (27226f0)


## [3.6.0] - 2022-04-28
### Added
- #2136 Passing CustomId matches into contexts (4ce1801)
- #2222 V2 Permissions (d98b3cc)

### Fixed
- #2260 Guarding against empty descriptions in `SlashCommandBuilder`/`SlashCommandOptionBuilder` (0554ac2)
- #2248 Fix SocketGuild not returning the AudioClient (daba58c)
- #2254 Fix browser property (275b833)

## [3.5.0] - 2022-04-05

### Added


+ 1
- 1
Discord.Net.targets View File

@@ -1,6 +1,6 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VersionPrefix>3.5.0</VersionPrefix>
<VersionPrefix>3.6.1</VersionPrefix>
<LangVersion>latest</LangVersion>
<Authors>Discord.Net Contributors</Authors>
<PackageTags>discord;discordapp</PackageTags>


+ 0
- 1
README.md View File

@@ -1,4 +1,3 @@
# Discord.Net
<p align="center">
<a href="https://discordnet.dev/" title="Click to visit the documentation!">
<img src="https://raw.githubusercontent.com/discord-net/Discord.Net/dev/docs/marketing/logo/SVG/Combinationmark%20White%20Border.svg" alt="Logo">


+ 1
- 1
docs/docfx.json View File

@@ -60,7 +60,7 @@
"overwrite": "_overwrites/**/**.md",
"globalMetadata": {
"_appTitle": "Discord.Net Documentation",
"_appFooter": "Discord.Net (c) 2015-2022 3.5.0",
"_appFooter": "Discord.Net (c) 2015-2022 3.6.1",
"_enableSearch": true,
"_appLogoPath": "marketing/logo/SVG/Logomark Purple.svg",
"_appFaviconPath": "favicon.ico"


+ 1
- 1
docs/guides/getting_started/first-bot.md View File

@@ -202,7 +202,7 @@ online in Discord.

To create commands for your bot, you may choose from a variety of
command processors available. Throughout the guides, we will be using
the one that Discord.Net ships with. @Guides.Commands.Intro will
the one that Discord.Net ships with. @Guides.TextCommands.Intro will
guide you through how to setup a program that is ready for
[CommandService].



+ 3
- 1
docs/guides/int_basics/application-commands/slash-commands/parameters.md View File

@@ -15,9 +15,10 @@ Slash commands can have a bunch of parameters, each their own type. Let's first
| Integer | A number. |
| Boolean | True or False. |
| User | A user |
| Channel | A channel, this includes voice text and categories |
| Role | A role. |
| Channel | A channel, this includes voice text and categories |
| Mentionable | A role or a user. |
| File | A file |

Each one of the parameter types has its own DNET type in the `SocketSlashCommandDataOption`'s Value field:
| Name | C# Type |
@@ -31,6 +32,7 @@ Each one of the parameter types has its own DNET type in the `SocketSlashCommand
| Role | `SocketRole` |
| Channel | `SocketChannel` |
| Mentionable | `SocketUser`, `SocketGuildUser`, or `SocketRole` |
| File | `IAttachment` |

Let's start by making a command that takes in a user and lists their roles.



+ 10
- 0
docs/guides/int_framework/intro.md View File

@@ -158,6 +158,14 @@ Interaction service complex parameter constructors are prioritized in the follow
2. Constuctor tagged with `[ComplexParameterCtor]`.
3. Type's only public constuctor.

#### DM Permissions

You can use the [EnabledInDmAttribute] to configure whether a globally-scoped top level command should be enabled in Dms or not. Only works on top level commands.

#### Default Member Permissions

[DefaultMemberPermissionsAttribute] can be used when creating a command to set the permissions a user must have to use the command. Permission overwrites can be configured from the Integrations page of Guild Settings. [DefaultMemberPermissionsAttribute] cumulatively propagates down the class hierarchy until it reaches a top level command. This attribute can be only used on top level commands and will not work on commands that are nested in command groups.

## User Commands

A valid User Command must have the following structure:
@@ -282,6 +290,8 @@ By nesting commands inside a module that is tagged with [GroupAttribute] you can
> Although creating nested module stuctures are allowed,
> you are not permitted to use more than 2 [GroupAttribute]'s in module hierarchy.

[!code-csharp[Command Group Example](samples/intro/groupmodule.cs)]

## Executing Commands

Any of the following socket events can be used to execute commands:


+ 59
- 0
docs/guides/int_framework/permissions.md View File

@@ -0,0 +1,59 @@
---
uid: Guides.IntFw.Perms
title: How to handle permissions.
---

# Permissions

This page covers everything to know about setting up permissions for Slash & context commands.

Application command (Slash, User & Message) permissions are set up at creation.
When you add your commands to a guild or globally, the permissions will be set up from the attributes you defined.

Commands that are added will only show up for members that meet the required permissions.
There is no further internal handling, as Discord deals with this on its own.

> [!WARNING]
> Permissions can only be configured at top level commands. Not in subcommands.

## Disallowing commands in DM

Commands can be blocked from being executed in DM if a guild is required to execute them in as followed:

[!code-csharp[no-DM permission](samples/permissions/guild-only.cs)]

> [!TIP]
> This attribute only works on global-level commands. Commands that are registered in guilds alone do not have a need for it.

## Server permissions

As previously shown, a command like ban can be blocked from being executed inside DMs,
as there are no members to ban inside of a DM. However, for a command like this,
we'll also want to make block it from being used by members that do not have the [permissions].
To do this, we can use the `DefaultMemberPermissions` attribute:

[!code-csharp[Server permissions](samples/permissions/guild-perms.cs)]

### Stacking permissions

If you want a user to have multiple [permissions] in order to execute a command, you can use the `|` operator, just like with setting up intents:

[!code-csharp[Permission stacking](samples/permissions/perm-stacking.cs)]

### Nesting permissions

Alternatively, permissions can also be nested.
It will look for all uses of `DefaultMemberPermissions` up until the highest level class.
The `EnabledInDm` attribute can be defined at top level as well,
and will be set up for all of the commands & nested modules inside this class.

[!code-csharp[Permission stacking](samples/permissions/perm-nesting.cs)]

The amount of nesting you can do is realistically endless.

> [!NOTE]
> If the nested class is marked with `Group`, as required for setting up subcommands, this example will not work.
> As mentioned before, subcommands cannot have seperate permissions from the top level command.

[permissions]: xref:Discord.GuildPermission


+ 21
- 0
docs/guides/int_framework/samples/intro/groupmodule.cs View File

@@ -0,0 +1,21 @@
// You can put commands in groups
[Group("group-name", "Group description")]
public class CommandGroupModule : InteractionModuleBase<SocketInteractionContext>
{
// This command will look like
// group-name ping
[SlashCommand("ping", "Get a pong")]
public async Task PongSubcommand()
=> await RespondAsync("Pong!");
// And even in sub-command groups
[Group("subcommand-group-name", "Subcommand group description")]
public class SubСommandGroupModule : InteractionModuleBase<SocketInteractionContext>
{
// This command will look like
// group-name subcommand-group-name echo
[SlashCommand("echo", "Echo an input")]
public async Task EchoSubcommand(string input)
=> await RespondAsync(input);
}
}

+ 6
- 0
docs/guides/int_framework/samples/permissions/guild-only.cs View File

@@ -0,0 +1,6 @@
[EnabledInDm(false)]
[SlashCommand("ban", "Bans a user in this guild")]
public async Task BanAsync(...)
{
...
}

+ 7
- 0
docs/guides/int_framework/samples/permissions/guild-perms.cs View File

@@ -0,0 +1,7 @@
[EnabledInDm(false)]
[DefaultMemberPermissions(GuildPermission.BanMembers)]
[SlashCommand("ban", "Bans a user in this guild")]
public async Task BanAsync(...)
{
...
}

+ 16
- 0
docs/guides/int_framework/samples/permissions/perm-nesting.cs View File

@@ -0,0 +1,16 @@
[EnabledInDm(true)]
[DefaultMemberPermissions(GuildPermission.ViewChannels)]
public class Module : InteractionModuleBase<SocketInteractionContext>
{
[DefaultMemberPermissions(GuildPermission.SendMessages)]
public class NestedModule : InteractionModuleBase<SocketInteractionContext>
{
// While looking for more permissions, it has found 'ViewChannels' and 'SendMessages'. The result of this lookup will be:
// ViewChannels + SendMessages + ManageMessages.
// If these together are not found for target user, the command will not show up for them.
[DefaultMemberPermissions(GuildPermission.ManageMessages)]
[SlashCommand("ping", "Pong!")]
public async Task Ping()
=> await RespondAsync("pong");
}
}

+ 4
- 0
docs/guides/int_framework/samples/permissions/perm-stacking.cs View File

@@ -0,0 +1,4 @@
[DefaultMemberPermissions(GuildPermission.SendMessages | GuildPermission.ViewChannels)]
[SlashCommand("ping", "Pong!")]
public async Task Ping()
=> await RespondAsync("pong");

+ 2
- 2
docs/guides/other_libs/samples/ModifyLogMethod.cs View File

@@ -6,8 +6,8 @@ private static async Task LogAsync(LogMessage message)
LogSeverity.Error => LogEventLevel.Error,
LogSeverity.Warning => LogEventLevel.Warning,
LogSeverity.Info => LogEventLevel.Information,
LogSeverity.Verbose => LogEventLevel.Debug,
LogSeverity.Debug => LogEventLevel.Verbose,
LogSeverity.Verbose => LogEventLevel.Verbose,
LogSeverity.Debug => LogEventLevel.Debug,
_ => LogEventLevel.Information
};
Log.Write(severity, message.Exception, "[{Source}] {Message}", message.Source, message.Message);


+ 2
- 0
docs/guides/toc.yml View File

@@ -57,6 +57,8 @@
topicUid: Guides.IntFw.DI
- name: Post-execution Handling
topicUid: Guides.IntFw.PostExecution
- name: Permissions
topicUid: Guides.IntFw.Perms
- name: Slash Command Basics
items:
- name: Introduction


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

@@ -14,7 +14,7 @@ namespace InteractionFramework.Modules

private InteractionHandler _handler;

// Constructor injection is also a valid way to access the dependecies
// Constructor injection is also a valid way to access the dependencies
public ExampleModule(InteractionHandler handler)
{
_handler = handler;


+ 0
- 16
samples/MediatRSample/MediatRSample.sln View File

@@ -1,16 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediatRSample", "MediatRSample\MediatRSample.csproj", "{CE066EE5-7ED1-42A0-8DB2-862D44F40EA7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CE066EE5-7ED1-42A0-8DB2-862D44F40EA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CE066EE5-7ED1-42A0-8DB2-862D44F40EA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CE066EE5-7ED1-42A0-8DB2-862D44F40EA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CE066EE5-7ED1-42A0-8DB2-862D44F40EA7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

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

@@ -58,6 +58,7 @@ namespace Discord
#endregion

#region General Actions (20XXX)
UnknownTag = 10087,
BotsCannotUse = 20001,
OnlyBotsCanUse = 20002,
CannotSendExplicitContent = 20009,
@@ -98,6 +99,8 @@ namespace Discord

#region General Request Errors (40XXX)
MaximumNumberOfEditsReached = 30046,
MaximumNumberOfPinnedThreadsInAForumChannelReached = 30047,
MaximumNumberOfTagsInAForumChannelReached = 30048,
TokenUnauthorized = 40001,
InvalidVerification = 40002,
OpeningDMTooFast = 40003,
@@ -112,6 +115,7 @@ namespace Discord

#region Action Preconditions/Checks (50XXX)
InteractionHasAlreadyBeenAcknowledged = 40060,
TagNamesMustBeUnique = 40061,
MissingPermissions = 50001,
InvalidAccountType = 50002,
CannotExecuteForDM = 50003,
@@ -148,6 +152,7 @@ namespace Discord
InvalidMessageType = 50068,
PaymentSourceRequiredForGift = 50070,
CannotDeleteRequiredCommunityChannel = 50074,
CannotEditStickersWithinAMessage = 50080,
InvalidSticker = 50081,
CannotExecuteOnArchivedThread = 50083,
InvalidThreadNotificationSettings = 50084,


+ 10
- 0
src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs View File

@@ -17,6 +17,16 @@ namespace Discord
/// </summary>
public Optional<bool> IsDefaultPermission { get; set; }

/// <summary>
/// Gets or sets whether or not this command can be used in DMs.
/// </summary>
public Optional<bool> IsDMEnabled { get; set; }

/// <summary>
/// Gets or sets the default permissions required by a user to execute this application command.
/// </summary>
public Optional<GuildPermission> DefaultMemberPermissions { get; set; }

internal ApplicationCommandProperties() { }
}
}

+ 35
- 1
src/Discord.Net.Core/Entities/Interactions/ContextMenus/MessageCommandBuilder.cs View File

@@ -31,6 +31,16 @@ namespace Discord
/// </summary>
public bool IsDefaultPermission { get; set; } = true;

/// <summary>
/// Gets or sets whether or not this command can be used in DMs.
/// </summary>
public bool IsDMEnabled { get; set; } = true;

/// <summary>
/// Gets or sets the default permission required to use this slash command.
/// </summary>
public GuildPermission? DefaultMemberPermissions { get; set; }

private string _name;

/// <summary>
@@ -44,7 +54,9 @@ namespace Discord
var props = new MessageCommandProperties
{
Name = Name,
IsDefaultPermission = IsDefaultPermission
IsDefaultPermission = IsDefaultPermission,
IsDMEnabled = IsDMEnabled,
DefaultMemberPermissions = DefaultMemberPermissions ?? Optional<GuildPermission>.Unspecified
};

return props;
@@ -73,5 +85,27 @@ namespace Discord
IsDefaultPermission = isDefaultPermission;
return this;
}

/// <summary>
/// Sets whether or not this command can be used in dms
/// </summary>
/// <param name="permission"><see langword="true"/> if the command is available in dms, otherwise <see langword="false"/>.</param>
/// <returns>The current builder.</returns>
public MessageCommandBuilder WithDMPermission(bool permission)
{
IsDMEnabled = permission;
return this;
}

/// <summary>
/// Sets the default member permissions required to use this application command.
/// </summary>
/// <param name="permissions">The permissions required to use this command.</param>
/// <returns>The current builder.</returns>
public MessageCommandBuilder WithDefaultMemberPermissions(GuildPermission? permissions)
{
DefaultMemberPermissions = permissions;
return this;
}
}
}

+ 35
- 1
src/Discord.Net.Core/Entities/Interactions/ContextMenus/UserCommandBuilder.cs View File

@@ -31,6 +31,16 @@ namespace Discord
/// </summary>
public bool IsDefaultPermission { get; set; } = true;

/// <summary>
/// Gets or sets whether or not this command can be used in DMs.
/// </summary>
public bool IsDMEnabled { get; set; } = true;

/// <summary>
/// Gets or sets the default permission required to use this slash command.
/// </summary>
public GuildPermission? DefaultMemberPermissions { get; set; }

private string _name;

/// <summary>
@@ -42,7 +52,9 @@ namespace Discord
var props = new UserCommandProperties
{
Name = Name,
IsDefaultPermission = IsDefaultPermission
IsDefaultPermission = IsDefaultPermission,
IsDMEnabled = IsDMEnabled,
DefaultMemberPermissions = DefaultMemberPermissions ?? Optional<GuildPermission>.Unspecified
};

return props;
@@ -71,5 +83,27 @@ namespace Discord
IsDefaultPermission = isDefaultPermission;
return this;
}

/// <summary>
/// Sets whether or not this command can be used in dms
/// </summary>
/// <param name="permission"><see langword="true"/> if the command is available in dms, otherwise <see langword="false"/>.</param>
/// <returns>The current builder.</returns>
public UserCommandBuilder WithDMPermission(bool permission)
{
IsDMEnabled = permission;
return this;
}

/// <summary>
/// Sets the default member permissions required to use this application command.
/// </summary>
/// <param name="permissions">The permissions required to use this command.</param>
/// <returns>The current builder.</returns>
public UserCommandBuilder WithDefaultMemberPermissions(GuildPermission? permissions)
{
DefaultMemberPermissions = permissions;
return this;
}
}
}

+ 13
- 0
src/Discord.Net.Core/Entities/Interactions/IApplicationCommand.cs View File

@@ -34,6 +34,19 @@ namespace Discord
/// </summary>
bool IsDefaultPermission { get; }

/// <summary>
/// Indicates whether the command is available in DMs with the app.
/// </summary>
/// <remarks>
/// Only for globally-scoped commands.
/// </remarks>
bool IsEnabledInDm { get; }

/// <summary>
/// Set of default <see cref="GuildPermission"/> required to invoke the command.
/// </summary>
GuildPermissions DefaultMemberPermissions { get; }

/// <summary>
/// Gets a collection of options for this application command.
/// </summary>


+ 42
- 19
src/Discord.Net.Core/Entities/Interactions/SlashCommands/SlashCommandBuilder.cs View File

@@ -81,6 +81,16 @@ namespace Discord
/// </summary>
public bool IsDefaultPermission { get; set; } = true;

/// <summary>
/// Gets or sets whether or not this command can be used in DMs.
/// </summary>
public bool IsDMEnabled { get; set; } = true;

/// <summary>
/// Gets or sets the default permission required to use this slash command.
/// </summary>
public GuildPermission? DefaultMemberPermissions { get; set; }

private string _name;
private string _description;
private List<SlashCommandOptionBuilder> _options;
@@ -96,6 +106,8 @@ namespace Discord
Name = Name,
Description = Description,
IsDefaultPermission = IsDefaultPermission,
IsDMEnabled = IsDMEnabled,
DefaultMemberPermissions = DefaultMemberPermissions ?? Optional<GuildPermission>.Unspecified
};

if (Options != null && Options.Any())
@@ -145,6 +157,28 @@ namespace Discord
return this;
}

/// <summary>
/// Sets whether or not this command can be used in dms
/// </summary>
/// <param name="permission"><see langword="true"/> if the command is available in dms, otherwise <see langword="false"/>.</param>
/// <returns>The current builder.</returns>
public SlashCommandBuilder WithDMPermission(bool permission)
{
IsDMEnabled = permission;
return this;
}

/// <summary>
/// Sets the default member permissions required to use this application command.
/// </summary>
/// <param name="permissions">The permissions required to use this command.</param>
/// <returns>The current builder.</returns>
public SlashCommandBuilder WithDefaultMemberPermissions(GuildPermission? permissions)
{
DefaultMemberPermissions = permissions;
return this;
}

/// <summary>
/// Adds an option to the current slash command.
/// </summary>
@@ -164,21 +198,13 @@ namespace Discord
string description, bool? isRequired = null, bool? isDefault = null, bool isAutocomplete = false, double? minValue = null, double? maxValue = null,
List<SlashCommandOptionBuilder> options = null, List<ChannelType> channelTypes = null, params ApplicationCommandOptionChoiceProperties[] choices)
{
// Make sure the name matches the requirements from discord
Preconditions.NotNullOrEmpty(name, nameof(name));
Preconditions.AtLeast(name.Length, 1, nameof(name));
Preconditions.AtMost(name.Length, MaxNameLength, nameof(name));
Preconditions.Options(name, description);

// Discord updated the docs, this regex prevents special characters like @!$%( and s p a c e s.. etc,
// https://discord.com/developers/docs/interactions/slash-commands#applicationcommand
if (!Regex.IsMatch(name, @"^[\w-]{1,32}$"))
throw new ArgumentException("Command name cannot contain any special characters or whitespaces!", nameof(name));

// same with description
Preconditions.NotNullOrEmpty(description, nameof(description));
Preconditions.AtLeast(description.Length, 1, nameof(description));
Preconditions.AtMost(description.Length, MaxDescriptionLength, nameof(description));

// make sure theres only one option with default set to true
if (isDefault == true && Options?.Any(x => x.IsDefault == true) == true)
throw new ArgumentException("There can only be one command option with default set to true!", nameof(isDefault));
@@ -214,6 +240,7 @@ namespace Discord
throw new InvalidOperationException($"Cannot have more than {MaxOptionsCount} options!");

Preconditions.NotNull(option, nameof(option));
Preconditions.Options(option.Name, option.Description); // this is a double-check when this method is called via AddOption(string name... )

Options.Add(option);
return this;
@@ -236,6 +263,9 @@ namespace Discord
if (Options.Count + options.Length > MaxOptionsCount)
throw new ArgumentOutOfRangeException(nameof(options), $"Cannot have more than {MaxOptionsCount} options!");

foreach (var option in options)
Preconditions.Options(option.Name, option.Description);

Options.AddRange(options);
return this;
}
@@ -379,7 +409,7 @@ namespace Discord
MinValue = MinValue,
MaxValue = MaxValue
};
}
}

/// <summary>
/// Adds an option to the current slash command.
@@ -400,21 +430,13 @@ namespace Discord
string description, bool? isRequired = null, bool isDefault = false, bool isAutocomplete = false, double? minValue = null, double? maxValue = null,
List<SlashCommandOptionBuilder> options = null, List<ChannelType> channelTypes = null, params ApplicationCommandOptionChoiceProperties[] choices)
{
// Make sure the name matches the requirements from discord
Preconditions.NotNullOrEmpty(name, nameof(name));
Preconditions.AtLeast(name.Length, 1, nameof(name));
Preconditions.AtMost(name.Length, SlashCommandBuilder.MaxNameLength, nameof(name));
Preconditions.Options(name, description);

// Discord updated the docs, this regex prevents special characters like @!$%( and s p a c e s.. etc,
// https://discord.com/developers/docs/interactions/slash-commands#applicationcommand
if (!Regex.IsMatch(name, @"^[\w-]{1,32}$"))
throw new ArgumentException("Command name cannot contain any special characters or whitespaces!", nameof(name));

// same with description
Preconditions.NotNullOrEmpty(description, nameof(description));
Preconditions.AtLeast(description.Length, 1, nameof(description));
Preconditions.AtMost(description.Length, SlashCommandBuilder.MaxDescriptionLength, nameof(description));

// make sure theres only one option with default set to true
if (isDefault && Options?.Any(x => x.IsDefault == true) == true)
throw new ArgumentException("There can only be one command option with default set to true!", nameof(isDefault));
@@ -449,6 +471,7 @@ namespace Discord
throw new InvalidOperationException($"There can only be {SlashCommandBuilder.MaxOptionsCount} options per sub command group!");

Preconditions.NotNull(option, nameof(option));
Preconditions.Options(option.Name, option.Description); // double check again

Options.Add(option);
return this;


+ 24
- 0
src/Discord.Net.Core/Interactions/IRouteMatchContainer.cs View File

@@ -0,0 +1,24 @@
using System.Collections.Generic;

namespace Discord
{
/// <summary>
/// Represents a container for temporarily storing CustomId wild card matches of a component.
/// </summary>
public interface IRouteMatchContainer
{
/// <summary>
/// Gets the collection of captured route segments in this container.
/// </summary>
/// <returns>
/// A collection of captured route segments.
///</returns>
IEnumerable<IRouteSegmentMatch> SegmentMatches { get; }

/// <summary>
/// Sets the <see cref="SegmentMatches"/> property of this container.
/// </summary>
/// <param name="segmentMatches">The collection of captured route segments.</param>
void SetSegmentMatches(IEnumerable<IRouteSegmentMatch> segmentMatches);
}
}

+ 16
- 0
src/Discord.Net.Core/Interactions/IRouteSegmentMatch.cs View File

@@ -0,0 +1,16 @@
namespace Discord
{
/// <summary>
/// Represents an object for storing a CustomId wild card match.
/// </summary>
public interface IRouteSegmentMatch
{
/// <summary>
/// Gets the captured value of this wild card match.
/// </summary>
/// <returns>
/// The value of this wild card.
/// </returns>
string Value { get; }
}
}

+ 16
- 0
src/Discord.Net.Core/Interactions/RouteSegmentMatch.cs View File

@@ -0,0 +1,16 @@
namespace Discord
{
/// <summary>
/// Represents an object for storing a CustomId wild card match.
/// </summary>
internal record RouteSegmentMatch : IRouteSegmentMatch
{
/// <inheritdoc/>
public string Value { get; }

public RouteSegmentMatch(string value)
{
Value = value;
}
}
}

+ 17
- 0
src/Discord.Net.Core/Utils/Preconditions.cs View File

@@ -297,5 +297,22 @@ namespace Discord
}
}
#endregion

#region SlashCommandOptions

/// <exception cref="ArgumentNullException"><paramref name="description"/> or <paramref name="name"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="description"/> or <paramref name="name"/> are either empty or their length exceed limits.</exception>
public static void Options(string name, string description)
{
// Make sure the name matches the requirements from discord
NotNullOrEmpty(name, nameof(name));
NotNullOrEmpty(description, nameof(description));
AtLeast(name.Length, 1, nameof(name));
AtMost(name.Length, SlashCommandBuilder.MaxNameLength, nameof(name));
AtLeast(description.Length, 1, nameof(description));
AtMost(description.Length, SlashCommandBuilder.MaxDescriptionLength, nameof(description));
}

#endregion
}
}

+ 25
- 0
src/Discord.Net.Interactions/Attributes/DefaultMemberPermissionAttribute.cs View File

@@ -0,0 +1,25 @@
using System;

namespace Discord.Interactions
{
/// <summary>
/// Sets the <see cref="IApplicationCommandInfo.DefaultMemberPermissions"/> of an application command or module.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class DefaultMemberPermissionsAttribute : Attribute
{
/// <summary>
/// Gets the default permission required to use this command.
/// </summary>
public GuildPermission Permissions { get; }

/// <summary>
/// Sets the <see cref="IApplicationCommandInfo.DefaultMemberPermissions"/> of an application command or module.
/// </summary>
/// <param name="permissions">The default permission required to use this command.</param>
public DefaultMemberPermissionsAttribute(GuildPermission permissions)
{
Permissions = permissions;
}
}
}

+ 1
- 0
src/Discord.Net.Interactions/Attributes/DefaultPermissionAttribute.cs View File

@@ -6,6 +6,7 @@ namespace Discord.Interactions
/// Set the "Default Permission" property of an Application Command.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
[Obsolete($"Soon to be deprecated, use Permissions-v2 attributes like {nameof(EnabledInDmAttribute)} and {nameof(DefaultMemberPermissionsAttribute)}")]
public class DefaultPermissionAttribute : Attribute
{
/// <summary>


+ 25
- 0
src/Discord.Net.Interactions/Attributes/EnabledInDmAttribute.cs View File

@@ -0,0 +1,25 @@
using System;

namespace Discord.Interactions
{
/// <summary>
/// Sets the <see cref="IApplicationCommandInfo.IsEnabledInDm"/> property of an application command or module.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class EnabledInDmAttribute : Attribute
{
/// <summary>
/// Gets whether or not this command can be used in DMs.
/// </summary>
public bool IsEnabled { get; }

/// <summary>
/// Sets the <see cref="IApplicationCommandInfo.IsEnabledInDm"/> property of an application command or module.
/// </summary>
/// <param name="isEnabled">Whether or not this command can be used in DMs.</param>
public EnabledInDmAttribute(bool isEnabled)
{
IsEnabled = isEnabled;
}
}
}

+ 38
- 0
src/Discord.Net.Interactions/Builders/Commands/ContextCommandBuilder.cs View File

@@ -17,8 +17,19 @@ namespace Discord.Interactions.Builders
/// <summary>
/// Gets the default permission of this command.
/// </summary>
[Obsolete($"To be deprecated soon, use {nameof(IsEnabledInDm)} and {nameof(DefaultMemberPermissions)} instead.")]
public bool DefaultPermission { get; set; } = true;

/// <summary>
/// Gets whether this command can be used in DMs.
/// </summary>
public bool IsEnabledInDm { get; set; } = true;

/// <summary>
/// Gets the default permissions needed for executing this command.
/// </summary>
public GuildPermission? DefaultMemberPermissions { get; set; } = null;

internal ContextCommandBuilder (ModuleBuilder module) : base(module) { }

/// <summary>
@@ -49,6 +60,7 @@ namespace Discord.Interactions.Builders
/// <returns>
/// The builder instance.
/// </returns>
[Obsolete($"To be deprecated soon, use {nameof(SetEnabledInDm)} and {nameof(WithDefaultMemberPermissions)} instead.")]
public ContextCommandBuilder SetDefaultPermission (bool defaultPermision)
{
DefaultPermission = defaultPermision;
@@ -70,6 +82,32 @@ namespace Discord.Interactions.Builders
return this;
}

/// <summary>
/// Sets <see cref="IsEnabledInDm"/>.
/// </summary>
/// <param name="isEnabled">New value of the <see cref="IsEnabledInDm"/>.</param>
/// <returns>
/// The builder instance.
/// </returns>
public ContextCommandBuilder SetEnabledInDm(bool isEnabled)
{
IsEnabledInDm = isEnabled;
return this;
}

/// <summary>
/// Sets <see cref="DefaultMemberPermissions"/>.
/// </summary>
/// <param name="permissions">New value of the <see cref="DefaultMemberPermissions"/>.</param>
/// <returns>
/// The builder instance.
/// </returns>
public ContextCommandBuilder WithDefaultMemberPermissions(GuildPermission permissions)
{
DefaultMemberPermissions = permissions;
return this;
}

internal override ContextCommandInfo Build (ModuleInfo module, InteractionService commandService) =>
ContextCommandInfo.Create(this, module, commandService);
}


+ 38
- 0
src/Discord.Net.Interactions/Builders/Commands/SlashCommandBuilder.cs View File

@@ -17,8 +17,19 @@ namespace Discord.Interactions.Builders
/// <summary>
/// Gets and sets the default permission of this command.
/// </summary>
[Obsolete($"To be deprecated soon, use {nameof(IsEnabledInDm)} and {nameof(DefaultMemberPermissions)} instead.")]
public bool DefaultPermission { get; set; } = true;

/// <summary>
/// Gets whether this command can be used in DMs.
/// </summary>
public bool IsEnabledInDm { get; set; } = true;

/// <summary>
/// Gets the default permissions needed for executing this command.
/// </summary>
public GuildPermission? DefaultMemberPermissions { get; set; } = null;

internal SlashCommandBuilder (ModuleBuilder module) : base(module) { }

/// <summary>
@@ -49,6 +60,7 @@ namespace Discord.Interactions.Builders
/// <returns>
/// The builder instance.
/// </returns>
[Obsolete($"To be deprecated soon, use {nameof(SetEnabledInDm)} and {nameof(WithDefaultMemberPermissions)} instead.")]
public SlashCommandBuilder WithDefaultPermission (bool permission)
{
DefaultPermission = permission;
@@ -70,6 +82,32 @@ namespace Discord.Interactions.Builders
return this;
}

/// <summary>
/// Sets <see cref="IsEnabledInDm"/>.
/// </summary>
/// <param name="isEnabled">New value of the <see cref="IsEnabledInDm"/>.</param>
/// <returns>
/// The builder instance.
/// </returns>
public SlashCommandBuilder SetEnabledInDm(bool isEnabled)
{
IsEnabledInDm = isEnabled;
return this;
}

/// <summary>
/// Sets <see cref="DefaultMemberPermissions"/>.
/// </summary>
/// <param name="permissions">New value of the <see cref="DefaultMemberPermissions"/>.</param>
/// <returns>
/// The builder instance.
/// </returns>
public SlashCommandBuilder WithDefaultMemberPermissions(GuildPermission permissions)
{
DefaultMemberPermissions = permissions;
return this;
}

internal override SlashCommandInfo Build (ModuleInfo module, InteractionService commandService) =>
new SlashCommandInfo(this, module, commandService);
}


+ 38
- 0
src/Discord.Net.Interactions/Builders/ModuleBuilder.cs View File

@@ -51,8 +51,19 @@ namespace Discord.Interactions.Builders
/// <summary>
/// Gets and sets the default permission of this module.
/// </summary>
[Obsolete($"To be deprecated soon, use {nameof(IsEnabledInDm)} and {nameof(DefaultMemberPermissions)} instead.")]
public bool DefaultPermission { get; set; } = true;

/// <summary>
/// Gets whether this command can be used in DMs.
/// </summary>
public bool IsEnabledInDm { get; set; } = true;

/// <summary>
/// Gets the default permissions needed for executing this command.
/// </summary>
public GuildPermission? DefaultMemberPermissions { get; set; } = null;

/// <summary>
/// Gets and sets whether this has a <see cref="DontAutoRegisterAttribute"/>.
/// </summary>
@@ -159,12 +170,39 @@ namespace Discord.Interactions.Builders
/// <returns>
/// The builder instance.
/// </returns>
[Obsolete($"To be deprecated soon, use {nameof(SetEnabledInDm)} and {nameof(WithDefaultMemberPermissions)} instead.")]
public ModuleBuilder WithDefaultPermission (bool permission)
{
DefaultPermission = permission;
return this;
}

/// <summary>
/// Sets <see cref="IsEnabledInDm"/>.
/// </summary>
/// <param name="isEnabled">New value of the <see cref="IsEnabledInDm"/>.</param>
/// <returns>
/// The builder instance.
/// </returns>
public ModuleBuilder SetEnabledInDm(bool isEnabled)
{
IsEnabledInDm = isEnabled;
return this;
}

/// <summary>
/// Sets <see cref="DefaultMemberPermissions"/>.
/// </summary>
/// <param name="permissions">New value of the <see cref="DefaultMemberPermissions"/>.</param>
/// <returns>
/// The builder instance.
/// </returns>
public ModuleBuilder WithDefaultMemberPermissions(GuildPermission permissions)
{
DefaultMemberPermissions = permissions;
return this;
}

/// <summary>
/// Adds attributes to <see cref="Attributes"/>.
/// </summary>


+ 30
- 0
src/Discord.Net.Interactions/Builders/ModuleClassBuilder.cs View File

@@ -85,6 +85,16 @@ namespace Discord.Interactions.Builders
builder.DefaultPermission = defPermission.IsDefaultPermission;
}
break;
case EnabledInDmAttribute enabledInDm:
{
builder.IsEnabledInDm = enabledInDm.IsEnabled;
}
break;
case DefaultMemberPermissionsAttribute memberPermission:
{
builder.DefaultMemberPermissions = memberPermission.Permissions;
}
break;
case PreconditionAttribute precondition:
builder.AddPreconditions(precondition);
break;
@@ -169,6 +179,16 @@ namespace Discord.Interactions.Builders
builder.DefaultPermission = defaultPermission.IsDefaultPermission;
}
break;
case EnabledInDmAttribute enabledInDm:
{
builder.IsEnabledInDm = enabledInDm.IsEnabled;
}
break;
case DefaultMemberPermissionsAttribute memberPermission:
{
builder.DefaultMemberPermissions = memberPermission.Permissions;
}
break;
case PreconditionAttribute precondition:
builder.WithPreconditions(precondition);
break;
@@ -211,6 +231,16 @@ namespace Discord.Interactions.Builders
builder.DefaultPermission = defaultPermission.IsDefaultPermission;
}
break;
case EnabledInDmAttribute enabledInDm:
{
builder.IsEnabledInDm = enabledInDm.IsEnabled;
}
break;
case DefaultMemberPermissionsAttribute memberPermission:
{
builder.DefaultMemberPermissions = memberPermission.Permissions;
}
break;
case PreconditionAttribute precondition:
builder.WithPreconditions(precondition);
break;


+ 8
- 0
src/Discord.Net.Interactions/Info/Commands/ContextCommands/ContextCommandInfo.cs View File

@@ -17,6 +17,12 @@ namespace Discord.Interactions
/// <inheritdoc/>
public bool DefaultPermission { get; }

/// <inheritdoc/>
public bool IsEnabledInDm { get; }

/// <inheritdoc/>
public GuildPermission? DefaultMemberPermissions { get; }

/// <inheritdoc/>
public override IReadOnlyCollection<CommandParameterInfo> Parameters { get; }

@@ -31,6 +37,8 @@ namespace Discord.Interactions
{
CommandType = builder.CommandType;
DefaultPermission = builder.DefaultPermission;
IsEnabledInDm = builder.IsEnabledInDm;
DefaultMemberPermissions = builder.DefaultMemberPermissions;
Parameters = builder.Parameters.Select(x => x.Build(this)).ToImmutableArray();
}



+ 8
- 0
src/Discord.Net.Interactions/Info/Commands/SlashCommandInfo.cs View File

@@ -26,6 +26,12 @@ namespace Discord.Interactions
/// <inheritdoc/>
public bool DefaultPermission { get; }

/// <inheritdoc/>
public bool IsEnabledInDm { get; }

/// <inheritdoc/>
public GuildPermission? DefaultMemberPermissions { get; }

/// <inheritdoc/>
public override IReadOnlyCollection<SlashCommandParameterInfo> Parameters { get; }

@@ -41,6 +47,8 @@ namespace Discord.Interactions
{
Description = builder.Description;
DefaultPermission = builder.DefaultPermission;
IsEnabledInDm = builder.IsEnabledInDm;
DefaultMemberPermissions = builder.DefaultMemberPermissions;
Parameters = builder.Parameters.Select(x => x.Build(this)).ToImmutableArray();
FlattenedParameters = FlattenParameters(Parameters).ToImmutableArray();



+ 13
- 0
src/Discord.Net.Interactions/Info/IApplicationCommandInfo.cs View File

@@ -1,3 +1,5 @@
using System;

namespace Discord.Interactions
{
/// <summary>
@@ -18,6 +20,17 @@ namespace Discord.Interactions
/// <summary>
/// Gets the DefaultPermission of this command.
/// </summary>
[Obsolete($"To be deprecated soon, use {nameof(IsEnabledInDm)} and {nameof(DefaultMemberPermissions)} instead.")]
bool DefaultPermission { get; }

/// <summary>
/// Gets whether this command can be used in DMs.
/// </summary>
public bool IsEnabledInDm { get; }

/// <summary>
/// Gets the default permissions needed for executing this command.
/// </summary>
public GuildPermission? DefaultMemberPermissions { get; }
}
}

+ 28
- 0
src/Discord.Net.Interactions/Info/ModuleInfo.cs View File

@@ -41,8 +41,19 @@ namespace Discord.Interactions
/// <summary>
/// Gets the default Permission of this module.
/// </summary>
[Obsolete($"To be deprecated soon, use {nameof(IsEnabledInDm)} and {nameof(DefaultMemberPermissions)} instead.")]
public bool DefaultPermission { get; }

/// <summary>
/// Gets whether this command can be used in DMs.
/// </summary>
public bool IsEnabledInDm { get; }

/// <summary>
/// Gets the default permissions needed for executing this command.
/// </summary>
public GuildPermission? DefaultMemberPermissions { get; }

/// <summary>
/// Gets the collection of Sub Modules of this module.
/// </summary>
@@ -110,6 +121,8 @@ namespace Discord.Interactions
Description = builder.Description;
Parent = parent;
DefaultPermission = builder.DefaultPermission;
IsEnabledInDm = builder.IsEnabledInDm;
DefaultMemberPermissions = BuildDefaultMemberPermissions(builder);
SlashCommands = BuildSlashCommands(builder).ToImmutableArray();
ContextCommands = BuildContextCommands(builder).ToImmutableArray();
ComponentCommands = BuildComponentCommands(builder).ToImmutableArray();
@@ -226,5 +239,20 @@ namespace Discord.Interactions
}
return true;
}

private static GuildPermission? BuildDefaultMemberPermissions(ModuleBuilder builder)
{
var permissions = builder.DefaultMemberPermissions;

var parent = builder.Parent;

while (parent != null)
{
permissions = (permissions ?? 0) | (parent.DefaultMemberPermissions ?? 0);
parent = parent.Parent;
}

return permissions;
}
}
}

+ 13
- 1
src/Discord.Net.Interactions/InteractionContext.cs View File

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

namespace Discord.Interactions
{
/// <inheritdoc cref="IInteractionContext"/>
public class InteractionContext : IInteractionContext
public class InteractionContext : IInteractionContext, IRouteMatchContainer
{
/// <inheritdoc/>
public IDiscordClient Client { get; }
@@ -13,6 +16,8 @@ namespace Discord.Interactions
public IUser User { get; }
/// <inheritdoc/>
public IDiscordInteraction Interaction { get; }
/// <inheritdoc cref="IRouteMatchContainer.SegmentMatches"/>
public IReadOnlyCollection<IRouteSegmentMatch> SegmentMatches { get; private set; }

/// <summary>
/// Initializes a new <see cref="SocketInteractionContext{TInteraction}"/>.
@@ -30,5 +35,12 @@ namespace Discord.Interactions
User = interaction.User;
Interaction = interaction;
}

/// <inheritdoc/>
public void SetSegmentMatches(IEnumerable<IRouteSegmentMatch> segmentMatches) => SegmentMatches = segmentMatches.ToImmutableArray();

//IRouteMatchContainer
/// <inheritdoc/>
IEnumerable<IRouteSegmentMatch> IRouteMatchContainer.SegmentMatches => SegmentMatches;
}
}

+ 24
- 0
src/Discord.Net.Interactions/InteractionService.cs View File

@@ -775,6 +775,9 @@ namespace Discord.Interactions
await _componentCommandExecutedEvent.InvokeAsync(null, context, result).ConfigureAwait(false);
return result;
}

SetMatchesIfApplicable(context, result);

return await result.Command.ExecuteAsync(context, services, result.RegexCaptureGroups).ConfigureAwait(false);
}

@@ -819,9 +822,30 @@ namespace Discord.Interactions
await _componentCommandExecutedEvent.InvokeAsync(null, context, result).ConfigureAwait(false);
return result;
}

SetMatchesIfApplicable(context, result);

return await result.Command.ExecuteAsync(context, services, result.RegexCaptureGroups).ConfigureAwait(false);
}

private static void SetMatchesIfApplicable<T>(IInteractionContext context, SearchResult<T> searchResult)
where T : class, ICommandInfo
{
if (!searchResult.Command.SupportsWildCards || context is not IRouteMatchContainer matchContainer)
return;

if (searchResult.RegexCaptureGroups?.Length > 0)
{
var matches = new RouteSegmentMatch[searchResult.RegexCaptureGroups.Length];
for (var i = 0; i < searchResult.RegexCaptureGroups.Length; i++)
matches[i] = new RouteSegmentMatch(searchResult.RegexCaptureGroups[i]);

matchContainer.SetSegmentMatches(matches);
}
else
matchContainer.SetSegmentMatches(Array.Empty<RouteSegmentMatch>());
}

internal TypeConverter GetTypeConverter(Type type, IServiceProvider services = null)
=> _typeConverterMap.Get(type, services);



+ 18
- 3
src/Discord.Net.Interactions/Utilities/ApplicationCommandRestUtil.cs View File

@@ -40,7 +40,8 @@ namespace Discord.Interactions
{
Name = commandInfo.Name,
Description = commandInfo.Description,
IsDefaultPermission = commandInfo.DefaultPermission,
IsDMEnabled = commandInfo.IsEnabledInDm,
DefaultMemberPermissions = (commandInfo.DefaultMemberPermissions ?? 0) | (commandInfo.Module.DefaultMemberPermissions ?? 0)
}.Build();

if (commandInfo.Parameters.Count > SlashCommandBuilder.MaxOptionsCount)
@@ -64,8 +65,20 @@ namespace Discord.Interactions
public static ApplicationCommandProperties ToApplicationCommandProps(this ContextCommandInfo commandInfo)
=> commandInfo.CommandType switch
{
ApplicationCommandType.Message => new MessageCommandBuilder { Name = commandInfo.Name, IsDefaultPermission = commandInfo.DefaultPermission}.Build(),
ApplicationCommandType.User => new UserCommandBuilder { Name = commandInfo.Name, IsDefaultPermission=commandInfo.DefaultPermission}.Build(),
ApplicationCommandType.Message => new MessageCommandBuilder
{
Name = commandInfo.Name,
IsDefaultPermission = commandInfo.DefaultPermission,
DefaultMemberPermissions = (commandInfo.DefaultMemberPermissions ?? 0) | (commandInfo.Module.DefaultMemberPermissions ?? 0),
IsDMEnabled = commandInfo.IsEnabledInDm
}.Build(),
ApplicationCommandType.User => new UserCommandBuilder
{
Name = commandInfo.Name,
IsDefaultPermission = commandInfo.DefaultPermission,
DefaultMemberPermissions = (commandInfo.DefaultMemberPermissions ?? 0) | (commandInfo.Module.DefaultMemberPermissions ?? 0),
IsDMEnabled = commandInfo.IsEnabledInDm
}.Build(),
_ => throw new InvalidOperationException($"{commandInfo.CommandType} isn't a supported command type.")
};
#endregion
@@ -113,6 +126,8 @@ namespace Discord.Interactions
Name = moduleInfo.SlashGroupName,
Description = moduleInfo.Description,
IsDefaultPermission = moduleInfo.DefaultPermission,
IsDMEnabled = moduleInfo.IsEnabledInDm,
DefaultMemberPermissions = moduleInfo.DefaultMemberPermissions
}.Build();

if (options.Count > SlashCommandBuilder.MaxOptionsCount)


+ 7
- 0
src/Discord.Net.Rest/API/Common/ApplicationCommand.cs View File

@@ -24,5 +24,12 @@ namespace Discord.API

[JsonProperty("default_permission")]
public Optional<bool> DefaultPermissions { get; set; }

// V2 Permissions
[JsonProperty("dm_permission")]
public Optional<bool?> DmPermission { get; set; }

[JsonProperty("default_member_permissions")]
public Optional<GuildPermission?> DefaultMemberPermission { get; set; }
}
}

+ 6
- 0
src/Discord.Net.Rest/API/Rest/CreateApplicationCommandParams.cs View File

@@ -19,6 +19,12 @@ namespace Discord.API.Rest
[JsonProperty("default_permission")]
public Optional<bool> DefaultPermission { get; set; }

[JsonProperty("dm_permission")]
public Optional<bool?> DmPermission { get; set; }

[JsonProperty("default_member_permissions")]
public Optional<GuildPermission?> DefaultMemberPermission { get; set; }

public CreateApplicationCommandParams() { }
public CreateApplicationCommandParams(string name, string description, ApplicationCommandType type, ApplicationCommandOption[] options = null)
{


+ 21
- 4
src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs View File

@@ -100,7 +100,12 @@ namespace Discord.Rest
Type = arg.Type,
DefaultPermission = arg.IsDefaultPermission.IsSpecified
? arg.IsDefaultPermission.Value
: Optional<bool>.Unspecified
: Optional<bool>.Unspecified,

// TODO: better conversion to nullable optionals
DefaultMemberPermission = arg.DefaultMemberPermissions.ToNullable(),
DmPermission = arg.IsDMEnabled.ToNullable()
};

if (arg is SlashCommandProperties slashProps)
@@ -134,7 +139,11 @@ namespace Discord.Rest
Type = arg.Type,
DefaultPermission = arg.IsDefaultPermission.IsSpecified
? arg.IsDefaultPermission.Value
: Optional<bool>.Unspecified
: Optional<bool>.Unspecified,

// TODO: better conversion to nullable optionals
DefaultMemberPermission = arg.DefaultMemberPermissions.ToNullable(),
DmPermission = arg.IsDMEnabled.ToNullable()
};

if (arg is SlashCommandProperties slashProps)
@@ -171,7 +180,11 @@ namespace Discord.Rest
Type = arg.Type,
DefaultPermission = arg.IsDefaultPermission.IsSpecified
? arg.IsDefaultPermission.Value
: Optional<bool>.Unspecified
: Optional<bool>.Unspecified,

// TODO: better conversion to nullable optionals
DefaultMemberPermission = arg.DefaultMemberPermissions.ToNullable(),
DmPermission = arg.IsDMEnabled.ToNullable()
};

if (arg is SlashCommandProperties slashProps)
@@ -285,7 +298,11 @@ namespace Discord.Rest
Type = arg.Type,
DefaultPermission = arg.IsDefaultPermission.IsSpecified
? arg.IsDefaultPermission.Value
: Optional<bool>.Unspecified
: Optional<bool>.Unspecified,

// TODO: better conversion to nullable optionals
DefaultMemberPermission = arg.DefaultMemberPermissions.ToNullable(),
DmPermission = arg.IsDMEnabled.ToNullable()
};

if (arg is SlashCommandProperties slashProps)


+ 9
- 0
src/Discord.Net.Rest/Entities/Interactions/RestApplicationCommand.cs View File

@@ -27,6 +27,12 @@ namespace Discord.Rest
/// <inheritdoc/>
public bool IsDefaultPermission { get; private set; }

/// <inheritdoc/>
public bool IsEnabledInDm { get; private set; }

/// <inheritdoc/>
public GuildPermissions DefaultMemberPermissions { get; private set; }

/// <summary>
/// Gets a collection of options for this command.
/// </summary>
@@ -57,6 +63,9 @@ namespace Discord.Rest
Options = model.Options.IsSpecified
? model.Options.Value.Select(RestApplicationCommandOption.Create).ToImmutableArray()
: ImmutableArray.Create<RestApplicationCommandOption>();

IsEnabledInDm = model.DmPermission.GetValueOrDefault(true).GetValueOrDefault(true);
DefaultMemberPermissions = new GuildPermissions((ulong)model.DefaultMemberPermission.GetValueOrDefault(0).GetValueOrDefault(0));
}

/// <inheritdoc/>


+ 13
- 1
src/Discord.Net.Rest/Interactions/RestInteractionContext.cs View File

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

namespace Discord.Rest
@@ -6,7 +8,7 @@ namespace Discord.Rest
/// <summary>
/// Represents a Rest based context of an <see cref="IDiscordInteraction"/>.
/// </summary>
public class RestInteractionContext<TInteraction> : IRestInteractionContext
public class RestInteractionContext<TInteraction> : IRestInteractionContext, IRouteMatchContainer
where TInteraction : RestInteraction
{
/// <summary>
@@ -45,6 +47,9 @@ namespace Discord.Rest
/// </remarks>
public Func<string, Task> InteractionResponseCallback { get; set; }

/// <inheritdoc cref="IRouteMatchContainer.SegmentMatches"/>
public IReadOnlyCollection<IRouteSegmentMatch> SegmentMatches { get; private set; }

/// <summary>
/// Initializes a new <see cref="RestInteractionContext{TInteraction}"/>.
/// </summary>
@@ -71,6 +76,13 @@ namespace Discord.Rest
InteractionResponseCallback = interactionResponseCallback;
}

/// <inheritdoc/>
public void SetSegmentMatches(IEnumerable<IRouteSegmentMatch> segmentMatches) => SegmentMatches = segmentMatches.ToImmutableArray();

//IRouteMatchContainer
/// <inheritdoc/>
IEnumerable<IRouteSegmentMatch> IRouteMatchContainer.SegmentMatches => SegmentMatches;

// IInterationContext
/// <inheritdoc/>
IDiscordClient IInteractionContext.Client => Client;


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

@@ -274,7 +274,7 @@ namespace Discord.API
{
["$device"] = "Discord.Net",
["$os"] = Environment.OSVersion.Platform.ToString(),
[$"browser"] = "Discord.Net"
["$browser"] = "Discord.Net"
};
var msg = new IdentifyParams()
{


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

@@ -1732,7 +1732,7 @@ namespace Discord.WebSocket
/// <inheritdoc />
ulong? IGuild.AFKChannelId => AFKChannelId;
/// <inheritdoc />
IAudioClient IGuild.AudioClient => null;
IAudioClient IGuild.AudioClient => AudioClient;
/// <inheritdoc />
bool IGuild.Available => true;
/// <inheritdoc />


+ 9
- 0
src/Discord.Net.WebSocket/Entities/Interaction/SocketBaseCommand/SocketApplicationCommand.cs View File

@@ -36,6 +36,12 @@ namespace Discord.WebSocket
/// <inheritdoc/>
public bool IsDefaultPermission { get; private set; }

/// <inheritdoc/>
public bool IsEnabledInDm { get; private set; }

/// <inheritdoc/>
public GuildPermissions DefaultMemberPermissions { get; private set; }

/// <summary>
/// Gets a collection of <see cref="SocketApplicationCommandOption"/>s for this command.
/// </summary>
@@ -86,6 +92,9 @@ namespace Discord.WebSocket
Options = model.Options.IsSpecified
? model.Options.Value.Select(SocketApplicationCommandOption.Create).ToImmutableArray()
: ImmutableArray.Create<SocketApplicationCommandOption>();

IsEnabledInDm = model.DmPermission.GetValueOrDefault(true).GetValueOrDefault(true);
DefaultMemberPermissions = new GuildPermissions((ulong)model.DefaultMemberPermission.GetValueOrDefault(0).GetValueOrDefault(0));
}

/// <inheritdoc/>


+ 13
- 1
src/Discord.Net.WebSocket/Interactions/SocketInteractionContext.cs View File

@@ -1,11 +1,13 @@
using Discord.WebSocket;
using System.Collections.Generic;
using System.Collections.Immutable;

namespace Discord.Interactions
{
/// <summary>
/// Represents a Web-Socket based context of an <see cref="IDiscordInteraction"/>.
/// </summary>
public class SocketInteractionContext<TInteraction> : IInteractionContext
public class SocketInteractionContext<TInteraction> : IInteractionContext, IRouteMatchContainer
where TInteraction : SocketInteraction
{
/// <summary>
@@ -36,6 +38,9 @@ namespace Discord.Interactions
/// </summary>
public TInteraction Interaction { get; }

/// <inheritdoc cref="IRouteMatchContainer.SegmentMatches"/>
public IReadOnlyCollection<IRouteSegmentMatch> SegmentMatches { get; private set; }

/// <summary>
/// Initializes a new <see cref="SocketInteractionContext{TInteraction}"/>.
/// </summary>
@@ -50,6 +55,13 @@ namespace Discord.Interactions
Interaction = interaction;
}

/// <inheritdoc/>
public void SetSegmentMatches(IEnumerable<IRouteSegmentMatch> segmentMatches) => SegmentMatches = segmentMatches.ToImmutableArray();

//IRouteMatchContainer
/// <inheritdoc/>
IEnumerable<IRouteSegmentMatch> IRouteMatchContainer.SegmentMatches => SegmentMatches;

// IInteractionContext
/// <inheritdoc/>
IDiscordClient IInteractionContext.Client => Client;


+ 31
- 31
src/Discord.Net/Discord.Net.nuspec View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Discord.Net</id>
<version>3.5.0$suffix$</version>
<version>3.6.1$suffix$</version>
<title>Discord.Net</title>
<authors>Discord.Net Contributors</authors>
<owners>foxbot</owners>
@@ -14,44 +14,44 @@
<iconUrl>https://github.com/RogueException/Discord.Net/raw/dev/docs/marketing/logo/PackageLogo.png</iconUrl>
<dependencies>
<group targetFramework="net6.0">
<dependency id="Discord.Net.Core" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Rest" version="3.5.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Commands" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Core" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.1$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.1$suffix$" />
</group>
<group targetFramework="net5.0">
<dependency id="Discord.Net.Core" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Rest" version="3.5.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Commands" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Core" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.1$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.1$suffix$" />
</group>
<group targetFramework="net461">
<dependency id="Discord.Net.Core" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Rest" version="3.5.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Commands" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Core" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.1$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.1$suffix$" />
</group>
<group targetFramework="netstandard2.0">
<dependency id="Discord.Net.Core" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Rest" version="3.5.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Commands" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Core" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.1$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.1$suffix$" />
</group>
<group targetFramework="netstandard2.1">
<dependency id="Discord.Net.Core" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Rest" version="3.5.0$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Commands" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.5.0$suffix$" />
<dependency id="Discord.Net.Core" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Rest" version="3.6.1$suffix$" />
<dependency id="Discord.Net.WebSocket" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Commands" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Webhook" version="3.6.1$suffix$" />
<dependency id="Discord.Net.Interactions" version="3.6.1$suffix$" />
</group>
</dependencies>
</metadata>

Loading…
Cancel
Save