From 088d95340fa9a051ae5735b44ee3ee5804f1e477 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Tue, 22 Aug 2017 05:48:45 +0200 Subject: [PATCH] Started work on the xp system --- src/NadekoBot/Common/BotConfigEditType.cs | 2 + .../20170821085106_xp-stuff.Designer.cs | 1825 +++++++++++++++++ .../Migrations/20170821085106_xp-stuff.cs | 153 ++ .../NadekoSqliteContextModelSnapshot.cs | 112 + .../Modules/Games/Services/GamesService.cs | 10 +- .../Modules/Xp/Extensions/Extensions.cs | 34 + .../Modules/Xp/Services/UserCacheItem.cs | 18 + .../Modules/Xp/Services/XpService.cs | 229 +++ src/NadekoBot/Modules/Xp/Xp.cs | 105 + src/NadekoBot/NadekoBot.csproj | 6 + src/NadekoBot/Resources/CommandStrings.resx | 27 + .../Services/Database/IUnitOfWork.cs | 1 + .../Services/Database/Models/BotConfig.cs | 2 + .../Services/Database/Models/GuildConfig.cs | 2 + .../Services/Database/Models/UserXpStats.cs | 10 + .../Services/Database/Models/XpSettings.cs | 50 + .../Services/Database/NadekoContext.cs | 13 + .../Repositories/IGuildConfigRepository.cs | 1 + .../Database/Repositories/IXpRepository.cs | 9 + .../Impl/GuildConfigRepository.cs | 14 + .../Repositories/Impl/XpRepository.cs | 31 + src/NadekoBot/Services/Database/UnitOfWork.cs | 3 + .../Services/Impl/BotConfigProvider.cs | 12 + .../_strings/ResponseStrings.en-US.json | 13 +- 24 files changed, 2676 insertions(+), 6 deletions(-) create mode 100644 src/NadekoBot/Migrations/20170821085106_xp-stuff.Designer.cs create mode 100644 src/NadekoBot/Migrations/20170821085106_xp-stuff.cs create mode 100644 src/NadekoBot/Modules/Xp/Extensions/Extensions.cs create mode 100644 src/NadekoBot/Modules/Xp/Services/UserCacheItem.cs create mode 100644 src/NadekoBot/Modules/Xp/Services/XpService.cs create mode 100644 src/NadekoBot/Modules/Xp/Xp.cs create mode 100644 src/NadekoBot/Services/Database/Models/UserXpStats.cs create mode 100644 src/NadekoBot/Services/Database/Models/XpSettings.cs create mode 100644 src/NadekoBot/Services/Database/Repositories/IXpRepository.cs create mode 100644 src/NadekoBot/Services/Database/Repositories/Impl/XpRepository.cs diff --git a/src/NadekoBot/Common/BotConfigEditType.cs b/src/NadekoBot/Common/BotConfigEditType.cs index 5eadbe9d..92be166a 100644 --- a/src/NadekoBot/Common/BotConfigEditType.cs +++ b/src/NadekoBot/Common/BotConfigEditType.cs @@ -17,6 +17,8 @@ CurrencyDropAmountMax, MinimumBetAmount, TriviaCurrencyReward, + XpPerMessage, + XpMinutesTimeout, //ErrorColor, //after i fix the nadekobot.cs static variables //OkColor diff --git a/src/NadekoBot/Migrations/20170821085106_xp-stuff.Designer.cs b/src/NadekoBot/Migrations/20170821085106_xp-stuff.Designer.cs new file mode 100644 index 00000000..ba66fe44 --- /dev/null +++ b/src/NadekoBot/Migrations/20170821085106_xp-stuff.Designer.cs @@ -0,0 +1,1825 @@ +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; + +namespace NadekoBot.Migrations +{ + [DbContext(typeof(NadekoContext))] + [Migration("20170821085106_xp-stuff")] + partial class xpstuff + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.1"); + + 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.Property("MuteTime"); + + 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.BlockedCmdOrMdl", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("BotConfigId1"); + + b.Property("DateAdded"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.HasIndex("BotConfigId1"); + + b.ToTable("BlockedCmdOrMdl"); + }); + + 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("CurrencyDropAmountMax"); + + b.Property("CurrencyGenerationChance"); + + b.Property("CurrencyGenerationCooldown"); + + b.Property("CurrencyName"); + + b.Property("CurrencyPluralName"); + + b.Property("CurrencySign"); + + b.Property("CustomReactionsStartWith"); + + b.Property("DMHelpString"); + + b.Property("DateAdded"); + + b.Property("DefaultPrefix"); + + 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("PermissionVersion"); + + b.Property("RemindMessageFormat"); + + b.Property("RotatingStatuses"); + + b.Property("TriviaCurrencyReward"); + + b.Property("XpMinutesTimeout"); + + b.Property("XpPerMessage"); + + 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.CommandAlias", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("Mapping"); + + b.Property("Trigger"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("CommandAlias"); + }); + + 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("AutoDeleteTrigger"); + + b.Property("ContainsAnywhere"); + + b.Property("DateAdded"); + + b.Property("DmResponse"); + + 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.ExcludedItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("ItemId"); + + b.Property("ItemType"); + + b.Property("XpSettingsId"); + + b.HasKey("Id"); + + b.HasIndex("XpSettingsId"); + + b.ToTable("ExcludedItem"); + }); + + 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("GameVoiceChannel"); + + b.Property("GreetMessageChannelId"); + + b.Property("GuildId"); + + b.Property("Locale"); + + b.Property("LogSettingId"); + + b.Property("MuteRoleName"); + + b.Property("PermissionRole"); + + b.Property("Prefix"); + + b.Property("RootPermissionId"); + + b.Property("SendChannelByeMessage"); + + b.Property("SendChannelGreetMessage"); + + b.Property("SendDmGreetMessage"); + + b.Property("TimeZoneId"); + + b.Property("VerboseErrors"); + + b.Property("VerbosePermissions"); + + b.Property("VoicePlusTextEnabled"); + + b.Property("WarningsInitialized"); + + 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.Property("StartTimeOfDay"); + + 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.NsfwBlacklitedTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("Tag"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("NsfwBlacklitedTag"); + }); + + 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.RewardedUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AmountRewardedThisMonth"); + + b.Property("DateAdded"); + + b.Property("LastReward"); + + b.Property("PatreonUserId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("RewardedUsers"); + }); + + 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.ShopEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuthorId"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("Index"); + + b.Property("Name"); + + b.Property("Price"); + + b.Property("RoleId"); + + b.Property("RoleName"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("ShopEntry"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("ShopEntryId"); + + b.Property("Text"); + + b.HasKey("Id"); + + b.HasIndex("ShopEntryId"); + + b.ToTable("ShopEntryItem"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("RoleId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("SlowmodeIgnoredRole"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("SlowmodeIgnoredUser"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.StartupCommand", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("ChannelId"); + + b.Property("ChannelName"); + + b.Property("CommandText"); + + b.Property("DateAdded"); + + b.Property("GuildId"); + + b.Property("GuildName"); + + b.Property("Index"); + + b.Property("VoiceChannelId"); + + b.Property("VoiceChannelName"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("StartupCommand"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("StreamRoleSettingsId"); + + b.Property("UserId"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasIndex("StreamRoleSettingsId"); + + b.ToTable("StreamRoleBlacklistedUser"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddRoleId"); + + b.Property("DateAdded"); + + b.Property("Enabled"); + + b.Property("FromRoleId"); + + b.Property("GuildConfigId"); + + b.Property("Keyword"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("StreamRoleSettings"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("StreamRoleSettingsId"); + + b.Property("UserId"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasIndex("StreamRoleSettingsId"); + + b.ToTable("StreamRoleWhitelistedUser"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("UnmuteAt"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("UnmuteTimer"); + }); + + 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.UserXpStats", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildId"); + + b.Property("NotifyOnLevelUp"); + + b.Property("UserId"); + + b.Property("Xp"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "GuildId") + .IsUnique(); + + b.ToTable("UserXpStats"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("RoleId"); + + b.Property("VoiceChannelId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("VcRoleInfo"); + }); + + 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.WaifuItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("Item"); + + b.Property("ItemEmoji"); + + b.Property("Price"); + + b.Property("WaifuInfoId"); + + b.HasKey("Id"); + + b.HasIndex("WaifuInfoId"); + + b.ToTable("WaifuItem"); + }); + + 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.Warning", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("Forgiven"); + + b.Property("ForgivenBy"); + + b.Property("GuildId"); + + b.Property("Moderator"); + + b.Property("Reason"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.ToTable("Warnings"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WarningPunishment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Count"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("Punishment"); + + b.Property("Time"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("WarningPunishment"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("Level"); + + b.Property("RoleId"); + + b.Property("XpSettingsId"); + + b.HasKey("Id"); + + b.HasIndex("XpSettingsId"); + + b.ToTable("XpRoleReward"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("NotifyMessage"); + + b.Property("ServerExcluded"); + + b.Property("XpRoleRewardExclusive"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("XpSettings"); + }); + + 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.BlockedCmdOrMdl", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("BlockedCommands") + .HasForeignKey("BotConfigId"); + + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("BlockedModules") + .HasForeignKey("BotConfigId1"); + }); + + 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.CommandAlias", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("CommandAliases") + .HasForeignKey("GuildConfigId"); + }); + + 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.ExcludedItem", b => + { + b.HasOne("NadekoBot.Services.Database.Models.XpSettings") + .WithMany("ExclusionList") + .HasForeignKey("XpSettingsId"); + }); + + 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.NsfwBlacklitedTag", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("NsfwBlacklistedTags") + .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.ShopEntry", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("ShopEntries") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntryItem", b => + { + b.HasOne("NadekoBot.Services.Database.Models.ShopEntry") + .WithMany("Items") + .HasForeignKey("ShopEntryId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredRole", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("SlowmodeIgnoredRoles") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredUser", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("SlowmodeIgnoredUsers") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.StartupCommand", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("StartupCommands") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b => + { + b.HasOne("NadekoBot.Services.Database.Models.StreamRoleSettings") + .WithMany("Blacklist") + .HasForeignKey("StreamRoleSettingsId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("StreamRole") + .HasForeignKey("NadekoBot.Services.Database.Models.StreamRoleSettings", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b => + { + b.HasOne("NadekoBot.Services.Database.Models.StreamRoleSettings") + .WithMany("Whitelist") + .HasForeignKey("StreamRoleSettingsId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("UnmuteTimers") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("VcRoleInfos") + .HasForeignKey("GuildConfigId"); + }); + + 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.WaifuItem", b => + { + b.HasOne("NadekoBot.Services.Database.Models.WaifuInfo") + .WithMany("Items") + .HasForeignKey("WaifuInfoId"); + }); + + 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); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WarningPunishment", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("WarnPunishments") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b => + { + b.HasOne("NadekoBot.Services.Database.Models.XpSettings") + .WithMany("RoleRewards") + .HasForeignKey("XpSettingsId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("XpSettings") + .HasForeignKey("NadekoBot.Services.Database.Models.XpSettings", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/NadekoBot/Migrations/20170821085106_xp-stuff.cs b/src/NadekoBot/Migrations/20170821085106_xp-stuff.cs new file mode 100644 index 00000000..166a53f6 --- /dev/null +++ b/src/NadekoBot/Migrations/20170821085106_xp-stuff.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace NadekoBot.Migrations +{ + public partial class xpstuff : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "XpMinutesTimeout", + table: "BotConfig", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "XpPerMessage", + table: "BotConfig", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + name: "UserXpStats", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + DateAdded = table.Column(nullable: true), + GuildId = table.Column(nullable: false), + NotifyOnLevelUp = table.Column(nullable: false), + UserId = table.Column(nullable: false), + Xp = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserXpStats", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "XpSettings", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + DateAdded = table.Column(nullable: true), + GuildConfigId = table.Column(nullable: false), + NotifyMessage = table.Column(nullable: true), + ServerExcluded = table.Column(nullable: false), + XpRoleRewardExclusive = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_XpSettings", x => x.Id); + table.ForeignKey( + name: "FK_XpSettings_GuildConfigs_GuildConfigId", + column: x => x.GuildConfigId, + principalTable: "GuildConfigs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ExcludedItem", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + DateAdded = table.Column(nullable: true), + ItemId = table.Column(nullable: false), + ItemType = table.Column(nullable: false), + XpSettingsId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ExcludedItem", x => x.Id); + table.ForeignKey( + name: "FK_ExcludedItem_XpSettings_XpSettingsId", + column: x => x.XpSettingsId, + principalTable: "XpSettings", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "XpRoleReward", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + DateAdded = table.Column(nullable: true), + Level = table.Column(nullable: false), + RoleId = table.Column(nullable: false), + XpSettingsId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_XpRoleReward", x => x.Id); + table.ForeignKey( + name: "FK_XpRoleReward_XpSettings_XpSettingsId", + column: x => x.XpSettingsId, + principalTable: "XpSettings", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_ExcludedItem_XpSettingsId", + table: "ExcludedItem", + column: "XpSettingsId"); + + migrationBuilder.CreateIndex( + name: "IX_UserXpStats_UserId_GuildId", + table: "UserXpStats", + columns: new[] { "UserId", "GuildId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_XpRoleReward_XpSettingsId", + table: "XpRoleReward", + column: "XpSettingsId"); + + migrationBuilder.CreateIndex( + name: "IX_XpSettings_GuildConfigId", + table: "XpSettings", + column: "GuildConfigId", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ExcludedItem"); + + migrationBuilder.DropTable( + name: "UserXpStats"); + + migrationBuilder.DropTable( + name: "XpRoleReward"); + + migrationBuilder.DropTable( + name: "XpSettings"); + + migrationBuilder.DropColumn( + name: "XpMinutesTimeout", + table: "BotConfig"); + + migrationBuilder.DropColumn( + name: "XpPerMessage", + table: "BotConfig"); + } + } +} diff --git a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs index 97c06375..3ebfeafa 100644 --- a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs +++ b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs @@ -183,6 +183,10 @@ namespace NadekoBot.Migrations b.Property("TriviaCurrencyReward"); + b.Property("XpMinutesTimeout"); + + b.Property("XpPerMessage"); + b.HasKey("Id"); b.ToTable("BotConfig"); @@ -445,6 +449,26 @@ namespace NadekoBot.Migrations b.ToTable("EightBallResponses"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("ItemId"); + + b.Property("ItemType"); + + b.Property("XpSettingsId"); + + b.HasKey("Id"); + + b.HasIndex("XpSettingsId"); + + b.ToTable("ExcludedItem"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => { b.Property("Id") @@ -1252,6 +1276,29 @@ namespace NadekoBot.Migrations b.ToTable("PokeGame"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildId"); + + b.Property("NotifyOnLevelUp"); + + b.Property("UserId"); + + b.Property("Xp"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "GuildId") + .IsUnique(); + + b.ToTable("UserXpStats"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b => { b.Property("Id") @@ -1393,6 +1440,49 @@ namespace NadekoBot.Migrations b.ToTable("WarningPunishment"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("Level"); + + b.Property("RoleId"); + + b.Property("XpSettingsId"); + + b.HasKey("Id"); + + b.HasIndex("XpSettingsId"); + + b.ToTable("XpRoleReward"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("NotifyMessage"); + + b.Property("ServerExcluded"); + + b.Property("XpRoleRewardExclusive"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("XpSettings"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => { b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") @@ -1470,6 +1560,13 @@ namespace NadekoBot.Migrations .HasForeignKey("BotConfigId"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b => + { + b.HasOne("NadekoBot.Services.Database.Models.XpSettings") + .WithMany("ExclusionList") + .HasForeignKey("XpSettingsId"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => { b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") @@ -1707,6 +1804,21 @@ namespace NadekoBot.Migrations .WithMany("WarnPunishments") .HasForeignKey("GuildConfigId"); }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b => + { + b.HasOne("NadekoBot.Services.Database.Models.XpSettings") + .WithMany("RoleRewards") + .HasForeignKey("XpSettingsId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("XpSettings") + .HasForeignKey("NadekoBot.Services.Database.Models.XpSettings", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); } } } diff --git a/src/NadekoBot/Modules/Games/Services/GamesService.cs b/src/NadekoBot/Modules/Games/Services/GamesService.cs index f09c606b..39164486 100644 --- a/src/NadekoBot/Modules/Games/Services/GamesService.cs +++ b/src/NadekoBot/Modules/Games/Services/GamesService.cs @@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Games.Services public readonly ImmutableArray EightBallResponses; private readonly Timer _t; - private readonly DiscordSocketClient _client; + private readonly CommandHandler _cmd; private readonly NadekoStrings _strings; private readonly IImagesService _images; private readonly Logger _log; @@ -38,11 +38,11 @@ namespace NadekoBot.Modules.Games.Services public List TypingArticles { get; } = new List(); - public GamesService(DiscordSocketClient client, IBotConfigProvider bc, IEnumerable gcs, + public GamesService(CommandHandler cmd, IBotConfigProvider bc, IEnumerable gcs, NadekoStrings strings, IImagesService images, CommandHandler cmdHandler) { _bc = bc; - _client = client; + _cmd = cmd; _strings = strings; _images = images; _cmdHandler = cmdHandler; @@ -59,7 +59,7 @@ namespace NadekoBot.Modules.Games.Services }, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1)); //plantpick - client.MessageReceived += PotentialFlowerGeneration; + _cmd.OnMessageNoTrigger += PotentialFlowerGeneration; GenerationChannels = new ConcurrentHashSet(gcs .SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId))); @@ -102,7 +102,7 @@ namespace NadekoBot.Modules.Games.Services private string GetText(ITextChannel ch, string key, params object[] rep) => _strings.GetText(key, ch.GuildId, "Games".ToLowerInvariant(), rep); - private Task PotentialFlowerGeneration(SocketMessage imsg) + private Task PotentialFlowerGeneration(IUserMessage imsg) { var msg = imsg as SocketUserMessage; if (msg == null || msg.Author.IsBot) diff --git a/src/NadekoBot/Modules/Xp/Extensions/Extensions.cs b/src/NadekoBot/Modules/Xp/Extensions/Extensions.cs new file mode 100644 index 00000000..c5d6605b --- /dev/null +++ b/src/NadekoBot/Modules/Xp/Extensions/Extensions.cs @@ -0,0 +1,34 @@ +using NadekoBot.Modules.Xp.Services; +using NadekoBot.Services.Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Modules.Xp.Extensions +{ + public static class Extensions + { + public static (int Level, int LevelXp, int LevelRequiredXp) GetLevelData(this UserXpStats stats) + { + var baseXp = XpService.XP_REQUIRED_LVL_1; + + var required = baseXp; + var totalXp = 0; + var lvl = 1; + while (true) + { + required = (int)(baseXp + baseXp / 4.0 * (lvl - 1)); + + if (required + totalXp > stats.Xp) + break; + + totalXp += required; + lvl++; + } + + return (lvl - 1, stats.Xp - totalXp, required); + } + } +} diff --git a/src/NadekoBot/Modules/Xp/Services/UserCacheItem.cs b/src/NadekoBot/Modules/Xp/Services/UserCacheItem.cs new file mode 100644 index 00000000..4cebef92 --- /dev/null +++ b/src/NadekoBot/Modules/Xp/Services/UserCacheItem.cs @@ -0,0 +1,18 @@ +namespace NadekoBot.Modules.Xp.Services +{ + public class UserCacheItem + { + public ulong UserId { get; set; } + public ulong GuildId { get; set; } + + public override int GetHashCode() + { + return UserId.GetHashCode(); + } + + public override bool Equals(object obj) + { + return obj is UserCacheItem uci && uci.UserId == UserId; + } + } +} diff --git a/src/NadekoBot/Modules/Xp/Services/XpService.cs b/src/NadekoBot/Modules/Xp/Services/XpService.cs new file mode 100644 index 00000000..ccd3c6ed --- /dev/null +++ b/src/NadekoBot/Modules/Xp/Services/XpService.cs @@ -0,0 +1,229 @@ +using Discord; +using Discord.WebSocket; +using NadekoBot.Common.Collections; +using NadekoBot.Services; +using NadekoBot.Services.Database.Models; +using NLog; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace NadekoBot.Modules.Xp.Services +{ + public class XpService : INService + { + private readonly DbService _db; + private readonly CommandHandler _cmd; + private readonly IBotConfigProvider _bc; + private readonly Logger _log; + public const int XP_REQUIRED_LVL_1 = 36; + + private readonly ConcurrentDictionary> _excludedRoles + = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary> _excludedChannels + = new ConcurrentDictionary>(); + private readonly ConcurrentHashSet _excludedServers = new ConcurrentHashSet(); + + private readonly ConcurrentHashSet _rewardedUsers = new ConcurrentHashSet(); + + private readonly ConcurrentQueue _addMessageXp = new ConcurrentQueue(); + + private readonly Timer updateXpTimer; + private readonly Timer clearRewardedUsersTimer; + + public XpService(CommandHandler cmd, IBotConfigProvider bc, + IEnumerable allGuildConfigs, DbService db) + { + _db = db; + _cmd = cmd; + _bc = bc; + _log = LogManager.GetCurrentClassLogger(); + + _cmd.OnMessageNoTrigger += _cmd_OnMessageNoTrigger; + + updateXpTimer = new Timer(_ => + { + using (var uow = _db.UnitOfWork) + { + while (_addMessageXp.TryDequeue(out var usr)) + { + var usrObj = uow.Xp.GetOrCreateUser(usr.GuildId, usr.UserId); + usrObj.Xp += _bc.BotConfig.XpPerMessage; + } + uow.Complete(); + } + }, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); + + clearRewardedUsersTimer = new Timer(_ => + { + _rewardedUsers.Clear(); + }, null, TimeSpan.FromSeconds(bc.BotConfig.XpMinutesTimeout), TimeSpan.FromSeconds(bc.BotConfig.XpMinutesTimeout)); + } + + private Task _cmd_OnMessageNoTrigger(IUserMessage arg) + { + if (!(arg.Author is SocketGuildUser user) || user.IsBot) + return Task.CompletedTask; + + var _ = Task.Run(() => + { + if (!SetUserRewarded(user.Id)) + return; + + if (_excludedChannels.TryGetValue(user.Guild.Id, out var chans) && + chans.Contains(arg.Channel.Id)) + return; + + if (_excludedServers.Contains(user.Guild.Id)) + return; + + if (_excludedRoles.TryGetValue(user.Guild.Id, out var roles) && + user.Roles.Any(x => roles.Contains(x.Id))) + return; + + _log.Info("Adding {0} xp to {1} on {2} server", _bc.BotConfig.XpPerMessage, user.ToString(), user.Guild.Name); + _addMessageXp.Enqueue(new UserCacheItem { GuildId = user.Guild.Id, UserId = user.Id }); + }); + return Task.CompletedTask; + } + + public bool IsServerExcluded(ulong id) + { + return _excludedServers.Contains(id); + } + + public IEnumerable GetExcludedRoles(ulong id) + { + if (_excludedRoles.TryGetValue(id, out var val)) + return val.ToArray(); + + return Enumerable.Empty(); + } + + public IEnumerable GetExcludedChannels(ulong id) + { + if (_excludedChannels.TryGetValue(id, out var val)) + return val.ToArray(); + + return Enumerable.Empty(); + } + + private bool SetUserRewarded(ulong userId) + { + return _rewardedUsers.Add(userId); + } + + public UserXpStats GetUserStats(ulong guildId, ulong userId) + { + UserXpStats user; + using (var uow = _db.UnitOfWork) + { + user = uow.Xp.GetOrCreateUser(guildId, userId); + } + + return user; + } + + public string GenerateXpBar(int currentXp, int requiredXp) + { + //todo + return $"{currentXp}/{requiredXp}"; + } + + + //todo exclude in database + public bool ToggleExcludeServer(ulong id) + { + using (var uow = _db.UnitOfWork) + { + var xpSetting = uow.GuildConfigs.XpSettingsFor(id); + if (_excludedServers.Add(id)) + { + xpSetting.ServerExcluded = true; + uow.Complete(); + return true; + } + + _excludedServers.TryRemove(id); + xpSetting.ServerExcluded = false; + uow.Complete(); + return false; + } + } + + public bool ToggleExcludeRole(ulong guildId, ulong rId) + { + var roles = _excludedRoles.GetOrAdd(guildId, _ => new ConcurrentHashSet()); + using (var uow = _db.UnitOfWork) + { + var xpSetting = uow.GuildConfigs.XpSettingsFor(guildId); + var excludeObj = new ExcludedItem + { + ItemId = rId, + ItemType = ExcludedItemType.Role, + }; + + if (roles.Add(rId)) + { + + if (xpSetting.ExclusionList.Add(excludeObj)) + { + uow.Complete(); + } + + return true; + } + else + { + roles.TryRemove(rId); + + if (xpSetting.ExclusionList.Remove(excludeObj)) + { + uow.Complete(); + } + + return false; + } + } + } + + public bool ToggleExcludeChannel(ulong guildId, ulong chId) + { + var channels = _excludedChannels.GetOrAdd(guildId, _ => new ConcurrentHashSet()); + using (var uow = _db.UnitOfWork) + { + var xpSetting = uow.GuildConfigs.XpSettingsFor(guildId); + var excludeObj = new ExcludedItem + { + ItemId = chId, + ItemType = ExcludedItemType.Channel, + }; + + if (channels.Add(chId)) + { + + if (xpSetting.ExclusionList.Add(excludeObj)) + { + uow.Complete(); + } + + return true; + } + else + { + channels.TryRemove(chId); + + if (xpSetting.ExclusionList.Remove(excludeObj)) + { + uow.Complete(); + } + + return false; + } + } + } + } +} diff --git a/src/NadekoBot/Modules/Xp/Xp.cs b/src/NadekoBot/Modules/Xp/Xp.cs new file mode 100644 index 00000000..44b5b006 --- /dev/null +++ b/src/NadekoBot/Modules/Xp/Xp.cs @@ -0,0 +1,105 @@ +using Discord; +using Discord.Commands; +using NadekoBot.Common.Attributes; +using NadekoBot.Extensions; +using NadekoBot.Modules.Xp.Extensions; +using NadekoBot.Modules.Xp.Services; +using System.Linq; +using System.Threading.Tasks; + +namespace NadekoBot.Modules.Xp +{ + public partial class Xp : NadekoTopLevelModule + { + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + public async Task Experience(IUser user = null) + { + user = user ?? Context.User; + await Task.Delay(64).ConfigureAwait(false); // wait a bit in case user got XP with this message + + var stats = _service.GetUserStats(Context.Guild.Id, user.Id); + + var levelData = stats.GetLevelData(); + var xpBarStr = _service.GenerateXpBar(levelData.LevelXp, levelData.LevelRequiredXp); + + await Context.Channel.EmbedAsync(new EmbedBuilder() + .WithTitle(user.ToString()) + //.AddField(GetText("server_level"), stats.ServerLevel.ToString(), true) + .AddField(GetText("level"), levelData.Level.ToString(), true) + //.AddField(GetText("club"), stats.ClubName ?? "-", true) + .AddField(GetText("xp"), xpBarStr, false) + .WithOkColor()) + .ConfigureAwait(false); + } + + public enum Server { Server }; + + //[NadekoCommand, Usage, Description, Aliases] + //[RequireContext(ContextType.Guild)] + //[OwnerOnly] + //[Priority(1)] + //public async Task XpExclude(Server _, IGuild guild) + //{ + //} + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [RequireUserPermission(GuildPermission.Administrator)] + public async Task XpExclude(Server _) + { + var ex = _service.ToggleExcludeServer(Context.Guild.Id); + + await ReplyConfirmLocalized((ex ? "excluded" : "not_excluded"), Format.Bold(Context.Guild.ToString())).ConfigureAwait(false); + } + + public enum Role { Role }; + + [NadekoCommand, Usage, Description, Aliases] + [RequireUserPermission(GuildPermission.ManageRoles)] + [RequireContext(ContextType.Guild)] + public async Task XpExclude(Role _, [Remainder] IRole role) + { + var ex = _service.ToggleExcludeRole(Context.Guild.Id, role.Id); + + await ReplyConfirmLocalized((ex ? "excluded" : "not_excluded"), Format.Bold(role.ToString())).ConfigureAwait(false); + } + + public enum Channel { Channel }; + + [NadekoCommand, Usage, Description, Aliases] + [RequireUserPermission(GuildPermission.ManageChannels)] + [RequireContext(ContextType.Guild)] + public async Task XpExclude(Channel _, [Remainder] ITextChannel channel) + { + var ex = _service.ToggleExcludeChannel(Context.Guild.Id, channel.Id); + + await ReplyConfirmLocalized((ex ? "excluded" : "not_excluded"), Format.Bold(channel.ToString())).ConfigureAwait(false); + } + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + public async Task XpExclusionList() + { + var serverExcluded = _service.IsServerExcluded(Context.Guild.Id); + var roles = _service.GetExcludedRoles(Context.Guild.Id) + .Select(x => Context.Guild.GetRole(x)?.Name) + .Where(x => x != null); + + var chans = (await Task.WhenAll(_service.GetExcludedChannels(Context.Guild.Id) + .Select(x => Context.Guild.GetChannelAsync(x))) + .ConfigureAwait(false)) + .Where(x => x != null) + .Select(x => x.Name); + + var embed = new EmbedBuilder() + .WithTitle(GetText("exclusion_list")) + .WithDescription((serverExcluded ? GetText("server_is_excluded") : GetText("server_is_not_excluded"))) + .AddField(GetText("excluded_roles"), roles.Any() ? string.Join("\n", roles) : "-", false) + .AddField(GetText("excluded_channels"), chans.Any() ? string.Join("\n", chans) : "-", false) + .WithOkColor(); + + await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); + } + } +} diff --git a/src/NadekoBot/NadekoBot.csproj b/src/NadekoBot/NadekoBot.csproj index 5e1db116..f18c711c 100644 --- a/src/NadekoBot/NadekoBot.csproj +++ b/src/NadekoBot/NadekoBot.csproj @@ -91,4 +91,10 @@ + + + + Designer + + diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index e547c0cb..8493d383 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3573,4 +3573,31 @@ Toggles whether the tag is blacklisted or not in nsfw searches. Provide no parameters to see the list of blacklisted tags. + + experience xp + + + `{0}xp` + + + Shows your xp stats. Specify the user to show that user's stats instead. + + + xpexclusionlist xpexl + + + `{0}xpexl` + + + Shows the roles and channels excluded from the XP system on this server, as well as whether the whole server is excluded. + + + xpexclude xpex + + + `{0}xpex User @b1nzy` `{0}xpex Server` + + + Exclude a user or a role from the xp system, or whole current server. + diff --git a/src/NadekoBot/Services/Database/IUnitOfWork.cs b/src/NadekoBot/Services/Database/IUnitOfWork.cs index 52cb01cf..fdd2eb0d 100644 --- a/src/NadekoBot/Services/Database/IUnitOfWork.cs +++ b/src/NadekoBot/Services/Database/IUnitOfWork.cs @@ -24,6 +24,7 @@ namespace NadekoBot.Services.Database IWaifuRepository Waifus { get; } IDiscordUserRepository DiscordUsers { get; } IWarningsRepository Warnings { get; } + IXpRepository Xp { get; } int Complete(); Task CompleteAsync(); diff --git a/src/NadekoBot/Services/Database/Models/BotConfig.cs b/src/NadekoBot/Services/Database/Models/BotConfig.cs index d765b813..98b84594 100644 --- a/src/NadekoBot/Services/Database/Models/BotConfig.cs +++ b/src/NadekoBot/Services/Database/Models/BotConfig.cs @@ -68,6 +68,8 @@ Nadeko Support Server: https://discord.gg/nadekobot"; public int PermissionVersion { get; set; } public string DefaultPrefix { get; set; } = "."; public bool CustomReactionsStartWith { get; set; } = false; + public int XpPerMessage { get; set; } = 3; + public int XpMinutesTimeout { get; set; } = 5; } public class BlockedCmdOrMdl : DbEntity diff --git a/src/NadekoBot/Services/Database/Models/GuildConfig.cs b/src/NadekoBot/Services/Database/Models/GuildConfig.cs index 7ae60508..82cc8bdf 100644 --- a/src/NadekoBot/Services/Database/Models/GuildConfig.cs +++ b/src/NadekoBot/Services/Database/Models/GuildConfig.cs @@ -86,6 +86,8 @@ namespace NadekoBot.Services.Database.Models public StreamRoleSettings StreamRole { get; set; } + public XpSettings XpSettings { get; set; } + //public List ProtectionIgnoredChannels { get; set; } = new List(); } diff --git a/src/NadekoBot/Services/Database/Models/UserXpStats.cs b/src/NadekoBot/Services/Database/Models/UserXpStats.cs new file mode 100644 index 00000000..9c515ec2 --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/UserXpStats.cs @@ -0,0 +1,10 @@ +namespace NadekoBot.Services.Database.Models +{ + public class UserXpStats : DbEntity + { + public ulong UserId { get; set; } + public ulong GuildId { get; set; } + public int Xp { get; set; } + public bool NotifyOnLevelUp { get; set; } + } +} diff --git a/src/NadekoBot/Services/Database/Models/XpSettings.cs b/src/NadekoBot/Services/Database/Models/XpSettings.cs new file mode 100644 index 00000000..cf89d611 --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/XpSettings.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; + +namespace NadekoBot.Services.Database.Models +{ + public class XpSettings : DbEntity + { + public int GuildConfigId { get; set; } + public GuildConfig GuildConfig { get; set; } + + public HashSet RoleRewards { get; set; } = new HashSet(); + public bool XpRoleRewardExclusive { get; set; } + public string NotifyMessage { get; set; } = "Congratulations {0}! You have reached level {1}!"; + public HashSet ExclusionList { get; set; } = new HashSet(); + public bool ServerExcluded { get; set; } + } + + public enum ExcludedItemType { Channel, Role } + + public class XpRoleReward : DbEntity + { + public int Level { get; set; } + public ulong RoleId { get; set; } + + public override int GetHashCode() + { + return Level.GetHashCode() ^ RoleId.GetHashCode(); + } + + public override bool Equals(object obj) + { + return obj is XpRoleReward xrr && xrr.Level == Level && xrr.RoleId == RoleId; + } + } + + public class ExcludedItem : DbEntity + { + public ulong ItemId { get; set; } + public ExcludedItemType ItemType { get; set; } + + public override int GetHashCode() + { + return ItemId.GetHashCode() ^ ItemType.GetHashCode(); + } + + public override bool Equals(object obj) + { + return obj is ExcludedItem ei && ei.ItemId == ItemId && ei.ItemType == ItemType; + } + } +} diff --git a/src/NadekoBot/Services/Database/NadekoContext.cs b/src/NadekoBot/Services/Database/NadekoContext.cs index 7c00e1f3..5ae153d0 100644 --- a/src/NadekoBot/Services/Database/NadekoContext.cs +++ b/src/NadekoBot/Services/Database/NadekoContext.cs @@ -43,6 +43,7 @@ namespace NadekoBot.Services.Database public DbSet PokeGame { get; set; } public DbSet WaifuUpdates { get; set; } public DbSet Warnings { get; set; } + public DbSet UserXpStats { get; set; } //logging public DbSet LogSettings { get; set; } @@ -292,6 +293,18 @@ namespace NadekoBot.Services.Database pr.HasIndex(x => x.UserId) .IsUnique(); #endregion + + #region XpStatas + modelBuilder.Entity() + .HasIndex(x => new { x.UserId, x.GuildId }) + .IsUnique(); + #endregion + + #region XpSettings + modelBuilder.Entity() + .HasOne(x => x.GuildConfig) + .WithOne(x => x.XpSettings); + #endregion } } } diff --git a/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs b/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs index e56a9026..498b72ed 100644 --- a/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/IGuildConfigRepository.cs @@ -16,5 +16,6 @@ namespace NadekoBot.Services.Database.Repositories void SetCleverbotEnabled(ulong id, bool cleverbotEnabled); IEnumerable Permissionsv2ForAll(List include); GuildConfig GcWithPermissionsv2For(ulong guildId); + XpSettings XpSettingsFor(ulong guildId); } } diff --git a/src/NadekoBot/Services/Database/Repositories/IXpRepository.cs b/src/NadekoBot/Services/Database/Repositories/IXpRepository.cs new file mode 100644 index 00000000..1973504a --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/IXpRepository.cs @@ -0,0 +1,9 @@ +using NadekoBot.Services.Database.Models; + +namespace NadekoBot.Services.Database.Repositories +{ + public interface IXpRepository : IRepository + { + UserXpStats GetOrCreateUser(ulong guildId, ulong userId); + } +} diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs index e6087bec..1c296db7 100644 --- a/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs @@ -189,5 +189,19 @@ namespace NadekoBot.Services.Database.Repositories.Impl conf.CleverbotEnabled = cleverbotEnabled; } + + public XpSettings XpSettingsFor(ulong guildId) + { + var gc = For(guildId, + set => set.Include(x => x.XpSettings) + .ThenInclude(x => x.RoleRewards) + .Include(x => x.XpSettings) + .ThenInclude(x => x.ExclusionList)); + + if (gc.XpSettings == null) + gc.XpSettings = new XpSettings(); + + return gc.XpSettings; + } } } diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/XpRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/XpRepository.cs new file mode 100644 index 00000000..6326587d --- /dev/null +++ b/src/NadekoBot/Services/Database/Repositories/Impl/XpRepository.cs @@ -0,0 +1,31 @@ +using NadekoBot.Services.Database.Models; +using System.Linq; +using Microsoft.EntityFrameworkCore; + +namespace NadekoBot.Services.Database.Repositories.Impl +{ + public class XpRepository : Repository, IXpRepository + { + public XpRepository(DbContext context) : base(context) + { + } + + public UserXpStats GetOrCreateUser(ulong guildId, ulong userId) + { + var usr = _set.FirstOrDefault(x => x.UserId == userId); + + if (usr == null) + { + _context.Add(usr = new UserXpStats() + { + Xp = 0, + UserId = userId, + NotifyOnLevelUp = false, + GuildId = guildId, + }); + } + + return usr; + } + } +} diff --git a/src/NadekoBot/Services/Database/UnitOfWork.cs b/src/NadekoBot/Services/Database/UnitOfWork.cs index 02e78a97..3b450f86 100644 --- a/src/NadekoBot/Services/Database/UnitOfWork.cs +++ b/src/NadekoBot/Services/Database/UnitOfWork.cs @@ -57,6 +57,9 @@ namespace NadekoBot.Services.Database private IWarningsRepository _warnings; public IWarningsRepository Warnings => _warnings ?? (_warnings = new WarningsRepository(_context)); + private IXpRepository _xp; + public IXpRepository Xp => _xp ?? (_xp = new XpRepository(_context)); + public UnitOfWork(NadekoContext context) { _context = context; diff --git a/src/NadekoBot/Services/Impl/BotConfigProvider.cs b/src/NadekoBot/Services/Impl/BotConfigProvider.cs index 0ed97550..2d293401 100644 --- a/src/NadekoBot/Services/Impl/BotConfigProvider.cs +++ b/src/NadekoBot/Services/Impl/BotConfigProvider.cs @@ -122,6 +122,18 @@ namespace NadekoBot.Services.Impl else return false; break; + case BotConfigEditType.XpPerMessage: + if (int.TryParse(newValue, out var xp) && xp > 0) + bc.XpPerMessage = xp; + else + return false; + break; + case BotConfigEditType.XpMinutesTimeout: + if (int.TryParse(newValue, out var min) && min > 0) + bc.XpMinutesTimeout = min; + else + return false; + break; default: return false; } diff --git a/src/NadekoBot/_strings/ResponseStrings.en-US.json b/src/NadekoBot/_strings/ResponseStrings.en-US.json index 2d123486..2ccc0b70 100644 --- a/src/NadekoBot/_strings/ResponseStrings.en-US.json +++ b/src/NadekoBot/_strings/ResponseStrings.en-US.json @@ -822,5 +822,16 @@ "administration_prefix_current": "Prefix on this server is {0}", "administration_prefix_new": "Changed prefix on this server from {0} to {1}", "administration_defprefix_current": "Default bot prefix is {0}", - "administration_defprefix_new": "Changed Default bot prefix from {0} to {1}" + "administration_defprefix_new": "Changed Default bot prefix from {0} to {1}", + "xp_server_level": "Server Level", + "xp_level": "Level", + "xp_club": "Club", + "xp_xp": "Experience", + "xp_excluded": "{0} has been excluded from the XP system on this server.", + "xp_not_excluded": "{0} is no longer excluded from the XP system on this server.", + "xp_exclusion_list": "Exclusion List", + "xp_server_is_excluded": "This server is excluded.", + "xp_server_is_not_excluded": "This server is not excluded.", + "xp_excluded_roles": "Excluded Roles", + "xp_excluded_channels": "Excluded Channels" } \ No newline at end of file