From ef688d096b427fb860df08a4ed80970265433841 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 10 Jan 2017 20:40:52 +0100 Subject: [PATCH] Woopsie --- .../20170110180534_protection.Designer.cs | 942 ++++++++++++++++++ .../Migrations/20170110180534_protection.cs | 104 ++ .../Commands/ProtectionCommands.cs | 425 ++++++++ .../Database/Models/AntiProtection.cs | 53 + 4 files changed, 1524 insertions(+) create mode 100644 src/NadekoBot/Migrations/20170110180534_protection.Designer.cs create mode 100644 src/NadekoBot/Migrations/20170110180534_protection.cs create mode 100644 src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs create mode 100644 src/NadekoBot/Services/Database/Models/AntiProtection.cs diff --git a/src/NadekoBot/Migrations/20170110180534_protection.Designer.cs b/src/NadekoBot/Migrations/20170110180534_protection.Designer.cs new file mode 100644 index 00000000..ecc134cd --- /dev/null +++ b/src/NadekoBot/Migrations/20170110180534_protection.Designer.cs @@ -0,0 +1,942 @@ +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("20170110180534_protection")] + partial class protection + { + 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("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.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("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("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("BufferSize"); + + b.Property("CurrencyGenerationChance"); + + b.Property("CurrencyGenerationCooldown"); + + b.Property("CurrencyName"); + + b.Property("CurrencyPluralName"); + + b.Property("CurrencySign"); + + b.Property("DMHelpString"); + + b.Property("ForwardMessages"); + + b.Property("ForwardToAllOwners"); + + b.Property("HelpString"); + + b.Property("MigrationVersion"); + + b.Property("RemindMessageFormat"); + + b.Property("RotatingStatuses"); + + 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("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("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("GuildConfigId"); + + b.Property("Seconds"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("CommandCooldown"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + 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("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("Reason"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.ToTable("CurrencyTransactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CustomReaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + 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.Donator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + 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("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("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("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("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("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("DefaultMusicVolume"); + + b.Property("DeleteMessageOnCommand"); + + b.Property("DmGreetMessageText"); + + b.Property("ExclusiveSelfAssignedRoles"); + + b.Property("FilterInvites"); + + b.Property("FilterWords"); + + b.Property("GreetMessageChannelId"); + + b.Property("GuildId"); + + b.Property("LogSettingId"); + + b.Property("MuteRoleName"); + + b.Property("PermissionRole"); + + b.Property("RootPermissionId"); + + b.Property("SendChannelByeMessage"); + + b.Property("SendChannelGreetMessage"); + + b.Property("SendDmGreetMessage"); + + 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("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("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("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("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("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("Name"); + + b.HasKey("Id"); + + b.ToTable("MusicPlaylists"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + 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("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.PlayingStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + 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("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("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("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("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("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("UserId"); + + b.Property("type"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("PokeGame"); + }); + + 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.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.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"); + }); + } + } +} diff --git a/src/NadekoBot/Migrations/20170110180534_protection.cs b/src/NadekoBot/Migrations/20170110180534_protection.cs new file mode 100644 index 00000000..fca7f541 --- /dev/null +++ b/src/NadekoBot/Migrations/20170110180534_protection.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace NadekoBot.Migrations +{ + public partial class protection : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AntiRaidSetting", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Action = table.Column(nullable: false), + GuildConfigId = table.Column(nullable: false), + Seconds = table.Column(nullable: false), + UserThreshold = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AntiRaidSetting", x => x.Id); + table.ForeignKey( + name: "FK_AntiRaidSetting_GuildConfigs_GuildConfigId", + column: x => x.GuildConfigId, + principalTable: "GuildConfigs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "AntiSpamSetting", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Action = table.Column(nullable: false), + GuildConfigId = table.Column(nullable: false), + MessageThreshold = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AntiSpamSetting", x => x.Id); + table.ForeignKey( + name: "FK_AntiSpamSetting_GuildConfigs_GuildConfigId", + column: x => x.GuildConfigId, + principalTable: "GuildConfigs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "AntiSpamIgnore", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AntiSpamSettingId = table.Column(nullable: true), + ChannelId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AntiSpamIgnore", x => x.Id); + table.ForeignKey( + name: "FK_AntiSpamIgnore_AntiSpamSetting_AntiSpamSettingId", + column: x => x.AntiSpamSettingId, + principalTable: "AntiSpamSetting", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_AntiRaidSetting_GuildConfigId", + table: "AntiRaidSetting", + column: "GuildConfigId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AntiSpamIgnore_AntiSpamSettingId", + table: "AntiSpamIgnore", + column: "AntiSpamSettingId"); + + migrationBuilder.CreateIndex( + name: "IX_AntiSpamSetting_GuildConfigId", + table: "AntiSpamSetting", + column: "GuildConfigId", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AntiRaidSetting"); + + migrationBuilder.DropTable( + name: "AntiSpamIgnore"); + + migrationBuilder.DropTable( + name: "AntiSpamSetting"); + } + } +} diff --git a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs new file mode 100644 index 00000000..e37f4850 --- /dev/null +++ b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs @@ -0,0 +1,425 @@ +using Discord; +using Discord.Commands; +using Microsoft.EntityFrameworkCore; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using NadekoBot.Services; +using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; +using NLog; +using System; +using System.Collections.Concurrent; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Threading.Tasks; + +namespace NadekoBot.Modules.Administration +{ + public partial class Administration + { + public enum ProtectionType + { + Raiding, + Spamming, + } + + public class AntiRaidStats + { + public AntiRaidSetting AntiRaidSettings { get; set; } + public int UsersCount { get; set; } = 0; + public ConcurrentHashSet RaidUsers { get; set; } = new ConcurrentHashSet(); + + public override string ToString() => + $"If **{AntiRaidSettings.UserThreshold}** or more users join within **{AntiRaidSettings.Seconds}** seconds," + + $" I will **{AntiRaidSettings.Action}** them."; + } + + public class AntiSpamStats + { + public AntiSpamSetting AntiSpamSettings { get; set; } + public ConcurrentDictionary UserStats { get; set; } + = new ConcurrentDictionary(); + + public override string ToString() + { + var ignoredString = string.Join(", ", AntiSpamSettings.IgnoredChannels.Select(c => $"<#{c.ChannelId}>")); + + if (string.IsNullOrWhiteSpace(ignoredString)) + ignoredString = "none"; + return $"If a user posts **{AntiSpamSettings.MessageThreshold}** same messages in a row, I will **{AntiSpamSettings.Action}** them." + + $"\n\t__IgnoredChannels__: {ignoredString}"; + } + } + + public class UserSpamStats + { + public int Count { get; set; } + public string LastMessage { get; set; } + + public UserSpamStats(string msg) + { + Count = 1; + LastMessage = msg.ToUpperInvariant(); + } + + public void ApplyNextMessage(string message) + { + var upperMsg = message.ToUpperInvariant(); + if (upperMsg == LastMessage) + Count++; + else + { + LastMessage = upperMsg; + Count = 0; + } + } + } + + [Group] + public class ProtectionCommands : ModuleBase + { + private static ConcurrentDictionary antiRaidGuilds = + new ConcurrentDictionary(); + // guildId | (userId|messages) + private static ConcurrentDictionary antiSpamGuilds = + new ConcurrentDictionary(); + + private static Logger _log { get; } + + static ProtectionCommands() + { + _log = LogManager.GetCurrentClassLogger(); + + foreach (var gc in NadekoBot.AllGuildConfigs) + { + var raid = gc.AntiRaidSetting; + var spam = gc.AntiSpamSetting; + + if (raid != null) + { + var raidStats = new AntiRaidStats() { AntiRaidSettings = raid }; + antiRaidGuilds.TryAdd(gc.GuildId, raidStats); + } + + if (spam != null) + antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam }); + } + + NadekoBot.Client.MessageReceived += async (imsg) => + { + + try + { + var msg = imsg as IUserMessage; + if (msg == null || msg.Author.IsBot) + return; + + var channel = msg.Channel as ITextChannel; + if (channel == null) + return; + AntiSpamStats spamSettings; + if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) || + spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore() + { + ChannelId = channel.Id + })) + return; + + var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, new UserSpamStats(msg.Content), + (id, old) => { old.ApplyNextMessage(msg.Content); return old; }); + + if (stats.Count >= spamSettings.AntiSpamSettings.MessageThreshold) + { + if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats)) + { + await PunishUsers(spamSettings.AntiSpamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author) + .ConfigureAwait(false); + } + } + } + catch { } + }; + + NadekoBot.Client.UserJoined += async (usr) => + { + try + { + if (usr.IsBot) + return; + AntiRaidStats settings; + if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings)) + return; + if (!settings.RaidUsers.Add(usr)) + return; + + ++settings.UsersCount; + + if (settings.UsersCount >= settings.AntiRaidSettings.UserThreshold) + { + var users = settings.RaidUsers.ToArray(); + settings.RaidUsers.Clear(); + + await PunishUsers(settings.AntiRaidSettings.Action, ProtectionType.Raiding, users).ConfigureAwait(false); + } + await Task.Delay(1000 * settings.AntiRaidSettings.Seconds).ConfigureAwait(false); + + settings.RaidUsers.TryRemove(usr); + --settings.UsersCount; + + } + catch { } + }; + } + + private static async Task PunishUsers(PunishmentAction action, ProtectionType pt, params IGuildUser[] gus) + { + foreach (var gu in gus) + { + switch (action) + { + case PunishmentAction.Mute: + try + { + await MuteCommands.MuteUser(gu).ConfigureAwait(false); + } + catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); } + break; + case PunishmentAction.Kick: + try + { + await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false); + try + { + await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false); + } + catch + { + await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false); + // try it twice, really don't want to ban user if + // only kick has been specified as the punishement + } + } + catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); } + break; + case PunishmentAction.Ban: + try + { + await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false); + } + catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); } + break; + default: + break; + } + } + await LogCommands.TriggeredAntiProtection(gus, action, pt).ConfigureAwait(false); + } + + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [RequireUserPermission(GuildPermission.Administrator)] + public async Task AntiRaid(int userThreshold = 5, int seconds = 10, PunishmentAction action = PunishmentAction.Mute) + { + if (userThreshold < 2 || userThreshold > 30) + { + await Context.Channel.SendErrorAsync("❗️User threshold must be between **2** and **30**.").ConfigureAwait(false); + return; + } + + if (seconds < 2 || seconds > 300) + { + await Context.Channel.SendErrorAsync("❗️Time must be between **2** and **300** seconds.").ConfigureAwait(false); + return; + } + + AntiRaidStats throwaway; + if (antiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway)) + { + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiRaidSetting)); + + gc.AntiRaidSetting = null; + await uow.CompleteAsync().ConfigureAwait(false); + } + await Context.Channel.SendConfirmAsync("**Anti-Raid** feature has been **disabled** on this server.").ConfigureAwait(false); + return; + } + + try + { + await MuteCommands.GetMuteRole(Context.Guild).ConfigureAwait(false); + } + catch (Exception ex) + { + await Context.Channel.SendConfirmAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" + + "or create 'nadeko-mute' role with disabled SendMessages and try again.") + .ConfigureAwait(false); + _log.Warn(ex); + return; + } + + var stats = new AntiRaidStats() + { + AntiRaidSettings = new AntiRaidSetting() + { + Action = action, + Seconds = seconds, + UserThreshold = userThreshold, + } + }; + + antiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats); + + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiRaidSetting)); + + gc.AntiRaidSetting = stats.AntiRaidSettings; + await uow.CompleteAsync().ConfigureAwait(false); + } + + await Context.Channel.SendConfirmAsync("Anti-Raid Enabled", $"{Context.User.Mention} {stats.ToString()}") + .ConfigureAwait(false); + } + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [RequireUserPermission(GuildPermission.Administrator)] + public async Task AntiSpam(int messageCount = 3, PunishmentAction action = PunishmentAction.Mute) + { + if (messageCount < 2 || messageCount > 10) + return; + + AntiSpamStats throwaway; + if (antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway)) + { + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiSpamSetting) + .ThenInclude(x => x.IgnoredChannels)); + + gc.AntiSpamSetting = null; + await uow.CompleteAsync().ConfigureAwait(false); + } + await Context.Channel.SendConfirmAsync("**Anti-Spam** has been **disabled** on this server.").ConfigureAwait(false); + return; + } + + try + { + await MuteCommands.GetMuteRole(Context.Guild).ConfigureAwait(false); + } + catch (Exception ex) + { + await Context.Channel.SendErrorAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" + + "or create 'nadeko-mute' role with disabled SendMessages and try again.") + .ConfigureAwait(false); + _log.Warn(ex); + return; + } + + var stats = new AntiSpamStats + { + AntiSpamSettings = new AntiSpamSetting() + { + Action = action, + MessageThreshold = messageCount, + } + }; + + antiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats); + + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiSpamSetting)); + + gc.AntiSpamSetting = stats.AntiSpamSettings; + await uow.CompleteAsync().ConfigureAwait(false); + } + + await Context.Channel.SendConfirmAsync("Anti-Spam Enabled", $"{Context.User.Mention} {stats.ToString()}").ConfigureAwait(false); + } + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + public async Task AntispamIgnore() + { + var channel = (ITextChannel)Context.Channel; + + var obj = new AntiSpamIgnore() + { + ChannelId = channel.Id + }; + bool added; + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiSpamSetting).ThenInclude(x => x.IgnoredChannels)); + var spam = gc.AntiSpamSetting; + if (spam == null) + { + return; + } + + if (spam.IgnoredChannels.Add(obj)) + { + AntiSpamStats temp; + if (antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp)) + temp.AntiSpamSettings.IgnoredChannels.Add(obj); + added = true; + } + else + { + spam.IgnoredChannels.Remove(obj); + AntiSpamStats temp; + if (antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp)) + temp.AntiSpamSettings.IgnoredChannels.Remove(obj); + added = false; + } + + await uow.CompleteAsync().ConfigureAwait(false); + } + if (added) + await Context.Channel.SendConfirmAsync("Anti-Spam will ignore this channel.").ConfigureAwait(false); + else + await Context.Channel.SendConfirmAsync("Anti-Spam will no longer ignore this channel.").ConfigureAwait(false); + + } + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + public async Task AntiList() + { + var channel = (ITextChannel)Context.Channel; + + AntiSpamStats spam; + antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam); + + AntiRaidStats raid; + antiRaidGuilds.TryGetValue(Context.Guild.Id, out raid); + + if (spam == null && raid == null) + { + await Context.Channel.SendConfirmAsync("No protections enabled."); + return; + } + + var embed = new EmbedBuilder().WithOkColor() + .WithTitle("Protections Enabled"); + + if (spam != null) + embed.AddField(efb => efb.WithName("Anti-Spam") + .WithValue(spam.ToString()) + .WithIsInline(true)); + + if (raid != null) + embed.AddField(efb => efb.WithName("Anti-Raid") + .WithValue(raid.ToString()) + .WithIsInline(true)); + + await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Services/Database/Models/AntiProtection.cs b/src/NadekoBot/Services/Database/Models/AntiProtection.cs new file mode 100644 index 00000000..0172dd90 --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/AntiProtection.cs @@ -0,0 +1,53 @@ +using Discord; +using NadekoBot.Services.Database.Models; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +namespace NadekoBot.Services.Database.Models +{ + public class AntiRaidSetting : DbEntity + { + public int GuildConfigId { get; set; } + public GuildConfig GuildConfig { get; set; } + + public int UserThreshold { get; set; } + public int Seconds { get; set; } + public PunishmentAction Action { get; set; } + } + + public class AntiSpamSetting : DbEntity + { + public int GuildConfigId { get; set; } + public GuildConfig GuildConfig { get; set; } + + public PunishmentAction Action { get; set; } + public int MessageThreshold { get; set; } = 3; + public HashSet IgnoredChannels { get; set; } = new HashSet(); + } + + + public enum PunishmentAction + { + Mute, + Kick, + Ban, + } + + public class AntiSpamIgnore : DbEntity + { + public ulong ChannelId { get; set; } + + public override int GetHashCode() => ChannelId.GetHashCode(); + + public override bool Equals(object obj) + { + var inst = obj as AntiSpamIgnore; + + if (inst == null) + return false; + + return inst.ChannelId == ChannelId; + + } + } +} \ No newline at end of file