CustomReactions almost done

This commit is contained in:
Master Kwoth 2017-05-25 04:24:43 +02:00
parent 9e3dc6d5a1
commit 351ebabd64
14 changed files with 211 additions and 123 deletions

View File

@ -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;
}
}
}

View File

@ -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
{
/// <summary>
/// Implemented by modules which can execute something and prevent further commands from being executed.
/// </summary>
public interface IBlockingExecutor
{
/// <summary>
/// Try to execute some logic within some module's service.
/// </summary>
/// <returns>Whether it should block other command executions after it.</returns>
Task<bool> TryExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg);
}
}

View File

@ -3,26 +3,31 @@ using System.Threading.Tasks;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using System.Collections.Concurrent;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using Discord; using Discord;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NLog;
using System.Diagnostics;
using Discord.WebSocket; using Discord.WebSocket;
using System; using System;
using NadekoBot.DataStructures; using NadekoBot.Services.CustomReactions;
namespace NadekoBot.Modules.CustomReactions namespace NadekoBot.Modules.CustomReactions
{ {
public static class CustomReactionExtensions
{
}
[NadekoModule("CustomReactions", ".")]
public class CustomReactions : NadekoTopLevelModule 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] [NadekoCommand, Usage, Description, Aliases]
public async Task AddCustReact(string key, [Remainder] string message) public async Task AddCustReact(string key, [Remainder] string message)
{ {
@ -32,7 +37,7 @@ namespace NadekoBot.Modules.CustomReactions
key = key.ToLowerInvariant(); 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); await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false);
return; return;
@ -55,12 +60,12 @@ namespace NadekoBot.Modules.CustomReactions
if (channel == null) if (channel == null)
{ {
Array.Resize(ref _globalReactions, _globalReactions.Length + 1); Array.Resize(ref _crs.GlobalReactions, _crs.GlobalReactions.Length + 1);
_globalReactions[_globalReactions.Length - 1] = cr; _crs.GlobalReactions[_crs.GlobalReactions.Length - 1] = cr;
} }
else else
{ {
GuildReactions.AddOrUpdate(Context.Guild.Id, _crs.GuildReactions.AddOrUpdate(Context.Guild.Id,
new CustomReaction[] { cr }, new CustomReaction[] { cr },
(k, old) => (k, old) =>
{ {
@ -86,9 +91,9 @@ namespace NadekoBot.Modules.CustomReactions
return; return;
CustomReaction[] customReactions; CustomReaction[] customReactions;
if (Context.Guild == null) if (Context.Guild == null)
customReactions = GlobalReactions.Where(cr => cr != null).ToArray(); customReactions = _crs.GlobalReactions.Where(cr => cr != null).ToArray();
else else
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, Array.Empty<CustomReaction>()).Where(cr => cr != null).ToArray(); customReactions = _crs.GuildReactions.GetOrAdd(Context.Guild.Id, Array.Empty<CustomReaction>()).Where(cr => cr != null).ToArray();
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
{ {
@ -97,7 +102,7 @@ namespace NadekoBot.Modules.CustomReactions
} }
var lastPage = customReactions.Length / 20; var lastPage = customReactions.Length / 20;
await Context.Channel.SendPaginatedConfirmAsync(page, curPage => await Context.Channel.SendPaginatedConfirmAsync(_client, page, curPage =>
new EmbedBuilder().WithOkColor() new EmbedBuilder().WithOkColor()
.WithTitle(GetText("name")) .WithTitle(GetText("name"))
.WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger) .WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger)
@ -130,9 +135,9 @@ namespace NadekoBot.Modules.CustomReactions
{ {
CustomReaction[] customReactions; CustomReaction[] customReactions;
if (Context.Guild == null) if (Context.Guild == null)
customReactions = GlobalReactions.Where(cr => cr != null).ToArray(); customReactions = _crs.GlobalReactions.Where(cr => cr != null).ToArray();
else 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()) if (customReactions == null || !customReactions.Any())
{ {
@ -160,9 +165,9 @@ namespace NadekoBot.Modules.CustomReactions
return; return;
CustomReaction[] customReactions; CustomReaction[] customReactions;
if (Context.Guild == null) if (Context.Guild == null)
customReactions = GlobalReactions.Where(cr => cr != null).ToArray(); customReactions = _crs.GlobalReactions.Where(cr => cr != null).ToArray();
else 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()) if (customReactions == null || !customReactions.Any())
{ {
@ -176,7 +181,7 @@ namespace NadekoBot.Modules.CustomReactions
.ToList(); .ToList();
var lastPage = ordered.Count / 20; var lastPage = ordered.Count / 20;
await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => await Context.Channel.SendPaginatedConfirmAsync(_client, page, (curPage) =>
new EmbedBuilder().WithOkColor() new EmbedBuilder().WithOkColor()
.WithTitle(GetText("name")) .WithTitle(GetText("name"))
.WithDescription(string.Join("\r\n", ordered .WithDescription(string.Join("\r\n", ordered
@ -192,9 +197,9 @@ namespace NadekoBot.Modules.CustomReactions
{ {
CustomReaction[] customReactions; CustomReaction[] customReactions;
if (Context.Guild == null) if (Context.Guild == null)
customReactions = GlobalReactions; customReactions = _crs.GlobalReactions;
else 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); var found = customReactions.FirstOrDefault(cr => cr?.Id == id);
@ -216,7 +221,7 @@ namespace NadekoBot.Modules.CustomReactions
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task DelCustReact(int id) 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); await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false);
return; return;
@ -235,13 +240,13 @@ namespace NadekoBot.Modules.CustomReactions
{ {
uow.CustomReactions.Remove(toDelete); uow.CustomReactions.Remove(toDelete);
//todo i can dramatically improve performance of this, if Ids are ordered. //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; success = true;
} }
else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId) else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId)
{ {
uow.CustomReactions.Remove(toDelete); 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(); return old.Where(cr => cr?.Id != toDelete.Id).ToArray();
}); });
@ -269,7 +274,7 @@ namespace NadekoBot.Modules.CustomReactions
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task CrDm(int id) 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)) (Context.Guild != null && !((IGuildUser)Context.User).GuildPermissions.Administrator))
{ {
await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false); await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false);
@ -279,10 +284,10 @@ namespace NadekoBot.Modules.CustomReactions
CustomReaction[] reactions = new CustomReaction[0]; CustomReaction[] reactions = new CustomReaction[0];
if (Context.Guild == null) if (Context.Guild == null)
reactions = GlobalReactions; reactions = _crs.GlobalReactions;
else else
{ {
GuildReactions.TryGetValue(Context.Guild.Id, out reactions); _crs.GuildReactions.TryGetValue(Context.Guild.Id, out reactions);
} }
if (reactions.Any()) if (reactions.Any())
{ {
@ -320,7 +325,7 @@ namespace NadekoBot.Modules.CustomReactions
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task CrAd(int id) 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)) (Context.Guild != null && !((IGuildUser)Context.User).GuildPermissions.Administrator))
{ {
await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false); await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false);
@ -330,10 +335,10 @@ namespace NadekoBot.Modules.CustomReactions
CustomReaction[] reactions = new CustomReaction[0]; CustomReaction[] reactions = new CustomReaction[0];
if (Context.Guild == null) if (Context.Guild == null)
reactions = GlobalReactions; reactions = _crs.GlobalReactions;
else else
{ {
GuildReactions.TryGetValue(Context.Guild.Id, out reactions); _crs.GuildReactions.TryGetValue(Context.Guild.Id, out reactions);
} }
if (reactions.Any()) if (reactions.Any())
{ {
@ -374,13 +379,13 @@ namespace NadekoBot.Modules.CustomReactions
{ {
if (string.IsNullOrWhiteSpace(trigger)) if (string.IsNullOrWhiteSpace(trigger))
{ {
ClearStats(); _crs.ClearStats();
await ReplyConfirmLocalized("all_stats_cleared").ConfigureAwait(false); await ReplyConfirmLocalized("all_stats_cleared").ConfigureAwait(false);
} }
else else
{ {
uint throwaway; uint throwaway;
if (ReactionStats.TryRemove(trigger, out throwaway)) if (_crs.ReactionStats.TryRemove(trigger, out throwaway))
{ {
await ReplyErrorLocalized("stats_cleared", Format.Bold(trigger)).ConfigureAwait(false); await ReplyErrorLocalized("stats_cleared", Format.Bold(trigger)).ConfigureAwait(false);
} }
@ -396,11 +401,11 @@ namespace NadekoBot.Modules.CustomReactions
{ {
if (page < 1) if (page < 1)
return; return;
var ordered = ReactionStats.OrderByDescending(x => x.Value).ToArray(); var ordered = _crs.ReactionStats.OrderByDescending(x => x.Value).ToArray();
if (!ordered.Any()) if (!ordered.Any())
return; return;
var lastPage = ordered.Length / 9; var lastPage = ordered.Length / 9;
await Context.Channel.SendPaginatedConfirmAsync(page, await Context.Channel.SendPaginatedConfirmAsync(_client, page,
(curPage) => ordered.Skip((curPage - 1) * 9) (curPage) => ordered.Skip((curPage - 1) * 9)
.Take(9) .Take(9)
.Aggregate(new EmbedBuilder().WithOkColor().WithTitle(GetText("stats")), .Aggregate(new EmbedBuilder().WithOkColor().WithTitle(GetText("stats")),

View File

@ -38,14 +38,14 @@ namespace NadekoBot.Modules.Gambling
{ {
using (var heads = _images.Heads.ToStream()) 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 else
{ {
using (var tails = _images.Tails.ToStream()) 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; return;

View File

@ -20,6 +20,7 @@ using NadekoBot.Modules.Utility;
using NadekoBot.Services.Searches; using NadekoBot.Services.Searches;
using NadekoBot.Services.ClashOfClans; using NadekoBot.Services.ClashOfClans;
using NadekoBot.Services.Music; using NadekoBot.Services.Music;
using NadekoBot.Services.CustomReactions;
namespace NadekoBot namespace NadekoBot
{ {
@ -99,6 +100,7 @@ namespace NadekoBot
var searchesService = new SearchesService(); var searchesService = new SearchesService();
var clashService = new ClashOfClansService(Client, db, localization, strings); var clashService = new ClashOfClansService(Client, db, localization, strings);
var musicService = new MusicService(google, strings, localization, db, soundcloud, credentials); var musicService = new MusicService(google, strings, localization, db, soundcloud, credentials);
var crService = new CustomReactionsService(db, Client);
//initialize Services //initialize Services
Services = new NServiceProvider.ServiceProviderBuilder() //todo all Adds should be interfaces Services = new NServiceProvider.ServiceProviderBuilder() //todo all Adds should be interfaces
@ -111,16 +113,17 @@ namespace NadekoBot
.Add<CommandService>(commandService) .Add<CommandService>(commandService)
.Add<NadekoStrings>(strings) .Add<NadekoStrings>(strings)
.Add<DiscordShardedClient>(Client) .Add<DiscordShardedClient>(Client)
.Add<GreetSettingsService>(greetSettingsService)
.Add<BotConfig>(BotConfig) .Add<BotConfig>(BotConfig)
.Add<CurrencyHandler>(currencyHandler) .Add<CurrencyHandler>(currencyHandler)
.Add(musicService)
.Add<CommandHandler>(commandHandler) .Add<CommandHandler>(commandHandler)
.Add<DbHandler>(db) .Add<DbHandler>(db)
//modules //modules
.Add<UtilityService>(utilityService) .Add<UtilityService>(utilityService)
.Add<SearchesService>(searchesService) .Add<SearchesService>(searchesService)
.Add<ClashOfClansService>(clashService) .Add<ClashOfClansService>(clashService)
.Add<MusicService>(musicService)
.Add<GreetSettingsService>(greetSettingsService)
.Add<CustomReactionsService>(crService)
.Build(); .Build();
commandHandler.AddServices(Services); commandHandler.AddServices(Services);

View File

@ -29,28 +29,21 @@
<ItemGroup> <ItemGroup>
<Compile Remove="data\**\*;credentials.json;credentials_example.json" /> <Compile Remove="data\**\*;credentials.json;credentials_example.json" />
<Compile Remove="Modules\Administration\**" /> <Compile Remove="Modules\Administration\**" />
<Compile Remove="Modules\CustomReactions\**" />
<Compile Remove="Modules\Games\**" /> <Compile Remove="Modules\Games\**" />
<Compile Remove="Modules\NSFW\**" /> <Compile Remove="Modules\NSFW\**" />
<Compile Remove="Modules\Permissions\**" /> <Compile Remove="Modules\Permissions\**" />
<Compile Remove="Modules\Searches\**" /> <Compile Remove="Modules\Searches\**" />
<Compile Remove="Services\CustomReactions\**" />
<EmbeddedResource Remove="Modules\Administration\**" /> <EmbeddedResource Remove="Modules\Administration\**" />
<EmbeddedResource Remove="Modules\CustomReactions\**" />
<EmbeddedResource Remove="Modules\Games\**" /> <EmbeddedResource Remove="Modules\Games\**" />
<EmbeddedResource Remove="Modules\NSFW\**" /> <EmbeddedResource Remove="Modules\NSFW\**" />
<EmbeddedResource Remove="Modules\Permissions\**" /> <EmbeddedResource Remove="Modules\Permissions\**" />
<EmbeddedResource Remove="Modules\Searches\**" /> <EmbeddedResource Remove="Modules\Searches\**" />
<EmbeddedResource Remove="Services\CustomReactions\**" />
<None Remove="Modules\Administration\**" /> <None Remove="Modules\Administration\**" />
<None Remove="Modules\CustomReactions\**" />
<None Remove="Modules\Games\**" /> <None Remove="Modules\Games\**" />
<None Remove="Modules\NSFW\**" /> <None Remove="Modules\NSFW\**" />
<None Remove="Modules\Permissions\**" /> <None Remove="Modules\Permissions\**" />
<None Remove="Modules\Searches\**" /> <None Remove="Modules\Searches\**" />
<None Remove="Services\CustomReactions\**" />
<Compile Remove="Modules\Gambling\Commands\Lucky7Commands.cs" /> <Compile Remove="Modules\Gambling\Commands\Lucky7Commands.cs" />
<Compile Remove="Modules\Gambling\Commands\WaifuClaimCommands.cs" />
<None Update="libsodium.dll;opus.dll;libsodium.so;libopus.so"> <None Update="libsodium.dll;opus.dll;libsodium.so;libopus.so">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>

View File

@ -1,5 +1,6 @@
using Discord; using Discord;
using Discord.WebSocket; using Discord.WebSocket;
using NadekoBot.DataStructures.ModuleBehaviors;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System; using System;

View File

@ -11,6 +11,7 @@ using System.Collections.Concurrent;
using System.Threading; using System.Threading;
using NadekoBot.DataStructures; using NadekoBot.DataStructures;
using System.Collections.Immutable; using System.Collections.Immutable;
using NadekoBot.DataStructures.ModuleBehaviors;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
@ -201,7 +202,7 @@ namespace NadekoBot.Services
private const float _oneThousandth = 1.0f / 1000; 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" + _log.Info("Command Executed after " + string.Join("/", execPoints.Select(x => x * _oneThousandth)) + "s\n\t" +
"User: {0}\n\t" + "User: {0}\n\t" +
@ -216,7 +217,7 @@ namespace NadekoBot.Services
return Task.CompletedTask; 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" + _log.Warn("Command Errored after " + string.Join("/", execPoints.Select(x => x * _oneThousandth)) + "s\n\t" +
"User: {0}\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.Guild.Name + " [" + channel.Guild.Id + "]"), // {1}
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
usrMsg.Content,// {3} usrMsg.Content,// {3}
exec.Result.ErrorReason // {4} exec
//exec.Result.ErrorReason // {4}
); );
} }
////todo invite filtering ////todo invite filtering
@ -323,6 +325,17 @@ namespace NadekoBot.Services
{ {
var execTime = Environment.TickCount; 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 ////todo word and invite filtering
//if (guild != null && guild.OwnerId != usrMsg.Author.Id) //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); var exec = await Task.Run(() => ExecuteCommandAsync(new CommandContext(_client, usrMsg), NadekoBot.Prefix.Length, _services, MultiMatchHandling.Best)).ConfigureAwait(false);
execTime = Environment.TickCount - execTime; execTime = Environment.TickCount - execTime;
if (exec.Result.IsSuccess) ////todo permissions
{ //if (exec.Result.IsSuccess)
await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false); //{
await LogSuccessfulExecution(usrMsg, exec, channel, exec2, exec3, execTime).ConfigureAwait(false); // await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false);
return; // await LogSuccessfulExecution(usrMsg, exec, channel, exec2, exec3, execTime).ConfigureAwait(false);
} // return;
else if (!exec.Result.IsSuccess && exec.Result.Error != CommandError.UnknownCommand) //}
{ //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) // 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 { } // if (exec.PermissionCache != null && exec.PermissionCache.Verbose)
} // try { await usrMsg.Channel.SendMessageAsync("⚠️ " + exec.Result.ErrorReason).ConfigureAwait(false); } catch { }
return; // }
} // return;
//}
} }
if (usrMsg.Channel is IPrivateChannel) if (usrMsg.Channel is IPrivateChannel)
@ -455,15 +469,15 @@ namespace NadekoBot.Services
} }
} }
public Task<ExecuteCommandResult> ExecuteCommandAsync(CommandContext context, int argPos, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) public Task<bool> ExecuteCommandAsync(CommandContext context, int argPos, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
=> ExecuteCommand(context, context.Message.Content.Substring(argPos), serviceProvider, multiMatchHandling); => ExecuteCommand(context, context.Message.Content.Substring(argPos), serviceProvider, multiMatchHandling);
public async Task<ExecuteCommandResult> ExecuteCommand(CommandContext context, string input, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) public async Task<bool> ExecuteCommand(CommandContext context, string input, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
{ {
var searchResult = _commandService.Search(context, input); var searchResult = _commandService.Search(context, input);
if (!searchResult.IsSuccess) if (!searchResult.IsSuccess)
return new ExecuteCommandResult(null, null, searchResult); return false;
var commands = searchResult.Commands; var commands = searchResult.Commands;
for (int i = commands.Count - 1; i >= 0; i--) for (int i = commands.Count - 1; i >= 0; i--)
@ -472,7 +486,7 @@ namespace NadekoBot.Services
if (!preconditionResult.IsSuccess) if (!preconditionResult.IsSuccess)
{ {
if (commands.Count == 1) if (commands.Count == 1)
return new ExecuteCommandResult(null, null, preconditionResult); return false;
else else
continue; continue;
} }
@ -496,7 +510,7 @@ namespace NadekoBot.Services
if (!parseResult.IsSuccess) if (!parseResult.IsSuccess)
{ {
if (commands.Count == 1) if (commands.Count == 1)
return new ExecuteCommandResult(null, null, parseResult); return false;
else else
continue; continue;
} }
@ -549,16 +563,19 @@ namespace NadekoBot.Services
// Bot will ignore commands which are ran more often than what specified by // Bot will ignore commands which are ran more often than what specified by
// GlobalCommandsCooldown constant (miliseconds) // GlobalCommandsCooldown constant (miliseconds)
if (!UsersOnShortCooldown.Add(context.Message.Author.Id)) 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 ////todo cmdcds
//if (CmdCdsCommands.HasCooldown(cmd, context.Guild, context.User)) //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, 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."));
} }
} }
} }

View File

@ -1,34 +1,39 @@
using Discord; using Discord;
using Discord.WebSocket; using Discord.WebSocket;
using NadekoBot.DataStructures.ModuleBehaviors;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; 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 = new CustomReaction[] { };
public CustomReaction[] GlobalReactions => _globalReactions;
public ConcurrentDictionary<ulong, CustomReaction[]> GuildReactions { get; } = new ConcurrentDictionary<ulong, CustomReaction[]>(); public ConcurrentDictionary<ulong, CustomReaction[]> GuildReactions { get; } = new ConcurrentDictionary<ulong, CustomReaction[]>();
public ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>(); public ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>();
private readonly Logger _log; private readonly Logger _log;
private readonly DbHandler _db; private readonly DbHandler _db;
private readonly DiscordShardedClient _client;
public CustomReactions(DbHandler db) public CustomReactionsService(DbHandler db, DiscordShardedClient client)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_db = db; _db = db;
_client = client;
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
using (var uow = _db.UnitOfWork) using (var uow = _db.UnitOfWork)
{ {
var items = uow.CustomReactions.GetAll(); var items = uow.CustomReactions.GetAll();
GuildReactions = new ConcurrentDictionary<ulong, CustomReaction[]>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => g.ToArray())); GuildReactions = new ConcurrentDictionary<ulong, CustomReaction[]>(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(); sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
@ -54,7 +59,7 @@ namespace NadekoBot.Services
return false; return false;
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%"); 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); return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
}).ToArray(); }).ToArray();
@ -75,7 +80,7 @@ namespace NadekoBot.Services
if (cr == null) if (cr == null)
return false; return false;
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%"); 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); return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
}).ToArray(); }).ToArray();
if (grs.Length == 0) if (grs.Length == 0)
@ -84,5 +89,46 @@ namespace NadekoBot.Services
return greaction; return greaction;
} }
public async Task<bool> 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;
}
} }
} }

