Initial split of the modules

This commit is contained in:
Master Kwoth
2017-09-30 00:46:33 +02:00
parent cdc2c43913
commit 599245b1ca
499 changed files with 469 additions and 256 deletions

View File

@ -0,0 +1,30 @@
using System.Linq;
using System.Threading.Tasks;
using Discord;
using NadekoBot.Common.Collections;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Permissions.Services
{
public class BlacklistService : IEarlyBlocker, INService
{
public ConcurrentHashSet<ulong> BlacklistedUsers { get; }
public ConcurrentHashSet<ulong> BlacklistedGuilds { get; }
public ConcurrentHashSet<ulong> BlacklistedChannels { get; }
public BlacklistService(IBotConfigProvider bc)
{
var blacklist = bc.BotConfig.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));
}
public Task<bool> TryBlockEarly(IGuild guild, IUserMessage usrMsg)
=> Task.FromResult((guild != null && BlacklistedGuilds.Contains(guild.Id)) ||
BlacklistedChannels.Contains(usrMsg.Channel.Id) ||
BlacklistedUsers.Contains(usrMsg.Author.Id));
}
}

View File

@ -0,0 +1,67 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using NadekoBot.Common.Collections;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Permissions.Services
{
public class CmdCdService : ILateBlocker, INService
{
public ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> CommandCooldowns { get; }
public ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> ActiveCooldowns { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>();
public CmdCdService(IEnumerable<GuildConfig> gcs)
{
CommandCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>>(
gcs.ToDictionary(k => k.GuildId,
v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
}
public Task<bool> TryBlockLate(DiscordSocketClient client, IUserMessage msg, IGuild guild,
IMessageChannel channel, IUser user, string moduleName, string commandName)
{
if (guild == null)
return Task.FromResult(false);
var cmdcds = CommandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<CommandCooldown>());
CommandCooldown cdRule;
if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == commandName.ToLowerInvariant())) != null)
{
var activeCdsForGuild = ActiveCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<ActiveCooldown>());
if (activeCdsForGuild.FirstOrDefault(ac => ac.UserId == user.Id && ac.Command == commandName.ToLowerInvariant()) != null)
{
return Task.FromResult(true);
}
activeCdsForGuild.Add(new ActiveCooldown()
{
UserId = user.Id,
Command = commandName.ToLowerInvariant(),
});
var _ = Task.Run(async () =>
{
try
{
await Task.Delay(cdRule.Seconds * 1000);
activeCdsForGuild.RemoveWhere(ac => ac.Command == commandName.ToLowerInvariant() && ac.UserId == user.Id);
}
catch
{
// ignored
}
});
}
return Task.FromResult(false);
}
}
public class ActiveCooldown
{
public string Command { get; set; }
public ulong UserId { get; set; }
}
}

View File

