diff --git a/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs b/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs
index 45010f23f..6f7da89e0 100644
--- a/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs
+++ b/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs
@@ -1,329 +1,327 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Collections.Generic;
using System.Linq;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace DisCatSharp.ApplicationCommands.Checks;
internal static class ApplicationCommandEqualityChecks
{
///
/// Whether two application commands are equal.
///
/// Source command.
/// Command to check against.
/// The discord client.
/// Whether the equal check is performed for a guild command.
internal static bool IsEqualTo(this DiscordApplicationCommand ac1, DiscordApplicationCommand targetApplicationCommand, DiscordClient client, bool isGuild)
{
if (targetApplicationCommand is null || ac1 is null)
return false;
DiscordApplicationCommand sourceApplicationCommand = new(
ac1.Name, ac1.Description, ac1.Options,
ac1.Type,
ac1.NameLocalizations, ac1.DescriptionLocalizations,
- ac1.DefaultMemberPermissions, ac1.DmPermission ?? true//, ac1.IsNsfw
+ ac1.DefaultMemberPermissions, ac1.DmPermission ?? true, ac1.IsNsfw
);
if (sourceApplicationCommand.DefaultMemberPermissions == Permissions.None && targetApplicationCommand.DefaultMemberPermissions == null)
sourceApplicationCommand.DefaultMemberPermissions = null;
if (isGuild)
{
sourceApplicationCommand.DmPermission = null;
targetApplicationCommand.DmPermission = null;
}
client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, "[AC Change Check] Command {name}\n\n[{jsonOne},{jsontwo}]\n\n", ac1.Name, JsonConvert.SerializeObject(sourceApplicationCommand), JsonConvert.SerializeObject(targetApplicationCommand));
return ac1.Type == targetApplicationCommand.Type && sourceApplicationCommand.SoftEqual(targetApplicationCommand, ac1.Type, ApplicationCommandsExtension.Configuration?.EnableLocalization ?? false, isGuild);
}
///
/// Checks softly whether two s are the same.
/// Excluding id, application id and version here.
///
/// Source application command.
/// Application command to check against.
/// The application command type.
/// Whether localization is enabled.
/// Whether the equal check is performed for a guild command.
internal static bool SoftEqual(this DiscordApplicationCommand source, DiscordApplicationCommand target, ApplicationCommandType type, bool localizationEnabled = false, bool guild = false)
{
bool? sDmPerm = source.DmPermission ?? true;
bool? tDmPerm = target.DmPermission ?? true;
if (guild)
{
sDmPerm = null;
tDmPerm = null;
}
return localizationEnabled
? type switch
{
ApplicationCommandType.ChatInput => DeepEqual(source, target, true, sDmPerm, tDmPerm),
_ => source.Name == target.Name
&& source.Type == target.Type && source.NameLocalizations == target.NameLocalizations
&& source.DefaultMemberPermissions == target.DefaultMemberPermissions
- && sDmPerm == tDmPerm
- //&& (source.IsNsfw == target.IsNsfw)
+ && sDmPerm == tDmPerm && source.IsNsfw == target.IsNsfw
}
: type switch
{
ApplicationCommandType.ChatInput => DeepEqual(source, target, false, sDmPerm, tDmPerm),
_ => source.Name == target.Name
&& source.Type == target.Type
&& source.DefaultMemberPermissions == target.DefaultMemberPermissions
- && sDmPerm == tDmPerm
- //&& (source.IsNsfw == target.IsNsfw)
+ && sDmPerm == tDmPerm && source.IsNsfw == target.IsNsfw
};
}
///
/// Checks deeply whether two s are the same.
/// Excluding id, application id and version here.
///
/// Source application command.
/// Application command to check against.
/// Whether localization is enabled.
/// The source dm permission.
/// The target dm permission.
internal static bool DeepEqual(DiscordApplicationCommand source, DiscordApplicationCommand target, bool localizationEnabled = false, bool? sDmPerm = null, bool? tDmPerm = null)
{
var rootCheck = true;
/*Console.WriteLine($"{source.Name == target.Name}");
Console.WriteLine($"{source.Description == target.Description}");
Console.WriteLine($"{source.Type == target.Type}");
Console.WriteLine($"{source.DefaultMemberPermissions == target.DefaultMemberPermissions} - {source.DefaultMemberPermissions} == {target.DefaultMemberPermissions}");
Console.WriteLine($"{sDmPerm == tDmPerm}");*/
- rootCheck = source.Name == target.Name && source.Description == target.Description && source.Type == target.Type && source.DefaultMemberPermissions == target.DefaultMemberPermissions && sDmPerm == tDmPerm;
+ rootCheck = source.Name == target.Name && source.Description == target.Description && source.Type == target.Type && source.DefaultMemberPermissions == target.DefaultMemberPermissions && sDmPerm == tDmPerm && source.IsNsfw == target.IsNsfw;
if (localizationEnabled)
rootCheck = rootCheck && source.NameLocalizations == target.NameLocalizations && source.DescriptionLocalizations == target.DescriptionLocalizations;
//Console.WriteLine($"{rootCheck}");
if (source.Options == null && target.Options == null)
return rootCheck;
else if ((source.Options != null && target.Options == null) || (source.Options == null && target.Options != null))
return false;
else if (source.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommandGroup) && target.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommandGroup))
{
List minimalSourceOptions = new();
List minimalTargetOptions = new();
foreach (var option in source.Options)
{
List minimalSubSourceOptions = new();
if (option.Options != null)
{
foreach (var subOption in option.Options)
{
List minimalSubSubSourceOptions = null;
if (subOption.Options != null)
{
minimalSubSubSourceOptions = new();
foreach (var subSubOption in subOption.Options)
minimalSubSubSourceOptions.Add(new DiscordApplicationCommandOption(
subSubOption.Name, subSubOption.Description, subSubOption.Type, subSubOption.Required,
subSubOption.Choices, null, subSubOption.ChannelTypes, subSubOption.AutoComplete,
subSubOption.MinimumValue, subSubOption.MaximumValue,
localizationEnabled ? subSubOption.NameLocalizations : null,
localizationEnabled ? subSubOption.DescriptionLocalizations : null,
subSubOption.MinimumLength, subSubOption.MaximumLength
));
minimalSubSourceOptions.Add(new DiscordApplicationCommandOption(
subOption.Name, subOption.Description, subOption.Type,
options: minimalSubSubSourceOptions,
nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null,
descriptionLocalizations: localizationEnabled ? subOption.DescriptionLocalizations : null
));
}
}
}
minimalSourceOptions.Add(new DiscordApplicationCommandOption(
option.Name, option.Description, option.Type,
options: minimalSubSourceOptions,
nameLocalizations: localizationEnabled ? option.NameLocalizations : null,
descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null
));
}
foreach (var option in target.Options)
{
List minimalSubTargetOptions = new();
foreach (var subOption in option.Options)
{
List minimalSubSubTargetOptions = null;
if (subOption.Options != null && subOption.Options.Any())
{
minimalSubSubTargetOptions = new();
foreach (var subSubOption in subOption.Options)
minimalSubSubTargetOptions.Add(new DiscordApplicationCommandOption(
subSubOption.Name, subSubOption.Description, subSubOption.Type, subSubOption.Required,
subSubOption.Choices, null, subSubOption.ChannelTypes, subSubOption.AutoComplete,
subSubOption.MinimumValue, subSubOption.MaximumValue,
localizationEnabled ? subSubOption.NameLocalizations : null,
localizationEnabled ? subSubOption.DescriptionLocalizations : null,
subSubOption.MinimumLength, subSubOption.MaximumLength
));
minimalSubTargetOptions.Add(new DiscordApplicationCommandOption(
subOption.Name, subOption.Description, subOption.Type,
options: minimalSubSubTargetOptions,
nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null,
descriptionLocalizations: localizationEnabled ? subOption.DescriptionLocalizations : null
));
}
}
minimalTargetOptions.Add(new DiscordApplicationCommandOption(
option.Name, option.Description, option.Type,
options: minimalSubTargetOptions,
nameLocalizations: localizationEnabled ? option.NameLocalizations : null,
descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null
));
}
var sOpt = JsonConvert.SerializeObject(minimalSourceOptions, Formatting.None);
var tOpt = JsonConvert.SerializeObject(minimalTargetOptions, Formatting.None);
//Console.WriteLine("Checking equality subcommandgroup");
//Console.WriteLine($"{rootCheck}");
//Console.WriteLine($"{sOpt}");
//Console.WriteLine($"{tOpt}");
return rootCheck && sOpt == tOpt;
}
else if (source.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommand) && target.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommand))
{
List minimalSourceOptions = new();
List minimalTargetOptions = new();
foreach (var option in source.Options)
{
List minimalSubSourceOptions =null;
if (option.Options != null)
{
minimalSubSourceOptions = new();
foreach (var subOption in option.Options)
minimalSubSourceOptions.Add(new DiscordApplicationCommandOption(
subOption.Name, subOption.Description, subOption.Type, subOption.Required,
subOption.Choices, null, subOption.ChannelTypes, subOption.AutoComplete,
subOption.MinimumValue, subOption.MaximumValue,
localizationEnabled ? subOption.NameLocalizations : null,
localizationEnabled ? subOption.DescriptionLocalizations : null,
subOption.MinimumLength, subOption.MaximumLength
));
}
minimalSourceOptions.Add(new DiscordApplicationCommandOption(
option.Name, option.Description, option.Type,
options: minimalSubSourceOptions,
nameLocalizations: localizationEnabled ? option.NameLocalizations : null,
descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null
));
}
foreach (var option in target.Options)
{
List minimalSubTargetOptions = null;
if (option.Options != null && option.Options.Any())
{
minimalSubTargetOptions = new();
foreach (var subOption in option.Options)
minimalSubTargetOptions.Add(new DiscordApplicationCommandOption(
subOption.Name, subOption.Description, subOption.Type, subOption.Required,
subOption.Choices, null, subOption.ChannelTypes, subOption.AutoComplete,
subOption.MinimumValue, subOption.MaximumValue,
localizationEnabled ? subOption.NameLocalizations : null,
localizationEnabled ? subOption.DescriptionLocalizations : null,
subOption.MinimumLength, subOption.MaximumLength
));
}
minimalTargetOptions.Add(new DiscordApplicationCommandOption(
option.Name, option.Description, option.Type,
options: minimalSubTargetOptions,
nameLocalizations: localizationEnabled ? option.NameLocalizations : null,
descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null
));
}
var sOpt = JsonConvert.SerializeObject(minimalSourceOptions, Formatting.None);
var tOpt = JsonConvert.SerializeObject(minimalTargetOptions, Formatting.None);
//Console.WriteLine("Checking equality subcommand");
//Console.WriteLine($"{rootCheck}");
//Console.WriteLine($"{sOpt}");
//Console.WriteLine($"{tOpt}");
return rootCheck && sOpt == tOpt;
}
else
{
List minimalSourceOptions = new();
List minimalTargetOptions = new();
foreach (var option in source.Options)
minimalSourceOptions.Add(new DiscordApplicationCommandOption(
option.Name, option.Description, option.Type, option.Required,
option.Choices, null, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue,
localizationEnabled ? option.NameLocalizations : null,
localizationEnabled ? option.DescriptionLocalizations : null,
option.MinimumLength, option.MaximumLength
));
foreach (var option in target.Options)
minimalTargetOptions.Add(new DiscordApplicationCommandOption(
option.Name, option.Description, option.Type, option.Required,
option.Choices, null, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue,
localizationEnabled ? option.NameLocalizations : null,
localizationEnabled ? option.DescriptionLocalizations : null,
option.MinimumLength, option.MaximumLength
));
var sOpt = JsonConvert.SerializeObject(minimalSourceOptions, Formatting.None);
var tOpt = JsonConvert.SerializeObject(minimalTargetOptions, Formatting.None);
//Console.WriteLine("Checking equality other");
//Console.WriteLine($"{rootCheck}");
//Console.WriteLine($"{sOpt}");
//Console.WriteLine($"{tOpt}");
return rootCheck && sOpt == tOpt;
}
}
}
diff --git a/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs b/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs
index b676b5d01..abdc0560a 100644
--- a/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs
+++ b/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs
@@ -1,371 +1,371 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using DisCatSharp.ApplicationCommands.Attributes;
using DisCatSharp.ApplicationCommands.Context;
using DisCatSharp.ApplicationCommands.Entities;
using DisCatSharp.ApplicationCommands.Enums;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
namespace DisCatSharp.ApplicationCommands.Workers;
///
/// Represents a .
///
internal class CommandWorker
{
///
/// Parses context menu application commands.
///
/// The type.
/// List of method infos.
/// The optional command translations.
/// Too much.
internal static Task<
(
List applicationCommands,
List> commandTypeSources,
List contextMenuCommands,
bool withLocalization
)
> ParseContextMenuCommands(Type type, IEnumerable methods, List translator = null)
{
List commands = new();
List> commandTypeSources = new();
List contextMenuCommands = new();
foreach (var contextMethod in methods)
{
var contextAttribute = contextMethod.GetCustomAttribute();
DiscordApplicationCommandLocalization nameLocalizations = null;
var commandTranslation = translator?.Single(c => c.Name == contextAttribute.Name && c.Type == contextAttribute.Type);
if (commandTranslation != null)
nameLocalizations = commandTranslation.NameTranslations;
- var command = new DiscordApplicationCommand(contextAttribute.Name, null, null, contextAttribute.Type, nameLocalizations, null, contextAttribute.DefaultMemberPermissions, contextAttribute.DmPermission ?? true);
+ var command = new DiscordApplicationCommand(contextAttribute.Name, null, null, contextAttribute.Type, nameLocalizations, null, contextAttribute.DefaultMemberPermissions, contextAttribute.DmPermission ?? true, isNsfw: contextAttribute.IsNsfw);
var parameters = contextMethod.GetParameters();
if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.FirstOrDefault()?.ParameterType, typeof(ContextMenuContext)))
throw new ArgumentException($"The first argument of the command '{contextAttribute.Name}' has to be an ContextMenuContext!");
if (parameters.Length > 1)
throw new ArgumentException($"The context menu command '{contextAttribute.Name}' cannot have parameters!");
contextMenuCommands.Add(new ContextMenuCommand { Method = contextMethod, Name = contextAttribute.Name });
commands.Add(command);
commandTypeSources.Add(new KeyValuePair(type, type));
}
return Task.FromResult((commands, commandTypeSources, contextMenuCommands, translator != null));
}
///
/// Parses single application commands.
///
/// The type.
/// List of method infos.
/// The optional guild id.
/// The optional command translations.
/// Too much.
internal static async Task<
(
List applicationCommands,
List> commandTypeSources,
List commandMethods,
bool withLocalization
)
> ParseBasicSlashCommandsAsync(Type type, IEnumerable methods, ulong? guildId = null, List translator = null)
{
List commands = new();
List> commandTypeSources = new();
List commandMethods = new();
foreach (var method in methods)
{
var commandAttribute = method.GetCustomAttribute();
var parameters = method.GetParameters();
if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.FirstOrDefault()?.ParameterType, typeof(InteractionContext)))
throw new ArgumentException($"The first argument of the command '{commandAttribute.Name}' has to be an InteractionContext!");
var options = await ApplicationCommandsExtension.ParseParametersAsync(parameters.Skip(1), commandAttribute.Name, guildId);
commandMethods.Add(new CommandMethod { Method = method, Name = commandAttribute.Name });
DiscordApplicationCommandLocalization nameLocalizations = null;
DiscordApplicationCommandLocalization descriptionLocalizations = null;
List localizedOptions = null;
var commandTranslation = translator?.Single(c => c.Name == commandAttribute.Name && c.Type == ApplicationCommandType.ChatInput);
if (commandTranslation != null && commandTranslation.Options != null)
{
localizedOptions = new List(options.Count);
foreach (var option in options)
{
var choices = option.Choices != null ? new List(option.Choices.Count) : null;
if (option.Choices != null)
foreach (var choice in option.Choices)
choices.Add(new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, commandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations));
localizedOptions.Add(new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, option.Required,
choices, option.Options, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue,
commandTranslation.Options.Single(o => o.Name == option.Name).NameTranslations, commandTranslation.Options.Single(o => o.Name == option.Name).DescriptionTranslations,
option.MinimumLength, option.MaximumLength
));
}
nameLocalizations = commandTranslation.NameTranslations;
descriptionLocalizations = commandTranslation.DescriptionTranslations;
}
- var payload = new DiscordApplicationCommand(commandAttribute.Name, commandAttribute.Description, (localizedOptions != null && localizedOptions.Any() ? localizedOptions : null) ?? (options != null && options.Any() ? options : null), ApplicationCommandType.ChatInput, nameLocalizations, descriptionLocalizations, commandAttribute.DefaultMemberPermissions, commandAttribute.DmPermission ?? true);
+ var payload = new DiscordApplicationCommand(commandAttribute.Name, commandAttribute.Description, (localizedOptions != null && localizedOptions.Any() ? localizedOptions : null) ?? (options != null && options.Any() ? options : null), ApplicationCommandType.ChatInput, nameLocalizations, descriptionLocalizations, commandAttribute.DefaultMemberPermissions, commandAttribute.DmPermission ?? true, isNsfw: commandAttribute.IsNsfw);
commands.Add(payload);
commandTypeSources.Add(new KeyValuePair(type, type));
}
return (commands, commandTypeSources, commandMethods, translator != null);
}
}
///
/// Represents a .
///
internal class NestedCommandWorker
{
///
/// Parses application command groups.
///
/// The type.
/// List of type infos.
/// The optional guild id.
/// The optional group translations.
/// Too much.
internal static async Task<
(
List applicationCommands,
List> commandTypeSources,
List