From 39b66cd5b48eb415e325355a458b6b1ed816a5e9 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 9 Mar 2017 00:58:58 +0100 Subject: [PATCH] Complete rework of how permissions are stored. Also permissions indexes are inverted now (;asm enable is always #1) --- .../DataStructures/PermissionsCollection.cs | 140 ++ .../20170308033058_permsv2.Designer.cs | 1206 +++++++++++++++++ .../Migrations/20170308033058_permsv2.cs | 49 + .../NadekoSqliteContextModelSnapshot.cs | 35 + .../Modules/Administration/Administration.cs | 15 +- .../Games/Commands/PlantAndPickCommands.cs | 15 +- src/NadekoBot/Modules/Games/Games.cs | 4 +- .../Permissions/PermissionExtensions.cs | 120 +- .../Modules/Permissions/Permissions.cs | 648 ++++----- .../{ => Permissions}/Pokemon/PokeStats.cs | 0 .../{ => Permissions}/Pokemon/Pokemon.cs | 0 .../{ => Permissions}/Pokemon/PokemonType.cs | 0 src/NadekoBot/Modules/Utility/Utility.cs | 6 +- .../Resources/CommandStrings.Designer.cs | 2 +- src/NadekoBot/Resources/CommandStrings.resx | 2 +- .../Resources/ResponseStrings.Designer.cs | 22 +- src/NadekoBot/Resources/ResponseStrings.resx | 10 +- src/NadekoBot/Services/CommandHandler.cs | 23 +- .../Services/Database/Models/GuildConfig.cs | 3 +- .../Services/Database/Models/Permission.cs | 99 +- .../Repositories/IGuildConfigRepository.cs | 5 +- .../Impl/GuildConfigRepository.cs | 80 +- src/NadekoBot/Services/Impl/Localization.cs | 2 + src/NadekoBot/Services/Impl/StatsService.cs | 2 +- 24 files changed, 1882 insertions(+), 606 deletions(-) create mode 100644 src/NadekoBot/DataStructures/PermissionsCollection.cs create mode 100644 src/NadekoBot/Migrations/20170308033058_permsv2.Designer.cs create mode 100644 src/NadekoBot/Migrations/20170308033058_permsv2.cs rename src/NadekoBot/Modules/{ => Permissions}/Pokemon/PokeStats.cs (100%) rename src/NadekoBot/Modules/{ => Permissions}/Pokemon/Pokemon.cs (100%) rename src/NadekoBot/Modules/{ => Permissions}/Pokemon/PokemonType.cs (100%) diff --git a/src/NadekoBot/DataStructures/PermissionsCollection.cs b/src/NadekoBot/DataStructures/PermissionsCollection.cs new file mode 100644 index 00000000..e7963e64 --- /dev/null +++ b/src/NadekoBot/DataStructures/PermissionsCollection.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using NadekoBot.Services.Database.Models; + +namespace NadekoBot.DataStructures +{ + public class PermissionsCollection : IList where T : IIndexed + { + public List Source { get; } + private readonly object _locker = new object(); + + public PermissionsCollection(IEnumerable source) + { + lock (_locker) + { + Source = source.OrderBy(x => x.Index).ToList(); + for (var i = 0; i < Source.Count; i++) + { + if(Source[i].Index != i) + Source[i].Index = i; + } + } + } + + public static implicit operator List(PermissionsCollection x) => + x.Source; + + public IEnumerator GetEnumerator() => + Source.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => + Source.GetEnumerator(); + + public void Add(T item) + { + lock (_locker) + { + item.Index = Source.Count; + Source.Add(item); + } + } + + public void Clear() + { + lock (_locker) + { + var first = Source[0]; + Source.Clear(); + Source[0] = first; + } + } + + public bool Contains(T item) + { + lock (_locker) + { + return Source.Contains(item); + } + } + + public void CopyTo(T[] array, int arrayIndex) + { + lock (_locker) + { + Source.CopyTo(array, arrayIndex); + } + } + + public bool Remove(T item) + { + bool removed; + lock (_locker) + { + if(Source.IndexOf(item) == 0) + throw new ArgumentException("You can't remove first permsission (allow all)"); + if (removed = Source.Remove(item)) + { + for (int i = 0; i < Source.Count; i++) + { + // hm, no idea how ef works, so I don't want to set if it's not changed, + // maybe it will try to update db? + // But most likely it just compares old to new values, meh. + if (Source[i].Index != i) + Source[i].Index = i; + } + } + } + return removed; + } + + public int Count => Source.Count; + public bool IsReadOnly => false; + public int IndexOf(T item) => item.Index; + + public void Insert(int index, T item) + { + lock (_locker) + { + if(index == 0) // can't insert on first place. Last item is always allow all. + throw new IndexOutOfRangeException(nameof(index)); + Source.Insert(index, item); + for (int i = index; i < Source.Count; i++) + { + Source[i].Index = i; + } + } + } + + public void RemoveAt(int index) + { + lock (_locker) + { + if(index == 0) // you can't remove first permission (allow all) + throw new IndexOutOfRangeException(nameof(index)); + + Source.RemoveAt(index); + for (int i = index; i < Source.Count; i++) + { + Source[i].Index = i; + } + } + } + + public T this[int index] { + get { return Source[index]; } + set { + lock (_locker) + { + if(index == 0) // can't set first element. It's always allow all + throw new IndexOutOfRangeException(nameof(index)); + value.Index = index; + Source[index] = value; + } + } + } + } + +} diff --git a/src/NadekoBot/Migrations/20170308033058_permsv2.Designer.cs b/src/NadekoBot/Migrations/20170308033058_permsv2.Designer.cs new file mode 100644 index 00000000..ea8eaee1 --- /dev/null +++ b/src/NadekoBot/Migrations/20170308033058_permsv2.Designer.cs @@ -0,0 +1,1206 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; +using NadekoBot.Modules.Music.Classes; + +namespace NadekoBot.Migrations +{ + [DbContext(typeof(NadekoContext))] + [Migration("20170308033058_permsv2")] + partial class permsv2 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752"); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Action"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("Seconds"); + + b.Property("UserThreshold"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("AntiRaidSetting"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AntiSpamSettingId"); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.HasKey("Id"); + + b.HasIndex("AntiSpamSettingId"); + + b.ToTable("AntiSpamIgnore"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Action"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("MessageThreshold"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("AntiSpamSetting"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("DateAdded"); + + b.Property("ItemId"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("BlacklistItem"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BotConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BetflipMultiplier"); + + b.Property("Betroll100Multiplier"); + + b.Property("Betroll67Multiplier"); + + b.Property("Betroll91Multiplier"); + + b.Property("BufferSize"); + + b.Property("CurrencyDropAmount"); + + b.Property("CurrencyGenerationChance"); + + b.Property("CurrencyGenerationCooldown"); + + b.Property("CurrencyName"); + + b.Property("CurrencyPluralName"); + + b.Property("CurrencySign"); + + b.Property("DMHelpString"); + + b.Property("DateAdded"); + + b.Property("ErrorColor"); + + b.Property("ForwardMessages"); + + b.Property("ForwardToAllOwners"); + + b.Property("HelpString"); + + b.Property("Locale"); + + b.Property("MigrationVersion"); + + b.Property("MinimumBetAmount"); + + b.Property("OkColor"); + + b.Property("RemindMessageFormat"); + + b.Property("RotatingStatuses"); + + b.Property("TriviaCurrencyReward"); + + b.HasKey("Id"); + + b.ToTable("BotConfig"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BaseDestroyed"); + + b.Property("CallUser"); + + b.Property("ClashWarId"); + + b.Property("DateAdded"); + + b.Property("SequenceNumber"); + + b.Property("Stars"); + + b.Property("TimeAdded"); + + b.HasKey("Id"); + + b.HasIndex("ClashWarId"); + + b.ToTable("ClashCallers"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashWar", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("EnemyClan"); + + b.Property("GuildId"); + + b.Property("Size"); + + b.Property("StartedAt"); + + b.Property("WarState"); + + b.HasKey("Id"); + + b.ToTable("ClashOfClans"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CommandName"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("Seconds"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("CommandCooldown"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("CommandName"); + + b.Property("DateAdded"); + + b.Property("Price"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.HasIndex("Price") + .IsUnique(); + + b.ToTable("CommandPrice"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("InternalTrigger"); + + b.Property("Modifier"); + + b.Property("UnitType"); + + b.HasKey("Id"); + + b.ToTable("ConversionUnits"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("DateAdded"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Currency"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("DateAdded"); + + b.Property("Reason"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.ToTable("CurrencyTransactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CustomReaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildId"); + + b.Property("IsRegex"); + + b.Property("OwnerOnly"); + + b.Property("Response"); + + b.Property("Trigger"); + + b.HasKey("Id"); + + b.ToTable("CustomReactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AvatarId"); + + b.Property("DateAdded"); + + b.Property("Discriminator"); + + b.Property("UserId"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasAlternateKey("UserId"); + + b.ToTable("DiscordUser"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("DateAdded"); + + b.Property("Name"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Donators"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("DateAdded"); + + b.Property("Text"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("EightBallResponses"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("GuildConfigId1"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.HasIndex("GuildConfigId1"); + + b.ToTable("FilterChannelId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("Word"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FilteredWord"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("Type"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FollowedStream"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("GCChannelId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AutoAssignRoleId"); + + b.Property("AutoDeleteByeMessages"); + + b.Property("AutoDeleteByeMessagesTimer"); + + b.Property("AutoDeleteGreetMessages"); + + b.Property("AutoDeleteGreetMessagesTimer"); + + b.Property("AutoDeleteSelfAssignedRoleMessages"); + + b.Property("ByeMessageChannelId"); + + b.Property("ChannelByeMessageText"); + + b.Property("ChannelGreetMessageText"); + + b.Property("CleverbotEnabled"); + + b.Property("DateAdded"); + + b.Property("DefaultMusicVolume"); + + b.Property("DeleteMessageOnCommand"); + + b.Property("DmGreetMessageText"); + + b.Property("ExclusiveSelfAssignedRoles"); + + b.Property("FilterInvites"); + + b.Property("FilterWords"); + + b.Property("GreetMessageChannelId"); + + b.Property("GuildId"); + + b.Property("Locale"); + + b.Property("LogSettingId"); + + b.Property("MuteRoleName"); + + b.Property("PermissionRole"); + + b.Property("RootPermissionId"); + + b.Property("SendChannelByeMessage"); + + b.Property("SendChannelGreetMessage"); + + b.Property("SendDmGreetMessage"); + + b.Property("TimeZoneId"); + + b.Property("VerbosePermissions"); + + b.Property("VoicePlusTextEnabled"); + + b.HasKey("Id"); + + b.HasIndex("GuildId") + .IsUnique(); + + b.HasIndex("LogSettingId"); + + b.HasIndex("RootPermissionId"); + + b.ToTable("GuildConfigs"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildRepeater", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("Interval"); + + b.Property("Message"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("GuildRepeater"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredLogChannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredVoicePresenceCHannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelCreated"); + + b.Property("ChannelCreatedId"); + + b.Property("ChannelDestroyed"); + + b.Property("ChannelDestroyedId"); + + b.Property("ChannelId"); + + b.Property("ChannelUpdated"); + + b.Property("ChannelUpdatedId"); + + b.Property("DateAdded"); + + b.Property("IsLogging"); + + b.Property("LogOtherId"); + + b.Property("LogUserPresence"); + + b.Property("LogUserPresenceId"); + + b.Property("LogVoicePresence"); + + b.Property("LogVoicePresenceId"); + + b.Property("LogVoicePresenceTTSId"); + + b.Property("MessageDeleted"); + + b.Property("MessageDeletedId"); + + b.Property("MessageUpdated"); + + b.Property("MessageUpdatedId"); + + b.Property("UserBanned"); + + b.Property("UserBannedId"); + + b.Property("UserJoined"); + + b.Property("UserJoinedId"); + + b.Property("UserLeft"); + + b.Property("UserLeftId"); + + b.Property("UserMutedId"); + + b.Property("UserPresenceChannelId"); + + b.Property("UserUnbanned"); + + b.Property("UserUnbannedId"); + + b.Property("UserUpdated"); + + b.Property("UserUpdatedId"); + + b.Property("VoicePresenceChannelId"); + + b.HasKey("Id"); + + b.ToTable("LogSettings"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("DateAdded"); + + b.Property("ModuleName"); + + b.Property("Prefix"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("ModulePrefixes"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Author"); + + b.Property("AuthorId"); + + b.Property("DateAdded"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("MusicPlaylists"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("MutedUserId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("NextId"); + + b.Property("PrimaryTarget"); + + b.Property("PrimaryTargetId"); + + b.Property("SecondaryTarget"); + + b.Property("SecondaryTargetName"); + + b.Property("State"); + + b.HasKey("Id"); + + b.HasIndex("NextId") + .IsUnique(); + + b.ToTable("Permission"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("Index"); + + b.Property("PrimaryTarget"); + + b.Property("PrimaryTargetId"); + + b.Property("SecondaryTarget"); + + b.Property("SecondaryTargetName"); + + b.Property("State"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("Permissionv2"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("DateAdded"); + + b.Property("Status"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("PlayingStatus"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("MusicPlaylistId"); + + b.Property("Provider"); + + b.Property("ProviderType"); + + b.Property("Query"); + + b.Property("Title"); + + b.Property("Uri"); + + b.HasKey("Id"); + + b.HasIndex("MusicPlaylistId"); + + b.ToTable("PlaylistSong"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuthorId"); + + b.Property("AuthorName") + .IsRequired(); + + b.Property("DateAdded"); + + b.Property("GuildId"); + + b.Property("Keyword") + .IsRequired(); + + b.Property("Text") + .IsRequired(); + + b.HasKey("Id"); + + b.ToTable("Quotes"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("DateAdded"); + + b.Property("Icon"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("RaceAnimals"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("IsPrivate"); + + b.Property("Message"); + + b.Property("ServerId"); + + b.Property("UserId"); + + b.Property("When"); + + b.HasKey("Id"); + + b.ToTable("Reminders"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildId"); + + b.Property("RoleId"); + + b.HasKey("Id"); + + b.HasIndex("GuildId", "RoleId") + .IsUnique(); + + b.ToTable("SelfAssignableRoles"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("UserId"); + + b.Property("type"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("PokeGame"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AffinityId"); + + b.Property("ClaimerId"); + + b.Property("DateAdded"); + + b.Property("Price"); + + b.Property("WaifuId"); + + b.HasKey("Id"); + + b.HasIndex("AffinityId"); + + b.HasIndex("ClaimerId"); + + b.HasIndex("WaifuId") + .IsUnique(); + + b.ToTable("WaifuInfo"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("NewId"); + + b.Property("OldId"); + + b.Property("UpdateType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("NewId"); + + b.HasIndex("OldId"); + + b.HasIndex("UserId"); + + b.ToTable("WaifuUpdates"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("AntiRaidSetting") + .HasForeignKey("NadekoBot.Services.Database.Models.AntiRaidSetting", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => + { + b.HasOne("NadekoBot.Services.Database.Models.AntiSpamSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("AntiSpamSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("AntiSpamSetting") + .HasForeignKey("NadekoBot.Services.Database.Models.AntiSpamSetting", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("Blacklist") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b => + { + b.HasOne("NadekoBot.Services.Database.Models.ClashWar", "ClashWar") + .WithMany("Bases") + .HasForeignKey("ClashWarId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("CommandCooldowns") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandPrice", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("CommandPrices") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("EightBallResponses") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilterInvitesChannelIds") + .HasForeignKey("GuildConfigId"); + + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilterWordsChannelIds") + .HasForeignKey("GuildConfigId1"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilteredWords") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FollowedStreams") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("GenerateCurrencyChannelIds") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany() + .HasForeignKey("LogSettingId"); + + b.HasOne("NadekoBot.Services.Database.Models.Permission", "RootPermission") + .WithMany() + .HasForeignKey("RootPermissionId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildRepeater", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("GuildRepeaters") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredVoicePresenceChannelIds") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("ModulePrefixes") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("MutedUsers") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => + { + b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next") + .WithOne("Previous") + .HasForeignKey("NadekoBot.Services.Database.Models.Permission", "NextId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("Permissions") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("RotatingStatusMessages") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => + { + b.HasOne("NadekoBot.Services.Database.Models.MusicPlaylist") + .WithMany("Songs") + .HasForeignKey("MusicPlaylistId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("RaceAnimals") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b => + { + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Affinity") + .WithMany() + .HasForeignKey("AffinityId"); + + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Claimer") + .WithMany() + .HasForeignKey("ClaimerId"); + + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Waifu") + .WithOne() + .HasForeignKey("NadekoBot.Services.Database.Models.WaifuInfo", "WaifuId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b => + { + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "New") + .WithMany() + .HasForeignKey("NewId"); + + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Old") + .WithMany() + .HasForeignKey("OldId"); + + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/NadekoBot/Migrations/20170308033058_permsv2.cs b/src/NadekoBot/Migrations/20170308033058_permsv2.cs new file mode 100644 index 00000000..c40879ed --- /dev/null +++ b/src/NadekoBot/Migrations/20170308033058_permsv2.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace NadekoBot.Migrations +{ + public partial class permsv2 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Permissionv2", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + DateAdded = table.Column(nullable: true), + GuildConfigId = table.Column(nullable: true), + Index = table.Column(nullable: false), + PrimaryTarget = table.Column(nullable: false), + PrimaryTargetId = table.Column(nullable: false), + SecondaryTarget = table.Column(nullable: false), + SecondaryTargetName = table.Column(nullable: true), + State = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Permissionv2", x => x.Id); + table.ForeignKey( + name: "FK_Permissionv2_GuildConfigs_GuildConfigId", + column: x => x.GuildConfigId, + principalTable: "GuildConfigs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_Permissionv2_GuildConfigId", + table: "Permissionv2", + column: "GuildConfigId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Permissionv2"); + } + } +} diff --git a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs index 981d53cf..34299dd0 100644 --- a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs +++ b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs @@ -764,6 +764,34 @@ namespace NadekoBot.Migrations b.ToTable("Permission"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("Index"); + + b.Property("PrimaryTarget"); + + b.Property("PrimaryTargetId"); + + b.Property("SecondaryTarget"); + + b.Property("SecondaryTargetName"); + + b.Property("State"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("Permissionv2"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => { b.Property("Id") @@ -1112,6 +1140,13 @@ namespace NadekoBot.Migrations .HasForeignKey("NadekoBot.Services.Database.Models.Permission", "NextId"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("Permissions") + .HasForeignKey("GuildConfigId"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => { b.HasOne("NadekoBot.Services.Database.Models.BotConfig") diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index f779a751..d98458b4 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -11,6 +11,7 @@ using Discord.WebSocket; using NadekoBot.Services.Database.Models; using static NadekoBot.Modules.Permissions.Permissions; using System.Collections.Concurrent; +using Microsoft.EntityFrameworkCore; using NLog; namespace NadekoBot.Modules.Administration @@ -56,20 +57,12 @@ namespace NadekoBot.Modules.Administration [RequireUserPermission(GuildPermission.Administrator)] public async Task ResetPermissions() { - var channel = (ITextChannel)Context.Channel; using (var uow = DbHandler.UnitOfWork()) { - var config = uow.GuildConfigs.PermissionsFor(Context.Guild.Id); - config.RootPermission = Permission.GetDefaultRoot(); - var toAdd = new PermissionCache() - { - RootPermission = config.RootPermission, - PermRole = config.PermissionRole, - Verbose = config.VerbosePermissions, - }; - Cache.AddOrUpdate(channel.Guild.Id, - toAdd, (id, old) => toAdd); + var config = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.Permissions)); + config.Permissions = Permissionv2.GetDefaultPermlist; await uow.CompleteAsync(); + UpdateCache(config); } await ReplyConfirmLocalized("perms_reset").ConfigureAwait(false); } diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 42fea1a4..f5c66cb7 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -81,8 +81,10 @@ namespace NadekoBot.Modules.Games var msgs = new IUserMessage[dropAmount]; var prefix = NadekoBot.ModulePrefixes[typeof(Games).Name]; var toSend = dropAmount == 1 - ? GetLocalText(channel, "curgen_sn", NadekoBot.BotConfig.CurrencySign, prefix) - : GetLocalText(channel, "curgen_pl", dropAmount, NadekoBot.BotConfig.CurrencySign, prefix); + ? GetLocalText(channel, "curgen_sn", NadekoBot.BotConfig.CurrencySign) + + GetLocalText(channel, "pick_sn", prefix) + : GetLocalText(channel, "curgen_pl", dropAmount, NadekoBot.BotConfig.CurrencySign) + + GetLocalText(channel, "pick_pl", prefix); var file = GetRandomCurrencyImage(); using (var fileStream = file.Value.ToStream()) { @@ -155,10 +157,15 @@ namespace NadekoBot.Modules.Games //and then var msgToSend = GetText("planted", - Format.Bold(Context.User.ToString()), - amount + NadekoBot.BotConfig.CurrencySign, + Format.Bold(Context.User.ToString()), + amount + NadekoBot.BotConfig.CurrencySign, Prefix); + if (amount > 1) + msgToSend += GetText("pick_pl", Prefix); + else + msgToSend += GetText("pick_sn", Prefix); + IUserMessage msg; using (var toSend = imgData.Value.ToStream()) { diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index 2acd06dd..5552dbf4 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -236,7 +236,7 @@ namespace NadekoBot.Modules.Games { hot = NextDouble(8, 10); crazy = NextDouble(5, 7); - advice = "Above an 8 hot, and between about 7 and a 5 crazy - this is WIFE ZONE. You you meet this girl, you should consider long-term " + + advice = "Above an 8 hot, and between about 7 and a 5 crazy - this is WIFE ZONE. If you meet this girl, you should consider long-term " + "relationship. Rare."; } else if (roll < 999) @@ -244,7 +244,7 @@ namespace NadekoBot.Modules.Games hot = NextDouble(8, 10); crazy = NextDouble(2, 3.99d); advice = "You've met a girl she's above 8 hot, and not crazy at all (below 4)... totally cool?" + - " You should be careful. That's a dude. It's a tranny."; + " You should be careful. That's a dude. You're talking to a tranny!"; } else { diff --git a/src/NadekoBot/Modules/Permissions/PermissionExtensions.cs b/src/NadekoBot/Modules/Permissions/PermissionExtensions.cs index cf8f0ed3..d9afcc1f 100644 --- a/src/NadekoBot/Modules/Permissions/PermissionExtensions.cs +++ b/src/NadekoBot/Modules/Permissions/PermissionExtensions.cs @@ -10,25 +10,12 @@ namespace NadekoBot.Modules.Permissions { public static class PermissionExtensions { - public static bool CheckPermissions(this IEnumerable permsEnumerable, IUserMessage message, CommandInfo command) + public static bool CheckPermissions(this IEnumerable permsEnumerable, IUserMessage message, + string commandName, string moduleName, out int permIndex) { - var perms = permsEnumerable as List ?? permsEnumerable.ToList(); - int throwaway; - return perms.CheckPermissions(message, command.Name, command.Module.Name, out throwaway); - } + var perms = permsEnumerable as List ?? permsEnumerable.ToList(); - public static bool CheckPermissions(this IEnumerable permsEnumerable, IUserMessage message, string commandName, string moduleName) - { - var perms = permsEnumerable as List ?? permsEnumerable.ToList(); - int throwaway; - return perms.CheckPermissions(message, commandName, moduleName, out throwaway); - } - - public static bool CheckPermissions(this IEnumerable permsEnumerable, IUserMessage message, string commandName, string moduleName, out int permIndex) - { - var perms = permsEnumerable as List ?? permsEnumerable.ToList(); - - for (int i = 0; i < perms.Count; i++) + for (int i = perms.Count - 1; i >= 0; i--) { var perm = perms[i]; @@ -38,11 +25,8 @@ namespace NadekoBot.Modules.Permissions { continue; } - else - { - permIndex = i; - return result.Value; - } + permIndex = i; + return result.Value; } permIndex = -1; //defaut behaviour return true; @@ -51,7 +35,7 @@ namespace NadekoBot.Modules.Permissions //null = not applicable //true = applicable, allowed //false = applicable, not allowed - public static bool? CheckPermission(this Permission perm, IUserMessage message, string commandName, string moduleName) + public static bool? CheckPermission(this Permissionv2 perm, IUserMessage message, string commandName, string moduleName) { if (!((perm.SecondaryTarget == SecondaryPermissionType.Command && perm.SecondaryTargetName.ToLowerInvariant() == commandName.ToLowerInvariant()) || @@ -86,7 +70,7 @@ namespace NadekoBot.Modules.Permissions return null; } - public static string GetCommand(this Permission perm, SocketGuild guild = null) + public static string GetCommand(this Permissionv2 perm, SocketGuild guild = null) { var com = ""; switch (perm.PrimaryTarget) @@ -143,98 +127,10 @@ namespace NadekoBot.Modules.Permissions return NadekoBot.ModulePrefixes[typeof(Permissions).Name] + com; } - public static void Prepend(this Permission perm, Permission toAdd) - { - perm = perm.GetRoot(); - - perm.Previous = toAdd; - toAdd.Next = perm; - } - - /* /this can't work if index < 0 and perm isn't roo - public static void Insert(this Permission perm, int index, Permission toAdd) - { - if (index < 0) - throw new IndexOutOfRangeException(); - - if (index == 0) - { - perm.Prepend(toAdd); - return; - } - - var atIndex = perm; - var i = 0; - while (i != index) - { - atIndex = atIndex.Next; - i++; - if (atIndex == null) - throw new IndexOutOfRangeException(); - } - var previous = atIndex.Previous; - - //connect right side - atIndex.Previous = toAdd; - toAdd.Next = atIndex; - - //connect left side - toAdd.Previous = previous; - previous.Next = toAdd; - } - */ - public static Permission RemoveAt(this Permission perm, int index) - { - if (index <= 0) //can't really remove at 0, that means deleting the element right now. Just use perm.Next if its 0 - throw new IndexOutOfRangeException(); - - var toRemove = perm; - var i = 0; - while (i != index) - { - toRemove = toRemove.Next; - i++; - if (toRemove == null) - throw new IndexOutOfRangeException(); - } - - toRemove.Previous.Next = toRemove.Next; - if (toRemove.Next != null) - toRemove.Next.Previous = toRemove.Previous; - return toRemove; - } - - public static Permission GetAt(this Permission perm, int index) - { - if (index < 0) - throw new IndexOutOfRangeException(); - var temp = perm; - while (index > 0) { temp = temp?.Next; index--; } - if (temp == null) - throw new IndexOutOfRangeException(); - return temp; - } - - public static int Count(this Permission perm) - { - var i = 1; - var temp = perm; - while ((temp = temp.Next) != null) { i++; } - return i; - } - public static IEnumerable AsEnumerable(this Permission perm) { do yield return perm; while ((perm = perm.Next) != null); } - - public static Permission GetRoot(this Permission perm) - { - Permission toReturn; - do toReturn = perm; - while ((perm = perm.Previous) != null); - return toReturn; - } } } diff --git a/src/NadekoBot/Modules/Permissions/Permissions.cs b/src/NadekoBot/Modules/Permissions/Permissions.cs index 04d1352d..4cebe2dc 100644 --- a/src/NadekoBot/Modules/Permissions/Permissions.cs +++ b/src/NadekoBot/Modules/Permissions/Permissions.cs @@ -7,9 +7,11 @@ using NadekoBot.Services; using Discord; using NadekoBot.Services.Database.Models; using System.Collections.Concurrent; -using NadekoBot.Extensions; +using System.Collections.Generic; using Discord.WebSocket; using System.Diagnostics; +using Microsoft.EntityFrameworkCore; +using NadekoBot.DataStructures; using NLog; namespace NadekoBot.Modules.Permissions @@ -17,38 +19,139 @@ namespace NadekoBot.Modules.Permissions [NadekoModule("Permissions", ";")] public partial class Permissions : NadekoTopLevelModule { - public class PermissionCache + public class OldPermissionCache { public string PermRole { get; set; } public bool Verbose { get; set; } = true; public Permission RootPermission { get; set; } } + public class PermissionCache + { + public string PermRole { get; set; } + public bool Verbose { get; set; } = true; + public PermissionsCollection Permissions { get; set; } + } + //guildid, root permission - public static ConcurrentDictionary Cache { get; } + public static ConcurrentDictionary Cache { get; } = + new ConcurrentDictionary(); static Permissions() { var log = LogManager.GetCurrentClassLogger(); var sw = Stopwatch.StartNew(); + TryMigratePermissions(); + 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 - })); + foreach (var x in uow.GuildConfigs.Permissionsv2ForAll()) + { + Cache.TryAdd(x.GuildId, new PermissionCache() + { + Verbose = x.VerbosePermissions, + PermRole = x.PermissionRole, + Permissions = new PermissionsCollection(x.Permissions) + }); + } } sw.Stop(); log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); } + private static void TryMigratePermissions() + { + var log = LogManager.GetCurrentClassLogger(); + using (var uow = DbHandler.UnitOfWork()) + { + var oldCache = new ConcurrentDictionary(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); + var gc = uow.GuildConfigs.For(oc.Key, set => set.Include(x => x.Permissions)); + + var oldPerms = oc.Value.RootPermission.AsEnumerable().Reverse().ToList(); + 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(newPerms), + Verbose = gc.VerbosePermissions, + PermRole = gc.PermissionRole, + }); + gc.Permissions = newPerms; + } + } + log.Info("Permission migration to v2 is done."); + uow.Complete(); + } + } + } + + private static async Task AddPermissions(ulong guildId, params Permissionv2[] perms) + { + using (var uow = DbHandler.UnitOfWork()) + { + var config = uow.GuildConfigs.For(guildId, set => set.Include(x => x.Permissions)); + //var orderedPerms = new PermissionsCollection(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 static void UpdateCache(GuildConfig config) + { + Cache.AddOrUpdate(config.GuildId, new PermissionCache() + { + Permissions = new PermissionsCollection(config.Permissions), + PermRole = config.PermissionRole, + Verbose = config.VerbosePermissions + }, (id, old) => + { + old.Permissions = new PermissionsCollection(config.Permissions); + old.PermRole = config.PermissionRole; + old.Verbose = config.VerbosePermissions; + return old; + }); + } + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Verbose(PermissionAction action) @@ -57,13 +160,8 @@ namespace NadekoBot.Modules.Permissions { var config = uow.GuildConfigs.For(Context.Guild.Id, set => set); config.VerbosePermissions = action.Value; - Cache.AddOrUpdate(Context.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); + UpdateCache(config); } if (action.Value) { @@ -91,13 +189,8 @@ namespace NadekoBot.Modules.Permissions return; } config.PermissionRole = role.Name.Trim(); - Cache.AddOrUpdate(Context.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); + UpdateCache(config); } await ReplyConfirmLocalized("permrole_changed", Format.Bold(role.Name)).ConfigureAwait(false); @@ -109,19 +202,32 @@ namespace NadekoBot.Modules.Permissions { if (page < 1 || page > 4) return; - string toSend; - using (var uow = DbHandler.UnitOfWork()) + + PermissionCache permCache; + IList perms; + + if (Cache.TryGetValue(Context.Guild.Id, out permCache)) { - var perms = uow.GuildConfigs.PermissionsFor(Context.Guild.Id).RootPermission; - var i = 1 + 20 * (page - 1); - toSend = Format.Bold(GetText("page", page)) + "\n\n" + string.Join("\n", - perms.AsEnumerable() + perms = permCache.Permissions.Source.ToList(); + } + else + { + perms = Permissionv2.GetDefaultPermlist; + } + + var startPos = 20 * (page - 1); + var toSend = Format.Bold(GetText("page", page)) + "\n\n" + string.Join("\n", + perms.Reverse() .Skip((page - 1) * 20) .Take(20) - .Select( - p => - $"`{(i++)}.` {(p.Next == null ? Format.Bold(p.GetCommand((SocketGuild) Context.Guild) + $" [{GetText("uneditable")}]") : (p.GetCommand((SocketGuild) Context.Guild)))}")); - } + .Select(p => + { + var str = + $"`{p.Index + startPos + 1}.` {Format.Bold(p.GetCommand((SocketGuild) Context.Guild))}"; + if (p.Index == 0) + str += $" [{GetText("uneditable")}]"; + return str; + })); await Context.Channel.SendMessageAsync(toSend).ConfigureAwait(false); } @@ -131,43 +237,24 @@ namespace NadekoBot.Modules.Permissions public async Task RemovePerm(int index) { index -= 1; + if (index < 0) + return; try { - Permission p; + Permissionv2 p; using (var uow = DbHandler.UnitOfWork()) { - var config = uow.GuildConfigs.PermissionsFor(Context.Guild.Id); - var perms = config.RootPermission; - if (index == perms.Count() - 1) - { - return; - } - if (index == 0) - { - p = perms; - config.RootPermission = perms.Next; - } - else - { - p = perms.RemoveAt(index); - } - Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache() - { - PermRole = config.PermissionRole, - RootPermission = config.RootPermission, - Verbose = config.VerbosePermissions - }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); + var config = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.Permissions)); + var permsCol = new PermissionsCollection(config.Permissions); + p = permsCol[index]; + permsCol.RemoveAt(index); + uow._context.Remove(p); await uow.CompleteAsync().ConfigureAwait(false); + UpdateCache(config); } - - using (var uow2 = DbHandler.UnitOfWork()) - { - uow2._context.Remove(p); - uow2._context.SaveChanges(); - } - await ReplyConfirmLocalized("removed", - index+1, - Format.Code(p.GetCommand((SocketGuild)Context.Guild))).ConfigureAwait(false); + await ReplyConfirmLocalized("removed", + index + 1, + Format.Code(p.GetCommand((SocketGuild) Context.Guild))).ConfigureAwait(false); } catch (IndexOutOfRangeException) { @@ -185,96 +272,36 @@ namespace NadekoBot.Modules.Permissions { try { - Permission fromPerm = null; - Permission toPerm = null; + Permissionv2 fromPerm; using (var uow = DbHandler.UnitOfWork()) { - var config = uow.GuildConfigs.PermissionsFor(Context.Guild.Id); - var perms = config.RootPermission; - var index = 0; - var fromFound = false; - var toFound = false; - while ((!toFound || !fromFound) && perms != null) - { - if (index == from) - { - fromPerm = perms; - fromFound = true; - } - if (index == to) - { - toPerm = perms; - toFound = true; - } - if (!toFound) - { - toPerm = perms; //In case of to > size - } - perms = perms.Next; - index++; - } - if (perms == null) - { - if (!fromFound) - { - await ReplyErrorLocalized("not_found", ++from).ConfigureAwait(false); - return; - } + var config = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.Permissions)); + var permsCol = new PermissionsCollection(config.Permissions); - if (!toFound) - { - await ReplyErrorLocalized("not_found", ++to).ConfigureAwait(false); - return; - } + var fromFound = from < permsCol.Count; + var toFound = to < permsCol.Count; + + if (!fromFound) + { + await ReplyErrorLocalized("not_found", ++from).ConfigureAwait(false); + return; } - //Change chain for from indx - var next = fromPerm.Next; - var pre = fromPerm.Previous; - if (pre != null) - pre.Next = next; - if (fromPerm.Next == null || toPerm.Next == null) - { - throw new IndexOutOfRangeException(); - } - next.Previous = pre; - if (from == 0) + if (!toFound) { + await ReplyErrorLocalized("not_found", ++to).ConfigureAwait(false); + return; } + fromPerm = permsCol[from]; + + permsCol.RemoveAt(from); + permsCol.Insert(to, fromPerm); await uow.CompleteAsync().ConfigureAwait(false); - //Inserting - if (to > from) - { - fromPerm.Previous = toPerm; - fromPerm.Next = toPerm.Next; - - toPerm.Next.Previous = fromPerm; - toPerm.Next = fromPerm; - } - else - { - pre = toPerm.Previous; - - fromPerm.Next = toPerm; - fromPerm.Previous = pre; - - toPerm.Previous = fromPerm; - if (pre != null) - pre.Next = fromPerm; - } - - config.RootPermission = fromPerm.GetRoot(); - Cache.AddOrUpdate(Context.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); + UpdateCache(config); } await ReplyConfirmLocalized("moved_permission", - Format.Code(fromPerm.GetCommand((SocketGuild) Context.Guild)), - ++from, + Format.Code(fromPerm.GetCommand((SocketGuild) Context.Guild)), + ++from, ++to) .ConfigureAwait(false); return; @@ -290,36 +317,24 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task SrvrCmd(CommandInfo command, PermissionAction action) { - using (var uow = DbHandler.UnitOfWork()) + await AddPermissions(Context.Guild.Id, new Permissionv2 { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.Server, - PrimaryTargetId = 0, - SecondaryTarget = SecondaryPermissionType.Command, - SecondaryTargetName = command.Aliases.First().ToLowerInvariant(), - State = action.Value, - }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); - Cache.AddOrUpdate(Context.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); - } + PrimaryTarget = PrimaryPermissionType.Server, + PrimaryTargetId = 0, + SecondaryTarget = SecondaryPermissionType.Command, + SecondaryTargetName = command.Aliases.First().ToLowerInvariant(), + State = action.Value, + }); if (action.Value) { - await ReplyConfirmLocalized("sx_enable", + await ReplyConfirmLocalized("sx_enable", Format.Code(command.Aliases.First()), GetText("of_command")).ConfigureAwait(false); } else { - await ReplyConfirmLocalized("sx_disable", + await ReplyConfirmLocalized("sx_disable", Format.Code(command.Aliases.First()), GetText("of_command")).ConfigureAwait(false); } @@ -329,25 +344,14 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task SrvrMdl(ModuleInfo module, PermissionAction action) { - using (var uow = DbHandler.UnitOfWork()) + await AddPermissions(Context.Guild.Id, new Permissionv2 { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.Server, - PrimaryTargetId = 0, - SecondaryTarget = SecondaryPermissionType.Module, - SecondaryTargetName = module.Name.ToLowerInvariant(), - State = action.Value, - }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); - Cache.AddOrUpdate(Context.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); - } + PrimaryTarget = PrimaryPermissionType.Server, + PrimaryTargetId = 0, + SecondaryTarget = SecondaryPermissionType.Module, + SecondaryTargetName = module.Name.ToLowerInvariant(), + State = action.Value, + }); if (action.Value) { @@ -367,36 +371,25 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task UsrCmd(CommandInfo command, PermissionAction action, [Remainder] IGuildUser user) { - using (var uow = DbHandler.UnitOfWork()) + await AddPermissions(Context.Guild.Id, new Permissionv2 { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.User, - PrimaryTargetId = user.Id, - SecondaryTarget = SecondaryPermissionType.Command, - SecondaryTargetName = command.Aliases.First().ToLowerInvariant(), - State = action.Value, - }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); - Cache.AddOrUpdate(Context.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); - } + PrimaryTarget = PrimaryPermissionType.User, + PrimaryTargetId = user.Id, + SecondaryTarget = SecondaryPermissionType.Command, + SecondaryTargetName = command.Aliases.First().ToLowerInvariant(), + State = action.Value, + }); if (action.Value) { - await ReplyConfirmLocalized("ux_enable", + await ReplyConfirmLocalized("ux_enable", Format.Code(command.Aliases.First()), GetText("of_command"), Format.Code(user.ToString())).ConfigureAwait(false); } else { - await ReplyConfirmLocalized("ux_disable", + await ReplyConfirmLocalized("ux_disable", Format.Code(command.Aliases.First()), GetText("of_command"), Format.Code(user.ToString())).ConfigureAwait(false); @@ -407,25 +400,14 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task UsrMdl(ModuleInfo module, PermissionAction action, [Remainder] IGuildUser user) { - using (var uow = DbHandler.UnitOfWork()) + await AddPermissions(Context.Guild.Id, new Permissionv2 { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.User, - PrimaryTargetId = user.Id, - SecondaryTarget = SecondaryPermissionType.Module, - SecondaryTargetName = module.Name.ToLowerInvariant(), - State = action.Value, - }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); - Cache.AddOrUpdate(Context.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); - } + PrimaryTarget = PrimaryPermissionType.User, + PrimaryTargetId = user.Id, + SecondaryTarget = SecondaryPermissionType.Module, + SecondaryTargetName = module.Name.ToLowerInvariant(), + State = action.Value, + }); if (action.Value) { @@ -450,25 +432,14 @@ namespace NadekoBot.Modules.Permissions if (role == role.Guild.EveryoneRole) return; - using (var uow = DbHandler.UnitOfWork()) + await AddPermissions(Context.Guild.Id, new Permissionv2 { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.Role, - PrimaryTargetId = role.Id, - SecondaryTarget = SecondaryPermissionType.Command, - SecondaryTargetName = command.Aliases.First().ToLowerInvariant(), - State = action.Value, - }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); - Cache.AddOrUpdate(Context.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); - } + PrimaryTarget = PrimaryPermissionType.Role, + PrimaryTargetId = role.Id, + SecondaryTarget = SecondaryPermissionType.Command, + SecondaryTargetName = command.Aliases.First().ToLowerInvariant(), + State = action.Value, + }); if (action.Value) { @@ -493,25 +464,14 @@ namespace NadekoBot.Modules.Permissions if (role == role.Guild.EveryoneRole) return; - using (var uow = DbHandler.UnitOfWork()) + await AddPermissions(Context.Guild.Id, new Permissionv2 { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.Role, - PrimaryTargetId = role.Id, - SecondaryTarget = SecondaryPermissionType.Module, - SecondaryTargetName = module.Name.ToLowerInvariant(), - State = action.Value, - }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); - Cache.AddOrUpdate(Context.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); - } + PrimaryTarget = PrimaryPermissionType.Role, + PrimaryTargetId = role.Id, + SecondaryTarget = SecondaryPermissionType.Module, + SecondaryTargetName = module.Name.ToLowerInvariant(), + State = action.Value, + }); if (action.Value) @@ -534,25 +494,14 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task ChnlCmd(CommandInfo command, PermissionAction action, [Remainder] ITextChannel chnl) { - using (var uow = DbHandler.UnitOfWork()) + await AddPermissions(Context.Guild.Id, new Permissionv2 { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.Channel, - PrimaryTargetId = chnl.Id, - SecondaryTarget = SecondaryPermissionType.Command, - SecondaryTargetName = command.Aliases.First().ToLowerInvariant(), - State = action.Value, - }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); - Cache.AddOrUpdate(Context.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); - } + PrimaryTarget = PrimaryPermissionType.Channel, + PrimaryTargetId = chnl.Id, + SecondaryTarget = SecondaryPermissionType.Command, + SecondaryTargetName = command.Aliases.First().ToLowerInvariant(), + State = action.Value, + }); if (action.Value) { @@ -574,25 +523,14 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task ChnlMdl(ModuleInfo module, PermissionAction action, [Remainder] ITextChannel chnl) { - using (var uow = DbHandler.UnitOfWork()) + await AddPermissions(Context.Guild.Id, new Permissionv2 { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.Channel, - PrimaryTargetId = chnl.Id, - SecondaryTarget = SecondaryPermissionType.Module, - SecondaryTargetName = module.Name.ToLowerInvariant(), - State = action.Value, - }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); - Cache.AddOrUpdate(Context.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); - } + PrimaryTarget = PrimaryPermissionType.Channel, + PrimaryTargetId = chnl.Id, + SecondaryTarget = SecondaryPermissionType.Module, + SecondaryTargetName = module.Name.ToLowerInvariant(), + State = action.Value, + }); if (action.Value) { @@ -614,25 +552,14 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task AllChnlMdls(PermissionAction action, [Remainder] ITextChannel chnl) { - using (var uow = DbHandler.UnitOfWork()) + await AddPermissions(Context.Guild.Id, new Permissionv2 { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.Channel, - PrimaryTargetId = chnl.Id, - SecondaryTarget = SecondaryPermissionType.AllModules, - SecondaryTargetName = "*", - State = action.Value, - }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); - Cache.AddOrUpdate(Context.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); - } + PrimaryTarget = PrimaryPermissionType.Channel, + PrimaryTargetId = chnl.Id, + SecondaryTarget = SecondaryPermissionType.AllModules, + SecondaryTargetName = "*", + State = action.Value, + }); if (action.Value) { @@ -653,25 +580,14 @@ namespace NadekoBot.Modules.Permissions if (role == role.Guild.EveryoneRole) return; - using (var uow = DbHandler.UnitOfWork()) + await AddPermissions(Context.Guild.Id, new Permissionv2 { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.Role, - PrimaryTargetId = role.Id, - SecondaryTarget = SecondaryPermissionType.AllModules, - SecondaryTargetName = "*", - State = action.Value, - }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); - Cache.AddOrUpdate(Context.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); - } + PrimaryTarget = PrimaryPermissionType.Role, + PrimaryTargetId = role.Id, + SecondaryTarget = SecondaryPermissionType.AllModules, + SecondaryTargetName = "*", + State = action.Value, + }); if (action.Value) { @@ -689,25 +605,14 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task AllUsrMdls(PermissionAction action, [Remainder] IUser user) { - using (var uow = DbHandler.UnitOfWork()) + await AddPermissions(Context.Guild.Id, new Permissionv2 { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.User, - PrimaryTargetId = user.Id, - SecondaryTarget = SecondaryPermissionType.AllModules, - SecondaryTargetName = "*", - State = action.Value, - }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); - Cache.AddOrUpdate(Context.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); - } + PrimaryTarget = PrimaryPermissionType.User, + PrimaryTargetId = user.Id, + SecondaryTarget = SecondaryPermissionType.AllModules, + SecondaryTargetName = "*", + State = action.Value, + }); if (action.Value) { @@ -725,36 +630,27 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task AllSrvrMdls(PermissionAction action) { - using (var uow = DbHandler.UnitOfWork()) + var newPerm = new Permissionv2 { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.Server, - PrimaryTargetId = 0, - SecondaryTarget = SecondaryPermissionType.AllModules, - SecondaryTargetName = "*", - State = action.Value, - }; - uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); + PrimaryTarget = PrimaryPermissionType.Server, + PrimaryTargetId = 0, + SecondaryTarget = SecondaryPermissionType.AllModules, + SecondaryTargetName = "*", + State = action.Value, + }; - var allowUser = new Permission - { - PrimaryTarget = PrimaryPermissionType.User, - PrimaryTargetId = Context.User.Id, - SecondaryTarget = SecondaryPermissionType.AllModules, - SecondaryTargetName = "*", - State = true, - }; + var allowUser = new Permissionv2 + { + PrimaryTarget = PrimaryPermissionType.User, + PrimaryTargetId = Context.User.Id, + SecondaryTarget = SecondaryPermissionType.AllModules, + SecondaryTargetName = "*", + State = true, + }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, allowUser); - Cache.AddOrUpdate(Context.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 AddPermissions(Context.Guild.Id, + newPerm, + allowUser); if (action.Value) { @@ -766,4 +662,4 @@ namespace NadekoBot.Modules.Permissions } } } -} +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Pokemon/PokeStats.cs b/src/NadekoBot/Modules/Permissions/Pokemon/PokeStats.cs similarity index 100% rename from src/NadekoBot/Modules/Pokemon/PokeStats.cs rename to src/NadekoBot/Modules/Permissions/Pokemon/PokeStats.cs diff --git a/src/NadekoBot/Modules/Pokemon/Pokemon.cs b/src/NadekoBot/Modules/Permissions/Pokemon/Pokemon.cs similarity index 100% rename from src/NadekoBot/Modules/Pokemon/Pokemon.cs rename to src/NadekoBot/Modules/Permissions/Pokemon/Pokemon.cs diff --git a/src/NadekoBot/Modules/Pokemon/PokemonType.cs b/src/NadekoBot/Modules/Permissions/Pokemon/PokemonType.cs similarity index 100% rename from src/NadekoBot/Modules/Pokemon/PokemonType.cs rename to src/NadekoBot/Modules/Permissions/Pokemon/PokemonType.cs diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index fefa35bd..5e5dbae0 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Utility [NadekoModule("Utility", ".")] public partial class Utility : NadekoTopLevelModule { - private static ConcurrentDictionary rotatingRoleColors = new ConcurrentDictionary(); + private static ConcurrentDictionary _rotatingRoleColors = new ConcurrentDictionary(); //[NadekoCommand, Usage, Description, Aliases] //[RequireContext(ContextType.Guild)] @@ -114,7 +114,7 @@ namespace NadekoBot.Modules.Utility Timer t; if (timeout == 0 || hexes.Length == 0) { - if (rotatingRoleColors.TryRemove(role.Id, out t)) + if (_rotatingRoleColors.TryRemove(role.Id, out t)) { t.Change(Timeout.Infinite, Timeout.Infinite); await ReplyConfirmLocalized("rrc_stop", Format.Bold(role.Name)).ConfigureAwait(false); @@ -157,7 +157,7 @@ namespace NadekoBot.Modules.Utility catch { } }, null, 0, timeout * 1000); - rotatingRoleColors.AddOrUpdate(role.Id, t, (key, old) => + _rotatingRoleColors.AddOrUpdate(role.Id, t, (key, old) => { old.Change(Timeout.Infinite, Timeout.Infinite); return t; diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 3b42debd..e049a7f9 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -96,7 +96,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60). + /// Looks up a localized string similar to Starts an Acrophobia game. Second argument is optional round length in seconds. (default is 60). /// public static string acro_desc { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 528700c3..f1b4bc6a 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -2794,7 +2794,7 @@ acrophobia acro - Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) + Starts an Acrophobia game. Second argument is optional round length in seconds. (default is 60) `{0}acro` or `{0}acro 30` diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 44f51416..e88e6288 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2914,7 +2914,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to {0} random {1} appeared! Pick them up by typing `{2}pick`. + /// Looks up a localized string similar to {0} random {1} appeared!. /// public static string games_curgen_pl { get { @@ -2923,7 +2923,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to A random {0} appeared! Pick it up by typing `{1}pick`. + /// Looks up a localized string similar to A random {0} appeared!. /// public static string games_curgen_sn { get { @@ -3030,6 +3030,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Pick them up by typing `{0}pick`. + /// + public static string games_pick_pl { + get { + return ResourceManager.GetString("games_pick_pl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Pick it up by typing `{0}pick`. + /// + public static string games_pick_sn { + get { + return ResourceManager.GetString("games_pick_sn", resourceCulture); + } + } + /// /// Looks up a localized string similar to picked {0}. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index a74c16db..f34940a2 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1239,11 +1239,11 @@ Don't forget to leave your discord name or id in the message. Currency generation has been enabled on this channel. - {0} random {1} appeared! Pick them up by typing `{2}pick` + {0} random {1} appeared! plural - A random {0} appeared! Pick it up by typing `{1}pick` + A random {0} appeared! Failed loading a question. @@ -2218,4 +2218,10 @@ Owner ID: {2} {0} total votes cast. + + Pick them up by typing `{0}pick` + + + Pick it up by typing `{0}pick` + \ No newline at end of file diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 0f4cf950..d6678d23 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -16,6 +16,7 @@ using NadekoBot.Modules.CustomReactions; using NadekoBot.Modules.Games; using System.Collections.Concurrent; using System.Threading; +using Microsoft.EntityFrameworkCore; using NadekoBot.DataStructures; namespace NadekoBot.Services @@ -378,28 +379,26 @@ namespace NadekoBot.Services } var cmd = commands[i].Command; - bool resetCommand = cmd.Name == "resetperms"; + var resetCommand = cmd.Name == "resetperms"; var module = cmd.Module.GetTopLevelModule(); PermissionCache pc; if (context.Guild != null) { - pc = Permissions.Cache.GetOrAdd(context.Guild.Id, (id) => + if (!Permissions.Cache.TryGetValue(context.Guild.Id, out pc)) { using (var uow = DbHandler.UnitOfWork()) { - var config = uow.GuildConfigs.PermissionsFor(context.Guild.Id); - return new PermissionCache() - { - Verbose = config.VerbosePermissions, - RootPermission = config.RootPermission, - PermRole = config.PermissionRole.Trim().ToLowerInvariant(), - }; + var config = uow.GuildConfigs.For(context.Guild.Id, set => set.Include(x => x.Permissions)); + Permissions.UpdateCache(config); } - }); + Permissions.Cache.TryGetValue(context.Guild.Id, out pc); + if(pc == null) + throw new Exception("Cache is null."); + } int index; - if (!resetCommand && !pc.RootPermission.AsEnumerable().CheckPermissions(context.Message, cmd.Aliases.First(), module.Name, out index)) + if (!resetCommand && !pc.Permissions.CheckPermissions(context.Message, cmd.Aliases.First(), module.Name, out index)) { - var returnMsg = $"Permission number #{index + 1} **{pc.RootPermission.GetAt(index).GetCommand((SocketGuild)context.Guild)}** is preventing this action."; + var returnMsg = $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand((SocketGuild)context.Guild)}** is preventing this action."; return new ExecuteCommandResult(cmd, pc, SearchResult.FromError(CommandError.Exception, returnMsg)); } diff --git a/src/NadekoBot/Services/Database/Models/GuildConfig.cs b/src/NadekoBot/Services/Database/Models/GuildConfig.cs index 9bf945cd..b04a9d78 100644 --- a/src/NadekoBot/Services/Database/Models/GuildConfig.cs +++ b/src/NadekoBot/Services/Database/Models/GuildConfig.cs @@ -41,7 +41,8 @@ namespace NadekoBot.Services.Database.Models public HashSet GenerateCurrencyChannelIds { get; set; } = new HashSet(); //permissions - public Permission RootPermission { get; set; } + public Permission RootPermission { get; set; } = null; + public List Permissions { get; set; } public bool VerbosePermissions { get; set; } = true; public string PermissionRole { get; set; } = "Nadeko"; diff --git a/src/NadekoBot/Services/Database/Models/Permission.cs b/src/NadekoBot/Services/Database/Models/Permission.cs index e12d6d5c..16988277 100644 --- a/src/NadekoBot/Services/Database/Models/Permission.cs +++ b/src/NadekoBot/Services/Database/Models/Permission.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using System.Diagnostics; namespace NadekoBot.Services.Database.Models @@ -17,46 +18,100 @@ namespace NadekoBot.Services.Database.Models public bool State { get; set; } + public Permissionv2 Tov2() => + new Permissionv2() + { + PrimaryTarget = PrimaryTarget, + PrimaryTargetId = PrimaryTargetId, + SecondaryTarget = SecondaryTarget, + SecondaryTargetName = SecondaryTargetName, + State = State, + }; + + //[NotMapped] + //private static Permission AllowAllPerm => new Permission() + //{ + // PrimaryTarget = PrimaryPermissionType.Server, + // PrimaryTargetId = 0, + // SecondaryTarget = SecondaryPermissionType.AllModules, + // SecondaryTargetName = "*", + // State = true, + //}; + //[NotMapped] + //private static Permission BlockNsfwPerm => new Permission() + //{ + // PrimaryTarget = PrimaryPermissionType.Server, + // PrimaryTargetId = 0, + // SecondaryTarget = SecondaryPermissionType.Module, + // SecondaryTargetName = "nsfw", + // State = false, + //}; + + //public Permission Clone() => new Permission() + //{ + // PrimaryTarget = PrimaryTarget, + // SecondaryTarget = SecondaryTarget, + // PrimaryTargetId = PrimaryTargetId, + // SecondaryTargetName = SecondaryTargetName, + // State = State, + //}; + } + + public interface IIndexed + { + int Index { get; set; } + } + + public class Permissionv2 : DbEntity, IIndexed + { + public int GuildConfigId { get; set; } + public int Index { get; set; } + + public PrimaryPermissionType PrimaryTarget { get; set; } + public ulong PrimaryTargetId { get; set; } + + public SecondaryPermissionType SecondaryTarget { get; set; } + public string SecondaryTargetName { get; set; } + + public bool State { get; set; } + [NotMapped] - private static Permission AllowAllPerm => new Permission() + public static Permissionv2 AllowAllPerm => new Permissionv2() { PrimaryTarget = PrimaryPermissionType.Server, PrimaryTargetId = 0, SecondaryTarget = SecondaryPermissionType.AllModules, SecondaryTargetName = "*", State = true, + Index = 0, }; [NotMapped] - private static Permission BlockNsfwPerm => new Permission() + private static Permissionv2 BlockNsfwPerm => new Permissionv2() { PrimaryTarget = PrimaryPermissionType.Server, PrimaryTargetId = 0, SecondaryTarget = SecondaryPermissionType.Module, SecondaryTargetName = "nsfw", State = false, + Index = 1 }; - public static Permission GetDefaultRoot() - { - var root = AllowAllPerm; - var blockNsfw = BlockNsfwPerm; + public static List GetDefaultPermlist => + new List + { + BlockNsfwPerm, + AllowAllPerm + }; - root.Previous = blockNsfw; - blockNsfw.Next = root; - - return blockNsfw; - } - - public Permission Clone() => new Permission() - { - PrimaryTarget = PrimaryTarget, - SecondaryTarget = SecondaryTarget, - PrimaryTargetId = PrimaryTargetId, - SecondaryTargetName = SecondaryTargetName, - State = State, - }; + //public Permission Clone() => new Permission() + //{ + // PrimaryTarget = PrimaryTarget, + // SecondaryTarget = SecondaryTarget, + // PrimaryTargetId = PrimaryTargetId, + // SecondaryTargetName = SecondaryTargetName, + // State = State, + //}; } - public enum PrimaryPermissionType { User, diff --git a/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs b/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs index 34f3f564..c71b2824 100644 --- a/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs @@ -10,11 +10,10 @@ namespace NadekoBot.Services.Database.Repositories { GuildConfig For(ulong guildId, Func, IQueryable> includes = null); GuildConfig LogSettingsFor(ulong guildId); - GuildConfig PermissionsFor(ulong guildId); - IEnumerable PermissionsForAll(); + IEnumerable OldPermissionsForAll(); IEnumerable GetAllGuildConfigs(); - GuildConfig SetNewRootPermission(ulong guildId, Permission p); IEnumerable GetAllFollowedStreams(); void SetCleverbotEnabled(ulong id, bool cleverbotEnabled); + IEnumerable Permissionsv2ForAll(); } } diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs index 89c59419..0fc19bc5 100644 --- a/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; -using NadekoBot.Modules.Permissions; using System; namespace NadekoBot.Services.Database.Repositories.Impl @@ -16,10 +15,6 @@ namespace NadekoBot.Services.Database.Repositories.Impl public IEnumerable GetAllGuildConfigs() => _set.Include(gc => gc.LogSetting) .ThenInclude(ls => ls.IgnoredChannels) - .Include(gc => gc.RootPermission) - .ThenInclude(gc => gc.Previous) - .Include(gc => gc.RootPermission) - .ThenInclude(gc => gc.Next) .Include(gc => gc.MutedUsers) .Include(gc => gc.GenerateCurrencyChannelIds) .Include(gc => gc.FilterInvitesChannelIds) @@ -44,15 +39,15 @@ namespace NadekoBot.Services.Database.Repositories.Impl if (includes == null) { config = _set - .Include(gc => gc.FollowedStreams) - .Include(gc => gc.LogSetting) - .ThenInclude(ls => ls.IgnoredChannels) - .Include(gc => gc.FilterInvitesChannelIds) - .Include(gc => gc.FilterWordsChannelIds) - .Include(gc => gc.FilteredWords) - .Include(gc => gc.GenerateCurrencyChannelIds) - .Include(gc => gc.CommandCooldowns) - .FirstOrDefault(c => c.GuildId == guildId); + .Include(gc => gc.FollowedStreams) + .Include(gc => gc.LogSetting) + .ThenInclude(ls => ls.IgnoredChannels) + .Include(gc => gc.FilterInvitesChannelIds) + .Include(gc => gc.FilterWordsChannelIds) + .Include(gc => gc.FilteredWords) + .Include(gc => gc.GenerateCurrencyChannelIds) + .Include(gc => gc.CommandCooldowns) + .FirstOrDefault(c => c.GuildId == guildId); } else { @@ -65,10 +60,15 @@ namespace NadekoBot.Services.Database.Repositories.Impl _set.Add((config = new GuildConfig { GuildId = guildId, - RootPermission = Permission.GetDefaultRoot(), + Permissions = Permissionv2.GetDefaultPermlist })); _context.SaveChanges(); } + else if (config.Permissions == null) + { + config.Permissions = Permissionv2.GetDefaultPermlist; + _context.SaveChanges(); + } return config; } @@ -79,36 +79,11 @@ namespace NadekoBot.Services.Database.Repositories.Impl .FirstOrDefault(); } - public GuildConfig PermissionsFor(ulong guildId) + public IEnumerable OldPermissionsForAll() { - 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); - } - - var config = query.FirstOrDefault(c => c.GuildId == guildId); - - if (config == null) - { - _set.Add((config = new GuildConfig - { - GuildId = guildId, - RootPermission = Permission.GetDefaultRoot(), - })); - _context.SaveChanges(); - } - return config; - } - - public IEnumerable PermissionsForAll() - { - var query = _set.Include(gc => gc.RootPermission); + var query = _set + .Where(gc => gc.RootPermission != null) + .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 @@ -122,20 +97,19 @@ namespace NadekoBot.Services.Database.Repositories.Impl return query.ToList(); } + public IEnumerable Permissionsv2ForAll() + { + var query = _set + .Include(gc => gc.Permissions); + + return query.ToList(); + } + public IEnumerable GetAllFollowedStreams() => _set.Include(gc => gc.FollowedStreams) .SelectMany(gc => gc.FollowedStreams) .ToList(); - public GuildConfig SetNewRootPermission(ulong guildId, Permission p) - { - var data = PermissionsFor(guildId); - - data.RootPermission.Prepend(p); - data.RootPermission = p; - return data; - } - public void SetCleverbotEnabled(ulong id, bool cleverbotEnabled) { var conf = _set.FirstOrDefault(gc => gc.GuildId == id); diff --git a/src/NadekoBot/Services/Impl/Localization.cs b/src/NadekoBot/Services/Impl/Localization.cs index e9d792ca..53718ebf 100644 --- a/src/NadekoBot/Services/Impl/Localization.cs +++ b/src/NadekoBot/Services/Impl/Localization.cs @@ -42,6 +42,8 @@ namespace NadekoBot.Services CultureInfo cultureInfo = null; try { + if (x.Value == null) + return null; cultureInfo = new CultureInfo(x.Value); } catch { } diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index 946bf11f..11f7802c 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -16,7 +16,7 @@ namespace NadekoBot.Services.Impl private readonly DiscordShardedClient _client; private readonly DateTime _started; - public const string BotVersion = "1.21"; + public const string BotVersion = "1.22"; public string Author => "Kwoth#2560"; public string Library => "Discord.Net";