@ -0,0 +1,140 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Net;
using Discord.WebSocket;
using NadekoBot.Common.Collections;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NLog;
namespace NadekoBot.Modules.Permissions.Services
{
public class FilterService : IEarlyBlocker, INService
{
private readonly Logger _log;
public ConcurrentHashSet<ulong> InviteFilteringChannels { get; }
public ConcurrentHashSet<ulong> InviteFilteringServers { get; }
//serverid, filteredwords
public ConcurrentDictionary<ulong, ConcurrentHashSet<string>> ServerFilteredWords { get; }
public ConcurrentHashSet<ulong> WordFilteringChannels { get; }
public ConcurrentHashSet<ulong> WordFilteringServers { get; }
public ConcurrentHashSet<string> FilteredWordsForChannel(ulong channelId, ulong guildId)
{
ConcurrentHashSet<string> words = new ConcurrentHashSet<string>();
if (WordFilteringChannels.Contains(channelId))
ServerFilteredWords.TryGetValue(guildId, out words);
return words;
}
public ConcurrentHashSet<string> FilteredWordsForServer(ulong guildId)
{
var words = new ConcurrentHashSet<string>();
if (WordFilteringServers.Contains(guildId))
ServerFilteredWords.TryGetValue(guildId, out words);
return words;
}
public FilterService(DiscordSocketClient _client, IEnumerable<GuildConfig> gcs)
{
_log = LogManager.GetCurrentClassLogger();
InviteFilteringServers = new ConcurrentHashSet<ulong>(gcs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
InviteFilteringChannels = new ConcurrentHashSet<ulong>(gcs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
var dict = gcs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word)));
ServerFilteredWords = new ConcurrentDictionary<ulong, ConcurrentHashSet<string>>(dict);
var serverFiltering = gcs.Where(gc => gc.FilterWords);
WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId));
WordFilteringChannels = new ConcurrentHashSet<ulong>(gcs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId)));
_client.MessageUpdated += (oldData, newMsg, channel) =>
{
var _ = Task.Run(() =>
{
var guild = (channel as ITextChannel)?.Guild;
var usrMsg = newMsg as IUserMessage;
if (guild == null || usrMsg == null)
return Task.CompletedTask;
return TryBlockEarly(guild, usrMsg);
});
return Task.CompletedTask;
};
}
public async Task<bool> TryBlockEarly(IGuild guild, IUserMessage msg)
=> !(msg.Author is IGuildUser gu) //it's never filtered outside of guilds, and never block administrators
? false
: !gu.GuildPermissions.Administrator && (await FilterInvites(guild, msg) || await FilterWords(guild, msg));
public async Task<bool> FilterWords(IGuild guild, IUserMessage usrMsg)
{
if (guild is null)
return false;
if (usrMsg is null)
return false;
var filteredChannelWords = FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id) ?? new ConcurrentHashSet<string>();
var filteredServerWords = FilteredWordsForServer(guild.Id) ?? new ConcurrentHashSet<string>();
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))
{
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;
}
public async Task<bool> FilterInvites(IGuild guild, IUserMessage usrMsg)
{
if (guild is null)
return false;
if (usrMsg is null)
return false;
if ((InviteFilteringChannels.Contains(usrMsg.Channel.Id)
|| 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;
}
}
}

View File

@ -0,0 +1,37 @@
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using NadekoBot.Common.Collections;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Services;
namespace NadekoBot.Modules.Permissions.Services
{
public class GlobalPermissionService : ILateBlocker, INService
{
public readonly ConcurrentHashSet<string> BlockedModules;
public readonly ConcurrentHashSet<string> BlockedCommands;
public GlobalPermissionService(IBotConfigProvider bc)
{
BlockedModules = new ConcurrentHashSet<string>(bc.BotConfig.BlockedModules.Select(x => x.Name));
BlockedCommands = new ConcurrentHashSet<string>(bc.BotConfig.BlockedCommands.Select(x => x.Name));
}
public async Task<bool> TryBlockLate(DiscordSocketClient client, IUserMessage msg, IGuild guild, IMessageChannel channel, IUser user, string moduleName, string commandName)
{
await Task.Yield();
commandName = commandName.ToLowerInvariant();
if (commandName != "resetglobalperms" &&
(BlockedCommands.Contains(commandName) ||
BlockedModules.Contains(moduleName.ToLowerInvariant())))
{
return true;
//return new ExecuteCommandResult(cmd, null, SearchResult.FromError(CommandError.Exception, $"Command or module is blocked globally by the bot owner."));
}
return false;
}
}
}

View File

