| @@ -18,9 +18,6 @@ The name and description help users find your command among many others, and the | |||
| Message and User commands are only a name, to the user. So try to make the name descriptive. | |||
| They're accessed by right clicking (or long press, on mobile) a user or a message, respectively. | |||
| > [!IMPORTANT] | |||
| > Context menu commands are currently not supported on mobile. | |||
| All three varieties of application commands have both Global and Guild variants. | |||
| Your global commands are available in every guild that adds your application. | |||
| You can also make commands for a specific guild; they're only available in that guild. | |||
| @@ -172,13 +172,14 @@ namespace Discord | |||
| ServerRequiresMonetization = 50097, | |||
| ServerRequiresBoosts = 50101, | |||
| RequestBodyContainsInvalidJSON = 50109, | |||
| #endregion | |||
| #region 2FA (60XXX) | |||
| FailedToResizeAssetBelowTheMaximumSize = 50138, | |||
| OwnershipCannotBeTransferredToABotUser = 50132, | |||
| AssetResizeBelowTheMaximumSize= 50138, | |||
| UploadedFileNotFound = 50146, | |||
| MissingPermissionToSendThisSticker = 50600, | |||
| #endregion | |||
| #region 2FA (60XXX) | |||
| Requires2FA = 60003, | |||
| #endregion | |||
| @@ -106,13 +106,17 @@ namespace Discord | |||
| get => _nameLocalizations; | |||
| set | |||
| { | |||
| foreach (var (locale, name) in value) | |||
| if (value != null) | |||
| { | |||
| if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$")) | |||
| throw new ArgumentException($"Invalid locale: {locale}", nameof(locale)); | |||
| foreach (var (locale, name) in value) | |||
| { | |||
| if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$")) | |||
| throw new ArgumentException($"Invalid locale: {locale}", nameof(locale)); | |||
| EnsureValidOptionName(name); | |||
| EnsureValidOptionName(name); | |||
| } | |||
| } | |||
| _nameLocalizations = value; | |||
| } | |||
| } | |||
| @@ -126,13 +130,17 @@ namespace Discord | |||
| get => _descriptionLocalizations; | |||
| set | |||
| { | |||
| foreach (var (locale, description) in value) | |||
| if (value != null) | |||
| { | |||
| if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$")) | |||
| throw new ArgumentException($"Invalid locale: {locale}", nameof(locale)); | |||
| foreach (var (locale, description) in value) | |||
| { | |||
| if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$")) | |||
| throw new ArgumentException($"Invalid locale: {locale}", nameof(locale)); | |||
| EnsureValidOptionDescription(description); | |||
| EnsureValidOptionDescription(description); | |||
| } | |||
| } | |||
| _descriptionLocalizations = value; | |||
| } | |||
| } | |||
| @@ -55,18 +55,21 @@ namespace Discord | |||
| get => _nameLocalizations; | |||
| set | |||
| { | |||
| foreach (var (locale, name) in value) | |||
| if (value != null) | |||
| { | |||
| if (!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$")) | |||
| throw new ArgumentException("Key values of the dictionary must be valid language codes."); | |||
| switch (name.Length) | |||
| foreach (var (locale, name) in value) | |||
| { | |||
| case > 100: | |||
| throw new ArgumentOutOfRangeException(nameof(value), | |||
| "Name length must be less than or equal to 100."); | |||
| case 0: | |||
| throw new ArgumentOutOfRangeException(nameof(value), "Name length must at least 1."); | |||
| if (!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$")) | |||
| throw new ArgumentException("Key values of the dictionary must be valid language codes."); | |||
| switch (name.Length) | |||
| { | |||
| case > 100: | |||
| throw new ArgumentOutOfRangeException(nameof(value), | |||
| "Name length must be less than or equal to 100."); | |||
| case 0: | |||
| throw new ArgumentOutOfRangeException(nameof(value), "Name length must at least 1."); | |||
| } | |||
| } | |||
| } | |||
| @@ -35,17 +35,21 @@ namespace Discord | |||
| get => _nameLocalizations; | |||
| set | |||
| { | |||
| foreach (var (locale, name) in value) | |||
| if (value != null) | |||
| { | |||
| if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$")) | |||
| throw new ArgumentException($"Invalid locale: {locale}", nameof(locale)); | |||
| foreach (var (locale, name) in value) | |||
| { | |||
| if (!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$")) | |||
| throw new ArgumentException($"Invalid locale: {locale}", nameof(locale)); | |||
| Preconditions.AtLeast(name.Length, 1, nameof(name)); | |||
| Preconditions.AtMost(name.Length, SlashCommandBuilder.MaxNameLength, nameof(name)); | |||
| Preconditions.AtLeast(name.Length, 1, nameof(name)); | |||
| Preconditions.AtMost(name.Length, SlashCommandBuilder.MaxNameLength, nameof(name)); | |||
| if (Type == ApplicationCommandType.Slash && !Regex.IsMatch(name, @"^[-_\p{L}\p{N}\p{IsDevanagari}\p{IsThai}]{1,32}$")) | |||
| throw new ArgumentException(@"Name must match the regex ^[-_\p{L}\p{N}\p{IsDevanagari}\p{IsThai}]{1,32}$", nameof(name)); | |||
| if (Type == ApplicationCommandType.Slash && !Regex.IsMatch(name, @"^[-_\p{L}\p{N}\p{IsDevanagari}\p{IsThai}]{1,32}$")) | |||
| throw new ArgumentException(@"Name must match the regex ^[-_\p{L}\p{N}\p{IsDevanagari}\p{IsThai}]{1,32}$", nameof(name)); | |||
| } | |||
| } | |||
| _nameLocalizations = value; | |||
| } | |||
| } | |||
| @@ -58,14 +62,18 @@ namespace Discord | |||
| get => _descriptionLocalizations; | |||
| set | |||
| { | |||
| foreach (var (locale, description) in value) | |||
| if (value != null) | |||
| { | |||
| if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$")) | |||
| throw new ArgumentException($"Invalid locale: {locale}", nameof(locale)); | |||
| foreach (var (locale, description) in value) | |||
| { | |||
| if (!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$")) | |||
| throw new ArgumentException($"Invalid locale: {locale}", nameof(locale)); | |||
| Preconditions.AtLeast(description.Length, 1, nameof(description)); | |||
| Preconditions.AtMost(description.Length, SlashCommandBuilder.MaxDescriptionLength, nameof(description)); | |||
| Preconditions.AtLeast(description.Length, 1, nameof(description)); | |||
| Preconditions.AtMost(description.Length, SlashCommandBuilder.MaxDescriptionLength, nameof(description)); | |||
| } | |||
| } | |||
| _descriptionLocalizations = value; | |||
| } | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| using System; | |||
| using Discord; | |||
| using Xunit; | |||
| namespace Discord; | |||
| public class CommandBuilderTests | |||
| { | |||
| [Fact] | |||
| public void BuildSimpleSlashCommand() | |||
| { | |||
| var command = new SlashCommandBuilder() | |||
| .WithName("command") | |||
| .WithDescription("description") | |||
| .AddOption( | |||
| "option1", | |||
| ApplicationCommandOptionType.String, | |||
| "option1 description", | |||
| isRequired: true, | |||
| choices: new [] | |||
| { | |||
| new ApplicationCommandOptionChoiceProperties() | |||
| { | |||
| Name = "choice1", Value = "1" | |||
| } | |||
| }) | |||
| .AddOptions(new SlashCommandOptionBuilder() | |||
| .WithName("option2") | |||
| .WithDescription("option2 description") | |||
| .WithType(ApplicationCommandOptionType.String) | |||
| .WithRequired(true) | |||
| .AddChannelType(ChannelType.Text) | |||
| .AddChoice("choice1", "1") | |||
| .AddChoice("choice2", "2")); | |||
| command.Build(); | |||
| } | |||
| } | |||