Skip to content

Commit

Permalink
Rewrite command registration
Browse files Browse the repository at this point in the history
Cleans up registration and properly registers user-install and guild-install commands

Resolves #29
  • Loading branch information
FloatingMilkshake committed Dec 16, 2024
1 parent 2fb89c4 commit a16edb9
Show file tree
Hide file tree
Showing 12 changed files with 208 additions and 88 deletions.
2 changes: 0 additions & 2 deletions Commands/EmojiCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,6 @@ await ctx.FollowupAsync(new DiscordFollowupMessageBuilder().WithContent(

[Command("enlarge")]
[Description("Enlarge an emoji! Only works for custom emoji.")]
[InteractionInstallType(DiscordApplicationIntegrationType.GuildInstall)]
[InteractionAllowedContexts(DiscordInteractionContextType.Guild)]
public static async Task EnlargeEmoji(MechanicalMilkshake.SlashCommandContext ctx, [Parameter("emoji"), Description("The emoji to enlarge.")] string emoji)
{
await ctx.DeferResponseAsync();
Expand Down
2 changes: 1 addition & 1 deletion Commands/Err.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public static async Task ErrCmd(MechanicalMilkshake.SlashCommandContext ctx, [Pa

if (Program.DisabledCommands.Contains("err"))
{
await CommandHandlerHelpers.FailOnMissingInfo(ctx, true);
await CommandHelpers.FailOnMissingInfo(ctx, true);
return;
}

Expand Down
2 changes: 1 addition & 1 deletion Commands/Feedback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static async Task FeedbackCommand(MechanicalMilkshake.SlashCommandContext
{
if (Program.DisabledCommands.Contains("feedback"))
{
await CommandHandlerHelpers.FailOnMissingInfo(ctx, false);
await CommandHelpers.FailOnMissingInfo(ctx, false);
return;
}

Expand Down
6 changes: 3 additions & 3 deletions Commands/Owner/HomeServerCommands/CdnCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public static async Task Upload(MechanicalMilkshake.SlashCommandContext ctx,

if (Program.DisabledCommands.Contains("cdn"))
{
await CommandHandlerHelpers.FailOnMissingInfo(ctx, true);
await CommandHelpers.FailOnMissingInfo(ctx, true);
return;
}

Expand Down Expand Up @@ -119,7 +119,7 @@ public static async Task DeleteUpload(MechanicalMilkshake.SlashCommandContext ct

if (Program.DisabledCommands.Contains("cdn"))
{
await CommandHandlerHelpers.FailOnMissingInfo(ctx, true);
await CommandHelpers.FailOnMissingInfo(ctx, true);
return;
}

Expand Down Expand Up @@ -191,7 +191,7 @@ public static async Task CdnPreview(MechanicalMilkshake.SlashCommandContext ctx,
{
if (Program.DisabledCommands.Contains("cdn"))
{
await CommandHandlerHelpers.FailOnMissingInfo(ctx, false);
await CommandHelpers.FailOnMissingInfo(ctx, false);
return;
}

Expand Down
6 changes: 3 additions & 3 deletions Commands/Owner/HomeServerCommands/LinkCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static async Task SetWorkerLink(MechanicalMilkshake.SlashCommandContext c

if (Program.DisabledCommands.Contains("wl"))
{
await CommandHandlerHelpers.FailOnMissingInfo(ctx, true);
await CommandHelpers.FailOnMissingInfo(ctx, true);
return;
}

Expand Down Expand Up @@ -91,7 +91,7 @@ public static async Task DeleteWorkerLink(MechanicalMilkshake.SlashCommandContex

if (Program.DisabledCommands.Contains("wl"))
{
await CommandHandlerHelpers.FailOnMissingInfo(ctx, true);
await CommandHelpers.FailOnMissingInfo(ctx, true);
return;
}

Expand Down Expand Up @@ -154,7 +154,7 @@ public static async Task ListWorkerLinks(MechanicalMilkshake.SlashCommandContext

if (Program.DisabledCommands.Contains("wl"))
{
await CommandHandlerHelpers.FailOnMissingInfo(ctx, true);
await CommandHelpers.FailOnMissingInfo(ctx, true);
return;
}

Expand Down
2 changes: 2 additions & 0 deletions Commands/Owner/Tellraw.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ public class Tellraw
[Command("tellraw")]
[Description("???")]
[RequireAuth]
[InteractionInstallType(DiscordApplicationIntegrationType.GuildInstall)]
[InteractionAllowedContexts(DiscordInteractionContextType.Guild)]
public static async Task TellrawCommand(MechanicalMilkshake.SlashCommandContext ctx,
[Parameter("message"), Description("!!!")] [MinMaxLength(maxLength: 2000)]
string message,
Expand Down
2 changes: 1 addition & 1 deletion Commands/WolframAlpha.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static async Task WolframAlphaCommand(MechanicalMilkshake.SlashCommandCon

if (Program.DisabledCommands.Contains("wa"))
{
await CommandHandlerHelpers.FailOnMissingInfo(ctx, true);
await CommandHelpers.FailOnMissingInfo(ctx, true);
return;
}

Expand Down
21 changes: 0 additions & 21 deletions Helpers/CommandHandlerHelpers.cs

This file was deleted.

158 changes: 158 additions & 0 deletions Helpers/CommandHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
namespace MechanicalMilkshake.Helpers;

public class CommandHelpers
{
/// <summary>
/// Responds to an interaction with a failure message if the command is disabled.
/// Commands are disabled if required configuration information is missing.
/// </summary>
/// <param name="ctx">Interaction context used to respond to the interaction.</param>
/// <param name="isFollowUp">Whether to follow-up to the interaction (as opposed to creating a new interaction response).</param>
public static async Task FailOnMissingInfo(MechanicalMilkshake.SlashCommandContext ctx, bool isFollowUp)
{
const string failureMsg =
"This command is disabled! Please make sure you have provided values for all of the necessary keys in the config file.";

if (isFollowUp)
await ctx.FollowupAsync(failureMsg);
else
await ctx.RespondAsync(failureMsg);
}

/// <summary>
/// Registers commands with the provided CommandsExtension.
/// </summary>
/// <param name="extension">The extension to register commands with.</param>
/// <param name="homeServerId">The ID of the server to register home server-only commands in.</param>
public static void RegisterCommands(CommandsExtension extension, ulong homeServerId)
{
var publicInteractionCommandTypes = GetPublicInteractionCommandTypes();
var userInstallCommandTypes = GetUserInstallInteractionCommandTypes(publicInteractionCommandTypes);
var guildInstallCommandTypes = GetGuildInstallInteractionCommandTypes(publicInteractionCommandTypes);
var homeServerSlashCommandTypes = GetHomeServerInteractionCommandTypes();

// Always register owner commands in home server only
extension.AddCommands(homeServerSlashCommandTypes, homeServerId);

// Always register user-install commands globally
extension.AddCommands(userInstallCommandTypes);
#if DEBUG
// Register guild-install commands in home server when debugging
extension.AddCommands(guildInstallCommandTypes, homeServerId);

if (Program.ConfigJson.Base.UseServerSpecificFeatures)
{
// Register server-specific feature commands in home server when debugging
extension.AddCommands<ServerSpecificFeatures.RoleCommands>(homeServerId);
extension.AddCommands<ServerSpecificFeatures.MessageCommands>(homeServerId);
}
#else
// Register guild-install commands globally for 'production' bot
extension.AddCommands(guildInstallCommandTypes);

if (Program.ConfigJson.Base.UseServerSpecificFeatures)
{
// Register server-specific feature commands in respective guilds for 'production' bot
extension.AddCommands<ServerSpecificFeatures.RoleCommands>(homeServerId, 984903591816990730);
extension.AddCommands<ServerSpecificFeatures.MessageCommands>(homeServerId);
}
#endif
}

/// <summary>
/// Gets a list of all public interaction command types. To be used for registering commands.
/// </summary>
/// <returns>A list of public interaction command types.</returns>
private static List<Type> GetPublicInteractionCommandTypes()
{
return Assembly.GetExecutingAssembly().GetTypes().Where(t =>
t.IsClass && t.Namespace is not null && t.Namespace.Contains("MechanicalMilkshake.Commands") &&
!t.IsNested).ToList();
}

/// <summary>
/// Gets a list of all interaction command types that allow user-install. To be used for registering commands.
/// </summary>
/// <param name="publicInteractionCommandTypes">A list of all public interaction command types to filter through.</param>
/// <returns>A list of all interaction command types that allow user-install.</returns>
private static List<Type> GetUserInstallInteractionCommandTypes(List<Type> publicInteractionCommandTypes)
{
List<Type> userInstallInteractionCommandTypes = [];

foreach (var type in publicInteractionCommandTypes)
{
// Check type (class) for InteractionInstallTypeAttribute; if present, check if it allows user install
if (type.GetCustomAttributes(typeof(InteractionInstallTypeAttribute), true).FirstOrDefault() is InteractionInstallTypeAttribute installTypeAttribute
&& installTypeAttribute.InstallTypes.Contains(DiscordApplicationIntegrationType.UserInstall))
{
userInstallInteractionCommandTypes.Add(type);
}
else
{
// Check methods for InteractionInstallTypeAttribute; if present, check if it allows user install
var methods = type.GetMethods();
foreach (var method in methods)
{
if (method.GetCustomAttributes(typeof(InteractionInstallTypeAttribute), true).FirstOrDefault() is InteractionInstallTypeAttribute methodInstallTypeAttribute
&& methodInstallTypeAttribute.InstallTypes.Contains(DiscordApplicationIntegrationType.UserInstall))
{
userInstallInteractionCommandTypes.Add(type);
break;
}
}
}
}

return userInstallInteractionCommandTypes;
}

/// <summary>
/// Gets a list of all interaction command types that allow guild-install and do not allow user-install. To be used for registering commands.
/// </summary>
/// <param name="publicInteractionCommandTypes">A list of all public interaction command types to filter through.</param>
/// <returns>A list of all interaction command types that allow guild-install and do not allow user-install.</returns>
private static List<Type> GetGuildInstallInteractionCommandTypes(List<Type> publicInteractionCommandTypes)
{
List<Type> guildInstallInteractionCommandTypes = [];

foreach (var type in publicInteractionCommandTypes)
{
// Check type (class) for InteractionInstallTypeAttribute; if present, check if it allows guild install
if (type.GetCustomAttributes(typeof(InteractionInstallTypeAttribute), true).FirstOrDefault() is InteractionInstallTypeAttribute installTypeAttribute
&& installTypeAttribute.InstallTypes.Contains(DiscordApplicationIntegrationType.GuildInstall)
&& !installTypeAttribute.InstallTypes.Contains(DiscordApplicationIntegrationType.UserInstall))
{
guildInstallInteractionCommandTypes.Add(type);
}
else
{
// Check methods for InteractionInstallTypeAttribute; if present, check if it allows guild install
var methods = type.GetMethods();
foreach (var method in methods)
{
if (method.GetCustomAttributes(typeof(InteractionInstallTypeAttribute), true).FirstOrDefault() is InteractionInstallTypeAttribute methodInstallTypeAttribute
&& methodInstallTypeAttribute.InstallTypes.Contains(DiscordApplicationIntegrationType.GuildInstall)
&& !methodInstallTypeAttribute.InstallTypes.Contains(DiscordApplicationIntegrationType.UserInstall))
{
guildInstallInteractionCommandTypes.Add(type);
break;
}
}
}
}

return guildInstallInteractionCommandTypes;
}

/// <summary>
/// Gets a list of all interaction command types that are only allowed in the home server. To be used for registering commands.
/// </summary>
/// <returns>A list of all interaction command types that are only allowed in the home server.</returns>
private static List<Type> GetHomeServerInteractionCommandTypes()
{
return Assembly.GetExecutingAssembly().GetTypes().Where(t =>
t.IsClass && t.Namespace is not null &&
t.Namespace.Contains("MechanicalMilkshake.Commands.Owner.HomeServerCommands") &&
!t.IsNested).ToList();
}
}
5 changes: 3 additions & 2 deletions Helpers/DebugInfoHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static DebugInfo GetDebugInfo()
}

// If provided a DebugInfo object, use that...
private static Task<DiscordEmbed> GenerateDebugInfoEmbed(DebugInfo debugInfo, bool isOnStartup)
private static async Task<DiscordEmbed> GenerateDebugInfoEmbed(DebugInfo debugInfo, bool isOnStartup)
{
// Check whether GuildDownloadCompleted has been fired yet
// If not, wait until it has
Expand All @@ -67,13 +67,14 @@ private static Task<DiscordEmbed> GenerateDebugInfoEmbed(DebugInfo debugInfo, bo
embed.AddField("Platform", debugInfo.Platform, true);
embed.AddField("Library", debugInfo.Library, true);
embed.AddField("Server Count", Program.Discord.Guilds.Count.ToString(), true);
while (Program.ApplicationCommands is null) await Task.Delay(500);
embed.AddField("Command Count", Program.ApplicationCommands.Count.ToString(), true);
if (isOnStartup) embed.AddField("Time Since Process Start", debugInfo.TimeSinceProcessStart, true);
embed.AddField("Commit Hash", commitHash, true);
embed.AddField(debugInfo.CommitTimeDescription, debugInfo.CommitTimestamp, true);
embed.AddField("Commit Message", debugInfo.CommitMessage);

return Task.FromResult<DiscordEmbed>(embed);
return embed;
}

// ...otherwise, get debug info manually
Expand Down
Loading

0 comments on commit a16edb9

Please sign in to comment.