diff --git a/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs b/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs
index 70279c949..cef336e63 100644
--- a/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs
+++ b/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.Linq;
@@ -11,6 +10,9 @@
namespace DisCatSharp.ApplicationCommands.Checks;
+///
+/// The application command equality checks.
+///
internal static class ApplicationCommandEqualityChecks
{
///
@@ -20,42 +22,54 @@ internal static class ApplicationCommandEqualityChecks
/// 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)
+ 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.AllowedContexts, ac1.IntegrationTypes
- );
+ ac1.Name, ac1.Description, ac1.Options,
+ ac1.Type,
+ ac1.NameLocalizations, ac1.DescriptionLocalizations,
+ ac1.DefaultMemberPermissions, ac1.DmPermission ?? true,
+ ac1.IsNsfw, ac1.AllowedContexts, ac1.IntegrationTypes
+ );
if (sourceApplicationCommand.DefaultMemberPermissions == Permissions.None &&
- targetApplicationCommand.DefaultMemberPermissions == null)
+ targetApplicationCommand.DefaultMemberPermissions == null)
sourceApplicationCommand.DefaultMemberPermissions = null;
if (isGuild)
{
sourceApplicationCommand.DmPermission = null;
targetApplicationCommand.DmPermission = null;
+ sourceApplicationCommand.IntegrationTypes = null;
+ targetApplicationCommand.IntegrationTypes = null;
+ sourceApplicationCommand.AllowedContexts = null;
+ targetApplicationCommand.AllowedContexts = null;
}
else
{
- sourceApplicationCommand.IntegrationTypes ??= new() { ApplicationCommandIntegrationTypes.InstalledToGuild };
- targetApplicationCommand.IntegrationTypes ??= new() { ApplicationCommandIntegrationTypes.InstalledToGuild };
+ sourceApplicationCommand.IntegrationTypes ??= new()
+ {
+ ApplicationCommandIntegrationTypes.InstalledToGuild
+ };
+ targetApplicationCommand.IntegrationTypes ??= new()
+ {
+ ApplicationCommandIntegrationTypes.InstalledToGuild
+ };
}
client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel,
- "[AC Change Check] Command {name}\n\n[{jsonOne},{jsontwo}]\n\n", ac1.Name,
- JsonConvert.SerializeObject(sourceApplicationCommand),
- JsonConvert.SerializeObject(targetApplicationCommand));
+ "[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);
+ ac1.Type, client, ApplicationCommandsExtension.Configuration?.EnableLocalization ?? false, isGuild);
}
///
@@ -67,8 +81,10 @@ internal static bool IsEqualTo(this DiscordApplicationCommand ac1,
/// 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)
+ internal static bool SoftEqual(
+ this DiscordApplicationCommand source, DiscordApplicationCommand target,
+ ApplicationCommandType type, BaseDiscordClient client, bool localizationEnabled = false, bool guild = false
+ )
{
bool? sDmPerm = source.DmPermission ?? true;
bool? tDmPerm = target.DmPermission ?? true;
@@ -76,23 +92,24 @@ internal static bool SoftEqual(this DiscordApplicationCommand source, DiscordApp
return localizationEnabled
? type switch
{
- ApplicationCommandType.ChatInput => DeepEqual(source, target, true, sDmPerm, tDmPerm),
+ ApplicationCommandType.ChatInput => DeepEqual(source, target, client, 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
- && source.AllowedContexts.NullableSequenceEqual(target.AllowedContexts) &&
- source.IntegrationTypes.NullableSequenceEqual(target.IntegrationTypes)
+ && source.Type == target.Type && source.NameLocalizations == target.NameLocalizations
+ && source.DefaultMemberPermissions == target.DefaultMemberPermissions
+ && sDmPerm == tDmPerm && source.IsNsfw == target.IsNsfw
+ && source.AllowedContexts.NullableSequenceEqual(target.AllowedContexts) &&
+ source.IntegrationTypes.NullableSequenceEqual(target.IntegrationTypes) &&
+ source.RawNameLocalizations.AreDictionariesEqual(target.RawNameLocalizations)
}
: type switch
{
- ApplicationCommandType.ChatInput => DeepEqual(source, target, false, sDmPerm, tDmPerm),
+ ApplicationCommandType.ChatInput => DeepEqual(source, target, client, false, sDmPerm, tDmPerm),
_ => source.Name == target.Name
- && source.Type == target.Type
- && source.DefaultMemberPermissions == target.DefaultMemberPermissions
- && sDmPerm == tDmPerm && source.IsNsfw == target.IsNsfw
- && source.AllowedContexts.NullableSequenceEqual(target.AllowedContexts) &&
- source.IntegrationTypes.NullableSequenceEqual(target.IntegrationTypes)
+ && source.Type == target.Type
+ && source.DefaultMemberPermissions == target.DefaultMemberPermissions
+ && sDmPerm == tDmPerm && source.IsNsfw == target.IsNsfw
+ && source.AllowedContexts.NullableSequenceEqual(target.AllowedContexts) &&
+ source.IntegrationTypes.NullableSequenceEqual(target.IntegrationTypes)
};
sDmPerm = null;
@@ -100,23 +117,20 @@ internal static bool SoftEqual(this DiscordApplicationCommand source, DiscordApp
return localizationEnabled
? type switch
{
- ApplicationCommandType.ChatInput => DeepEqual(source, target, true, sDmPerm, tDmPerm),
+ ApplicationCommandType.ChatInput => DeepEqual(source, target, client, 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
- && source.AllowedContexts.NullableSequenceEqual(target.AllowedContexts) &&
- source.IntegrationTypes.NullableSequenceEqual(target.IntegrationTypes)
+ && source.Type == target.Type && source.NameLocalizations == target.NameLocalizations
+ && source.DefaultMemberPermissions == target.DefaultMemberPermissions
+ && sDmPerm == tDmPerm && source.IsNsfw == target.IsNsfw
+ && source.RawNameLocalizations.AreDictionariesEqual(target.RawNameLocalizations)
}
: type switch
{
- ApplicationCommandType.ChatInput => DeepEqual(source, target, false, sDmPerm, tDmPerm),
+ ApplicationCommandType.ChatInput => DeepEqual(source, target, client, false, sDmPerm, tDmPerm),
_ => source.Name == target.Name
- && source.Type == target.Type
- && source.DefaultMemberPermissions == target.DefaultMemberPermissions
- && sDmPerm == tDmPerm && source.IsNsfw == target.IsNsfw
- && source.AllowedContexts.NullableSequenceEqual(target.AllowedContexts) &&
- source.IntegrationTypes.NullableSequenceEqual(target.IntegrationTypes)
+ && source.Type == target.Type
+ && source.DefaultMemberPermissions == target.DefaultMemberPermissions
+ && sDmPerm == tDmPerm && source.IsNsfw == target.IsNsfw
};
}
@@ -127,13 +141,33 @@ internal static bool SoftEqual(this DiscordApplicationCommand source, DiscordApp
/// The source enumerable.
/// The target enumerable.
/// Whether both nullable enumerable are equal.
- internal static bool NullableSequenceEqual(this IEnumerable? source, IEnumerable? target)
+ internal static bool NullableSequenceEqual(this List? source, List? target)
{
if (source is not null && target is not null)
- return source.OrderBy(x => x).SequenceEqual(target.OrderBy(x => x));
+ return source.All(target.Contains) && source.Count == target.Count;
+
+ return source is null && target is null;
+ }
+
+ ///
+ /// Checks whether two dictionaries are equal.
+ ///
+ /// The source dictionary.
+ /// The target dictionary.
+ /// Whether both dictionaries are equal.
+ internal static bool AreDictionariesEqual(this Dictionary sourceDictionary, Dictionary targetDictionary)
+ {
+ if (sourceDictionary?.Count != targetDictionary?.Count)
+ return false;
+
+ if (sourceDictionary is null && targetDictionary is null)
+ return true;
+
+ foreach (var kvp in sourceDictionary)
+ if (!targetDictionary.TryGetValue(kvp.Key, out var value) || value != kvp.Value)
+ return false;
- return (source is null || target is not null) &&
- (source is not null || target is null);
+ return true;
}
///
@@ -145,29 +179,33 @@ internal static bool NullableSequenceEqual(this IEnumerable? source, IEnum
/// 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)
+ internal static bool DeepEqual(
+ DiscordApplicationCommand source, DiscordApplicationCommand target, BaseDiscordClient client,
+ bool localizationEnabled = false, bool? sDmPerm = null, bool? tDmPerm = null
+ )
{
var name = source.Name;
var rootCheck = source.Name == target.Name &&
- source.Description == target.Description &&
- source.Type == target.Type &&
- source.DefaultMemberPermissions == target.DefaultMemberPermissions &&
- sDmPerm == tDmPerm &&
- source.IsNsfw == target.IsNsfw
- && source.AllowedContexts.NullableSequenceEqual(target.AllowedContexts) &&
- source.IntegrationTypes.NullableSequenceEqual(target.IntegrationTypes);
+ source.Description == target.Description &&
+ source.Type == target.Type &&
+ source.DefaultMemberPermissions == target.DefaultMemberPermissions &&
+ sDmPerm == tDmPerm &&
+ source.IsNsfw == target.IsNsfw &&
+ source.AllowedContexts.NullableSequenceEqual(target.AllowedContexts) &&
+ source.IntegrationTypes.NullableSequenceEqual(target.IntegrationTypes);
if (localizationEnabled)
rootCheck = rootCheck &&
- source.NameLocalizations.Localizations.NullableSequenceEqual(target.NameLocalizations.Localizations) &&
- source.DescriptionLocalizations.Localizations.NullableSequenceEqual(target.DescriptionLocalizations
- .Localizations);
+ source.RawNameLocalizations.AreDictionariesEqual(target.RawNameLocalizations) &&
+ source.RawDescriptionLocalizations.AreDictionariesEqual(target.RawDescriptionLocalizations);
- // Compare the Options using recursion
var optionsEqual = DeepEqualOptions(source.Options, target.Options, localizationEnabled);
+ if (optionsEqual.Reason is not null)
+ client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, "Inequality found in options of {name} - {reason}", name, optionsEqual.Reason);
+ if (!rootCheck)
+ client.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, "Inequality found in root of {name}", name);
- return rootCheck && optionsEqual;
+ return rootCheck && optionsEqual.Equal;
}
///
@@ -176,17 +214,19 @@ internal static bool DeepEqual(DiscordApplicationCommand source, DiscordApplicat
/// Source options.
/// Options to check against.
/// Whether localization is enabled.
- private static bool DeepEqualOptions(IReadOnlyList? sourceOptions,
- IReadOnlyList? targetOptions, bool localizationEnabled)
+ private static (bool Equal, string? Reason) DeepEqualOptions(
+ IReadOnlyList? sourceOptions,
+ IReadOnlyList? targetOptions, bool localizationEnabled
+ )
{
if (sourceOptions == null && targetOptions == null)
- return true;
+ return (true, null);
- if ((sourceOptions != null && targetOptions == null) || (sourceOptions == null && targetOptions != null))
- return false;
+ if (sourceOptions != null && targetOptions == null || sourceOptions == null && targetOptions != null)
+ return (false, "source or target option null, but not both");
if (sourceOptions!.Count != targetOptions!.Count)
- return false;
+ return (false, "source option count mismatches target option count");
for (var i = 0; i < sourceOptions.Count; i++)
{
@@ -194,47 +234,47 @@ private static bool DeepEqualOptions(IReadOnlyList x.Name), Formatting.None);
var j2 = JsonConvert.SerializeObject(targetOption.Choices.OrderBy(x => x.Name), Formatting.None);
if (j1 != j2)
- return false;
+ return (false, "choice mismatch - " + sourceOption.Name);
}
- if ((sourceOption.ChannelTypes is null && targetOption.ChannelTypes is not null) ||
- (sourceOption.ChannelTypes is not null && targetOption.ChannelTypes is null) ||
- (sourceOption.ChannelTypes is not null && targetOption.ChannelTypes is not null &&
- !sourceOption.ChannelTypes.OrderBy(x => x).All(targetOption.ChannelTypes.OrderBy(x => x).Contains)))
- return false;
+ if (sourceOption.ChannelTypes is null && targetOption.ChannelTypes is not null ||
+ sourceOption.ChannelTypes is not null && targetOption.ChannelTypes is null ||
+ sourceOption.ChannelTypes is not null && targetOption.ChannelTypes is not null &&
+ !sourceOption.ChannelTypes.OrderBy(x => x).All(targetOption.ChannelTypes.OrderBy(x => x).Contains))
+ return (false, "channel type mismatch - " + sourceOption.Name);
- if (!DeepEqualOptions(sourceOption.Options, targetOption.Options, localizationEnabled))
- return false;
+ var rec = DeepEqualOptions(sourceOption.Options, targetOption.Options, localizationEnabled);
+ if (!rec.Equal)
+ return (false, "Options Recursive - " + sourceOption.Name + " -- " + rec.Reason);
if (!optionCheck)
- return false;
+ return (false, "OptionCheck - " + sourceOption.Name);
}
- return true;
+ return (true, null);
}
}