diff --git a/src/NadekoBot/DataStructures/ExecuteCommandResult.cs b/src/NadekoBot/DataStructures/ExecuteCommandResult.cs deleted file mode 100644 index c7106f19..00000000 --- a/src/NadekoBot/DataStructures/ExecuteCommandResult.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Discord.Commands; - -namespace NadekoBot.DataStructures -{ - public struct ExecuteCommandResult - { - public readonly CommandInfo CommandInfo; - public readonly PermissionCache PermissionCache; - public readonly IResult Result; - - public ExecuteCommandResult(CommandInfo commandInfo, PermissionCache cache, IResult result) - { - this.CommandInfo = commandInfo; - this.PermissionCache = cache; - this.Result = result; - } - } -} diff --git a/src/NadekoBot/DataStructures/ModuleBehaviors/IBlockingExecutor.cs b/src/NadekoBot/DataStructures/ModuleBehaviors/IBlockingExecutor.cs new file mode 100644 index 00000000..3754faa6 --- /dev/null +++ b/src/NadekoBot/DataStructures/ModuleBehaviors/IBlockingExecutor.cs @@ -0,0 +1,22 @@ +using Discord; +using Discord.WebSocket; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.DataStructures.ModuleBehaviors +{ + /// + /// Implemented by modules which can execute something and prevent further commands from being executed. + /// + public interface IBlockingExecutor + { + /// + /// Try to execute some logic within some module's service. + /// + /// Whether it should block other command executions after it. + Task TryExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg); + } +} diff --git a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs index e9b8827f..2b1d07c3 100644 --- a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs +++ b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs @@ -3,26 +3,31 @@ using System.Threading.Tasks; using Discord.Commands; using NadekoBot.Services; using NadekoBot.Attributes; -using System.Collections.Concurrent; using NadekoBot.Services.Database.Models; using Discord; using NadekoBot.Extensions; -using NLog; -using System.Diagnostics; using Discord.WebSocket; using System; -using NadekoBot.DataStructures; +using NadekoBot.Services.CustomReactions; namespace NadekoBot.Modules.CustomReactions { - public static class CustomReactionExtensions - { - - } - - [NadekoModule("CustomReactions", ".")] public class CustomReactions : NadekoTopLevelModule { + private readonly IBotCredentials _creds; + private readonly DbHandler _db; + private readonly CustomReactionsService _crs; + private readonly DiscordShardedClient _client; + + public CustomReactions(IBotCredentials creds, DbHandler db, CustomReactionsService crs, + DiscordShardedClient client) + { + _creds = creds; + _db = db; + _crs = crs; + _client = client; + } + [NadekoCommand, Usage, Description, Aliases] public async Task AddCustReact(string key, [Remainder] string message) { @@ -32,7 +37,7 @@ namespace NadekoBot.Modules.CustomReactions key = key.ToLowerInvariant(); - if ((channel == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (channel != null && !((IGuildUser)Context.User).GuildPermissions.Administrator)) + if ((channel == null && !_creds.IsOwner(Context.User)) || (channel != null && !((IGuildUser)Context.User).GuildPermissions.Administrator)) { await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false); return; @@ -55,12 +60,12 @@ namespace NadekoBot.Modules.CustomReactions if (channel == null) { - Array.Resize(ref _globalReactions, _globalReactions.Length + 1); - _globalReactions[_globalReactions.Length - 1] = cr; + Array.Resize(ref _crs.GlobalReactions, _crs.GlobalReactions.Length + 1); + _crs.GlobalReactions[_crs.GlobalReactions.Length - 1] = cr; } else { - GuildReactions.AddOrUpdate(Context.Guild.Id, + _crs.GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { cr }, (k, old) => { @@ -86,9 +91,9 @@ namespace NadekoBot.Modules.CustomReactions return; CustomReaction[] customReactions; if (Context.Guild == null) - customReactions = GlobalReactions.Where(cr => cr != null).ToArray(); + customReactions = _crs.GlobalReactions.Where(cr => cr != null).ToArray(); else - customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, Array.Empty()).Where(cr => cr != null).ToArray(); + customReactions = _crs.GuildReactions.GetOrAdd(Context.Guild.Id, Array.Empty()).Where(cr => cr != null).ToArray(); if (customReactions == null || !customReactions.Any()) { @@ -97,7 +102,7 @@ namespace NadekoBot.Modules.CustomReactions } var lastPage = customReactions.Length / 20; - await Context.Channel.SendPaginatedConfirmAsync(page, curPage => + await Context.Channel.SendPaginatedConfirmAsync(_client, page, curPage => new EmbedBuilder().WithOkColor() .WithTitle(GetText("name")) .WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger) @@ -130,9 +135,9 @@ namespace NadekoBot.Modules.CustomReactions { CustomReaction[] customReactions; if (Context.Guild == null) - customReactions = GlobalReactions.Where(cr => cr != null).ToArray(); + customReactions = _crs.GlobalReactions.Where(cr => cr != null).ToArray(); else - customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray(); + customReactions = _crs.GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray(); if (customReactions == null || !customReactions.Any()) { @@ -160,9 +165,9 @@ namespace NadekoBot.Modules.CustomReactions return; CustomReaction[] customReactions; if (Context.Guild == null) - customReactions = GlobalReactions.Where(cr => cr != null).ToArray(); + customReactions = _crs.GlobalReactions.Where(cr => cr != null).ToArray(); else - customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray(); + customReactions = _crs.GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray(); if (customReactions == null || !customReactions.Any()) { @@ -176,7 +181,7 @@ namespace NadekoBot.Modules.CustomReactions .ToList(); var lastPage = ordered.Count / 20; - await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => + await Context.Channel.SendPaginatedConfirmAsync(_client, page, (curPage) => new EmbedBuilder().WithOkColor() .WithTitle(GetText("name")) .WithDescription(string.Join("\r\n", ordered @@ -192,9 +197,9 @@ namespace NadekoBot.Modules.CustomReactions { CustomReaction[] customReactions; if (Context.Guild == null) - customReactions = GlobalReactions; + customReactions = _crs.GlobalReactions; else - customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }); + customReactions = _crs.GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }); var found = customReactions.FirstOrDefault(cr => cr?.Id == id); @@ -216,7 +221,7 @@ namespace NadekoBot.Modules.CustomReactions [NadekoCommand, Usage, Description, Aliases] public async Task DelCustReact(int id) { - if ((Context.Guild == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (Context.Guild != null && !((IGuildUser)Context.User).GuildPermissions.Administrator)) + if ((Context.Guild == null && !_creds.IsOwner(Context.User)) || (Context.Guild != null && !((IGuildUser)Context.User).GuildPermissions.Administrator)) { await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false); return; @@ -235,13 +240,13 @@ namespace NadekoBot.Modules.CustomReactions { uow.CustomReactions.Remove(toDelete); //todo i can dramatically improve performance of this, if Ids are ordered. - _globalReactions = GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray(); + _crs.GlobalReactions = _crs.GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray(); success = true; } else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId) { uow.CustomReactions.Remove(toDelete); - GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { }, (key, old) => + _crs.GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { }, (key, old) => { return old.Where(cr => cr?.Id != toDelete.Id).ToArray(); }); @@ -269,7 +274,7 @@ namespace NadekoBot.Modules.CustomReactions [NadekoCommand, Usage, Description, Aliases] public async Task CrDm(int id) { - if ((Context.Guild == null && !NadekoBot.Credentials.IsOwner(Context.User)) || + if ((Context.Guild == null && !_creds.IsOwner(Context.User)) || (Context.Guild != null && !((IGuildUser)Context.User).GuildPermissions.Administrator)) { await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false); @@ -279,10 +284,10 @@ namespace NadekoBot.Modules.CustomReactions CustomReaction[] reactions = new CustomReaction[0]; if (Context.Guild == null) - reactions = GlobalReactions; + reactions = _crs.GlobalReactions; else { - GuildReactions.TryGetValue(Context.Guild.Id, out reactions); + _crs.GuildReactions.TryGetValue(Context.Guild.Id, out reactions); } if (reactions.Any()) { @@ -320,7 +325,7 @@ namespace NadekoBot.Modules.CustomReactions [NadekoCommand, Usage, Description, Aliases] public async Task CrAd(int id) { - if ((Context.Guild == null && !NadekoBot.Credentials.IsOwner(Context.User)) || + if ((Context.Guild == null && !_creds.IsOwner(Context.User)) || (Context.Guild != null && !((IGuildUser)Context.User).GuildPermissions.Administrator)) { await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false); @@ -330,10 +335,10 @@ namespace NadekoBot.Modules.CustomReactions CustomReaction[] reactions = new CustomReaction[0]; if (Context.Guild == null) - reactions = GlobalReactions; + reactions = _crs.GlobalReactions; else { - GuildReactions.TryGetValue(Context.Guild.Id, out reactions); + _crs.GuildReactions.TryGetValue(Context.Guild.Id, out reactions); } if (reactions.Any()) { @@ -374,13 +379,13 @@ namespace NadekoBot.Modules.CustomReactions { if (string.IsNullOrWhiteSpace(trigger)) { - ClearStats(); + _crs.ClearStats(); await ReplyConfirmLocalized("all_stats_cleared").ConfigureAwait(false); } else { uint throwaway; - if (ReactionStats.TryRemove(trigger, out throwaway)) + if (_crs.ReactionStats.TryRemove(trigger, out throwaway)) { await ReplyErrorLocalized("stats_cleared", Format.Bold(trigger)).ConfigureAwait(false); } @@ -396,11 +401,11 @@ namespace NadekoBot.Modules.CustomReactions { if (page < 1) return; - var ordered = ReactionStats.OrderByDescending(x => x.Value).ToArray(); + var ordered = _crs.ReactionStats.OrderByDescending(x => x.Value).ToArray(); if (!ordered.Any()) return; var lastPage = ordered.Length / 9; - await Context.Channel.SendPaginatedConfirmAsync(page, + await Context.Channel.SendPaginatedConfirmAsync(_client, page, (curPage) => ordered.Skip((curPage - 1) * 9) .Take(9) .Aggregate(new EmbedBuilder().WithOkColor().WithTitle(GetText("stats")), diff --git a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs index ee3cd844..75a1425f 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs @@ -38,14 +38,14 @@ namespace NadekoBot.Modules.Gambling { using (var heads = _images.Heads.ToStream()) { - await Context.Channel.SendFileAsync(heads, "heads.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("heads"))) + ".").ConfigureAwait(false); + await Context.Channel.SendFileAsync(heads, "heads.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("heads")))).ConfigureAwait(false); } } else { using (var tails = _images.Tails.ToStream()) { - await Context.Channel.SendFileAsync(tails, "tails.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("tails"))) + ".").ConfigureAwait(false); + await Context.Channel.SendFileAsync(tails, "tails.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("tails")))).ConfigureAwait(false); } } return; diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index bb105ec1..ebacc438 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -20,6 +20,7 @@ using NadekoBot.Modules.Utility; using NadekoBot.Services.Searches; using NadekoBot.Services.ClashOfClans; using NadekoBot.Services.Music; +using NadekoBot.Services.CustomReactions; namespace NadekoBot { @@ -99,6 +100,7 @@ namespace NadekoBot var searchesService = new SearchesService(); var clashService = new ClashOfClansService(Client, db, localization, strings); var musicService = new MusicService(google, strings, localization, db, soundcloud, credentials); + var crService = new CustomReactionsService(db, Client); //initialize Services Services = new NServiceProvider.ServiceProviderBuilder() //todo all Adds should be interfaces @@ -111,16 +113,17 @@ namespace NadekoBot .Add(commandService) .Add(strings) .Add(Client) - .Add(greetSettingsService) .Add(BotConfig) .Add(currencyHandler) - .Add(musicService) .Add(commandHandler) .Add(db) //modules .Add(utilityService) .Add(searchesService) .Add(clashService) + .Add(musicService) + .Add(greetSettingsService) + .Add(crService) .Build(); commandHandler.AddServices(Services); diff --git a/src/NadekoBot/NadekoBot.csproj b/src/NadekoBot/NadekoBot.csproj index a2450bcb..51178169 100644 --- a/src/NadekoBot/NadekoBot.csproj +++ b/src/NadekoBot/NadekoBot.csproj @@ -29,28 +29,21 @@ - - - - - - - PreserveNewest diff --git a/src/NadekoBot/Services/ClashOfClans/ClashOfClansService.cs b/src/NadekoBot/Services/ClashOfClans/ClashOfClansService.cs index fadbf637..13ea5ba2 100644 --- a/src/NadekoBot/Services/ClashOfClans/ClashOfClansService.cs +++ b/src/NadekoBot/Services/ClashOfClans/ClashOfClansService.cs @@ -1,5 +1,6 @@ using Discord; using Discord.WebSocket; +using NadekoBot.DataStructures.ModuleBehaviors; using NadekoBot.Extensions; using NadekoBot.Services.Database.Models; using System; diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 12174924..6c72d118 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -11,6 +11,7 @@ using System.Collections.Concurrent; using System.Threading; using NadekoBot.DataStructures; using System.Collections.Immutable; +using NadekoBot.DataStructures.ModuleBehaviors; namespace NadekoBot.Services { @@ -201,7 +202,7 @@ namespace NadekoBot.Services private const float _oneThousandth = 1.0f / 1000; - private Task LogSuccessfulExecution(IUserMessage usrMsg, ExecuteCommandResult exec, ITextChannel channel, params int[] execPoints) + private Task LogSuccessfulExecution(IUserMessage usrMsg, bool exec, ITextChannel channel, params int[] execPoints) { _log.Info("Command Executed after " + string.Join("/", execPoints.Select(x => x * _oneThousandth)) + "s\n\t" + "User: {0}\n\t" + @@ -216,7 +217,7 @@ namespace NadekoBot.Services return Task.CompletedTask; } - private void LogErroredExecution(IUserMessage usrMsg, ExecuteCommandResult exec, ITextChannel channel, params int[] execPoints) + private void LogErroredExecution(IUserMessage usrMsg, bool exec, ITextChannel channel, params int[] execPoints) { _log.Warn("Command Errored after " + string.Join("/", execPoints.Select(x => x * _oneThousandth)) + "s\n\t" + "User: {0}\n\t" + @@ -228,7 +229,8 @@ namespace NadekoBot.Services (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} usrMsg.Content,// {3} - exec.Result.ErrorReason // {4} + exec + //exec.Result.ErrorReason // {4} ); } ////todo invite filtering @@ -323,6 +325,17 @@ namespace NadekoBot.Services { var execTime = Environment.TickCount; + foreach (var svc in _services) + { + if (svc is IBlockingExecutor _executor && + await _executor.TryExecute(_client, guild, usrMsg)) + { + _log.Info("User [{0}] executed [{1}] in [{2}]", usrMsg.Author, usrMsg.Content, svc.GetType().Name); + return; + } + } + + ////todo word and invite filtering //if (guild != null && guild.OwnerId != usrMsg.Author.Id) //{ @@ -423,22 +436,23 @@ namespace NadekoBot.Services var exec = await Task.Run(() => ExecuteCommandAsync(new CommandContext(_client, usrMsg), NadekoBot.Prefix.Length, _services, MultiMatchHandling.Best)).ConfigureAwait(false); execTime = Environment.TickCount - execTime; - if (exec.Result.IsSuccess) - { - await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false); - await LogSuccessfulExecution(usrMsg, exec, channel, exec2, exec3, execTime).ConfigureAwait(false); - return; - } - else if (!exec.Result.IsSuccess && exec.Result.Error != CommandError.UnknownCommand) - { - LogErroredExecution(usrMsg, exec, channel, exec2, exec3, execTime); - if (guild != null && exec.CommandInfo != null && exec.Result.Error == CommandError.Exception) - { - if (exec.PermissionCache != null && exec.PermissionCache.Verbose) - try { await usrMsg.Channel.SendMessageAsync("⚠️ " + exec.Result.ErrorReason).ConfigureAwait(false); } catch { } - } - return; - } + ////todo permissions + //if (exec.Result.IsSuccess) + //{ + // await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false); + // await LogSuccessfulExecution(usrMsg, exec, channel, exec2, exec3, execTime).ConfigureAwait(false); + // return; + //} + //else if (!exec.Result.IsSuccess && exec.Result.Error != CommandError.UnknownCommand) + //{ + // LogErroredExecution(usrMsg, exec, channel, exec2, exec3, execTime); + // if (guild != null && exec.CommandInfo != null && exec.Result.Error == CommandError.Exception) + // { + // if (exec.PermissionCache != null && exec.PermissionCache.Verbose) + // try { await usrMsg.Channel.SendMessageAsync("⚠️ " + exec.Result.ErrorReason).ConfigureAwait(false); } catch { } + // } + // return; + //} } if (usrMsg.Channel is IPrivateChannel) @@ -455,15 +469,15 @@ namespace NadekoBot.Services } } - public Task ExecuteCommandAsync(CommandContext context, int argPos, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) + public Task ExecuteCommandAsync(CommandContext context, int argPos, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) => ExecuteCommand(context, context.Message.Content.Substring(argPos), serviceProvider, multiMatchHandling); - public async Task ExecuteCommand(CommandContext context, string input, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) + public async Task ExecuteCommand(CommandContext context, string input, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) { var searchResult = _commandService.Search(context, input); if (!searchResult.IsSuccess) - return new ExecuteCommandResult(null, null, searchResult); + return false; var commands = searchResult.Commands; for (int i = commands.Count - 1; i >= 0; i--) @@ -472,7 +486,7 @@ namespace NadekoBot.Services if (!preconditionResult.IsSuccess) { if (commands.Count == 1) - return new ExecuteCommandResult(null, null, preconditionResult); + return false; else continue; } @@ -496,7 +510,7 @@ namespace NadekoBot.Services if (!parseResult.IsSuccess) { if (commands.Count == 1) - return new ExecuteCommandResult(null, null, parseResult); + return false; else continue; } @@ -549,16 +563,19 @@ namespace NadekoBot.Services // Bot will ignore commands which are ran more often than what specified by // GlobalCommandsCooldown constant (miliseconds) if (!UsersOnShortCooldown.Add(context.Message.Author.Id)) - return new ExecuteCommandResult(cmd, null, SearchResult.FromError(CommandError.Exception, "You are on a global cooldown.")); + return false; + //return SearchResult.FromError(CommandError.Exception, "You are on a global cooldown."); ////todo cmdcds //if (CmdCdsCommands.HasCooldown(cmd, context.Guild, context.User)) // return new ExecuteCommandResult(cmd, null, SearchResult.FromError(CommandError.Exception, "That command is on a cooldown for you.")); - - return new ExecuteCommandResult(cmd, null, await commands[i].ExecuteAsync(context, parseResult, serviceProvider)); + + await commands[i].ExecuteAsync(context, parseResult, serviceProvider); + return true; } - return new ExecuteCommandResult(null, null, SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload.")); + return false; + //return new ExecuteCommandResult(null, null, SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload.")); } } } \ No newline at end of file diff --git a/src/NadekoBot/Services/CustomReactions/CustomReactions.cs b/src/NadekoBot/Services/CustomReactions/CustomReactionsService.cs similarity index 53% rename from src/NadekoBot/Services/CustomReactions/CustomReactions.cs rename to src/NadekoBot/Services/CustomReactions/CustomReactionsService.cs index d2a3e08e..6f5a8228 100644 --- a/src/NadekoBot/Services/CustomReactions/CustomReactions.cs +++ b/src/NadekoBot/Services/CustomReactions/CustomReactionsService.cs @@ -1,34 +1,39 @@ using Discord; using Discord.WebSocket; +using NadekoBot.DataStructures.ModuleBehaviors; using NadekoBot.Services.Database.Models; using NLog; using System.Collections.Concurrent; using System.Diagnostics; using System.Linq; +using System; +using System.Threading.Tasks; -namespace NadekoBot.Services +namespace NadekoBot.Services.CustomReactions { - public class CustomReactions + public class CustomReactionsService : IBlockingExecutor { - private CustomReaction[] _globalReactions = new CustomReaction[] { }; - public CustomReaction[] GlobalReactions => _globalReactions; + public CustomReaction[] GlobalReactions = new CustomReaction[] { }; public ConcurrentDictionary GuildReactions { get; } = new ConcurrentDictionary(); public ConcurrentDictionary ReactionStats { get; } = new ConcurrentDictionary(); private readonly Logger _log; private readonly DbHandler _db; + private readonly DiscordShardedClient _client; - public CustomReactions(DbHandler db) + public CustomReactionsService(DbHandler db, DiscordShardedClient client) { _log = LogManager.GetCurrentClassLogger(); _db = db; + _client = client; + var sw = Stopwatch.StartNew(); using (var uow = _db.UnitOfWork) { var items = uow.CustomReactions.GetAll(); GuildReactions = new ConcurrentDictionary(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => g.ToArray())); - _globalReactions = items.Where(g => g.GuildId == null || g.GuildId == 0).ToArray(); + GlobalReactions = items.Where(g => g.GuildId == null || g.GuildId == 0).ToArray(); } sw.Stop(); _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); @@ -54,7 +59,7 @@ namespace NadekoBot.Services return false; var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%"); - var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant(); + var trigger = cr.TriggerWithContext(umsg, _client).Trim().ToLowerInvariant(); return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger); }).ToArray(); @@ -75,7 +80,7 @@ namespace NadekoBot.Services if (cr == null) return false; var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%"); - var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant(); + var trigger = cr.TriggerWithContext(umsg, _client).Trim().ToLowerInvariant(); return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger); }).ToArray(); if (grs.Length == 0) @@ -84,5 +89,46 @@ namespace NadekoBot.Services return greaction; } + + public async Task TryExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg) + { + //todo custom reactions + // maybe this message is a custom reaction + // todo log custom reaction executions. return struct with info + var cr = await Task.Run(() => TryGetCustomReaction(msg)).ConfigureAwait(false); + if (cr != null) //if it was, don't execute the command + { + try + { + //if (guild != null) + //{ + // PermissionCache pc = Permissions.GetCache(guild.Id); + + // if (!pc.Permissions.CheckPermissions(usrMsg, cr.Trigger, "ActualCustomReactions", + // out int index)) + // { + // //todo print in guild actually + // var returnMsg = + // $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(guild)}** is preventing this action."; + // _log.Info(returnMsg); + // return; + // } + //} + await cr.Send(msg, _client, this).ConfigureAwait(false); + + if (cr.AutoDeleteTrigger) + { + try { await msg.DeleteAsync().ConfigureAwait(false); } catch { } + } + return true; + } + catch (Exception ex) + { + _log.Warn("Sending CREmbed failed"); + _log.Warn(ex); + } + } + return false; + } } } diff --git a/src/NadekoBot/Services/CustomReactions/Extensions.cs b/src/NadekoBot/Services/CustomReactions/Extensions.cs index fdcd9f54..b6a7b12f 100644 --- a/src/NadekoBot/Services/CustomReactions/Extensions.cs +++ b/src/NadekoBot/Services/CustomReactions/Extensions.cs @@ -1,4 +1,5 @@ using Discord; +using Discord.WebSocket; using NadekoBot.DataStructures; using NadekoBot.Extensions; using NadekoBot.Services; @@ -8,7 +9,7 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using System.Threading.Tasks; -namespace NadekoBot.Modules.CustomReactions +namespace NadekoBot.Services.CustomReactions { public static class Extensions { @@ -17,11 +18,11 @@ namespace NadekoBot.Modules.CustomReactions {"%target%", (ctx, trigger) => { return ctx.Content.Substring(trigger.Length).Trim().SanitizeMentions(); } } }; - public static Dictionary> placeholders = new Dictionary>() + public static Dictionary> placeholders = new Dictionary>() { - {"%mention%", (ctx) => { return $"<@{NadekoBot.Client.CurrentUser.Id}>"; } }, - {"%user%", (ctx) => { return ctx.Author.Mention; } }, - {"%rnduser%", (ctx) => { + {"%mention%", (ctx, client) => { return $"<@{client.CurrentUser.Id}>"; } }, + {"%user%", (ctx, client) => { return ctx.Author.Mention; } }, + {"%rnduser%", (ctx, client) => { //var ch = ctx.Channel as ITextChannel; //if(ch == null) // return ""; @@ -69,20 +70,20 @@ namespace NadekoBot.Modules.CustomReactions } } }; - private static string ResolveTriggerString(this string str, IUserMessage ctx) + private static string ResolveTriggerString(this string str, IUserMessage ctx, DiscordShardedClient client) { foreach (var ph in placeholders) { - str = str.ToLowerInvariant().Replace(ph.Key, ph.Value(ctx)); + str = str.ToLowerInvariant().Replace(ph.Key, ph.Value(ctx, client)); } return str; } - private static string ResolveResponseString(this string str, IUserMessage ctx, string resolvedTrigger) + private static string ResolveResponseString(this string str, IUserMessage ctx, DiscordShardedClient client, string resolvedTrigger) { foreach (var ph in placeholders) { - str = str.Replace(ph.Key.ToLowerInvariant(), ph.Value(ctx)); + str = str.Replace(ph.Key.ToLowerInvariant(), ph.Value(ctx, client)); } foreach (var ph in responsePlaceholders) @@ -97,24 +98,23 @@ namespace NadekoBot.Modules.CustomReactions return str; } - public static string TriggerWithContext(this CustomReaction cr, IUserMessage ctx) - => cr.Trigger.ResolveTriggerString(ctx); + public static string TriggerWithContext(this CustomReaction cr, IUserMessage ctx, DiscordShardedClient client) + => cr.Trigger.ResolveTriggerString(ctx, client); - public static string ResponseWithContext(this CustomReaction cr, IUserMessage ctx) - => cr.Response.ResolveResponseString(ctx, cr.Trigger.ResolveTriggerString(ctx)); + public static string ResponseWithContext(this CustomReaction cr, IUserMessage ctx, DiscordShardedClient client) + => cr.Response.ResolveResponseString(ctx, client, cr.Trigger.ResolveTriggerString(ctx, client)); - public static async Task Send(this CustomReaction cr, IUserMessage context) + public static async Task Send(this CustomReaction cr, IUserMessage context, DiscordShardedClient client, CustomReactionsService crs) { var channel = cr.DmResponse ? await context.Author.CreateDMChannelAsync() : context.Channel; - CustomReactions.ReactionStats.AddOrUpdate(cr.Trigger, 1, (k, old) => ++old); + crs.ReactionStats.AddOrUpdate(cr.Trigger, 1, (k, old) => ++old); - CREmbed crembed; - if (CREmbed.TryParse(cr.Response, out crembed)) + if (CREmbed.TryParse(cr.Response, out CREmbed crembed)) { return await channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? ""); } - return await channel.SendMessageAsync(cr.ResponseWithContext(context).SanitizeMentions()); + return await channel.SendMessageAsync(cr.ResponseWithContext(context, client).SanitizeMentions()); } } } diff --git a/src/NadekoBot/DataStructures/PermissionCache.cs b/src/NadekoBot/Services/Permissions/PermissionCache.cs similarity index 93% rename from src/NadekoBot/DataStructures/PermissionCache.cs rename to src/NadekoBot/Services/Permissions/PermissionCache.cs index 9d2288bf..9ed7ec8c 100644 --- a/src/NadekoBot/DataStructures/PermissionCache.cs +++ b/src/NadekoBot/Services/Permissions/PermissionCache.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace NadekoBot.DataStructures +namespace NadekoBot.Services.Permissions { public class OldPermissionCache { diff --git a/src/NadekoBot/DataStructures/PermissionsCollection.cs b/src/NadekoBot/Services/Permissions/PermissionsCollection.cs similarity index 96% rename from src/NadekoBot/DataStructures/PermissionsCollection.cs rename to src/NadekoBot/Services/Permissions/PermissionsCollection.cs index 484c4f9d..5709c910 100644 --- a/src/NadekoBot/DataStructures/PermissionsCollection.cs +++ b/src/NadekoBot/Services/Permissions/PermissionsCollection.cs @@ -3,8 +3,9 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using NadekoBot.Services.Database.Models; +using NadekoBot.DataStructures; -namespace NadekoBot.DataStructures +namespace NadekoBot.Services.Permissions { public class PermissionsCollection : IndexedCollection where T : IIndexed { diff --git a/src/NadekoBot/Services/Permissions/PermissionsService.cs b/src/NadekoBot/Services/Permissions/PermissionsService.cs new file mode 100644 index 00000000..b10d8b21 --- /dev/null +++ b/src/NadekoBot/Services/Permissions/PermissionsService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Permissions +{ + public class PermissionsService + { + + } +} diff --git a/src/NadekoBot/Services/ServiceProvider.cs b/src/NadekoBot/Services/ServiceProvider.cs index b90be9aa..8b28af4d 100644 --- a/src/NadekoBot/Services/ServiceProvider.cs +++ b/src/NadekoBot/Services/ServiceProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; @@ -8,7 +9,7 @@ using System.Threading.Tasks; namespace NadekoBot.Services { - public interface INServiceProvider : IServiceProvider + public interface INServiceProvider : IServiceProvider, IEnumerable { T GetService(); } @@ -49,5 +50,9 @@ namespace NadekoBot.Services _services.TryGetValue(serviceType, out var toReturn); return toReturn; } + + IEnumerator IEnumerable.GetEnumerator() => _services.Values.GetEnumerator(); + + public IEnumerator GetEnumerator() => _services.Values.GetEnumerator(); } }