Interactions Command Localization (#2395)
* Request headers (#2394) * add support for per-request headers * remove unnecessary usings * Revert "remove unnecessary usings" This reverts commit 8d674fe4faf985b117f143fae3877a1698170ad2. * remove nullable strings from RequestOptions * Add Localization Support to Interaction Service (#2211) * add json and resx localization managers * add utils class for getting command paths * update json regex to make langage code optional * remove IServiceProvider from ILocalizationManager method params * replace the command path method in command map * add localization fields to rest and websocket application command entity implementations * move deconstruct extensions method to extensions folder * add withLocalizations parameter to rest methods * fix build error * add rest conversions to interaction service * add localization to the rest methods * add inline docs * fix implementation bugs * add missing inline docs * inline docs correction (Name/Description Localized properties) * add choice localization * fix conflicts * fix conflicts * add missing command props fields to ToApplicationCommandProps methods * add locale parameter to Get*ApplicationCommandsAsync methods for fetching localized command names/descriptions * Apply suggestions from code review Co-authored-by: Armano den Boef <68127614+Rozen4334@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Armano den Boef <68127614+Rozen4334@users.noreply.github.com> * Update src/Discord.Net.Core/Entities/Guilds/IGuild.cs Co-authored-by: Armano den Boef <68127614+Rozen4334@users.noreply.github.com> * add inline docs to LocalizationTarget * fix upstream merge errors * fix command parsing for context command names with space char * fix command parsing for context command names with space char * fix failed to generate buket id * fix get guild commands endpoint * update rexs localization manager to use single-file pattern * Upstream Merge Localization Branch (#2434) * fix ci/cd error (#2428) * Fix role icon & emoji assignment. (#2416) * Fix IGuild.GetBansAsync() (#2424) fix the problem of not being able to get more than 1000 bans * [DOCS] Add a note about `DontAutoRegisterAttribute` (#2430) * add a note about `DontAutoRegisterAttribute` * Remove "to to" and add punctuation Co-authored-by: MrCakeSlayer <13650699+MrCakeSlayer@users.noreply.github.com> * fix: Missing Fact attribute in ColorTests (#2425) * feat: Embed comparison (#2347) * Fix broken code snippet in dependency injection docs (#2420) * Fixed markdown formatting to show code snippet * Fixed constructor injection code snippet pointer * Added support for lottie stickers (#2359) Co-authored-by: Armano den Boef <68127614+Rozen4334@users.noreply.github.com> Co-authored-by: BokuNoPasya <49203428+1NieR@users.noreply.github.com> Co-authored-by: Misha133 <61027276+Misha-133@users.noreply.github.com> Co-authored-by: MrCakeSlayer <13650699+MrCakeSlayer@users.noreply.github.com> Co-authored-by: Ge <gehongyan1996@126.com> Co-authored-by: Charlie U <52503242+cpurules@users.noreply.github.com> Co-authored-by: Kuba_Z2 <77853483+KubaZ2@users.noreply.github.com> * remove unnecassary fields from ResxLocalizationManager * update int framework guides * remove space character tokenization from ResxLocalizationManager Co-authored-by: Armano den Boef <68127614+Rozen4334@users.noreply.github.com> Co-authored-by: BokuNoPasya <49203428+1NieR@users.noreply.github.com> Co-authored-by: Misha133 <61027276+Misha-133@users.noreply.github.com> Co-authored-by: MrCakeSlayer <13650699+MrCakeSlayer@users.noreply.github.com> Co-authored-by: Ge <gehongyan1996@126.com> Co-authored-by: Charlie U <52503242+cpurules@users.noreply.github.com> Co-authored-by: Kuba_Z2 <77853483+KubaZ2@users.noreply.github.com>
This commit is contained in:
@@ -83,6 +83,11 @@ namespace Discord.Interactions
|
||||
public event Func<ModalCommandInfo, IInteractionContext, IResult, Task> ModalCommandExecuted { add { _modalCommandExecutedEvent.Add(value); } remove { _modalCommandExecutedEvent.Remove(value); } }
|
||||
internal readonly AsyncEvent<Func<ModalCommandInfo, IInteractionContext, IResult, Task>> _modalCommandExecutedEvent = new();
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="ILocalizationManager"/> used by this Interaction Service instance to localize strings.
|
||||
/// </summary>
|
||||
public ILocalizationManager LocalizationManager { get; set; }
|
||||
|
||||
private readonly ConcurrentDictionary<Type, ModuleInfo> _typedModuleDefs;
|
||||
private readonly CommandMap<SlashCommandInfo> _slashCommandMap;
|
||||
private readonly ConcurrentDictionary<ApplicationCommandType, CommandMap<ContextCommandInfo>> _contextCommandMaps;
|
||||
@@ -203,6 +208,7 @@ namespace Discord.Interactions
|
||||
_enableAutocompleteHandlers = config.EnableAutocompleteHandlers;
|
||||
_autoServiceScopes = config.AutoServiceScopes;
|
||||
_restResponseCallback = config.RestResponseCallback;
|
||||
LocalizationManager = config.LocalizationManager;
|
||||
|
||||
_typeConverterMap = new TypeMap<TypeConverter, IApplicationCommandInteractionDataOption>(this, new ConcurrentDictionary<Type, TypeConverter>
|
||||
{
|
||||
|
||||
@@ -64,6 +64,11 @@ namespace Discord.Interactions
|
||||
/// Gets or sets whether a command execution should exit when a modal command encounters a missing modal component value.
|
||||
/// </summary>
|
||||
public bool ExitOnMissingModalField { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Localization provider to be used when registering application commands.
|
||||
/// </summary>
|
||||
public ILocalizationManager LocalizationManager { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Respresents a localization provider for Discord Application Commands.
|
||||
/// </summary>
|
||||
public interface ILocalizationManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Get every the resource name for every available locale.
|
||||
/// </summary>
|
||||
/// <param name="key">Location of the resource.</param>
|
||||
/// <param name="destinationType">Type of the resource.</param>
|
||||
/// <returns>
|
||||
/// A dictionary containing every available locale and the resource name.
|
||||
/// </returns>
|
||||
IDictionary<string, string> GetAllNames(IList<string> key, LocalizationTarget destinationType);
|
||||
|
||||
/// <summary>
|
||||
/// Get every the resource description for every available locale.
|
||||
/// </summary>
|
||||
/// <param name="key">Location of the resource.</param>
|
||||
/// <param name="destinationType">Type of the resource.</param>
|
||||
/// <returns>
|
||||
/// A dictionary containing every available locale and the resource name.
|
||||
/// </returns>
|
||||
IDictionary<string, string> GetAllDescriptions(IList<string> key, LocalizationTarget destinationType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// The default localization provider for Json resource files.
|
||||
/// </summary>
|
||||
public sealed class JsonLocalizationManager : ILocalizationManager
|
||||
{
|
||||
private const string NameIdentifier = "name";
|
||||
private const string DescriptionIdentifier = "description";
|
||||
private const string SpaceToken = "~";
|
||||
|
||||
private readonly string _basePath;
|
||||
private readonly string _fileName;
|
||||
private readonly Regex _localeParserRegex = new Regex(@"\w+.(?<locale>\w{2}(?:-\w{2})?).json", RegexOptions.Compiled | RegexOptions.Singleline);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonLocalizationManager"/> class.
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base path of the Json file.</param>
|
||||
/// <param name="fileName">Name of the Json file.</param>
|
||||
public JsonLocalizationManager(string basePath, string fileName)
|
||||
{
|
||||
_basePath = basePath;
|
||||
_fileName = fileName;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, string> GetAllDescriptions(IList<string> key, LocalizationTarget destinationType) =>
|
||||
GetValues(key, DescriptionIdentifier);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, string> GetAllNames(IList<string> key, LocalizationTarget destinationType) =>
|
||||
GetValues(key, NameIdentifier);
|
||||
|
||||
private string[] GetAllFiles() =>
|
||||
Directory.GetFiles(_basePath, $"{_fileName}.*.json", SearchOption.TopDirectoryOnly);
|
||||
|
||||
private IDictionary<string, string> GetValues(IList<string> key, string identifier)
|
||||
{
|
||||
var result = new Dictionary<string, string>();
|
||||
var files = GetAllFiles();
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
var match = _localeParserRegex.Match(Path.GetFileName(file));
|
||||
if (!match.Success)
|
||||
continue;
|
||||
|
||||
var locale = match.Groups["locale"].Value;
|
||||
|
||||
using var sr = new StreamReader(file);
|
||||
using var jr = new JsonTextReader(sr);
|
||||
var obj = JObject.Load(jr);
|
||||
var token = string.Join(".", key.Select(x => $"['{x}']")) + $".{identifier}";
|
||||
var value = (string)obj.SelectToken(token);
|
||||
if (value is not null)
|
||||
result[locale] = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
namespace Discord.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// The default localization provider for Resx files.
|
||||
/// </summary>
|
||||
public sealed class ResxLocalizationManager : ILocalizationManager
|
||||
{
|
||||
private const string NameIdentifier = "name";
|
||||
private const string DescriptionIdentifier = "description";
|
||||
|
||||
private readonly ResourceManager _resourceManager;
|
||||
private readonly IEnumerable<CultureInfo> _supportedLocales;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ResxLocalizationManager"/> class.
|
||||
/// </summary>
|
||||
/// <param name="baseResource">Name of the base resource.</param>
|
||||
/// <param name="assembly">The main assembly for the resources.</param>
|
||||
/// <param name="supportedLocales">Cultures the <see cref="ResxLocalizationManager"/> should search for.</param>
|
||||
public ResxLocalizationManager(string baseResource, Assembly assembly, params CultureInfo[] supportedLocales)
|
||||
{
|
||||
_supportedLocales = supportedLocales;
|
||||
_resourceManager = new ResourceManager(baseResource, assembly);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, string> GetAllDescriptions(IList<string> key, LocalizationTarget destinationType) =>
|
||||
GetValues(key, DescriptionIdentifier);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, string> GetAllNames(IList<string> key, LocalizationTarget destinationType) =>
|
||||
GetValues(key, NameIdentifier);
|
||||
|
||||
private IDictionary<string, string> GetValues(IList<string> key, string identifier)
|
||||
{
|
||||
var entryKey = (string.Join(".", key) + "." + identifier);
|
||||
|
||||
var result = new Dictionary<string, string>();
|
||||
|
||||
foreach (var locale in _supportedLocales)
|
||||
{
|
||||
var value = _resourceManager.GetString(entryKey, locale);
|
||||
if (value is not null)
|
||||
result[locale.Name] = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/Discord.Net.Interactions/LocalizationTarget.cs
Normal file
25
src/Discord.Net.Interactions/LocalizationTarget.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace Discord.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Resource targets for localization.
|
||||
/// </summary>
|
||||
public enum LocalizationTarget
|
||||
{
|
||||
/// <summary>
|
||||
/// Target is a <see cref="IInteractionModuleBase"/> tagged with a <see cref="GroupAttribute"/>.
|
||||
/// </summary>
|
||||
Group,
|
||||
/// <summary>
|
||||
/// Target is an application command method.
|
||||
/// </summary>
|
||||
Command,
|
||||
/// <summary>
|
||||
/// Target is a Slash Command parameter.
|
||||
/// </summary>
|
||||
Parameter,
|
||||
/// <summary>
|
||||
/// Target is a Slash Command parameter choice.
|
||||
/// </summary>
|
||||
Choice
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ namespace Discord.Interactions
|
||||
|
||||
public void RemoveCommand(T command)
|
||||
{
|
||||
var key = ParseCommandName(command);
|
||||
var key = CommandHierarchy.GetCommandPath(command);
|
||||
|
||||
_root.RemoveCommand(key, 0);
|
||||
}
|
||||
@@ -60,28 +60,9 @@ namespace Discord.Interactions
|
||||
|
||||
private void AddCommand(T command)
|
||||
{
|
||||
var key = ParseCommandName(command);
|
||||
var key = CommandHierarchy.GetCommandPath(command);
|
||||
|
||||
_root.AddCommand(key, 0, command);
|
||||
}
|
||||
|
||||
private IList<string> ParseCommandName(T command)
|
||||
{
|
||||
var keywords = new List<string>() { command.Name };
|
||||
|
||||
var currentParent = command.Module;
|
||||
|
||||
while (currentParent != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(currentParent.SlashGroupName))
|
||||
keywords.Add(currentParent.SlashGroupName);
|
||||
|
||||
currentParent = currentParent.Parent;
|
||||
}
|
||||
|
||||
keywords.Reverse();
|
||||
|
||||
return keywords;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Discord.Interactions
|
||||
@@ -9,6 +10,9 @@ namespace Discord.Interactions
|
||||
#region Parameters
|
||||
public static ApplicationCommandOptionProperties ToApplicationCommandOptionProps(this SlashCommandParameterInfo parameterInfo)
|
||||
{
|
||||
var localizationManager = parameterInfo.Command.Module.CommandService.LocalizationManager;
|
||||
var parameterPath = parameterInfo.GetParameterPath();
|
||||
|
||||
var props = new ApplicationCommandOptionProperties
|
||||
{
|
||||
Name = parameterInfo.Name,
|
||||
@@ -18,12 +22,15 @@ namespace Discord.Interactions
|
||||
Choices = parameterInfo.Choices?.Select(x => new ApplicationCommandOptionChoiceProperties
|
||||
{
|
||||
Name = x.Name,
|
||||
Value = x.Value
|
||||
Value = x.Value,
|
||||
NameLocalizations = localizationManager?.GetAllNames(parameterInfo.GetChoicePath(x), LocalizationTarget.Choice) ?? ImmutableDictionary<string, string>.Empty
|
||||
})?.ToList(),
|
||||
ChannelTypes = parameterInfo.ChannelTypes?.ToList(),
|
||||
IsAutocomplete = parameterInfo.IsAutocomplete,
|
||||
MaxValue = parameterInfo.MaxValue,
|
||||
MinValue = parameterInfo.MinValue,
|
||||
NameLocalizations = localizationManager?.GetAllNames(parameterPath, LocalizationTarget.Parameter) ?? ImmutableDictionary<string, string>.Empty,
|
||||
DescriptionLocalizations = localizationManager?.GetAllDescriptions(parameterPath, LocalizationTarget.Parameter) ?? ImmutableDictionary<string, string>.Empty,
|
||||
MinLength = parameterInfo.MinLength,
|
||||
MaxLength = parameterInfo.MaxLength,
|
||||
};
|
||||
@@ -38,13 +45,19 @@ namespace Discord.Interactions
|
||||
|
||||
public static SlashCommandProperties ToApplicationCommandProps(this SlashCommandInfo commandInfo)
|
||||
{
|
||||
var commandPath = commandInfo.GetCommandPath();
|
||||
var localizationManager = commandInfo.Module.CommandService.LocalizationManager;
|
||||
|
||||
var props = new SlashCommandBuilder()
|
||||
{
|
||||
Name = commandInfo.Name,
|
||||
Description = commandInfo.Description,
|
||||
IsDefaultPermission = commandInfo.DefaultPermission,
|
||||
IsDMEnabled = commandInfo.IsEnabledInDm,
|
||||
DefaultMemberPermissions = ((commandInfo.DefaultMemberPermissions ?? 0) | (commandInfo.Module.DefaultMemberPermissions ?? 0)).SanitizeGuildPermissions(),
|
||||
}.Build();
|
||||
}.WithNameLocalizations(localizationManager?.GetAllNames(commandPath, LocalizationTarget.Command) ?? ImmutableDictionary<string, string>.Empty)
|
||||
.WithDescriptionLocalizations(localizationManager?.GetAllDescriptions(commandPath, LocalizationTarget.Command) ?? ImmutableDictionary<string, string>.Empty)
|
||||
.Build();
|
||||
|
||||
if (commandInfo.Parameters.Count > SlashCommandBuilder.MaxOptionsCount)
|
||||
throw new InvalidOperationException($"Slash Commands cannot have more than {SlashCommandBuilder.MaxOptionsCount} command parameters");
|
||||
@@ -54,18 +67,30 @@ namespace Discord.Interactions
|
||||
return props;
|
||||
}
|
||||
|
||||
public static ApplicationCommandOptionProperties ToApplicationCommandOptionProps(this SlashCommandInfo commandInfo) =>
|
||||
new ApplicationCommandOptionProperties
|
||||
public static ApplicationCommandOptionProperties ToApplicationCommandOptionProps(this SlashCommandInfo commandInfo)
|
||||
{
|
||||
var localizationManager = commandInfo.Module.CommandService.LocalizationManager;
|
||||
var commandPath = commandInfo.GetCommandPath();
|
||||
|
||||
return new ApplicationCommandOptionProperties
|
||||
{
|
||||
Name = commandInfo.Name,
|
||||
Description = commandInfo.Description,
|
||||
Type = ApplicationCommandOptionType.SubCommand,
|
||||
IsRequired = false,
|
||||
Options = commandInfo.FlattenedParameters?.Select(x => x.ToApplicationCommandOptionProps())?.ToList()
|
||||
Options = commandInfo.FlattenedParameters?.Select(x => x.ToApplicationCommandOptionProps())
|
||||
?.ToList(),
|
||||
NameLocalizations = localizationManager?.GetAllNames(commandPath, LocalizationTarget.Command) ?? ImmutableDictionary<string, string>.Empty,
|
||||
DescriptionLocalizations = localizationManager?.GetAllDescriptions(commandPath, LocalizationTarget.Command) ?? ImmutableDictionary<string, string>.Empty
|
||||
};
|
||||
}
|
||||
|
||||
public static ApplicationCommandProperties ToApplicationCommandProps(this ContextCommandInfo commandInfo)
|
||||
=> commandInfo.CommandType switch
|
||||
{
|
||||
var localizationManager = commandInfo.Module.CommandService.LocalizationManager;
|
||||
var commandPath = commandInfo.GetCommandPath();
|
||||
|
||||
return commandInfo.CommandType switch
|
||||
{
|
||||
ApplicationCommandType.Message => new MessageCommandBuilder
|
||||
{
|
||||
@@ -73,16 +98,21 @@ namespace Discord.Interactions
|
||||
IsDefaultPermission = commandInfo.DefaultPermission,
|
||||
DefaultMemberPermissions = ((commandInfo.DefaultMemberPermissions ?? 0) | (commandInfo.Module.DefaultMemberPermissions ?? 0)).SanitizeGuildPermissions(),
|
||||
IsDMEnabled = commandInfo.IsEnabledInDm
|
||||
}.Build(),
|
||||
}
|
||||
.WithNameLocalizations(localizationManager?.GetAllNames(commandPath, LocalizationTarget.Command) ?? ImmutableDictionary<string, string>.Empty)
|
||||
.Build(),
|
||||
ApplicationCommandType.User => new UserCommandBuilder
|
||||
{
|
||||
Name = commandInfo.Name,
|
||||
IsDefaultPermission = commandInfo.DefaultPermission,
|
||||
DefaultMemberPermissions = ((commandInfo.DefaultMemberPermissions ?? 0) | (commandInfo.Module.DefaultMemberPermissions ?? 0)).SanitizeGuildPermissions(),
|
||||
IsDMEnabled = commandInfo.IsEnabledInDm
|
||||
}.Build(),
|
||||
}
|
||||
.WithNameLocalizations(localizationManager?.GetAllNames(commandPath, LocalizationTarget.Command) ?? ImmutableDictionary<string, string>.Empty)
|
||||
.Build(),
|
||||
_ => throw new InvalidOperationException($"{commandInfo.CommandType} isn't a supported command type.")
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Modules
|
||||
@@ -123,6 +153,9 @@ namespace Discord.Interactions
|
||||
|
||||
options.AddRange(moduleInfo.SubModules?.SelectMany(x => x.ParseSubModule(args, ignoreDontRegister)));
|
||||
|
||||
var localizationManager = moduleInfo.CommandService.LocalizationManager;
|
||||
var modulePath = moduleInfo.GetModulePath();
|
||||
|
||||
var props = new SlashCommandBuilder
|
||||
{
|
||||
Name = moduleInfo.SlashGroupName,
|
||||
@@ -130,7 +163,10 @@ namespace Discord.Interactions
|
||||
IsDefaultPermission = moduleInfo.DefaultPermission,
|
||||
IsDMEnabled = moduleInfo.IsEnabledInDm,
|
||||
DefaultMemberPermissions = moduleInfo.DefaultMemberPermissions
|
||||
}.Build();
|
||||
}
|
||||
.WithNameLocalizations(localizationManager?.GetAllNames(modulePath, LocalizationTarget.Group) ?? ImmutableDictionary<string, string>.Empty)
|
||||
.WithDescriptionLocalizations(localizationManager?.GetAllDescriptions(modulePath, LocalizationTarget.Group) ?? ImmutableDictionary<string, string>.Empty)
|
||||
.Build();
|
||||
|
||||
if (options.Count > SlashCommandBuilder.MaxOptionsCount)
|
||||
throw new InvalidOperationException($"Slash Commands cannot have more than {SlashCommandBuilder.MaxOptionsCount} command parameters");
|
||||
@@ -168,7 +204,11 @@ namespace Discord.Interactions
|
||||
Name = moduleInfo.SlashGroupName,
|
||||
Description = moduleInfo.Description,
|
||||
Type = ApplicationCommandOptionType.SubCommandGroup,
|
||||
Options = options
|
||||
Options = options,
|
||||
NameLocalizations = moduleInfo.CommandService.LocalizationManager?.GetAllNames(moduleInfo.GetModulePath(), LocalizationTarget.Group)
|
||||
?? ImmutableDictionary<string, string>.Empty,
|
||||
DescriptionLocalizations = moduleInfo.CommandService.LocalizationManager?.GetAllDescriptions(moduleInfo.GetModulePath(), LocalizationTarget.Group)
|
||||
?? ImmutableDictionary<string, string>.Empty,
|
||||
} };
|
||||
}
|
||||
|
||||
@@ -183,17 +223,29 @@ namespace Discord.Interactions
|
||||
Name = command.Name,
|
||||
Description = command.Description,
|
||||
IsDefaultPermission = command.IsDefaultPermission,
|
||||
Options = command.Options?.Select(x => x.ToApplicationCommandOptionProps())?.ToList() ?? Optional<List<ApplicationCommandOptionProperties>>.Unspecified
|
||||
DefaultMemberPermissions = (GuildPermission)command.DefaultMemberPermissions.RawValue,
|
||||
IsDMEnabled = command.IsEnabledInDm,
|
||||
Options = command.Options?.Select(x => x.ToApplicationCommandOptionProps())?.ToList() ?? Optional<List<ApplicationCommandOptionProperties>>.Unspecified,
|
||||
NameLocalizations = command.NameLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty,
|
||||
DescriptionLocalizations = command.DescriptionLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty,
|
||||
},
|
||||
ApplicationCommandType.User => new UserCommandProperties
|
||||
{
|
||||
Name = command.Name,
|
||||
IsDefaultPermission = command.IsDefaultPermission
|
||||
IsDefaultPermission = command.IsDefaultPermission,
|
||||
DefaultMemberPermissions = (GuildPermission)command.DefaultMemberPermissions.RawValue,
|
||||
IsDMEnabled = command.IsEnabledInDm,
|
||||
NameLocalizations = command.NameLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty,
|
||||
DescriptionLocalizations = command.DescriptionLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty
|
||||
},
|
||||
ApplicationCommandType.Message => new MessageCommandProperties
|
||||
{
|
||||
Name = command.Name,
|
||||
IsDefaultPermission = command.IsDefaultPermission
|
||||
IsDefaultPermission = command.IsDefaultPermission,
|
||||
DefaultMemberPermissions = (GuildPermission)command.DefaultMemberPermissions.RawValue,
|
||||
IsDMEnabled = command.IsEnabledInDm,
|
||||
NameLocalizations = command.NameLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty,
|
||||
DescriptionLocalizations = command.DescriptionLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty
|
||||
},
|
||||
_ => throw new InvalidOperationException($"Cannot create command properties for command type {command.Type}"),
|
||||
};
|
||||
@@ -206,18 +258,20 @@ namespace Discord.Interactions
|
||||
Description = commandOption.Description,
|
||||
Type = commandOption.Type,
|
||||
IsRequired = commandOption.IsRequired,
|
||||
ChannelTypes = commandOption.ChannelTypes?.ToList(),
|
||||
IsAutocomplete = commandOption.IsAutocomplete.GetValueOrDefault(),
|
||||
MinValue = commandOption.MinValue,
|
||||
MaxValue = commandOption.MaxValue,
|
||||
Choices = commandOption.Choices?.Select(x => new ApplicationCommandOptionChoiceProperties
|
||||
{
|
||||
Name = x.Name,
|
||||
Value = x.Value
|
||||
}).ToList(),
|
||||
Options = commandOption.Options?.Select(x => x.ToApplicationCommandOptionProps()).ToList(),
|
||||
NameLocalizations = commandOption.NameLocalizations?.ToImmutableDictionary(),
|
||||
DescriptionLocalizations = commandOption.DescriptionLocalizations?.ToImmutableDictionary(),
|
||||
MaxLength = commandOption.MaxLength,
|
||||
MinLength = commandOption.MinLength,
|
||||
MaxValue = commandOption.MaxValue,
|
||||
MinValue = commandOption.MinValue,
|
||||
IsAutocomplete = commandOption.IsAutocomplete.GetValueOrDefault(),
|
||||
ChannelTypes = commandOption.ChannelTypes.ToList(),
|
||||
};
|
||||
|
||||
public static Modal ToModal(this ModalInfo modalInfo, string customId, Action<ModalBuilder> modifyModal = null)
|
||||
|
||||
53
src/Discord.Net.Interactions/Utilities/CommandHierarchy.cs
Normal file
53
src/Discord.Net.Interactions/Utilities/CommandHierarchy.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Discord.Interactions
|
||||
{
|
||||
internal static class CommandHierarchy
|
||||
{
|
||||
public const char EscapeChar = '$';
|
||||
|
||||
public static IList<string> GetModulePath(this ModuleInfo moduleInfo)
|
||||
{
|
||||
var result = new List<string>();
|
||||
|
||||
var current = moduleInfo;
|
||||
while (current is not null)
|
||||
{
|
||||
if (current.IsSlashGroup)
|
||||
result.Insert(0, current.SlashGroupName);
|
||||
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static IList<string> GetCommandPath(this ICommandInfo commandInfo)
|
||||
{
|
||||
if (commandInfo.IgnoreGroupNames)
|
||||
return new string[] { commandInfo.Name };
|
||||
|
||||
var path = commandInfo.Module.GetModulePath();
|
||||
path.Add(commandInfo.Name);
|
||||
return path;
|
||||
}
|
||||
|
||||
public static IList<string> GetParameterPath(this IParameterInfo parameterInfo)
|
||||
{
|
||||
var path = parameterInfo.Command.GetCommandPath();
|
||||
path.Add(parameterInfo.Name);
|
||||
return path;
|
||||
}
|
||||
|
||||
public static IList<string> GetChoicePath(this IParameterInfo parameterInfo, ParameterChoice choice)
|
||||
{
|
||||
var path = parameterInfo.GetParameterPath();
|
||||
path.Add(choice.Name);
|
||||
return path;
|
||||
}
|
||||
|
||||
public static IList<string> GetTypePath(Type type) =>
|
||||
new string[] { EscapeChar + type.FullName };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user