diff --git a/src/Discord.Net.Commands/Attributes/FallbackToDefaultAttribute.cs b/src/Discord.Net.Commands/Attributes/FallbackToDefaultAttribute.cs
new file mode 100644
index 000000000..04c16e6df
--- /dev/null
+++ b/src/Discord.Net.Commands/Attributes/FallbackToDefaultAttribute.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace Discord.Commands
+{
+ ///
+ /// When applied to an optional command parameter,
+ /// indicates that its default value should be used
+ /// if the TypeReader fails to read a valid value.
+ ///
+ ///
+ ///
+ /// [Command("stats")]
+ /// public async Task GetStatsAsync([FallbackToDefault] IUser user = null)
+ /// {
+ /// if (user is null)
+ /// {
+ /// await ReplyAsync("Couldn't find that user");
+ /// return;
+ /// }
+ ///
+ /// // ...pull stats
+ /// }
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
+ public sealed class FallbackToDefaultAttribute : Attribute { }
+}
diff --git a/src/Discord.Net.Commands/Info/ParameterInfo.cs b/src/Discord.Net.Commands/Info/ParameterInfo.cs
index 7090ff59d..be9dfedab 100644
--- a/src/Discord.Net.Commands/Info/ParameterInfo.cs
+++ b/src/Discord.Net.Commands/Info/ParameterInfo.cs
@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
+using System.Linq;
using System.Threading.Tasks;
namespace Discord.Commands
@@ -36,6 +37,7 @@ namespace Discord.Commands
///
public bool IsRemainder { get; }
public bool IsMultiple { get; }
+ public bool UseDefaultOnFail { get; }
///
/// Gets the type of the parameter.
///
@@ -63,6 +65,7 @@ namespace Discord.Commands
IsOptional = builder.IsOptional;
IsRemainder = builder.IsRemainder;
IsMultiple = builder.IsMultiple;
+ UseDefaultOnFail = (IsOptional && builder.Attributes.Any(attr => attr is FallbackToDefaultAttribute));
Type = builder.ParameterType;
DefaultValue = builder.DefaultValue;
@@ -91,7 +94,7 @@ namespace Discord.Commands
{
services = services ?? EmptyServiceProvider.Instance;
var readerResult = await _reader.ReadAsync(context, input, services).ConfigureAwait(false);
- return (!readerResult.IsSuccess && IsOptional)
+ return (!readerResult.IsSuccess && UseDefaultOnFail)
? TypeReaderResult.FromSuccess(DefaultValue)
: readerResult;
}