diff --git a/src/NadekoBot/Modules/CustomReactions/Extensions.cs b/src/NadekoBot/Modules/CustomReactions/Extensions.cs index b2bb133f..147d6dcf 100644 --- a/src/NadekoBot/Modules/CustomReactions/Extensions.cs +++ b/src/NadekoBot/Modules/CustomReactions/Extensions.cs @@ -14,7 +14,7 @@ namespace NadekoBot.Modules.CustomReactions { public static Dictionary> placeholders = new Dictionary>() { - {"%mention%", (ctx) => { return $"<@{NadekoBot.Client.GetCurrentUserAsync().Id}>"; } }, + {"%mention%", (ctx) => { return $"<@{NadekoBot.Client.GetCurrentUser().Id}>"; } }, {"%user%", (ctx) => { return ctx.Author.Mention; } }, {"%target%", (ctx) => { return ctx.MentionedUsers.Shuffle().FirstOrDefault()?.Mention ?? "Nobody"; } }, {"%rng%", (ctx) => { return new NadekoRandom().Next(0,10).ToString(); } } diff --git a/src/NadekoBot/Modules/Permissions/Permissions.cs b/src/NadekoBot/Modules/Permissions/Permissions.cs index d8e2d167..1ebb4433 100644 --- a/src/NadekoBot/Modules/Permissions/Permissions.cs +++ b/src/NadekoBot/Modules/Permissions/Permissions.cs @@ -11,12 +11,39 @@ using Discord; using NadekoBot.Services.Database; using NadekoBot.Services.Database.Models; using Discord.API; +using System.Collections.Concurrent; namespace NadekoBot.Modules.Permissions { [NadekoModule("Permissions", ";")] public partial class Permissions : DiscordModule { + public class PermissionCache + { + public string PermRole { get; set; } + public bool Verbose { get; set; } = true; + public Permission RootPermission { get; set; } + } + + //guildid, root permission + public static ConcurrentDictionary Cache; + + static Permissions() + { + using (var uow = DbHandler.UnitOfWork()) + { + Cache = new ConcurrentDictionary(uow.GuildConfigs + .PermissionsForAll() + .ToDictionary(k => k.GuildId, + v => new PermissionCache() + { + RootPermission = v.RootPermission, + Verbose = v.VerbosePermissions, + PermRole = v.PermissionRole + })); + } + } + public Permissions(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) { } @@ -31,6 +58,12 @@ namespace NadekoBot.Modules.Permissions { var config = uow.GuildConfigs.For(channel.Guild.Id); config.VerbosePermissions = action.Value; + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = Permission.GetDefaultRoot(), + Verbose = config.VerbosePermissions + }, (id, old) => { old.Verbose = config.VerbosePermissions; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } @@ -52,6 +85,12 @@ namespace NadekoBot.Modules.Permissions } else { config.PermissionRole = role.Name.Trim(); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = Permission.GetDefaultRoot(), + Verbose = config.VerbosePermissions + }, (id, old) => { old.PermRole = role.Name.Trim(); return old; }); await uow.CompleteAsync().ConfigureAwait(false); } } @@ -107,6 +146,12 @@ namespace NadekoBot.Modules.Permissions { p = perms.RemoveAt(index); } + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } @@ -216,6 +261,12 @@ namespace NadekoBot.Modules.Permissions } config.RootPermission = fromPerm.GetRoot(); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } await channel.SendMessageAsync($"`Moved permission:` \"{fromPerm.GetCommand(channel.Guild)}\" `from #{++from} to #{++to}.`").ConfigureAwait(false); @@ -244,7 +295,14 @@ namespace NadekoBot.Modules.Permissions SecondaryTargetName = command.Text.ToLowerInvariant(), State = action.Value, }; - uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); + await uow.CompleteAsync().ConfigureAwait(false); } await channel.SendMessageAsync($"{(action.Value ? "Allowed" : "Denied")} usage of `{command.Text}` command on this server.").ConfigureAwait(false); @@ -266,7 +324,13 @@ namespace NadekoBot.Modules.Permissions SecondaryTargetName = module.Name.ToLowerInvariant(), State = action.Value, }; - uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } await channel.SendMessageAsync($"{(action.Value ? "Allowed" : "Denied")} usage of `{module.Name}` module on this server.").ConfigureAwait(false); @@ -288,7 +352,13 @@ namespace NadekoBot.Modules.Permissions SecondaryTargetName = command.Text.ToLowerInvariant(), State = action.Value, }; - uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } await channel.SendMessageAsync($"{(action.Value ? "Allowed" : "Denied")} usage of `{command.Text}` command for `{user}` user.").ConfigureAwait(false); @@ -310,7 +380,13 @@ namespace NadekoBot.Modules.Permissions SecondaryTargetName = module.Name.ToLowerInvariant(), State = action.Value, }; - uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } await channel.SendMessageAsync($"{(action.Value ? "Allowed" : "Denied")} usage of `{module.Name}` module for `{user}` user.").ConfigureAwait(false); @@ -332,7 +408,13 @@ namespace NadekoBot.Modules.Permissions SecondaryTargetName = command.Text.ToLowerInvariant(), State = action.Value, }; - uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } await channel.SendMessageAsync($"{(action.Value ? "Allowed" : "Denied")} usage of `{command.Text}` command for `{role}` role.").ConfigureAwait(false); @@ -354,7 +436,13 @@ namespace NadekoBot.Modules.Permissions SecondaryTargetName = module.Name.ToLowerInvariant(), State = action.Value, }; - uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } await channel.SendMessageAsync($"{(action.Value ? "Allowed" : "Denied")} usage of `{module.Name}` module for `{role}` role.").ConfigureAwait(false); @@ -377,7 +465,13 @@ namespace NadekoBot.Modules.Permissions SecondaryTargetName = command.Text.ToLowerInvariant(), State = action.Value, }; - uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } } @@ -403,7 +497,13 @@ namespace NadekoBot.Modules.Permissions SecondaryTargetName = module.Name.ToLowerInvariant(), State = action.Value, }; - uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } await channel.SendMessageAsync($"{(action.Value ? "Allowed" : "Denied")} usage of `{module.Name}` module for `{chnl}` channel.").ConfigureAwait(false); @@ -425,7 +525,13 @@ namespace NadekoBot.Modules.Permissions SecondaryTargetName = "*", State = action.Value, }; - uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } await channel.SendMessageAsync($"{(action.Value ? "Allowed" : "Denied")} usage of `ALL MODULES` for `{chnl}` channel.").ConfigureAwait(false); @@ -447,7 +553,13 @@ namespace NadekoBot.Modules.Permissions SecondaryTargetName = "*", State = action.Value, }; - uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } await channel.SendMessageAsync($"{(action.Value ? "Allowed" : "Denied")} usage of `ALL MODULES` for `{role}` role.").ConfigureAwait(false); @@ -469,7 +581,13 @@ namespace NadekoBot.Modules.Permissions SecondaryTargetName = "*", State = action.Value, }; - uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } await channel.SendMessageAsync($"{(action.Value ? "Allowed" : "Denied")} usage of `ALL MODULES` for `{user}` user.").ConfigureAwait(false); @@ -502,7 +620,13 @@ namespace NadekoBot.Modules.Permissions State = true, }; - uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, allowUser); + var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, allowUser); + Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } await channel.SendMessageAsync($"{(action.Value ? "Allowed" : "Denied")} usage of `ALL MODULES` on this server.").ConfigureAwait(false); diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 21bce20e..628e883a 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -129,7 +129,7 @@ namespace NadekoBot.Services { var t = await ExecuteCommand(usrMsg, usrMsg.Content, guild, usrMsg.Author, MultiMatchHandling.Best); var command = t.Item1; - var verbose = t.Item2; + var permCache = t.Item2; var result = t.Item3; sw.Stop(); var channel = (usrMsg.Channel as ITextChannel); @@ -165,7 +165,7 @@ namespace NadekoBot.Services ); if (guild != null && command != null && result.Error == CommandError.Exception) { - if (verbose) + if (permCache != null && permCache.Verbose) try { await msg.Channel.SendMessageAsync(":warning: " + result.ErrorReason).ConfigureAwait(false); } catch { } } } @@ -189,10 +189,10 @@ namespace NadekoBot.Services return; } - public async Task> ExecuteCommand(IUserMessage message, string input, IGuild guild, IUser user, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Best) { + public async Task> ExecuteCommand(IUserMessage message, string input, IGuild guild, IUser user, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Best) { var searchResult = _commandService.Search(message, input); if (!searchResult.IsSuccess) - return new Tuple(null, false, searchResult); + return new Tuple(null, null, searchResult); var commands = searchResult.Commands; for (int i = commands.Count - 1; i >= 0; i--) @@ -201,7 +201,7 @@ namespace NadekoBot.Services if (!preconditionResult.IsSuccess) { if (commands.Count == 1) - return new Tuple(null, false, searchResult); + return new Tuple(null, null, searchResult); else continue; } @@ -225,55 +225,55 @@ namespace NadekoBot.Services if (!parseResult.IsSuccess) { if (commands.Count == 1) - return new Tuple(null, false, parseResult); + return new Tuple(null, null, parseResult); else continue; } } - bool verbose = false; - Permission rootPerm = null; - string permRole = ""; - if (guild != null) - { - using (var uow = DbHandler.UnitOfWork()) - { - var config = uow.GuildConfigs.PermissionsFor(guild.Id); - verbose = config.VerbosePermissions; - rootPerm = config.RootPermission; - permRole = config.PermissionRole.Trim().ToLowerInvariant(); - } - } - _log.Info("Permissions retrieved"); + var cmd = commands[i]; bool resetCommand = cmd.Name == "ResetPermissions"; - //check permissions + PermissionCache pc; if (guild != null) { - int index; - if (!resetCommand && !rootPerm.AsEnumerable().CheckPermissions(message, cmd.Text, cmd.Module.Name, out index)) + pc = Permissions.Cache.GetOrAdd(guild.Id, (id) => { - var returnMsg = $"Permission number #{index + 1} **{rootPerm.GetAt(index).GetCommand()}** is preventing this action."; - return new Tuple(cmd, verbose, SearchResult.FromError(CommandError.Exception, returnMsg)); + using (var uow = DbHandler.UnitOfWork()) + { + var config = uow.GuildConfigs.PermissionsFor(guild.Id); + return new PermissionCache() + { + Verbose = config.VerbosePermissions, + RootPermission = config.RootPermission, + PermRole = config.PermissionRole.Trim().ToLowerInvariant(), + }; + } + }); + int index; + if (!resetCommand && !pc.RootPermission.AsEnumerable().CheckPermissions(message, cmd.Text, cmd.Module.Name, out index)) + { + var returnMsg = $"Permission number #{index + 1} **{pc.RootPermission.GetAt(index).GetCommand()}** is preventing this action."; + return new Tuple(cmd, pc, SearchResult.FromError(CommandError.Exception, returnMsg)); } if (cmd.Module.Source.Name == typeof(Permissions).Name) //permissions, you must have special role { - if (!((IGuildUser)user).Roles.Any(r => r.Name.Trim().ToLowerInvariant() == permRole)) + if (!((IGuildUser)user).Roles.Any(r => r.Name.Trim().ToLowerInvariant() == pc.PermRole.Trim().ToLowerInvariant())) { - return new Tuple(cmd, false, SearchResult.FromError(CommandError.Exception, $"You need the **{permRole}** role in order to use permission commands.")); + return new Tuple(cmd, pc, SearchResult.FromError(CommandError.Exception, $"You need the **{pc.PermRole}** role in order to use permission commands.")); } } } if (CmdCdsCommands.HasCooldown(cmd, guild, user)) - return new Tuple(cmd, false, SearchResult.FromError(CommandError.Exception, $"That command is on cooldown for you.")); + return new Tuple(cmd, null, SearchResult.FromError(CommandError.Exception, $"That command is on cooldown for you.")); - return new Tuple(commands[i], false, await commands[i].Execute(message, parseResult)); + return new Tuple(commands[i], null, await commands[i].Execute(message, parseResult)); } - return new Tuple(null, false, SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload.")); + return new Tuple(null, null, SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload.")); } } diff --git a/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs b/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs index e6797de1..7a780380 100644 --- a/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs @@ -12,7 +12,8 @@ namespace NadekoBot.Services.Database.Repositories { GuildConfig For(ulong guildId); GuildConfig PermissionsFor(ulong guildId); - void SetNewRootPermission(ulong guildId, Permission p); + IEnumerable PermissionsForAll(); + GuildConfig SetNewRootPermission(ulong guildId, Permission p); IEnumerable GetAllFollowedStreams(); } } diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs index 0e9151bb..1b3c1501 100644 --- a/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs @@ -90,12 +90,28 @@ namespace NadekoBot.Services.Database.Repositories.Impl return config; } + public IEnumerable PermissionsForAll() + { + var query = _set.Include(gc => gc.RootPermission); + + //todo this is possibly a disaster for performance + //What i could do instead is count the number of permissions in the permission table for this guild + // and make a for loop with those. + // or just select permissions for this guild and manually chain them + for (int i = 0; i < 60; i++) + { + query = query.ThenInclude(gc => gc.Next); + } + + return query.ToList(); + } + public IEnumerable GetAllFollowedStreams() => _set.Include(gc => gc.FollowedStreams) .SelectMany(gc => gc.FollowedStreams) .ToList(); - public void SetNewRootPermission(ulong guildId, Permission p) + public GuildConfig SetNewRootPermission(ulong guildId, Permission p) { var data = _set .Include(gc => gc.RootPermission) @@ -103,6 +119,7 @@ namespace NadekoBot.Services.Database.Repositories.Impl data.RootPermission.Prepend(p); data.RootPermission = p; + return data; } } } diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index 6523b3cf..1c84cf63 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -33,17 +33,17 @@ namespace NadekoBot.Services.Impl this.client.Disconnected += _ => Reset(); } - public Task Print() + public async Task Print() { - var curUser = client.GetCurrentUserAsync(); - return Task.FromResult($@"`Author: Kwoth` `Library: Discord.Net` + var curUser = await client.GetCurrentUserAsync(); + return $@"`Author: Kwoth` `Library: Discord.Net` `Bot Version: {BotVersion}` `Bot id: {curUser.Id}` `Owners' Ids: {string.Join(", ", NadekoBot.Credentials.OwnerIds)}` `Uptime: {GetUptimeString()}` `Servers: {client.GetGuilds().Count} | TextChannels: {client.GetGuilds().SelectMany(g => g.GetChannels().Where(c => c is ITextChannel)).Count()} | VoiceChannels: {client.GetGuilds().SelectMany(g => g.GetChannels().Where(c => c is IVoiceChannel)).Count()}` `Commands Ran this session: {commandsRan}` -`Messages: {messageCounter} ({messageCounter / (double)GetUptime().TotalSeconds:F2}/sec)` `Heap: {Heap} MB`"); +`Messages: {messageCounter} ({messageCounter / (double)GetUptime().TotalSeconds:F2}/sec)` `Heap: {Heap} MB`"; } public Task Reset() diff --git a/src/NadekoBot/credentials_example.json b/src/NadekoBot/credentials_example.json new file mode 100644 index 00000000..31a9414f --- /dev/null +++ b/src/NadekoBot/credentials_example.json @@ -0,0 +1,15 @@ +{ + "ClientId": 123123123, + "BotId": null, + "Token": "", + "OwnerIds": [ + 0 + ], + "LoLApiKey": "", + "GoogleApiKey": "", + "MashapeKey": "", + "OsuApiKey": "", + "SoundCloudClientId": "", + "Db": null, + "TotalShards": 1 +} \ No newline at end of file