Killed history

This commit is contained in:
Kwoth
2021-09-06 21:29:22 +02:00
commit 7aca29ae8a
950 changed files with 366651 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
using System.Collections.Generic;
using System.Linq;
using Discord;
using Discord.WebSocket;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Core.Services;
using NadekoBot.Core.Services.Database.Models;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Core.Common;
namespace NadekoBot.Modules.Permissions.Services
{
public sealed class BlacklistService : IEarlyBehavior, INService
{
private readonly DbService _db;
private readonly IPubSub _pubSub;
private readonly IBotCredentials _creds;
private IReadOnlyList<BlacklistEntry> _blacklist;
public int Priority => -100;
public ModuleBehaviorType BehaviorType => ModuleBehaviorType.Blocker;
private readonly TypedKey<BlacklistEntry[]> blPubKey = new TypedKey<BlacklistEntry[]>("blacklist.reload");
public BlacklistService(DbService db, IPubSub pubSub, IBotCredentials creds)
{
_db = db;
_pubSub = pubSub;
_creds = creds;
Reload(false);
_pubSub.Sub(blPubKey, OnReload);
}
private ValueTask OnReload(BlacklistEntry[] blacklist)
{
_blacklist = blacklist;
return default;
}
public Task<bool> RunBehavior(DiscordSocketClient _, IGuild guild, IUserMessage usrMsg)
{
foreach (var bl in _blacklist)
{
if (guild != null && bl.Type == BlacklistType.Server && bl.ItemId == guild.Id)
return Task.FromResult(true);
if (bl.Type == BlacklistType.Channel && bl.ItemId == usrMsg.Channel.Id)
return Task.FromResult(true);
if (bl.Type == BlacklistType.User && bl.ItemId == usrMsg.Author.Id)
return Task.FromResult(true);
}
return Task.FromResult(false);
}
public IReadOnlyList<BlacklistEntry> GetBlacklist()
=> _blacklist;
public void Reload(bool publish = true)
{
using var uow = _db.GetDbContext();
var toPublish = uow._context.Blacklist.AsNoTracking().ToArray();
_blacklist = toPublish;
if (publish)
{
_pubSub.Pub(blPubKey, toPublish);
}
}
public void Blacklist(BlacklistType type, ulong id)
{
if (_creds.OwnerIds.Contains(id))
return;
using var uow = _db.GetDbContext();
var item = new BlacklistEntry { ItemId = id, Type = type };
uow._context.Blacklist.Add(item);
uow.SaveChanges();
Reload(true);
}
public void UnBlacklist(BlacklistType type, ulong id)
{
using var uow = _db.GetDbContext();
var toRemove = uow._context.Blacklist
.FirstOrDefault(bi => bi.ItemId == id && bi.Type == type);
if (!(toRemove is null))
uow._context.Blacklist.Remove(toRemove);
uow.SaveChanges();
Reload(true);
}
public void BlacklistUsers(IReadOnlyCollection<ulong> toBlacklist)
{
using (var uow = _db.GetDbContext())
{
var bc = uow._context.Blacklist;
//blacklist the users
bc.AddRange(toBlacklist.Select(x =>
new BlacklistEntry
{
ItemId = x,
Type = BlacklistType.User,
}));
//clear their currencies
uow.DiscordUsers.RemoveFromMany(toBlacklist);
uow.SaveChanges();
}
Reload(true);
}
}
}

View File

@@ -0,0 +1,81 @@
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Common.Collections;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Core.Services;
using NadekoBot.Core.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 int Priority { get; } = 0;
public CmdCdService(NadekoBot bot)
{
CommandCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>>(
bot.AllGuildConfigs.ToDictionary(k => k.GuildId,
v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
}
public Task<bool> TryBlock(IGuild guild, IUser user, string commandName)
{
if (guild is null)
return Task.FromResult(false);
var cmdcds = CommandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<CommandCooldown>());
CommandCooldown cdRule;
if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == commandName)) != null)
{
var activeCdsForGuild = ActiveCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<ActiveCooldown>());
if (activeCdsForGuild.FirstOrDefault(ac => ac.UserId == user.Id && ac.Command == commandName) != null)
{
return Task.FromResult(true);
}
activeCdsForGuild.Add(new ActiveCooldown()
{
UserId = user.Id,
Command = commandName,
});
var _ = Task.Run(async () =>
{
try
{
await Task.Delay(cdRule.Seconds * 1000).ConfigureAwait(false);
activeCdsForGuild.RemoveWhere(ac => ac.Command == commandName && ac.UserId == user.Id);
}
catch
{
// ignored
}
});
}
return Task.FromResult(false);
}
public Task<bool> TryBlockLate(DiscordSocketClient client, ICommandContext ctx, string moduleName, CommandInfo command)
{
var guild = ctx.Guild;
var user = ctx.User;
var commandName = command.Name.ToLowerInvariant();
return TryBlock(guild, user, commandName);
}
}
public class ActiveCooldown
{
public string Command { get; set; }
public ulong UserId { get; set; }
}
}

