From fca9c6b618715baaeeb5602d70fb8acfd91f6fe3 Mon Sep 17 00:00:00 2001 From: Julian <54598714+akaJuliaan@users.noreply.github.com> Date: Fri, 2 Sep 2022 23:31:51 +0200 Subject: [PATCH 1/3] Fix: remove Module from _typedModuleDefs (#2417) --- src/Discord.Net.Commands/CommandService.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 57e0e430e..29bf6a428 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -270,6 +270,11 @@ namespace Discord.Commands await _moduleLock.WaitAsync().ConfigureAwait(false); try { + var typeModulePair = _typedModuleDefs.FirstOrDefault(x => x.Value.Equals(module)); + + if (!typeModulePair.Equals(default(KeyValuePair))) + _typedModuleDefs.TryRemove(typeModulePair.Key, out var _); + return RemoveModuleInternal(module); } finally From 3dec99f6dfb5902b823b6f1b101a7d7361b65efd Mon Sep 17 00:00:00 2001 From: SaculRennorb Date: Fri, 2 Sep 2022 23:56:19 +0200 Subject: [PATCH 2/3] adding scheduled events to audit log (#2437) * first draft * made changes be actually optional. not everything always changes * 'doc' text * more 'doc' stuff * more 'doc' stuff3 * 'doc' stuff --- .../Entities/AuditLogs/ActionType.cs | 14 ++ .../Entities/AuditLogs/AuditLogHelper.cs | 4 + .../ScheduledEventCreateAuditLogData.cs | 149 ++++++++++++++++++ .../ScheduledEventDeleteAuditLogData.cs | 34 ++++ .../AuditLogs/DataTypes/ScheduledEventInfo.cs | 80 ++++++++++ .../ScheduledEventUpdateAuditLogData.cs | 99 ++++++++++++ 6 files changed, 380 insertions(+) create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventCreateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventDeleteAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventInfo.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventUpdateAuditLogData.cs diff --git a/src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs b/src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs index 5092b4e7f..ad2d659ee 100644 --- a/src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs +++ b/src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs @@ -180,6 +180,20 @@ namespace Discord /// A sticker was deleted. /// StickerDeleted = 92, + + /// + /// A scheduled event was created. + /// + EventCreate = 100, + /// + /// A scheduled event was created. + /// + EventUpdate = 101, + /// + /// A scheduled event was created. + /// + EventDelete = 102, + /// /// A thread was created. /// diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs b/src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs index edbb2bea8..f071fc1f9 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs @@ -52,6 +52,10 @@ namespace Discord.Rest [ActionType.MessagePinned] = MessagePinAuditLogData.Create, [ActionType.MessageUnpinned] = MessageUnpinAuditLogData.Create, + [ActionType.EventCreate] = ScheduledEventCreateAuditLogData.Create, + [ActionType.EventUpdate] = ScheduledEventUpdateAuditLogData.Create, + [ActionType.EventDelete] = ScheduledEventDeleteAuditLogData.Create, + [ActionType.ThreadCreate] = ThreadCreateAuditLogData.Create, [ActionType.ThreadUpdate] = ThreadUpdateAuditLogData.Create, [ActionType.ThreadDelete] = ThreadDeleteAuditLogData.Create, diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventCreateAuditLogData.cs new file mode 100644 index 000000000..11faa3371 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventCreateAuditLogData.cs @@ -0,0 +1,149 @@ +using System; +using System.Linq; +using Discord.API; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + /// + /// Contains a piece of audit log data related to a scheduled event creation. + /// + public class ScheduledEventCreateAuditLogData : IAuditLogData + { + private ScheduledEventCreateAuditLogData(ulong id, ulong guildId, ulong? channelId, ulong? creatorId, string name, string description, DateTimeOffset scheduledStartTime, DateTimeOffset? scheduledEndTime, GuildScheduledEventPrivacyLevel privacyLevel, GuildScheduledEventStatus status, GuildScheduledEventType entityType, ulong? entityId, string location, RestUser creator, int userCount, string image) + { + Id = id ; + GuildId = guildId ; + ChannelId = channelId ; + CreatorId = creatorId ; + Name = name ; + Description = description ; + ScheduledStartTime = scheduledStartTime; + ScheduledEndTime = scheduledEndTime ; + PrivacyLevel = privacyLevel ; + Status = status ; + EntityType = entityType ; + EntityId = entityId ; + Location = location ; + Creator = creator ; + UserCount = userCount ; + Image = image ; + } + + internal static ScheduledEventCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var id = entry.TargetId.Value; + + var guildId = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "guild_id") + .NewValue.ToObject(discord.ApiClient.Serializer); + var channelId = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "channel_id") + .NewValue.ToObject(discord.ApiClient.Serializer); + var creatorId = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "channel_id") + .NewValue.ToObject>(discord.ApiClient.Serializer) + .GetValueOrDefault(); + var name = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name") + .NewValue.ToObject(discord.ApiClient.Serializer); + var description = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "description") + .NewValue.ToObject>(discord.ApiClient.Serializer) + .GetValueOrDefault(); + var scheduledStartTime = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "scheduled_start_time") + .NewValue.ToObject(discord.ApiClient.Serializer); + var scheduledEndTime = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "scheduled_end_time") + .NewValue.ToObject(discord.ApiClient.Serializer); + var privacyLevel = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "privacy_level") + .NewValue.ToObject(discord.ApiClient.Serializer); + var status = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "status") + .NewValue.ToObject(discord.ApiClient.Serializer); + var entityType = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "entity_type") + .NewValue.ToObject(discord.ApiClient.Serializer); + var entityId = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "entity_id") + .NewValue.ToObject(discord.ApiClient.Serializer); + var entityMetadata = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "entity_metadata") + .NewValue.ToObject(discord.ApiClient.Serializer); + var creator = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "creator") + .NewValue.ToObject>(discord.ApiClient.Serializer) + .GetValueOrDefault(); + var userCount = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "user_count") + .NewValue.ToObject>(discord.ApiClient.Serializer) + .GetValueOrDefault(); + var image = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "image") + .NewValue.ToObject>(discord.ApiClient.Serializer) + .GetValueOrDefault(); + + var creatorUser = creator == null ? null : RestUser.Create(discord, creator); + + return new ScheduledEventCreateAuditLogData(id, guildId, channelId, creatorId, name, description, scheduledStartTime, scheduledEndTime, privacyLevel, status, entityType, entityId, entityMetadata.Location.GetValueOrDefault(), creatorUser, userCount, image); + } + + // Doc Note: Corresponds to the *current* data + + /// + /// Gets the snowflake id of the event. + /// + public ulong Id { get; } + /// + /// Gets the snowflake id of the guild the event is associated with. + /// + public ulong GuildId { get; } + /// + /// Gets the snowflake id of the channel the event is associated with. + /// + public ulong? ChannelId { get; } + /// + /// Gets the snowflake id of the original creator of the event. + /// + public ulong? CreatorId { get; } + /// + /// Gets name of the event. + /// + public string Name { get; } + /// + /// Gets the description of the event. null if none is set. + /// + public string Description { get; } + /// + /// Gets the time the event was scheduled for. + /// + public DateTimeOffset ScheduledStartTime { get; } + /// + /// Gets the time the event was scheduled to end. + /// + public DateTimeOffset? ScheduledEndTime { get; } + /// + /// Gets the privacy level of the event. + /// + public GuildScheduledEventPrivacyLevel PrivacyLevel { get; } + /// + /// Gets the status of the event. + /// + public GuildScheduledEventStatus Status { get; } + /// + /// Gets the type of the entity associated with the event (stage / void / external). + /// + public GuildScheduledEventType EntityType { get; } + /// + /// Gets the snowflake id of the entity associated with the event (stage / void / external). + /// + public ulong? EntityId { get; } + /// + /// Gets the metadata for the entity associated with the event. + /// + public string Location { get; } + /// + /// Gets the user that originally created the event. + /// + public RestUser Creator { get; } + /// + /// Gets the count of users interested in this event. + /// + public int UserCount { get; } + /// + /// Gets the image hash of the image that was attached to the event. Null if not set. + /// + public string Image { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventDeleteAuditLogData.cs new file mode 100644 index 000000000..34fa96225 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventDeleteAuditLogData.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using Discord.API; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + /// + /// Contains a piece of audit log data related to a scheduled event deleteion. + /// + public class ScheduledEventDeleteAuditLogData : IAuditLogData + { + private ScheduledEventDeleteAuditLogData(ulong id) + { + Id = id; + } + + internal static ScheduledEventDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var id = entry.TargetId.Value; + + return new ScheduledEventDeleteAuditLogData(id); + } + + // Doc Note: Corresponds to the *current* data + + /// + /// Gets the snowflake id of the event. + /// + public ulong Id { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventInfo.cs new file mode 100644 index 000000000..a45956546 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventInfo.cs @@ -0,0 +1,80 @@ +using System; + +namespace Discord.Rest +{ + /// + /// Represents information for a scheduled event. + /// + public class ScheduledEventInfo + { + /// + /// Gets the snowflake id of the guild the event is associated with. + /// + public ulong? GuildId { get; } + /// + /// Gets the snowflake id of the channel the event is associated with. + /// + public ulong? ChannelId { get; } + /// + /// Gets name of the event. + /// + public string Name { get; } + /// + /// Gets the description of the event. null if none is set. + /// + public string Description { get; } + /// + /// Gets the time the event was scheduled for. + /// + public DateTimeOffset? ScheduledStartTime { get; } + /// + /// Gets the time the event was scheduled to end. + /// + public DateTimeOffset? ScheduledEndTime { get; } + /// + /// Gets the privacy level of the event. + /// + public GuildScheduledEventPrivacyLevel? PrivacyLevel { get; } + /// + /// Gets the status of the event. + /// + public GuildScheduledEventStatus? Status { get; } + /// + /// Gets the type of the entity associated with the event (stage / void / external). + /// + public GuildScheduledEventType? EntityType { get; } + /// + /// Gets the snowflake id of the entity associated with the event (stage / void / external). + /// + public ulong? EntityId { get; } + /// + /// Gets the metadata for the entity associated with the event. + /// + public string Location { get; } + /// + /// Gets the count of users interested in this event. + /// + public int? UserCount { get; } + /// + /// Gets the image hash of the image that was attached to the event. Null if not set. + /// + public string Image { get; } + + internal ScheduledEventInfo(ulong? guildId, ulong? channelId, string name, string description, DateTimeOffset? scheduledStartTime, DateTimeOffset? scheduledEndTime, GuildScheduledEventPrivacyLevel? privacyLevel, GuildScheduledEventStatus? status, GuildScheduledEventType? entityType, ulong? entityId, string location, int? userCount, string image) + { + GuildId = guildId ; + ChannelId = channelId ; + Name = name ; + Description = description ; + ScheduledStartTime = scheduledStartTime; + ScheduledEndTime = scheduledEndTime ; + PrivacyLevel = privacyLevel ; + Status = status ; + EntityType = entityType ; + EntityId = entityId ; + Location = location ; + UserCount = userCount ; + Image = image ; + } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventUpdateAuditLogData.cs new file mode 100644 index 000000000..2ef2ccff8 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ScheduledEventUpdateAuditLogData.cs @@ -0,0 +1,99 @@ +using System; +using System.Linq; +using Discord.API; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + /// + /// Contains a piece of audit log data related to a scheduled event updates. + /// + public class ScheduledEventUpdateAuditLogData : IAuditLogData + { + private ScheduledEventUpdateAuditLogData(ulong id, ScheduledEventInfo before, ScheduledEventInfo after) + { + Id = id; + Before = before; + After = after; + } + + internal static ScheduledEventUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var id = entry.TargetId.Value; + + var guildId = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "guild_id"); + var channelId = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "channel_id"); + var name = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); + var description = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "description"); + var scheduledStartTime = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "scheduled_start_time"); + var scheduledEndTime = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "scheduled_end_time"); + var privacyLevel = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "privacy_level"); + var status = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "status"); + var entityType = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "entity_type"); + var entityId = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "entity_id"); + var entityMetadata = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "entity_metadata"); + var userCount = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "user_count"); + var image = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "image"); + + var before = new ScheduledEventInfo( + guildId?.OldValue.ToObject(discord.ApiClient.Serializer), + channelId?.OldValue.ToObject(discord.ApiClient.Serializer), + name?.OldValue.ToObject(discord.ApiClient.Serializer), + description?.OldValue.ToObject>(discord.ApiClient.Serializer) + .GetValueOrDefault(), + scheduledStartTime?.OldValue.ToObject(discord.ApiClient.Serializer), + scheduledEndTime?.OldValue.ToObject(discord.ApiClient.Serializer), + privacyLevel?.OldValue.ToObject(discord.ApiClient.Serializer), + status?.OldValue.ToObject(discord.ApiClient.Serializer), + entityType?.OldValue.ToObject(discord.ApiClient.Serializer), + entityId?.OldValue.ToObject(discord.ApiClient.Serializer), + entityMetadata?.OldValue.ToObject(discord.ApiClient.Serializer) + ?.Location.GetValueOrDefault(), + userCount?.OldValue.ToObject>(discord.ApiClient.Serializer) + .GetValueOrDefault(), + image?.OldValue.ToObject>(discord.ApiClient.Serializer) + .GetValueOrDefault() + ); + var after = new ScheduledEventInfo( + guildId?.NewValue.ToObject(discord.ApiClient.Serializer), + channelId?.NewValue.ToObject(discord.ApiClient.Serializer), + name?.NewValue.ToObject(discord.ApiClient.Serializer), + description?.NewValue.ToObject>(discord.ApiClient.Serializer) + .GetValueOrDefault(), + scheduledStartTime?.NewValue.ToObject(discord.ApiClient.Serializer), + scheduledEndTime?.NewValue.ToObject(discord.ApiClient.Serializer), + privacyLevel?.NewValue.ToObject(discord.ApiClient.Serializer), + status?.NewValue.ToObject(discord.ApiClient.Serializer), + entityType?.NewValue.ToObject(discord.ApiClient.Serializer), + entityId?.NewValue.ToObject(discord.ApiClient.Serializer), + entityMetadata?.NewValue.ToObject(discord.ApiClient.Serializer) + ?.Location.GetValueOrDefault(), + userCount?.NewValue.ToObject>(discord.ApiClient.Serializer) + .GetValueOrDefault(), + image?.NewValue.ToObject>(discord.ApiClient.Serializer) + .GetValueOrDefault() + ); + + return new ScheduledEventUpdateAuditLogData(id, before, after); + } + + // Doc Note: Corresponds to the *current* data + + /// + /// Gets the snowflake id of the event. + /// + public ulong Id { get; } + /// + /// Gets the state before the change. + /// + public ScheduledEventInfo Before { get; } + /// + /// Gets the state after the change. + /// + public ScheduledEventInfo After { get; } + } +} From 11ece4bf169dd91cd13f49d2ce2f27b0c14bb6d6 Mon Sep 17 00:00:00 2001 From: d4n Date: Fri, 2 Sep 2022 21:47:13 -0500 Subject: [PATCH 3/3] Update app commands regex and fix localization on app context commands (#2452) Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com> --- .../Interactions/ApplicationCommandOption.cs | 6 ++-- .../ApplicationCommandProperties.cs | 5 ++-- .../ContextMenus/MessageCommandBuilder.cs | 11 ++----- .../ContextMenus/UserCommandBuilder.cs | 11 ++----- .../SlashCommands/SlashCommandBuilder.cs | 29 +++++++++---------- 5 files changed, 24 insertions(+), 38 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOption.cs b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOption.cs index bceefda32..df33cfe1d 100644 --- a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOption.cs +++ b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOption.cs @@ -145,10 +145,10 @@ namespace Discord if (name.Length > 32) throw new ArgumentOutOfRangeException(nameof(name), "Name length must be less than or equal to 32."); - if (!Regex.IsMatch(name, @"^[\w-]{1,32}$")) - throw new FormatException($"{nameof(name)} must match the regex ^[\\w-]{{1,32}}$"); + if (!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 (name.Any(x => char.IsUpper(x))) + if (name.Any(char.IsUpper)) throw new FormatException("Name cannot contain any uppercase characters."); } diff --git a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs index 7ca16a27d..98e050df9 100644 --- a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs +++ b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs @@ -42,8 +42,9 @@ namespace Discord Preconditions.AtLeast(name.Length, 1, nameof(name)); Preconditions.AtMost(name.Length, SlashCommandBuilder.MaxNameLength, nameof(name)); - if (!Regex.IsMatch(name, @"^[\w-]{1,32}$")) - throw new ArgumentException("Option name cannot contain any special characters or whitespaces!", 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; } diff --git a/src/Discord.Net.Core/Entities/Interactions/ContextMenus/MessageCommandBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/ContextMenus/MessageCommandBuilder.cs index ed49c685d..613e30376 100644 --- a/src/Discord.Net.Core/Entities/Interactions/ContextMenus/MessageCommandBuilder.cs +++ b/src/Discord.Net.Core/Entities/Interactions/ContextMenus/MessageCommandBuilder.cs @@ -67,7 +67,8 @@ namespace Discord Name = Name, IsDefaultPermission = IsDefaultPermission, IsDMEnabled = IsDMEnabled, - DefaultMemberPermissions = DefaultMemberPermissions ?? Optional.Unspecified + DefaultMemberPermissions = DefaultMemberPermissions ?? Optional.Unspecified, + NameLocalizations = NameLocalizations }; return props; @@ -157,14 +158,6 @@ namespace Discord Preconditions.NotNullOrEmpty(name, nameof(name)); Preconditions.AtLeast(name.Length, 1, nameof(name)); Preconditions.AtMost(name.Length, MaxNameLength, nameof(name)); - - // Discord updated the docs, this regex prevents special characters like @!$%(... 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)); - - if (name.Any(x => char.IsUpper(x))) - throw new FormatException("Name cannot contain any uppercase characters."); } /// diff --git a/src/Discord.Net.Core/Entities/Interactions/ContextMenus/UserCommandBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/ContextMenus/UserCommandBuilder.cs index d8bb2e056..8ac524582 100644 --- a/src/Discord.Net.Core/Entities/Interactions/ContextMenus/UserCommandBuilder.cs +++ b/src/Discord.Net.Core/Entities/Interactions/ContextMenus/UserCommandBuilder.cs @@ -65,7 +65,8 @@ namespace Discord Name = Name, IsDefaultPermission = IsDefaultPermission, IsDMEnabled = IsDMEnabled, - DefaultMemberPermissions = DefaultMemberPermissions ?? Optional.Unspecified + DefaultMemberPermissions = DefaultMemberPermissions ?? Optional.Unspecified, + NameLocalizations = NameLocalizations }; return props; @@ -155,14 +156,6 @@ namespace Discord Preconditions.NotNullOrEmpty(name, nameof(name)); Preconditions.AtLeast(name.Length, 1, nameof(name)); Preconditions.AtMost(name.Length, MaxNameLength, nameof(name)); - - // Discord updated the docs, this regex prevents special characters like @!$%(... 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)); - - if (name.Any(x => char.IsUpper(x))) - throw new FormatException("Name cannot contain any uppercase characters."); } /// diff --git a/src/Discord.Net.Core/Entities/Interactions/SlashCommands/SlashCommandBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/SlashCommands/SlashCommandBuilder.cs index b443c4468..1df886abe 100644 --- a/src/Discord.Net.Core/Entities/Interactions/SlashCommands/SlashCommandBuilder.cs +++ b/src/Discord.Net.Core/Entities/Interactions/SlashCommands/SlashCommandBuilder.cs @@ -207,10 +207,9 @@ namespace Discord { 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)); + // https://discord.com/developers/docs/interactions/application-commands + if (!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)); // make sure theres only one option with default set to true if (isDefault == true && Options?.Any(x => x.IsDefault == true) == true) @@ -376,12 +375,11 @@ namespace Discord Preconditions.AtLeast(name.Length, 1, nameof(name)); Preconditions.AtMost(name.Length, MaxNameLength, nameof(name)); - // Discord updated the docs, this regex prevents special characters like @!$%(... 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)); + // https://discord.com/developers/docs/interactions/application-commands + if (!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 (name.Any(x => char.IsUpper(x))) + if (name.Any(char.IsUpper)) throw new FormatException("Name cannot contain any uppercase characters."); } @@ -587,10 +585,9 @@ namespace Discord { 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)); + // https://discord.com/developers/docs/interactions/application-commands + if (!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)); // make sure theres only one option with default set to true if (isDefault && Options?.Any(x => x.IsDefault == true) == true) @@ -966,8 +963,10 @@ namespace Discord { Preconditions.AtLeast(name.Length, 1, nameof(name)); Preconditions.AtMost(name.Length, SlashCommandBuilder.MaxNameLength, nameof(name)); - if (!Regex.IsMatch(name, @"^[\w-]{1,32}$")) - throw new ArgumentException("Option name cannot contain any special characters or whitespaces!", nameof(name)); + + // https://discord.com/developers/docs/interactions/application-commands + if (!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)); } private static void EnsureValidCommandOptionDescription(string description)