diff --git a/src/Discord.Net.Commands/Attributes/CommandAttribute.cs b/src/Discord.Net.Commands/Attributes/CommandAttribute.cs index baac75ff9..5ae6092eb 100644 --- a/src/Discord.Net.Commands/Attributes/CommandAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/CommandAttribute.cs @@ -6,7 +6,7 @@ namespace Discord.Commands public class CommandAttribute : Attribute { public string Text { get; } - public RunMode RunMode { get; set; } = RunMode.Sync; + public RunMode RunMode { get; set; } = RunMode.Default; public CommandAttribute() { diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index 9884f18db..aaec43161 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -115,7 +115,7 @@ namespace Discord.Commands private static void BuildCommand(CommandBuilder builder, TypeInfo typeInfo, MethodInfo method, CommandService service) { var attributes = method.GetCustomAttributes(); - + foreach (var attribute in attributes) { // TODO: C#7 type switch @@ -140,6 +140,9 @@ namespace Discord.Commands builder.AddPrecondition(attribute as PreconditionAttribute); } + if (builder.Name == null) + builder.Name = method.Name; + var parameters = method.GetParameters(); int pos = 0, count = parameters.Length; foreach (var paramInfo in parameters) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 3c3760908..9420476d5 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -19,10 +19,13 @@ namespace Discord.Commands private readonly ConcurrentBag _moduleDefs; private readonly CommandMap _map; - public IEnumerable Modules => _typedModuleDefs.Select(x => x.Value); - public IEnumerable Commands => _typedModuleDefs.SelectMany(x => x.Value.Commands); + internal readonly RunMode _defaultRunMode; - public CommandService() + public IEnumerable Modules => _moduleDefs.Select(x => x); + public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); + + public CommandService() : this(new CommandServiceConfig()) { } + public CommandService(CommandServiceConfig config) { _moduleLock = new SemaphoreSlim(1, 1); _typedModuleDefs = new ConcurrentDictionary(); @@ -64,6 +67,7 @@ namespace Discord.Commands [typeof(IGroupUser)] = new UserTypeReader(), [typeof(IGuildUser)] = new UserTypeReader(), }; + _defaultRunMode = config.DefaultRunMode; } //Modules diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs new file mode 100644 index 000000000..97c98a54c --- /dev/null +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -0,0 +1,8 @@ +namespace Discord.Commands +{ + public class CommandServiceConfig + { + /// The default RunMode commands should have, if one is not specified on the Command attribute or builder. + public RunMode DefaultRunMode { get; set; } = RunMode.Mixed; + } +} diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 4d546b6fa..2ca8b1651 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -37,13 +37,21 @@ namespace Discord.Commands Summary = builder.Summary; Remarks = builder.Remarks; - RunMode = builder.RunMode; + RunMode = (builder.RunMode == RunMode.Default ? service._defaultRunMode : builder.RunMode); Priority = builder.Priority; - - if (module.Aliases.Count != 0) - Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => first + " " + second).ToImmutableArray(); - else + + // both command and module provide aliases + if (module.Aliases.Count > 0 && builder.Aliases.Count > 0) + Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => second != null ? first + " " + second : first).ToImmutableArray(); + // only module provides aliases + else if (module.Aliases.Count > 0) + Aliases = module.Aliases.ToImmutableArray(); + // only command provides aliases + else if (builder.Aliases.Count > 0) Aliases = builder.Aliases.ToImmutableArray(); + // neither provide aliases + else + throw new InvalidOperationException("Cannot build a command without any aliases"); Preconditions = builder.Preconditions.ToImmutableArray(); diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index 64fa29ea2..3baa2d34f 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -49,15 +49,21 @@ namespace Discord.Commands while (builderStack.Count() > 0) { - ModuleBuilder level = builderStack.Pop(); // get the topmost builder + ModuleBuilder level = builderStack.Pop(); //get the topmost builder if (result == null) - result = level.Aliases.ToList(); // create a shallow copy so we don't overwrite the builder unexpectedly + { + if (level.Aliases.Count > 0) + result = level.Aliases.ToList(); //create a shallow copy so we don't overwrite the builder unexpectedly + } else if (result.Count() > level.Aliases.Count) result = result.Permutate(level.Aliases, (first, second) => first + " " + second); else result = level.Aliases.Permutate(result, (second, first) => first + " " + second); } + if (result == null) //there were no aliases; default to an empty list + result = new List(); + return result; } diff --git a/src/Discord.Net.Commands/RunMode.cs b/src/Discord.Net.Commands/RunMode.cs index 0799f825c..2bb5dbbf6 100644 --- a/src/Discord.Net.Commands/RunMode.cs +++ b/src/Discord.Net.Commands/RunMode.cs @@ -2,6 +2,7 @@ { public enum RunMode { + Default, Sync, Mixed, Async diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index d050f12a4..6ec7c5fb2 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -498,14 +498,15 @@ namespace Discord.API break; } } - public async Task ModifyMessageAsync(ulong channelId, ulong messageId, ModifyMessageParams args, RequestOptions options = null) + public async Task ModifyMessageAsync(ulong channelId, ulong messageId, Rest.ModifyMessageParams args, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(messageId, 0, nameof(messageId)); Preconditions.NotNull(args, nameof(args)); if (args.Content.IsSpecified) { - Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); + if (!args.Embed.IsSpecified) + Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); } diff --git a/src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs b/src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs index 4901ddc9d..c87d82c51 100644 --- a/src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs @@ -8,5 +8,7 @@ namespace Discord.API.Rest { [JsonProperty("content")] public Optional Content { get; set; } + [JsonProperty("embed")] + public Optional Embed { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index a9dc4735c..c33b3e359 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Threading.Tasks; namespace Discord diff --git a/src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs b/src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs new file mode 100644 index 000000000..cc05e620a --- /dev/null +++ b/src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Discord +{ + public class ModifyMessageParams + { + public Optional Content { get; set; } + public Optional Embed { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Roles/Color.cs b/src/Discord.Net.Core/Entities/Roles/Color.cs index 563917959..ead46fd8a 100644 --- a/src/Discord.Net.Core/Entities/Roles/Color.cs +++ b/src/Discord.Net.Core/Entities/Roles/Color.cs @@ -32,6 +32,12 @@ namespace Discord } public Color(float r, float g, float b) { + if (r < 0.0f || r > 1.0f) + throw new ArgumentOutOfRangeException(nameof(r), "A float value must be within [0,1]"); + if (g < 0.0f || g > 1.0f) + throw new ArgumentOutOfRangeException(nameof(g), "A float value must be within [0,1]"); + if (b < 0.0f || b > 1.0f) + throw new ArgumentOutOfRangeException(nameof(b), "A float value must be within [0,1]"); RawValue = ((uint)(r * 255.0f) << 16) | ((uint)(g * 255.0f) << 8) | diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 358f6f5a9..91ca2ce1b 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -15,7 +15,12 @@ namespace Discord.Rest { var args = new ModifyMessageParams(); func(args); - return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, args, options).ConfigureAwait(false); + var apiArgs = new API.Rest.ModifyMessageParams + { + Content = args.Content, + Embed = args.Embed.IsSpecified ? args.Embed.Value.Build() : Optional.Create() + }; + return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); } public static async Task DeleteAsync(IMessage msg, BaseDiscordClient client, RequestOptions options)