diff --git a/VpSharp.Commands/Attributes/ExecutionChecks/PreExecutionCheckAttribute.cs b/VpSharp.Commands/Attributes/ExecutionChecks/PreExecutionCheckAttribute.cs
new file mode 100644
index 0000000..26edd99
--- /dev/null
+++ b/VpSharp.Commands/Attributes/ExecutionChecks/PreExecutionCheckAttribute.cs
@@ -0,0 +1,13 @@
+namespace VpSharp.Commands.Attributes.ExecutionChecks;
+
+///
+/// Represents the base class for all execution check attributes.
+///
+public abstract class PreExecutionCheckAttribute : Attribute
+{
+ ///
+ /// Performs the execution check.
+ ///
+ /// if the execution check has passed; otherwise, .
+ protected internal abstract Task PerformAsync(CommandContext context);
+}
diff --git a/VpSharp.Commands/Attributes/ExecutionChecks/RequireAvatarNameAttribute.cs b/VpSharp.Commands/Attributes/ExecutionChecks/RequireAvatarNameAttribute.cs
new file mode 100644
index 0000000..5b851a6
--- /dev/null
+++ b/VpSharp.Commands/Attributes/ExecutionChecks/RequireAvatarNameAttribute.cs
@@ -0,0 +1,31 @@
+namespace VpSharp.Commands.Attributes.ExecutionChecks;
+
+///
+/// Specifies that this command can only be run by bots.
+///
+public sealed class RequireAvatarNameAttribute : PreExecutionCheckAttribute
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// An array of allowed user names.
+ /// is .
+ public RequireAvatarNameAttribute(params string[] names)
+ {
+ ArgumentNullException.ThrowIfNull(names);
+ Names = names[..];
+ }
+
+ ///
+ /// Gets a read-only view of the user names allowed to run this command.
+ ///
+ /// A of representing the allowed user names.
+ public IReadOnlyList Names { get; }
+
+ ///
+ protected internal override Task PerformAsync(CommandContext context)
+ {
+ ArgumentNullException.ThrowIfNull(context);
+ return Task.FromResult(Names.Contains(context.Avatar.Name));
+ }
+}
diff --git a/VpSharp.Commands/Attributes/ExecutionChecks/RequireBotAttribute.cs b/VpSharp.Commands/Attributes/ExecutionChecks/RequireBotAttribute.cs
new file mode 100644
index 0000000..3afce7c
--- /dev/null
+++ b/VpSharp.Commands/Attributes/ExecutionChecks/RequireBotAttribute.cs
@@ -0,0 +1,14 @@
+namespace VpSharp.Commands.Attributes.ExecutionChecks;
+
+///
+/// Specifies that this command can only be run by bots.
+///
+public sealed class RequireBotAttribute : PreExecutionCheckAttribute
+{
+ ///
+ protected internal override Task PerformAsync(CommandContext context)
+ {
+ ArgumentNullException.ThrowIfNull(context);
+ return Task.FromResult(context.Avatar.IsBot);
+ }
+}
diff --git a/VpSharp.Commands/Attributes/ExecutionChecks/RequireHumanAttribute.cs b/VpSharp.Commands/Attributes/ExecutionChecks/RequireHumanAttribute.cs
new file mode 100644
index 0000000..83332a9
--- /dev/null
+++ b/VpSharp.Commands/Attributes/ExecutionChecks/RequireHumanAttribute.cs
@@ -0,0 +1,14 @@
+namespace VpSharp.Commands.Attributes.ExecutionChecks;
+
+///
+/// Specifies that this command cannot be run by bots.
+///
+public sealed class RequireHumanAttribute : PreExecutionCheckAttribute
+{
+ ///
+ protected internal override Task PerformAsync(CommandContext context)
+ {
+ ArgumentNullException.ThrowIfNull(context);
+ return Task.FromResult(!context.Avatar.IsBot);
+ }
+}
diff --git a/VpSharp.Commands/Attributes/ExecutionChecks/RequireUserIdAttribute.cs b/VpSharp.Commands/Attributes/ExecutionChecks/RequireUserIdAttribute.cs
new file mode 100644
index 0000000..57541b1
--- /dev/null
+++ b/VpSharp.Commands/Attributes/ExecutionChecks/RequireUserIdAttribute.cs
@@ -0,0 +1,31 @@
+namespace VpSharp.Commands.Attributes.ExecutionChecks;
+
+///
+/// Specifies that this command can only be run by bots.
+///
+public sealed class RequireUserIdAttribute : PreExecutionCheckAttribute
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// An array of allowed user IDs.
+ /// is .
+ public RequireUserIdAttribute(params int[] userIds)
+ {
+ ArgumentNullException.ThrowIfNull(userIds);
+ UserIds = userIds[..];
+ }
+
+ ///
+ /// Gets a read-only view of the user IDs allowed to run this command.
+ ///
+ /// A of representing the allowed user IDs.
+ public IReadOnlyList UserIds { get; }
+
+ ///
+ protected internal override Task PerformAsync(CommandContext context)
+ {
+ ArgumentNullException.ThrowIfNull(context);
+ return Task.FromResult(UserIds.Contains(context.Avatar.User.Id));
+ }
+}
diff --git a/VpSharp.Commands/Attributes/ExecutionChecks/RequireUserNameAttribute.cs b/VpSharp.Commands/Attributes/ExecutionChecks/RequireUserNameAttribute.cs
new file mode 100644
index 0000000..5ccb619
--- /dev/null
+++ b/VpSharp.Commands/Attributes/ExecutionChecks/RequireUserNameAttribute.cs
@@ -0,0 +1,31 @@
+namespace VpSharp.Commands.Attributes.ExecutionChecks;
+
+///
+/// Specifies that this command can only be run by bots.
+///
+public sealed class RequireUserNameAttribute : PreExecutionCheckAttribute
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// An array of allowed user names.
+ /// is .
+ public RequireUserNameAttribute(params string[] names)
+ {
+ ArgumentNullException.ThrowIfNull(names);
+ Names = names[..];
+ }
+
+ ///
+ /// Gets a read-only view of the user names allowed to run this command.
+ ///
+ /// A of representing the allowed user names.
+ public IReadOnlyList Names { get; }
+
+ ///
+ protected internal override Task PerformAsync(CommandContext context)
+ {
+ ArgumentNullException.ThrowIfNull(context);
+ return Task.FromResult(Names.Contains(context.Avatar.User.Name));
+ }
+}
diff --git a/VpSharp.Commands/CommandsExtension.cs b/VpSharp.Commands/CommandsExtension.cs
index 105e73f..fedfe32 100644
--- a/VpSharp.Commands/CommandsExtension.cs
+++ b/VpSharp.Commands/CommandsExtension.cs
@@ -2,6 +2,7 @@ using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using VpSharp.ClientExtensions;
using VpSharp.Commands.Attributes;
+using VpSharp.Commands.Attributes.ExecutionChecks;
using VpSharp.Entities;
using VpSharp.EventData;
@@ -218,8 +219,16 @@ public sealed class CommandsExtension : VirtualParadiseClientExtension
}
var context = new CommandContext(Client, message.Author, command.Name, commandNameString, rawArguments.ToString());
- object?[] arguments = {context};
+ foreach (var attribute in command.Method.GetCustomAttributes())
+ {
+ if (!attribute.PerformAsync(context).ConfigureAwait(false).GetAwaiter().GetResult())
+ {
+ return base.OnMessageReceived(args);
+ }
+ }
+
+ object?[] arguments = {context};
if (rawArguments.Length > 0)
{
spaceIndex = rawArguments.IndexOf(' ');