View File

@ -1,4 +1,5 @@
using Discord; using Discord;
using Discord.WebSocket;
using NadekoBot.DataStructures; using NadekoBot.DataStructures;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
@ -8,7 +9,7 @@ using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.CustomReactions namespace NadekoBot.Services.CustomReactions
{ {
public static class Extensions public static class Extensions
{ {
@ -17,11 +18,11 @@ namespace NadekoBot.Modules.CustomReactions
{"%target%", (ctx, trigger) => { return ctx.Content.Substring(trigger.Length).Trim().SanitizeMentions(); } } {"%target%", (ctx, trigger) => { return ctx.Content.Substring(trigger.Length).Trim().SanitizeMentions(); } }
}; };
public static Dictionary<string, Func<IUserMessage, string>> placeholders = new Dictionary<string, Func<IUserMessage, string>>() public static Dictionary<string, Func<IUserMessage, DiscordShardedClient, string>> placeholders = new Dictionary<string, Func<IUserMessage, DiscordShardedClient, string>>()
{ {
{"%mention%", (ctx) => { return $"<@{NadekoBot.Client.CurrentUser.Id}>"; } }, {"%mention%", (ctx, client) => { return $"<@{client.CurrentUser.Id}>"; } },
{"%user%", (ctx) => { return ctx.Author.Mention; } }, {"%user%", (ctx, client) => { return ctx.Author.Mention; } },
{"%rnduser%", (ctx) => { {"%rnduser%", (ctx, client) => {
//var ch = ctx.Channel as ITextChannel; //var ch = ctx.Channel as ITextChannel;
//if(ch == null) //if(ch == null)
// return ""; // 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) 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; 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) 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) foreach (var ph in responsePlaceholders)
@ -97,24 +98,23 @@ namespace NadekoBot.Modules.CustomReactions
return str; return str;
} }
public static string TriggerWithContext(this CustomReaction cr, IUserMessage ctx) public static string TriggerWithContext(this CustomReaction cr, IUserMessage ctx, DiscordShardedClient client)
=> cr.Trigger.ResolveTriggerString(ctx); => cr.Trigger.ResolveTriggerString(ctx, client);
public static string ResponseWithContext(this CustomReaction cr, IUserMessage ctx) public static string ResponseWithContext(this CustomReaction cr, IUserMessage ctx, DiscordShardedClient client)
=> cr.Response.ResolveResponseString(ctx, cr.Trigger.ResolveTriggerString(ctx)); => cr.Response.ResolveResponseString(ctx, client, cr.Trigger.ResolveTriggerString(ctx, client));
public static async Task<IUserMessage> Send(this CustomReaction cr, IUserMessage context) public static async Task<IUserMessage> Send(this CustomReaction cr, IUserMessage context, DiscordShardedClient client, CustomReactionsService crs)
{ {
var channel = cr.DmResponse ? await context.Author.CreateDMChannelAsync() : context.Channel; 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 crembed))
if (CREmbed.TryParse(cr.Response, out crembed))
{ {
return await channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? ""); 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());
} }
} }
} }

View File

@ -5,7 +5,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.DataStructures namespace NadekoBot.Services.Permissions
{ {
public class OldPermissionCache public class OldPermissionCache
{ {

View File

@ -3,8 +3,9 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NadekoBot.DataStructures;
namespace NadekoBot.DataStructures namespace NadekoBot.Services.Permissions
{ {
public class PermissionsCollection<T> : IndexedCollection<T> where T : IIndexed public class PermissionsCollection<T> : IndexedCollection<T> where T : IIndexed
{ {

View File

@ -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
{
}
}

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
@ -8,7 +9,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
public interface INServiceProvider : IServiceProvider public interface INServiceProvider : IServiceProvider, IEnumerable<object>
{ {
T GetService<T>(); T GetService<T>();
} }
@ -49,5 +50,9 @@ namespace NadekoBot.Services
_services.TryGetValue(serviceType, out var toReturn); _services.TryGetValue(serviceType, out var toReturn);
return toReturn; return toReturn;
} }
IEnumerator IEnumerable.GetEnumerator() => _services.Values.GetEnumerator();
public IEnumerator<object> GetEnumerator() => _services.Values.GetEnumerator();
} }
} }