@ -0,0 +1,226 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Extensions;
using NadekoBot.Modules.Permissions.Common;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NadekoBot.Services.Impl;
using NLog;
namespace NadekoBot.Modules.Permissions.Services
{
public class PermissionService : ILateBlocker, INService
{
private readonly DbService _db;
private readonly CommandHandler _cmd;
private readonly NadekoStrings _strings;
//guildid, root permission
public ConcurrentDictionary<ulong, PermissionCache> Cache { get; } =
new ConcurrentDictionary<ulong, PermissionCache>();
public PermissionService(DiscordSocketClient client, DbService db, CommandHandler cmd, NadekoStrings strings)
{
_db = db;
_cmd = cmd;
_strings = strings;
var sw = Stopwatch.StartNew();
if (client.ShardId == 0)
TryMigratePermissions();
using (var uow = _db.UnitOfWork)
{
foreach (var x in uow.GuildConfigs.Permissionsv2ForAll(client.Guilds.ToArray().Select(x => (long)x.Id).ToList()))
{
Cache.TryAdd(x.GuildId, new PermissionCache()
{
Verbose = x.VerbosePermissions,
PermRole = x.PermissionRole,
Permissions = new PermissionsCollection<Permissionv2>(x.Permissions)
});
}
}
}
public PermissionCache GetCache(ulong guildId)
{
if (!Cache.TryGetValue(guildId, out var pc))
{
using (var uow = _db.UnitOfWork)
{
var config = uow.GuildConfigs.For(guildId,
set => set.Include(x => x.Permissions));
UpdateCache(config);
}
Cache.TryGetValue(guildId, out pc);
if (pc == null)
throw new Exception("Cache is null.");
}
return pc;
}
private void TryMigratePermissions()
{
using (var uow = _db.UnitOfWork)
{
var bc = uow.BotConfig.GetOrCreate();
var log = LogManager.GetCurrentClassLogger();
if (bc.PermissionVersion <= 1)
{
log.Info("Permission version is 1, upgrading to 2.");
var oldCache = new ConcurrentDictionary<ulong, OldPermissionCache>(uow.GuildConfigs
.OldPermissionsForAll()
.Where(x => x.RootPermission != null) // there is a check inside already, but just in case
.ToDictionary(k => k.GuildId,
v => new OldPermissionCache()
{
RootPermission = v.RootPermission,
Verbose = v.VerbosePermissions,
PermRole = v.PermissionRole
}));
if (oldCache.Any())
{
log.Info("Old permissions found. Performing one-time migration to v2.");
var i = 0;
foreach (var oc in oldCache)
{
if (i % 3 == 0)
log.Info("Migrating Permissions #" + i + " - GuildId: " + oc.Key);
i++;
var gc = uow.GuildConfigs.GcWithPermissionsv2For(oc.Key);
var oldPerms = oc.Value.RootPermission.AsEnumerable().Reverse().ToList();
uow._context.Set<Permission>().RemoveRange(oldPerms);
gc.RootPermission = null;
if (oldPerms.Count > 2)
{
var newPerms = oldPerms.Take(oldPerms.Count - 1)
.Select(x => x.Tov2())
.ToList();
var allowPerm = Permissionv2.AllowAllPerm;
var firstPerm = newPerms[0];
if (allowPerm.State != firstPerm.State ||
allowPerm.PrimaryTarget != firstPerm.PrimaryTarget ||
allowPerm.SecondaryTarget != firstPerm.SecondaryTarget ||
allowPerm.PrimaryTargetId != firstPerm.PrimaryTargetId ||
allowPerm.SecondaryTargetName != firstPerm.SecondaryTargetName)
newPerms.Insert(0, Permissionv2.AllowAllPerm);
Cache.TryAdd(oc.Key, new PermissionCache
{
Permissions = new PermissionsCollection<Permissionv2>(newPerms),
Verbose = gc.VerbosePermissions,
PermRole = gc.PermissionRole,
});
gc.Permissions = newPerms;
}
}
log.Info("Permission migration to v2 is done.");
}
bc.PermissionVersion = 2;
uow.Complete();
}
if (bc.PermissionVersion <= 2)
{
var oldPrefixes = new[] { ".", ";", "!!", "!m", "!", "+", "-", "$", ">" };
uow._context.Database.ExecuteSqlCommand(
@"UPDATE Permissionv2
SET secondaryTargetName=trim(substr(secondaryTargetName, 3))
WHERE secondaryTargetName LIKE '!!%' OR secondaryTargetName LIKE '!m%';
UPDATE Permissionv2
SET secondaryTargetName=substr(secondaryTargetName, 2)
WHERE secondaryTargetName LIKE '.%' OR
secondaryTargetName LIKE '~%' OR
secondaryTargetName LIKE ';%' OR
secondaryTargetName LIKE '>%' OR
secondaryTargetName LIKE '-%' OR
secondaryTargetName LIKE '!%';");
bc.PermissionVersion = 3;
uow.Complete();
}
}
}
public async Task AddPermissions(ulong guildId, params Permissionv2[] perms)
{
using (var uow = _db.UnitOfWork)
{
var config = uow.GuildConfigs.GcWithPermissionsv2For(guildId);
//var orderedPerms = new PermissionsCollection<Permissionv2>(config.Permissions);
var max = config.Permissions.Max(x => x.Index); //have to set its index to be the highest
foreach (var perm in perms)
{
perm.Index = ++max;
config.Permissions.Add(perm);
}
await uow.CompleteAsync().ConfigureAwait(false);
UpdateCache(config);
}
}
public void UpdateCache(GuildConfig config)
{
Cache.AddOrUpdate(config.GuildId, new PermissionCache()
{
Permissions = new PermissionsCollection<Permissionv2>(config.Permissions),
PermRole = config.PermissionRole,
Verbose = config.VerbosePermissions
}, (id, old) =>
{
old.Permissions = new PermissionsCollection<Permissionv2>(config.Permissions);
old.PermRole = config.PermissionRole;
old.Verbose = config.VerbosePermissions;
return old;
});
}
public async Task<bool> TryBlockLate(DiscordSocketClient client, IUserMessage msg, IGuild guild, IMessageChannel channel, IUser user, string moduleName, string commandName)
{
await Task.Yield();
if (guild == null)
{
return false;
}
else
{
var resetCommand = commandName == "resetperms";
PermissionCache pc = GetCache(guild.Id);
if (!resetCommand && !pc.Permissions.CheckPermissions(msg, commandName, moduleName, out int index))
{
if (pc.Verbose)
try { await channel.SendErrorAsync(_strings.GetText("trigger", guild.Id, "Permissions".ToLowerInvariant(), index + 1, Format.Bold(pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), (SocketGuild)guild)))).ConfigureAwait(false); } catch { }
return true;
}
if (moduleName == "Permissions")
{
var roles = (user as SocketGuildUser)?.Roles ?? ((IGuildUser)user).RoleIds.Select(x => guild.GetRole(x)).Where(x => x != null);
if (!roles.Any(r => r.Name.Trim().ToLowerInvariant() == pc.PermRole.Trim().ToLowerInvariant()) && user.Id != ((IGuildUser)user).Guild.OwnerId)
{
var returnMsg = $"You need the **{pc.PermRole}** role in order to use permission commands.";
if (pc.Verbose)
try { await channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { }
return true;
}
}
}
return false;
}
}
}

View File

@ -0,0 +1,45 @@
using System.Threading.Tasks;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Permissions.Services
{
public class ResetPermissionsService : INService
{
private readonly PermissionService _perms;
private readonly GlobalPermissionService _globalPerms;
private readonly DbService _db;
public ResetPermissionsService(PermissionService perms, GlobalPermissionService globalPerms, DbService db)
{
_perms = perms;
_globalPerms = globalPerms;
_db = db;
}
public async Task ResetPermissions(ulong guildId)
{
using (var uow = _db.UnitOfWork)
{
var config = uow.GuildConfigs.GcWithPermissionsv2For(guildId);
config.Permissions = Permissionv2.GetDefaultPermlist;
await uow.CompleteAsync().ConfigureAwait(false);
_perms.UpdateCache(config);
}
}
public async Task ResetGlobalPermissions()
{
using (var uow = _db.UnitOfWork)
{
var gc = uow.BotConfig.GetOrCreate();
gc.BlockedCommands.Clear();
gc.BlockedModules.Clear();
_globalPerms.BlockedCommands.Clear();
_globalPerms.BlockedModules.Clear();
await uow.CompleteAsync().ConfigureAwait(false);
}
}
}
}