View File

@@ -0,0 +1,219 @@
using System.Collections.Concurrent;
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.Core.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using NadekoBot.Core.Services.Database.Models;
using Serilog;
namespace NadekoBot.Modules.Permissions.Services
{
public class FilterService : IEarlyBehavior, INService
{
private readonly DbService _db;
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<ulong> LinkFilteringChannels { get; }
public ConcurrentHashSet<ulong> LinkFilteringServers { get; }
public int Priority => -50;
public ModuleBehaviorType BehaviorType => ModuleBehaviorType.Blocker;
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 void ClearFilteredWords(ulong guildId)
{
using (var uow = _db.GetDbContext())
{
var gc = uow.GuildConfigs.ForId(guildId,
set => set.Include(x => x.FilteredWords)
.Include(x => x.FilterWordsChannelIds));
WordFilteringServers.TryRemove(guildId);
ServerFilteredWords.TryRemove(guildId, out _);
foreach (var c in gc.FilterWordsChannelIds)
{
WordFilteringChannels.TryRemove(c.ChannelId);
}
gc.FilterWords = false;
gc.FilteredWords.Clear();
gc.FilterWordsChannelIds.Clear();
uow.SaveChanges();
}
}
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, DbService db)
{
_db = db;
using(var uow = db.GetDbContext())
{
var ids = client.GetGuildIds();
var configs = uow._context.Set<GuildConfig>()
.AsQueryable()
.Include(x => x.FilteredWords)
.Include(x => x.FilterLinksChannelIds)
.Include(x => x.FilterWordsChannelIds)
.Include(x => x.FilterInvitesChannelIds)
.Where(gc => ids.Contains(gc.GuildId))
.ToList();
InviteFilteringServers = new ConcurrentHashSet<ulong>(configs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
InviteFilteringChannels = new ConcurrentHashSet<ulong>(configs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
LinkFilteringServers = new ConcurrentHashSet<ulong>(configs.Where(gc => gc.FilterLinks).Select(gc => gc.GuildId));
LinkFilteringChannels = new ConcurrentHashSet<ulong>(configs.SelectMany(gc => gc.FilterLinksChannelIds.Select(fci => fci.ChannelId)));
var dict = configs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word)));
ServerFilteredWords = new ConcurrentDictionary<ulong, ConcurrentHashSet<string>>(dict);
var serverFiltering = configs.Where(gc => gc.FilterWords);
WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId));
WordFilteringChannels = new ConcurrentHashSet<ulong>(configs.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 RunBehavior(null, guild, usrMsg);
});
return Task.CompletedTask;
};
}
public async Task<bool> RunBehavior(DiscordSocketClient _, IGuild guild, IUserMessage msg)
{
if (!(msg.Author is IGuildUser gu) || gu.GuildPermissions.Administrator)
return false;
var results = await Task.WhenAll(
FilterInvites(guild, msg),
FilterWords(guild, msg),
FilterLinks(guild, msg));
return results.Any(x => x);
}
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.Warning("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.Warning("I do not have permission to filter invites in channel with id " + usrMsg.Channel.Id, ex);
return true;
}
}
return false;
}
public async Task<bool> FilterLinks(IGuild guild, IUserMessage usrMsg)
{
if (guild is null)
return false;
if (usrMsg is null)
return false;
if ((LinkFilteringChannels.Contains(usrMsg.Channel.Id)
|| LinkFilteringServers.Contains(guild.Id))
&& usrMsg.Content.TryGetUrlPath(out _))
{
try
{
await usrMsg.DeleteAsync().ConfigureAwait(false);
return true;
}
catch (HttpException ex)
{
Log.Warning("I do not have permission to filter links in channel with id " + usrMsg.Channel.Id, ex);
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,101 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Core.Services;
namespace NadekoBot.Modules.Permissions.Services
{
public class GlobalPermissionService : ILateBlocker, INService
{
private readonly BotConfigService _bss;
public int Priority { get; } = 0;
public HashSet<string> BlockedCommands => _bss.Data.Blocked.Commands;
public HashSet<string> BlockedModules => _bss.Data.Blocked.Modules;
public GlobalPermissionService(BotConfigService bss)
{
_bss = bss;
}
public Task<bool> TryBlockLate(DiscordSocketClient client, ICommandContext ctx, string moduleName, CommandInfo command)
{
var settings = _bss.Data;
var commandName = command.Name.ToLowerInvariant();
if (commandName != "resetglobalperms" &&
(settings.Blocked.Commands.Contains(commandName) ||
settings.Blocked.Modules.Contains(moduleName.ToLowerInvariant())))
{
return Task.FromResult(true);
}
return Task.FromResult(false);
}
/// <summary>
/// Toggles module blacklist
/// </summary>
/// <param name="moduleName">Lowercase module name</param>
/// <returns>Whether the module is added</returns>
public bool ToggleModule(string moduleName)
{
var added = false;
_bss.ModifyConfig(bs =>
{
if (bs.Blocked.Modules.Add(moduleName))
{
added = true;
}
else
{
bs.Blocked.Modules.Remove(moduleName);
added = false;
}
});
return added;
}
/// <summary>
/// Toggles command blacklist
/// </summary>
/// <param name="commandName">Lowercase command name</param>
/// <returns>Whether the command is added</returns>
public bool ToggleCommand(string commandName)
{
var added = false;
_bss.ModifyConfig(bs =>
{
if (bs.Blocked.Commands.Add(commandName))
{
added = true;
}
else
{
bs.Blocked.Commands.Remove(commandName);
added = false;
}
});
return added;
}
/// <summary>
/// Resets all global permissions
/// </summary>
public Task Reset()
{
_bss.ModifyConfig(bs =>
{
bs.Blocked.Commands.Clear();
bs.Blocked.Modules.Clear();
});
return Task.CompletedTask;
}
}
}

View File

@@ -0,0 +1,184 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Extensions;
using NadekoBot.Modules.Permissions.Common;
using NadekoBot.Core.Services;
using NadekoBot.Core.Services.Database.Models;
namespace NadekoBot.Modules.Permissions.Services
{
public class PermissionService : ILateBlocker, INService
{
public int Priority { get; } = 0;
private readonly DbService _db;
private readonly CommandHandler _cmd;
private readonly IBotStrings _strings;
//guildid, root permission
public ConcurrentDictionary<ulong, PermissionCache> Cache { get; } =
new ConcurrentDictionary<ulong, PermissionCache>();
public PermissionService(DiscordSocketClient client, DbService db, CommandHandler cmd, IBotStrings strings)
{
_db = db;
_cmd = cmd;
_strings = strings;
using (var uow = _db.GetDbContext())
{
foreach (var x in uow.GuildConfigs.Permissionsv2ForAll(client.Guilds.ToArray().Select(x => x.Id).ToList()))
{
Cache.TryAdd(x.GuildId, new PermissionCache()
{
Verbose = x.VerbosePermissions,
PermRole = x.PermissionRole,
Permissions = new PermissionsCollection<Permissionv2>(x.Permissions)
});
}
}
}
public PermissionCache GetCacheFor(ulong guildId)
{
if (!Cache.TryGetValue(guildId, out var pc))
{
using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigs.ForId(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;
}
public async Task AddPermissions(ulong guildId, params Permissionv2[] perms)
{
using (var uow = _db.GetDbContext())
{
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.SaveChangesAsync();
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, ICommandContext ctx, string moduleName,
CommandInfo command)
{
var guild = ctx.Guild;
var msg = ctx.Message;
var user = ctx.User;
var channel = ctx.Channel;
var commandName = command.Name.ToLowerInvariant();
await Task.Yield();
if (guild == null)
{
return false;
}
else
{
var resetCommand = commandName == "resetperms";
PermissionCache pc = GetCacheFor(guild.Id);
if (!resetCommand && !pc.Permissions.CheckPermissions(msg, commandName, moduleName, out int index))
{
if (pc.Verbose)
{
try
{
await channel.SendErrorAsync(_strings.GetText("perm_prevent", guild.Id, index + 1,
Format.Bold(pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), (SocketGuild) guild))))
.ConfigureAwait(false);
}
catch
{
}
}
return true;
}
if (moduleName == nameof(Permissions))
{
if (!(user is IGuildUser guildUser))
return true;
if (guildUser.GuildPermissions.Administrator)
return false;
var permRole = pc.PermRole;
if (!ulong.TryParse(permRole, out var rid))
rid = 0;
string returnMsg;
IRole role;
if (string.IsNullOrWhiteSpace(permRole) || (role = guild.GetRole(rid)) == null)
{
returnMsg = $"You need Admin permissions in order to use permission commands.";
if (pc.Verbose)
try { await channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { }
return true;
}
else if (!guildUser.RoleIds.Contains(rid))
{
returnMsg = $"You need the {Format.Bold(role.Name)} role in order to use permission commands.";
if (pc.Verbose)
try { await channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { }
return true;
}
return false;
}
}
return false;
}
public async Task Reset(ulong guildId)
{
using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigs.GcWithPermissionsv2For(guildId);
config.Permissions = Permissionv2.GetDefaultPermlist;
await uow.SaveChangesAsync();
UpdateCache(config);
}
}
}
}