@@ -1 +1,3 @@ | |||
github: quinchs | |||
open_collective: discordnet | |||
custom: https://paypal.me/quinchs |
@@ -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,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> | |||
@@ -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"> | |||
@@ -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" | |||
@@ -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]. | |||
@@ -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. | |||
@@ -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: | |||
@@ -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 | |||
@@ -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); | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
[EnabledInDm(false)] | |||
[SlashCommand("ban", "Bans a user in this guild")] | |||
public async Task BanAsync(...) | |||
{ | |||
... | |||
} |
@@ -0,0 +1,7 @@ | |||
[EnabledInDm(false)] | |||
[DefaultMemberPermissions(GuildPermission.BanMembers)] | |||
[SlashCommand("ban", "Bans a user in this guild")] | |||
public async Task BanAsync(...) | |||
{ | |||
... | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -0,0 +1,4 @@ | |||
[DefaultMemberPermissions(GuildPermission.SendMessages | GuildPermission.ViewChannels)] | |||
[SlashCommand("ping", "Pong!")] | |||
public async Task Ping() | |||
=> await RespondAsync("pong"); |
@@ -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); | |||
@@ -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 | |||
@@ -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; | |||
@@ -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 |
@@ -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, | |||
@@ -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() { } | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -35,6 +35,19 @@ namespace Discord | |||
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> | |||
IReadOnlyCollection<IApplicationCommandOption> Options { get; } | |||
@@ -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()) | |||
@@ -146,6 +158,28 @@ namespace Discord | |||
} | |||
/// <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> | |||
/// <param name="name">The name of the option to add.</param> | |||
@@ -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; | |||
@@ -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); | |||
} | |||
} |
@@ -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; } | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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 | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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> | |||
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
@@ -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); | |||
} | |||
@@ -51,9 +51,20 @@ 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> | |||
public bool DontAutoRegister { get; set; } = false; | |||
@@ -159,6 +170,7 @@ 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; | |||
@@ -166,6 +178,32 @@ namespace Discord.Interactions.Builders | |||
} | |||
/// <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> | |||
/// <param name="attributes">New attributes to be added to <see cref="Attributes"/>.</param> | |||
@@ -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; | |||
@@ -18,6 +18,12 @@ namespace Discord.Interactions | |||
public bool DefaultPermission { get; } | |||
/// <inheritdoc/> | |||
public bool IsEnabledInDm { get; } | |||
/// <inheritdoc/> | |||
public GuildPermission? DefaultMemberPermissions { get; } | |||
/// <inheritdoc/> | |||
public override IReadOnlyCollection<CommandParameterInfo> Parameters { get; } | |||
/// <inheritdoc/> | |||
@@ -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(); | |||
} | |||
@@ -27,6 +27,12 @@ namespace Discord.Interactions | |||
public bool DefaultPermission { get; } | |||
/// <inheritdoc/> | |||
public bool IsEnabledInDm { get; } | |||
/// <inheritdoc/> | |||
public GuildPermission? DefaultMemberPermissions { get; } | |||
/// <inheritdoc/> | |||
public override IReadOnlyCollection<SlashCommandParameterInfo> Parameters { get; } | |||
/// <inheritdoc/> | |||
@@ -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(); | |||
@@ -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; } | |||
} | |||
} |
@@ -41,9 +41,20 @@ 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> | |||
public IReadOnlyList<ModuleInfo> SubModules { get; } | |||
@@ -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; | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
@@ -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) | |||
@@ -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; } | |||
} | |||
} |
@@ -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) | |||
{ | |||
@@ -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) | |||
@@ -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/> | |||
@@ -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; | |||
@@ -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() | |||
{ | |||
@@ -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 /> | |||
@@ -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/> | |||
@@ -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; | |||
@@ -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> |