CommandHandler refactor
This commit is contained in:
parent
8c2a94aeb8
commit
0427642af2
@ -23,13 +23,18 @@ namespace NadekoBot.Modules.Permissions
|
||||
[Group]
|
||||
public class BlacklistCommands : ModuleBase
|
||||
{
|
||||
public static ConcurrentHashSet<BlacklistItem> BlacklistedItems { get; set; } = new ConcurrentHashSet<BlacklistItem>();
|
||||
public static ConcurrentHashSet<ulong> BlacklistedUsers { get; set; } = new ConcurrentHashSet<ulong>();
|
||||
public static ConcurrentHashSet<ulong> BlacklistedGuilds { get; set; } = new ConcurrentHashSet<ulong>();
|
||||
public static ConcurrentHashSet<ulong> BlacklistedChannels { get; set; } = new ConcurrentHashSet<ulong>();
|
||||
|
||||
static BlacklistCommands()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
BlacklistedItems = new ConcurrentHashSet<BlacklistItem>(uow.BotConfig.GetOrCreate().Blacklist);
|
||||
var blacklist = uow.BotConfig.GetOrCreate().Blacklist;
|
||||
BlacklistedUsers = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.User).Select(c => c.ItemId));
|
||||
BlacklistedGuilds = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.Server).Select(c => c.ItemId));
|
||||
BlacklistedChannels = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.Channel).Select(c => c.ItemId));
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,12 +71,34 @@ namespace NadekoBot.Modules.Permissions
|
||||
{
|
||||
var item = new BlacklistItem { ItemId = id, Type = type };
|
||||
uow.BotConfig.GetOrCreate().Blacklist.Add(item);
|
||||
BlacklistedItems.Add(item);
|
||||
if (type == BlacklistType.Server)
|
||||
{
|
||||
BlacklistedGuilds.Add(id);
|
||||
}
|
||||
else if (type == BlacklistType.Channel)
|
||||
{
|
||||
BlacklistedChannels.Add(id);
|
||||
}
|
||||
else if (type == BlacklistType.User)
|
||||
{
|
||||
BlacklistedUsers.Add(id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uow.BotConfig.GetOrCreate().Blacklist.RemoveWhere(bi => bi.ItemId == id && bi.Type == type);
|
||||
BlacklistedItems.RemoveWhere(bi => bi.ItemId == id && bi.Type == type);
|
||||
if (type == BlacklistType.Server)
|
||||
{
|
||||
BlacklistedGuilds.TryRemove(id);
|
||||
}
|
||||
else if (type == BlacklistType.Channel)
|
||||
{
|
||||
BlacklistedChannels.TryRemove(id);
|
||||
}
|
||||
else if (type == BlacklistType.User)
|
||||
{
|
||||
BlacklistedUsers.TryRemove(id);
|
||||
}
|
||||
}
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ using NadekoBot.Modules.CustomReactions;
|
||||
using NadekoBot.Modules.Games;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using NadekoBot.DataStructures;
|
||||
|
||||
namespace NadekoBot.Services
|
||||
{
|
||||
@ -29,6 +30,8 @@ namespace NadekoBot.Services
|
||||
}
|
||||
public class CommandHandler
|
||||
{
|
||||
public const int GlobalCommandsCooldown = 1500;
|
||||
|
||||
private ShardedDiscordClient _client;
|
||||
private CommandService _commandService;
|
||||
private Logger _log;
|
||||
@ -52,7 +55,7 @@ namespace NadekoBot.Services
|
||||
clearUsersOnShortCooldown = new Timer((_) =>
|
||||
{
|
||||
UsersOnShortCooldown.Clear();
|
||||
}, null, 1500, 1500);
|
||||
}, null, GlobalCommandsCooldown, GlobalCommandsCooldown);
|
||||
}
|
||||
public async Task StartHandling()
|
||||
{
|
||||
@ -71,153 +74,187 @@ namespace NadekoBot.Services
|
||||
_client.MessageReceived += MessageReceivedHandler;
|
||||
}
|
||||
|
||||
private async void MessageReceivedHandler(SocketMessage msg)
|
||||
private async Task<bool> TryRunCleverbot(SocketUserMessage usrMsg, IGuild guild)
|
||||
{
|
||||
if (guild == null)
|
||||
return false;
|
||||
try
|
||||
{
|
||||
var usrMsg = msg as SocketUserMessage;
|
||||
if (usrMsg == null)
|
||||
return;
|
||||
|
||||
if (!usrMsg.IsAuthor())
|
||||
UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old);
|
||||
|
||||
if (!UsersOnShortCooldown.Add(usrMsg.Author.Id))
|
||||
return;
|
||||
|
||||
if (msg.Author.IsBot || !NadekoBot.Ready) //no bots
|
||||
return;
|
||||
|
||||
var guild = (msg.Channel as SocketTextChannel)?.Guild;
|
||||
|
||||
if (guild != null && guild.OwnerId != msg.Author.Id)
|
||||
var cleverbotExecuted = await Games.CleverBotCommands.TryAsk(usrMsg).ConfigureAwait(false);
|
||||
if (cleverbotExecuted)
|
||||
{
|
||||
//todo split checks into their own modules
|
||||
if (Permissions.FilterCommands.InviteFilteringChannels.Contains(msg.Channel.Id) ||
|
||||
Permissions.FilterCommands.InviteFilteringServers.Contains(guild.Id))
|
||||
{
|
||||
if (usrMsg.Content.IsDiscordInvite())
|
||||
{
|
||||
try
|
||||
{
|
||||
await usrMsg.DeleteAsync().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
_log.Warn("I do not have permission to filter invites in channel with id " + msg.Channel.Id, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var filteredWords = Permissions.FilterCommands.FilteredWordsForChannel(msg.Channel.Id, guild.Id).Concat(Permissions.FilterCommands.FilteredWordsForServer(guild.Id));
|
||||
var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
|
||||
if (filteredWords.Any(w => wordsInMessage.Contains(w)))
|
||||
{
|
||||
try
|
||||
{
|
||||
await usrMsg.DeleteAsync().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
_log.Warn("I do not have permission to filter words in channel with id " + msg.Channel.Id, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlacklistItem blacklistedItem;
|
||||
if ((blacklistedItem = Permissions.BlacklistCommands.BlacklistedItems.FirstOrDefault(bi =>
|
||||
(bi.Type == BlacklistItem.BlacklistType.Server && bi.ItemId == guild?.Id) ||
|
||||
(bi.Type == BlacklistItem.BlacklistType.Channel && bi.ItemId == msg.Channel.Id) ||
|
||||
(bi.Type == BlacklistItem.BlacklistType.User && bi.ItemId == msg.Author.Id))) != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var cleverbotExecuted = await Games.CleverBotCommands.TryAsk(usrMsg);
|
||||
if (cleverbotExecuted)
|
||||
{
|
||||
_log.Info($@"CleverBot Executed
|
||||
_log.Info($@"CleverBot Executed
|
||||
Server: {guild.Name} [{guild.Id}]
|
||||
Channel: {usrMsg.Channel?.Name} [{usrMsg.Channel?.Id}]
|
||||
UserId: {usrMsg.Author} [{usrMsg.Author.Id}]
|
||||
Message: {usrMsg.Content}");
|
||||
return;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); }
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); }
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// maybe this message is a custom reaction
|
||||
var crExecuted = await CustomReactions.TryExecuteCustomReaction(usrMsg).ConfigureAwait(false);
|
||||
private bool IsBlacklisted(IGuild guild, SocketUserMessage usrMsg) =>
|
||||
(guild != null && BlacklistCommands.BlacklistedGuilds.Contains(guild.Id)) ||
|
||||
BlacklistCommands.BlacklistedChannels.Contains(usrMsg.Channel.Id) ||
|
||||
BlacklistCommands.BlacklistedUsers.Contains(usrMsg.Author.Id);
|
||||
|
||||
//if it was, don't execute the command
|
||||
if (crExecuted)
|
||||
return;
|
||||
}
|
||||
catch { }
|
||||
|
||||
string messageContent = usrMsg.Content;
|
||||
private async Task LogSuccessfulExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, Stopwatch sw)
|
||||
{
|
||||
await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false);
|
||||
_log.Info("Command Executed after {4}s\n\t" +
|
||||
"User: {0}\n\t" +
|
||||
"Server: {1}\n\t" +
|
||||
"Channel: {2}\n\t" +
|
||||
"Message: {3}",
|
||||
usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0}
|
||||
(channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1}
|
||||
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
|
||||
usrMsg.Content, // {3}
|
||||
sw.Elapsed.TotalSeconds);
|
||||
}
|
||||
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
var exec = await ExecuteCommand(new CommandContext(_client.MainClient, usrMsg), messageContent, DependencyMap.Empty, MultiMatchHandling.Best);
|
||||
var command = exec.CommandInfo;
|
||||
var permCache = exec.PermissionCache;
|
||||
var result = exec.Result;
|
||||
sw.Stop();
|
||||
var channel = (msg.Channel as ITextChannel);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
await CommandExecuted(usrMsg, command);
|
||||
_log.Info("Command Executed after {4}s\n\t" +
|
||||
"User: {0}\n\t" +
|
||||
"Server: {1}\n\t" +
|
||||
"Channel: {2}\n\t" +
|
||||
"Message: {3}",
|
||||
msg.Author + " [" + msg.Author.Id + "]", // {0}
|
||||
(channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1}
|
||||
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
|
||||
usrMsg.Content, // {3}
|
||||
sw.Elapsed.TotalSeconds // {4}
|
||||
);
|
||||
}
|
||||
else if (!result.IsSuccess && result.Error != CommandError.UnknownCommand)
|
||||
{
|
||||
_log.Warn("Command Errored after {5}s\n\t" +
|
||||
private void LogErroredExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, Stopwatch sw)
|
||||
{
|
||||
_log.Warn("Command Errored after {5}s\n\t" +
|
||||
"User: {0}\n\t" +
|
||||
"Server: {1}\n\t" +
|
||||
"Channel: {2}\n\t" +
|
||||
"Message: {3}\n\t" +
|
||||
"Error: {4}",
|
||||
msg.Author + " [" + msg.Author.Id + "]", // {0}
|
||||
usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0}
|
||||
(channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1}
|
||||
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
|
||||
usrMsg.Content,// {3}
|
||||
result.ErrorReason, // {4}
|
||||
exec.Result.ErrorReason, // {4}
|
||||
sw.Elapsed.TotalSeconds // {5}
|
||||
);
|
||||
if (guild != null && command != null && result.Error == CommandError.Exception)
|
||||
}
|
||||
|
||||
private async Task<bool> InviteFiltered(IGuild guild, SocketUserMessage usrMsg)
|
||||
{
|
||||
if ((Permissions.FilterCommands.InviteFilteringChannels.Contains(usrMsg.Channel.Id) ||
|
||||
Permissions.FilterCommands.InviteFilteringServers.Contains(guild.Id)) &&
|
||||
usrMsg.Content.IsDiscordInvite())
|
||||
{
|
||||
try
|
||||
{
|
||||
await usrMsg.DeleteAsync().ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
_log.Warn("I do not have permission to filter invites in channel with id " + usrMsg.Channel.Id, ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> WordFiltered(IGuild guild, SocketUserMessage usrMsg)
|
||||
{
|
||||
var filteredChannelWords = Permissions.FilterCommands.FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id);
|
||||
var filteredServerWords = Permissions.FilterCommands.FilteredWordsForServer(guild.Id);
|
||||
var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
|
||||
if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0)
|
||||
{
|
||||
foreach (var word in wordsInMessage)
|
||||
{
|
||||
if (filteredChannelWords.Contains(word) ||
|
||||
filteredServerWords.Contains(word))
|
||||
{
|
||||
if (permCache != null && permCache.Verbose)
|
||||
try { await msg.Channel.SendMessageAsync("⚠️ " + result.ErrorReason).ConfigureAwait(false); } catch { }
|
||||
try
|
||||
{
|
||||
await usrMsg.DeleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
_log.Warn("I do not have permission to filter words in channel with id " + usrMsg.Channel.Id, ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async void MessageReceivedHandler(SocketMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (msg.Author.IsBot || !NadekoBot.Ready) //no bots, wait until bot connected and initialized
|
||||
return;
|
||||
|
||||
var usrMsg = msg as SocketUserMessage;
|
||||
if (usrMsg == null) //has to be an user message, not system/other messages.
|
||||
return;
|
||||
|
||||
// track how many messagges each user is sending
|
||||
UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old);
|
||||
|
||||
// Bot will ignore commands which are ran more often than what specified by
|
||||
// GlobalCommandsCooldown constant (miliseconds)
|
||||
if (!UsersOnShortCooldown.Add(usrMsg.Author.Id))
|
||||
return;
|
||||
|
||||
var channel = msg.Channel as SocketTextChannel;
|
||||
var guild = channel?.Guild;
|
||||
|
||||
if (guild != null && guild.OwnerId != msg.Author.Id)
|
||||
{
|
||||
if (await InviteFiltered(guild, usrMsg).ConfigureAwait(false))
|
||||
return;
|
||||
|
||||
if (await WordFiltered(guild, usrMsg).ConfigureAwait(false))
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsBlacklisted(guild, usrMsg))
|
||||
return;
|
||||
|
||||
var cleverBotRan = await TryRunCleverbot(usrMsg, guild).ConfigureAwait(false);
|
||||
if (cleverBotRan)
|
||||
return;
|
||||
|
||||
// maybe this message is a custom reaction
|
||||
var crExecuted = await CustomReactions.TryExecuteCustomReaction(usrMsg).ConfigureAwait(false);
|
||||
if (crExecuted) //if it was, don't execute the command
|
||||
return;
|
||||
|
||||
string messageContent = usrMsg.Content;
|
||||
|
||||
// execute the command and measure the time it took
|
||||
var sw = Stopwatch.StartNew();
|
||||
var exec = await ExecuteCommand(new CommandContext(_client.MainClient, usrMsg), messageContent, DependencyMap.Empty, MultiMatchHandling.Best);
|
||||
sw.Stop();
|
||||
|
||||
if (exec.Result.IsSuccess)
|
||||
{
|
||||
await LogSuccessfulExecution(usrMsg, exec, channel, sw).ConfigureAwait(false);
|
||||
}
|
||||
else if (!exec.Result.IsSuccess && exec.Result.Error != CommandError.UnknownCommand)
|
||||
{
|
||||
LogErroredExecution(usrMsg, exec, channel, sw);
|
||||
if (guild != null && exec.CommandInfo != null && exec.Result.Error == CommandError.Exception)
|
||||
{
|
||||
if (exec.PermissionCache != null && exec.PermissionCache.Verbose)
|
||||
try { await msg.Channel.SendMessageAsync("⚠️ " + exec.Result.ErrorReason).ConfigureAwait(false); } catch { }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (msg.Channel is IPrivateChannel)
|
||||
{
|
||||
//rofl, gotta do this to prevent this message from occuring on polls
|
||||
// rofl, gotta do this to prevent dm help message being sent to
|
||||
// users who are voting on private polls (sending a number in a DM)
|
||||
int vote;
|
||||
if (int.TryParse(msg.Content, out vote)) return;
|
||||
|
||||
|
||||
await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false);
|
||||
|
||||
await DMForwardCommands.HandleDMForwarding(msg, ownerChannels);
|
||||
await DMForwardCommands.HandleDMForwarding(msg, ownerChannels).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -231,9 +268,8 @@ namespace NadekoBot.Services
|
||||
_log.Warn(ex.InnerException);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public Task<ExecuteCommandResult> ExecuteCommandAsync(CommandContext context, int argPos, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
|
||||
=> ExecuteCommand(context, context.Message.Content.Substring(argPos), dependencyMap, multiMatchHandling);
|
||||
|
||||
@ -328,19 +364,5 @@ namespace NadekoBot.Services
|
||||
|
||||
return new ExecuteCommandResult(null, null, SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload."));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user