diff --git a/CHANGELOG.md b/CHANGELOG.md index 61a4ae665..6a8974dbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog. - Added `.deleteemptyservers` command - Added `.curtr ` which lets you see full information about one of your own transactions with the specified id - Added trovo.live support for stream notifications (`.stadd`) +- Added unclaimed waifu decay functionality + - Added 3 new settings to `data/gambling.yml` to control it: + - waifu.decay.percent - How much % to subtract from unclaimed waifu + - waifu.decay.hourInterval - How often to decay the price + - waifu.decay.minPrice - Unclaimed waifus with price lower than the one specified here will not be affected by the decay ### Fixed - Fixed an extra whitespace in usage part of command help if the command has no arguments @@ -18,6 +23,7 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog. - `.gvc` should now properly trigger when a user is already in a gvc and changes his activity - `.gvc` should now properly detect multiple activities - Fixed reference to non-existent command in bot.yml +- Comment indentation in .yml files should now make more sense ### Changed - CustomReactions module (and customreactions db table) has been renamed to Expressions. @@ -30,6 +36,7 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog. - Some of the old aliases like `.acr` `.dcr` `.lcr` and a few others have been kept - Currency output format improvement (will use guild locale now for some commands) - `.crypto` will now also show CoinMarketCap rank +- Waifus can now be claimed for much higher prices (int -> long) - Improved .curtrs (It will now have a lot more useful data in the database, show Tx ids, and be partially localized) - [dev] Reason renamed to Note - [dev] Added Type, Extra, OtherId fields to the database diff --git a/src/NadekoBot/Common/Yml/CommentsObjectGraphVisitor.cs b/src/NadekoBot/Common/Yml/CommentsObjectGraphVisitor.cs index 840780484..584a0520e 100644 --- a/src/NadekoBot/Common/Yml/CommentsObjectGraphVisitor.cs +++ b/src/NadekoBot/Common/Yml/CommentsObjectGraphVisitor.cs @@ -17,8 +17,15 @@ public class CommentsObjectGraphVisitor : ChainedObjectGraphVisitor { if (value is CommentsObjectDescriptor commentsDescriptor && !string.IsNullOrWhiteSpace(commentsDescriptor.Comment)) - context.Emit(new Comment(commentsDescriptor.Comment.Replace("\n", "\n# "), false)); - + { + var parts = commentsDescriptor.Comment.Split('\n'); + + foreach (var part in parts) + { + context.Emit(new Comment(part.Trim(), false)); + } + } + return base.EnterMapping(key, value, context); } } \ No newline at end of file diff --git a/src/NadekoBot/Db/Extensions/WaifuExtensions.cs b/src/NadekoBot/Db/Extensions/WaifuExtensions.cs index 8e606edcc..020f62e2e 100644 --- a/src/NadekoBot/Db/Extensions/WaifuExtensions.cs +++ b/src/NadekoBot/Db/Extensions/WaifuExtensions.cs @@ -9,7 +9,7 @@ namespace NadekoBot.Db; public class WaifuInfoStats { public string FullName { get; init; } - public int Price { get; init; } + public long Price { get; init; } public string ClaimerName { get; init; } public string AffinityName { get; init; } public int AffinityCount { get; init; } diff --git a/src/NadekoBot/Db/Models/Waifu.cs b/src/NadekoBot/Db/Models/Waifu.cs index 0a71d0e95..61a874b5b 100644 --- a/src/NadekoBot/Db/Models/Waifu.cs +++ b/src/NadekoBot/Db/Models/Waifu.cs @@ -14,7 +14,7 @@ public class WaifuInfo : DbEntity public int? AffinityId { get; set; } public DiscordUser Affinity { get; set; } - public int Price { get; set; } + public long Price { get; set; } public List Items { get; set; } = new(); public override string ToString() @@ -49,7 +49,7 @@ public class WaifuLbResult public string Affinity { get; set; } public string AffinityDiscrim { get; set; } - public int Price { get; set; } + public long Price { get; set; } public override string ToString() { diff --git a/src/NadekoBot/Migrations/NadekoContextModelSnapshot.cs b/src/NadekoBot/Migrations/NadekoContextModelSnapshot.cs index b0da85f7e..9f79efcfd 100644 --- a/src/NadekoBot/Migrations/NadekoContextModelSnapshot.cs +++ b/src/NadekoBot/Migrations/NadekoContextModelSnapshot.cs @@ -29,7 +29,7 @@ namespace NadekoBot.Migrations b.HasIndex("UserId"); - b.ToTable("ClubApplicants"); + b.ToTable("ClubApplicants", (string)null); }); modelBuilder.Entity("NadekoBot.Db.Models.ClubBans", b => @@ -44,7 +44,7 @@ namespace NadekoBot.Migrations b.HasIndex("UserId"); - b.ToTable("ClubBans"); + b.ToTable("ClubBans", (string)null); }); modelBuilder.Entity("NadekoBot.Db.Models.ClubInfo", b => @@ -86,7 +86,7 @@ namespace NadekoBot.Migrations b.HasIndex("OwnerId") .IsUnique(); - b.ToTable("Clubs"); + b.ToTable("Clubs", (string)null); }); modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b => @@ -155,7 +155,7 @@ namespace NadekoBot.Migrations b.HasIndex("UserId"); - b.ToTable("DiscordUser"); + b.ToTable("DiscordUser", (string)null); }); modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b => @@ -189,7 +189,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("FollowedStream"); + b.ToTable("FollowedStream", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b => @@ -218,7 +218,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId") .IsUnique(); - b.ToTable("AntiAltSetting"); + b.ToTable("AntiAltSetting", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => @@ -250,7 +250,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId") .IsUnique(); - b.ToTable("AntiRaidSetting"); + b.ToTable("AntiRaidSetting", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => @@ -272,7 +272,7 @@ namespace NadekoBot.Migrations b.HasIndex("AntiSpamSettingId"); - b.ToTable("AntiSpamIgnore"); + b.ToTable("AntiSpamIgnore", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => @@ -304,7 +304,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId") .IsUnique(); - b.ToTable("AntiSpamSetting"); + b.ToTable("AntiSpamSetting", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoCommand", b => @@ -342,7 +342,7 @@ namespace NadekoBot.Migrations b.HasKey("Id"); - b.ToTable("AutoCommands"); + b.ToTable("AutoCommands", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b => @@ -370,7 +370,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildId"); - b.ToTable("AutoTranslateChannels"); + b.ToTable("AutoTranslateChannels", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b => @@ -398,7 +398,7 @@ namespace NadekoBot.Migrations b.HasAlternateKey("ChannelId", "UserId"); - b.ToTable("AutoTranslateUsers"); + b.ToTable("AutoTranslateUsers", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b => @@ -421,7 +421,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildId") .IsUnique(); - b.ToTable("BanTemplates"); + b.ToTable("BanTemplates", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistEntry", b => @@ -441,7 +441,7 @@ namespace NadekoBot.Migrations b.HasKey("Id"); - b.ToTable("Blacklist"); + b.ToTable("Blacklist", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b => @@ -466,7 +466,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("CommandAlias"); + b.ToTable("CommandAlias", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => @@ -491,7 +491,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("CommandCooldown"); + b.ToTable("CommandCooldown", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b => @@ -529,7 +529,7 @@ namespace NadekoBot.Migrations b.HasIndex("UserId"); - b.ToTable("CurrencyTransactions"); + b.ToTable("CurrencyTransactions", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.DelMsgOnCmdChannel", b => @@ -554,7 +554,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("DelMsgOnCmdChannel"); + b.ToTable("DelMsgOnCmdChannel", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordPermOverride", b => @@ -580,7 +580,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildId", "Command") .IsUnique(); - b.ToTable("DiscordPermOverrides"); + b.ToTable("DiscordPermOverrides", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b => @@ -605,7 +605,7 @@ namespace NadekoBot.Migrations b.HasIndex("XpSettingsId"); - b.ToTable("ExcludedItem"); + b.ToTable("ExcludedItem", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.FeedSub", b => @@ -631,7 +631,7 @@ namespace NadekoBot.Migrations b.HasAlternateKey("GuildConfigId", "Url"); - b.ToTable("FeedSub"); + b.ToTable("FeedSub", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => @@ -653,7 +653,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("FilterChannelId"); + b.ToTable("FilterChannelId", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => @@ -675,7 +675,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("FilteredWord"); + b.ToTable("FilteredWord", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterLinksChannelId", b => @@ -697,7 +697,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("FilterLinksChannelId"); + b.ToTable("FilterLinksChannelId", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterWordsChannelId", b => @@ -719,7 +719,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("FilterWordsChannelId"); + b.ToTable("FilterWordsChannelId", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => @@ -741,7 +741,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("GCChannelId"); + b.ToTable("GCChannelId", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.GroupName", b => @@ -767,7 +767,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId", "Number") .IsUnique(); - b.ToTable("GroupName"); + b.ToTable("GroupName", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => @@ -897,7 +897,7 @@ namespace NadekoBot.Migrations b.HasIndex("WarnExpireHours"); - b.ToTable("GuildConfigs"); + b.ToTable("GuildConfigs", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogItem", b => @@ -923,7 +923,7 @@ namespace NadekoBot.Migrations b.HasIndex("LogSettingId", "LogItemId", "ItemType") .IsUnique(); - b.ToTable("IgnoredLogChannels"); + b.ToTable("IgnoredLogChannels", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => @@ -945,7 +945,7 @@ namespace NadekoBot.Migrations b.HasIndex("LogSettingId"); - b.ToTable("IgnoredVoicePresenceCHannels"); + b.ToTable("IgnoredVoicePresenceCHannels", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.ImageOnlyChannel", b => @@ -968,7 +968,7 @@ namespace NadekoBot.Migrations b.HasIndex("ChannelId") .IsUnique(); - b.ToTable("ImageOnlyChannels"); + b.ToTable("ImageOnlyChannels", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b => @@ -1033,7 +1033,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildId") .IsUnique(); - b.ToTable("LogSettings"); + b.ToTable("LogSettings", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlayerSettings", b => @@ -1067,7 +1067,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildId") .IsUnique(); - b.ToTable("MusicPlayerSettings"); + b.ToTable("MusicPlayerSettings", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b => @@ -1090,7 +1090,7 @@ namespace NadekoBot.Migrations b.HasKey("Id"); - b.ToTable("MusicPlaylists"); + b.ToTable("MusicPlaylists", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => @@ -1112,7 +1112,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("MutedUserId"); + b.ToTable("MutedUserId", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.NadekoExpression", b => @@ -1150,7 +1150,7 @@ namespace NadekoBot.Migrations b.HasKey("Id"); - b.ToTable("Expressions"); + b.ToTable("Expressions", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklistedTag", b => @@ -1172,7 +1172,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildId"); - b.ToTable("NsfwBlacklistedTags"); + b.ToTable("NsfwBlacklistedTags", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b => @@ -1212,7 +1212,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("Permissions"); + b.ToTable("Permissions", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.PlantedCurrency", b => @@ -1249,7 +1249,7 @@ namespace NadekoBot.Migrations b.HasIndex("MessageId") .IsUnique(); - b.ToTable("PlantedCurrency"); + b.ToTable("PlantedCurrency", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => @@ -1283,7 +1283,7 @@ namespace NadekoBot.Migrations b.HasIndex("MusicPlaylistId"); - b.ToTable("PlaylistSong"); + b.ToTable("PlaylistSong", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.Poll", b => @@ -1309,7 +1309,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildId") .IsUnique(); - b.ToTable("Poll"); + b.ToTable("Poll", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.PollAnswer", b => @@ -1334,7 +1334,7 @@ namespace NadekoBot.Migrations b.HasIndex("PollId"); - b.ToTable("PollAnswer"); + b.ToTable("PollAnswer", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.PollVote", b => @@ -1359,7 +1359,7 @@ namespace NadekoBot.Migrations b.HasIndex("PollId"); - b.ToTable("PollVote"); + b.ToTable("PollVote", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b => @@ -1395,7 +1395,7 @@ namespace NadekoBot.Migrations b.HasIndex("Keyword"); - b.ToTable("Quotes"); + b.ToTable("Quotes", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRole", b => @@ -1420,7 +1420,7 @@ namespace NadekoBot.Migrations b.HasIndex("ReactionRoleMessageId"); - b.ToTable("ReactionRole"); + b.ToTable("ReactionRole", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRoleMessage", b => @@ -1451,7 +1451,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("ReactionRoleMessage"); + b.ToTable("ReactionRoleMessage", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b => @@ -1485,7 +1485,7 @@ namespace NadekoBot.Migrations b.HasIndex("When"); - b.ToTable("Reminders"); + b.ToTable("Reminders", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b => @@ -1520,7 +1520,7 @@ namespace NadekoBot.Migrations b.HasKey("Id"); - b.ToTable("Repeaters"); + b.ToTable("Repeaters", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.RewardedUser", b => @@ -1549,7 +1549,7 @@ namespace NadekoBot.Migrations b.HasIndex("PatreonUserId") .IsUnique(); - b.ToTable("RewardedUsers"); + b.ToTable("RewardedUsers", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.RotatingPlayingStatus", b => @@ -1569,7 +1569,7 @@ namespace NadekoBot.Migrations b.HasKey("Id"); - b.ToTable("RotatingStatus"); + b.ToTable("RotatingStatus", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => @@ -1600,7 +1600,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildId", "RoleId") .IsUnique(); - b.ToTable("SelfAssignableRoles"); + b.ToTable("SelfAssignableRoles", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntry", b => @@ -1640,7 +1640,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("ShopEntry"); + b.ToTable("ShopEntry", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntryItem", b => @@ -1662,7 +1662,7 @@ namespace NadekoBot.Migrations b.HasIndex("ShopEntryId"); - b.ToTable("ShopEntryItem"); + b.ToTable("ShopEntryItem", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredRole", b => @@ -1684,7 +1684,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("SlowmodeIgnoredRole"); + b.ToTable("SlowmodeIgnoredRole", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredUser", b => @@ -1706,7 +1706,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("SlowmodeIgnoredUser"); + b.ToTable("SlowmodeIgnoredUser", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b => @@ -1731,7 +1731,7 @@ namespace NadekoBot.Migrations b.HasIndex("StreamRoleSettingsId"); - b.ToTable("StreamRoleBlacklistedUser"); + b.ToTable("StreamRoleBlacklistedUser", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b => @@ -1763,7 +1763,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId") .IsUnique(); - b.ToTable("StreamRoleSettings"); + b.ToTable("StreamRoleSettings", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b => @@ -1788,7 +1788,7 @@ namespace NadekoBot.Migrations b.HasIndex("StreamRoleSettingsId"); - b.ToTable("StreamRoleWhitelistedUser"); + b.ToTable("StreamRoleWhitelistedUser", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.UnbanTimer", b => @@ -1813,7 +1813,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("UnbanTimer"); + b.ToTable("UnbanTimer", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b => @@ -1838,7 +1838,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("UnmuteTimer"); + b.ToTable("UnmuteTimer", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.UnroleTimer", b => @@ -1866,7 +1866,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("UnroleTimer"); + b.ToTable("UnroleTimer", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b => @@ -1911,7 +1911,7 @@ namespace NadekoBot.Migrations b.HasIndex("UserId", "GuildId") .IsUnique(); - b.ToTable("UserXpStats"); + b.ToTable("UserXpStats", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b => @@ -1936,7 +1936,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("VcRoleInfo"); + b.ToTable("VcRoleInfo", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b => @@ -1971,7 +1971,7 @@ namespace NadekoBot.Migrations b.HasIndex("WaifuId") .IsUnique(); - b.ToTable("WaifuInfo"); + b.ToTable("WaifuInfo", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b => @@ -1996,7 +1996,7 @@ namespace NadekoBot.Migrations b.HasIndex("WaifuInfoId"); - b.ToTable("WaifuItem"); + b.ToTable("WaifuItem", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b => @@ -2028,7 +2028,7 @@ namespace NadekoBot.Migrations b.HasIndex("UserId"); - b.ToTable("WaifuUpdates"); + b.ToTable("WaifuUpdates", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.Warning", b => @@ -2071,7 +2071,7 @@ namespace NadekoBot.Migrations b.HasIndex("UserId"); - b.ToTable("Warnings"); + b.ToTable("Warnings", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.WarningPunishment", b => @@ -2102,7 +2102,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId"); - b.ToTable("WarningPunishment"); + b.ToTable("WarningPunishment", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.XpCurrencyReward", b => @@ -2127,7 +2127,7 @@ namespace NadekoBot.Migrations b.HasIndex("XpSettingsId"); - b.ToTable("XpCurrencyReward"); + b.ToTable("XpCurrencyReward", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b => @@ -2156,7 +2156,7 @@ namespace NadekoBot.Migrations b.HasIndex("XpSettingsId", "Level") .IsUnique(); - b.ToTable("XpRoleReward"); + b.ToTable("XpRoleReward", (string)null); }); modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b => @@ -2179,7 +2179,7 @@ namespace NadekoBot.Migrations b.HasIndex("GuildConfigId") .IsUnique(); - b.ToTable("XpSettings"); + b.ToTable("XpSettings", (string)null); }); modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b => diff --git a/src/NadekoBot/Modules/Administration/GameVoiceChannel/GameVoiceChannelService.cs b/src/NadekoBot/Modules/Administration/GameVoiceChannel/GameVoiceChannelService.cs index 87ac222a8..07747fa7b 100644 --- a/src/NadekoBot/Modules/Administration/GameVoiceChannel/GameVoiceChannelService.cs +++ b/src/NadekoBot/Modules/Administration/GameVoiceChannel/GameVoiceChannelService.cs @@ -3,7 +3,6 @@ using NadekoBot.Db; namespace NadekoBot.Modules.Administration.Services; -// todo timers public class GameVoiceChannelService : INService { public ConcurrentHashSet GameVoiceChannels { get; } diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index d34b6294b..d9d956947 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -207,7 +207,6 @@ public partial class Gambling : GamblingModule => InternalCurrencyTransactions(usr.Id, page); // todo curtrs max lifetime - // todo waifu decay private async Task InternalCurrencyTransactions(ulong userId, int page) { if (--page < 0) diff --git a/src/NadekoBot/Modules/Gambling/GamblingConfig.cs b/src/NadekoBot/Modules/Gambling/GamblingConfig.cs index 91ad79a5a..bcef8d65c 100644 --- a/src/NadekoBot/Modules/Gambling/GamblingConfig.cs +++ b/src/NadekoBot/Modules/Gambling/GamblingConfig.cs @@ -182,10 +182,14 @@ public partial class WheelOfFortuneSettings public sealed partial class WaifuConfig { [Comment(@"Minimum price a waifu can have")] - public int MinPrice { get; set; } = 50; + public long MinPrice { get; set; } = 50; public MultipliersData Multipliers { get; set; } = new(); + [Comment(@"Settings for periodic waifu price decay. +Waifu price decays only if the waifu has no claimer.")] + public WaifuDecayConfig Decay { get; set; } = new(); + [Comment(@"List of items available for gifting. If negative is true, gift will instead reduce waifu value.")] public List Items { get; set; } = new(); @@ -230,6 +234,21 @@ If negative is true, gift will instead reduce waifu value.")] new("๐Ÿš€", 30000, "Spaceship"), new("๐ŸŒ•", 50000, "Moon") }; + + public class WaifuDecayConfig + { + [Comment(@"Percentage (0 - 100) of the waifu value to reduce. +Set 0 to disable +For example if a waifu has a price of 500$, setting this value to 10 would reduce the waifu value by 10% (50$)")] + public int Percent { get; set; } = 0; + + [Comment(@"How often to decay waifu values, in hours")] + public int HourInterval { get; set; } = 24; + + [Comment(@"Minimum waifu price required for the decay to be applied. +For example if this value is set to 300, any waifu with the price 300 or less will not experience decay.")] + public long MinPrice { get; set; } = 300; + } } [Cloneable] @@ -283,7 +302,7 @@ public sealed class SlotsConfig public sealed partial class WaifuItemModel { public string ItemEmoji { get; set; } - public int Price { get; set; } + public long Price { get; set; } public string Name { get; set; } [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] @@ -295,7 +314,7 @@ public sealed partial class WaifuItemModel public WaifuItemModel( string itemEmoji, - int price, + long price, string name, bool negative = false) { diff --git a/src/NadekoBot/Modules/Gambling/GamblingConfigService.cs b/src/NadekoBot/Modules/Gambling/GamblingConfigService.cs index c7d403ce6..ba38d7c33 100644 --- a/src/NadekoBot/Modules/Gambling/GamblingConfigService.cs +++ b/src/NadekoBot/Modules/Gambling/GamblingConfigService.cs @@ -14,84 +14,131 @@ public sealed class GamblingConfigService : ConfigServiceBase private readonly IEnumerable _antiGiftSeed = new[] { - new WaifuItemModel("๐Ÿฅ€", 100, "WiltedRose", true), new WaifuItemModel("โœ‚๏ธ", 1000, "Haircut", true), + new WaifuItemModel("๐Ÿฅ€", 100, "WiltedRose", true), + new WaifuItemModel("โœ‚๏ธ", 1000, "Haircut", true), new WaifuItemModel("๐Ÿงป", 10000, "ToiletPaper", true) }; - - + public GamblingConfigService(IConfigSeria serializer, IPubSub pubSub) : base(FILE_PATH, serializer, pubSub, _changeKey) { - AddParsedProp("currency.name", gs => gs.Currency.Name, ConfigParsers.String, ConfigPrinters.ToString); - AddParsedProp("currency.sign", gs => gs.Currency.Sign, ConfigParsers.String, ConfigPrinters.ToString); + AddParsedProp("currency.name", + gs => gs.Currency.Name, + ConfigParsers.String, + ConfigPrinters.ToString); + + AddParsedProp("currency.sign", + gs => gs.Currency.Sign, + ConfigParsers.String, + ConfigPrinters.ToString); - AddParsedProp("minbet", gs => gs.MinBet, int.TryParse, ConfigPrinters.ToString, val => val >= 0); - AddParsedProp("maxbet", gs => gs.MaxBet, int.TryParse, ConfigPrinters.ToString, val => val >= 0); + AddParsedProp("minbet", + gs => gs.MinBet, + int.TryParse, + ConfigPrinters.ToString, + val => val >= 0); + + AddParsedProp("maxbet", + gs => gs.MaxBet, + int.TryParse, + ConfigPrinters.ToString, + val => val >= 0); - AddParsedProp("gen.min", gs => gs.Generation.MinAmount, int.TryParse, ConfigPrinters.ToString, val => val >= 1); - AddParsedProp("gen.max", gs => gs.Generation.MaxAmount, int.TryParse, ConfigPrinters.ToString, val => val >= 1); - AddParsedProp("gen.cd", gs => gs.Generation.GenCooldown, int.TryParse, ConfigPrinters.ToString, val => val > 0); + AddParsedProp("gen.min", + gs => gs.Generation.MinAmount, + int.TryParse, + ConfigPrinters.ToString, + val => val >= 1); + + AddParsedProp("gen.max", + gs => gs.Generation.MaxAmount, + int.TryParse, + ConfigPrinters.ToString, + val => val >= 1); + + AddParsedProp("gen.cd", + gs => gs.Generation.GenCooldown, + int.TryParse, + ConfigPrinters.ToString, + val => val > 0); + AddParsedProp("gen.chance", gs => gs.Generation.Chance, decimal.TryParse, ConfigPrinters.ToString, val => val is >= 0 and <= 1); - AddParsedProp("gen.has_pw", gs => gs.Generation.HasPassword, bool.TryParse, ConfigPrinters.ToString); + + AddParsedProp("gen.has_pw", + gs => gs.Generation.HasPassword, + bool.TryParse, + ConfigPrinters.ToString); + AddParsedProp("bf.multi", gs => gs.BetFlip.Multiplier, decimal.TryParse, ConfigPrinters.ToString, val => val >= 1); + AddParsedProp("waifu.min_price", gs => gs.Waifu.MinPrice, - int.TryParse, + long.TryParse, ConfigPrinters.ToString, val => val >= 0); + AddParsedProp("waifu.multi.reset", gs => gs.Waifu.Multipliers.WaifuReset, int.TryParse, ConfigPrinters.ToString, val => val >= 0); + AddParsedProp("waifu.multi.crush_claim", gs => gs.Waifu.Multipliers.CrushClaim, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0); + AddParsedProp("waifu.multi.normal_claim", gs => gs.Waifu.Multipliers.NormalClaim, decimal.TryParse, ConfigPrinters.ToString, val => val > 0); + AddParsedProp("waifu.multi.divorce_value", gs => gs.Waifu.Multipliers.DivorceNewValue, decimal.TryParse, ConfigPrinters.ToString, val => val > 0); + AddParsedProp("waifu.multi.all_gifts", gs => gs.Waifu.Multipliers.AllGiftPrices, decimal.TryParse, ConfigPrinters.ToString, val => val > 0); + AddParsedProp("waifu.multi.gift_effect", gs => gs.Waifu.Multipliers.GiftEffect, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0); + AddParsedProp("waifu.multi.negative_gift_effect", gs => gs.Waifu.Multipliers.NegativeGiftEffect, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0); + AddParsedProp("decay.percent", gs => gs.Decay.Percent, decimal.TryParse, ConfigPrinters.ToString, val => val is >= 0 and <= 1); + AddParsedProp("decay.maxdecay", gs => gs.Decay.MaxDecay, int.TryParse, ConfigPrinters.ToString, val => val >= 0); + AddParsedProp("decay.threshold", gs => gs.Decay.MinThreshold, int.TryParse, @@ -116,11 +163,11 @@ public sealed class GamblingConfigService : ConfigServiceBase c.Version = 3; c.VoteReward = 100; }); - - if (data.Version < 4) + + if(data.Version < 5) ModifyConfig(c => { - c.Version = 4; + c.Version = 5; }); } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Gambling/Waifus/WaifuClaimCommands.cs b/src/NadekoBot/Modules/Gambling/Waifus/WaifuClaimCommands.cs index 7bce5008a..1c7b76402 100644 --- a/src/NadekoBot/Modules/Gambling/Waifus/WaifuClaimCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Waifus/WaifuClaimCommands.cs @@ -37,7 +37,7 @@ public partial class Gambling [Cmd] [RequireContext(ContextType.Guild)] - public async partial Task WaifuClaim(int amount, [Leftover] IUser target) + public async partial Task WaifuClaim(long amount, [Leftover] IUser target) { if (amount < Config.Waifu.MinPrice) { diff --git a/src/NadekoBot/Modules/Gambling/Waifus/WaifuService.cs b/src/NadekoBot/Modules/Gambling/Waifus/WaifuService.cs index 4b5a30a5b..c0a49a08a 100644 --- a/src/NadekoBot/Modules/Gambling/Waifus/WaifuService.cs +++ b/src/NadekoBot/Modules/Gambling/Waifus/WaifuService.cs @@ -1,5 +1,7 @@ #nullable disable +using LinqToDB; using Microsoft.EntityFrameworkCore; +using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Db; using NadekoBot.Db.Models; using NadekoBot.Modules.Gambling.Common; @@ -8,23 +10,30 @@ using NadekoBot.Services.Database.Models; namespace NadekoBot.Modules.Gambling.Services; -public class WaifuService : INService +// todo waifu price int +public class WaifuService : INService, IReadyExecutor { private readonly DbService _db; private readonly ICurrencyService _cs; private readonly IDataCache _cache; private readonly GamblingConfigService _gss; + private readonly IBotCredentials _creds; + private readonly DiscordSocketClient _client; public WaifuService( DbService db, ICurrencyService cs, IDataCache cache, - GamblingConfigService gss) + GamblingConfigService gss, + IBotCredentials creds, + DiscordSocketClient client) { _db = db; _cs = cs; _cache = cache; _gss = gss; + _creds = creds; + _client = client; } public async Task WaifuTransfer(IUser owner, ulong waifuId, IUser newOwner) @@ -45,11 +54,11 @@ public class WaifuService : INService // if waifu likes the person, gotta pay the penalty if (waifu.AffinityId == ownerUser.Id) { - if (!await _cs.RemoveAsync(owner.Id, (int)(waifu.Price * 0.6), new("waifu", "affinity-penalty"))) + if (!await _cs.RemoveAsync(owner.Id, (long)(waifu.Price * 0.6), new("waifu", "affinity-penalty"))) // unable to pay 60% penalty return false; - waifu.Price = (int)(waifu.Price * 0.7); // half of 60% = 30% price reduction + waifu.Price = (long)(waifu.Price * 0.7); // half of 60% = 30% price reduction if (waifu.Price < settings.Waifu.MinPrice) waifu.Price = settings.Waifu.MinPrice; } @@ -58,7 +67,7 @@ public class WaifuService : INService if (!await _cs.RemoveAsync(owner.Id, waifu.Price / 10, new("waifu", "transfer"))) return false; - waifu.Price = (int)(waifu.Price * 0.95); // half of 10% = 5% price reduction + waifu.Price = (long)(waifu.Price * 0.95); // half of 10% = 5% price reduction if (waifu.Price < settings.Waifu.MinPrice) waifu.Price = settings.Waifu.MinPrice; } @@ -72,7 +81,7 @@ public class WaifuService : INService return true; } - public int GetResetPrice(IUser user) + public long GetResetPrice(IUser user) { var settings = _gss.Data; using var uow = _db.GetDbContext(); @@ -91,7 +100,7 @@ public class WaifuService : INService .GroupBy(x => x.New) .Count(); - return (int)Math.Ceiling(waifu.Price * 1.25f) + ((divorces + affs + 2) * settings.Waifu.Multipliers.WaifuReset); + return (long)Math.Ceiling(waifu.Price * 1.25f) + ((divorces + affs + 2) * settings.Waifu.Multipliers.WaifuReset); } public async Task TryReset(IUser user) @@ -131,7 +140,7 @@ public class WaifuService : INService return true; } - public async Task<(WaifuInfo, bool, WaifuClaimResult)> ClaimWaifuAsync(IUser user, IUser target, int amount) + public async Task<(WaifuInfo, bool, WaifuClaimResult)> ClaimWaifuAsync(IUser user, IUser target, long amount) { var settings = _gss.Data; WaifuClaimResult result; @@ -317,7 +326,7 @@ public class WaifuService : INService if (w.Affinity?.UserId == user.Id) { await _cs.AddAsync(w.Waifu.UserId, amount, new("waifu", "compensation")); - w.Price = (int)Math.Floor(w.Price * _gss.Data.Waifu.Multipliers.DivorceNewValue); + w.Price = (long)Math.Floor(w.Price * _gss.Data.Waifu.Multipliers.DivorceNewValue); result = DivorceResult.SucessWithPenalty; } else @@ -370,13 +379,13 @@ public class WaifuService : INService }); if (w.Claimer?.UserId == from.Id) - w.Price += (int)(itemObj.Price * _gss.Data.Waifu.Multipliers.GiftEffect); + w.Price += (long)(itemObj.Price * _gss.Data.Waifu.Multipliers.GiftEffect); else w.Price += itemObj.Price / 2; } else { - w.Price -= (int)(itemObj.Price * _gss.Data.Waifu.Multipliers.NegativeGiftEffect); + w.Price -= (long)(itemObj.Price * _gss.Data.Waifu.Multipliers.NegativeGiftEffect); if (w.Price < 1) w.Price = 1; } @@ -479,16 +488,65 @@ public class WaifuService : INService var conf = _gss.Data; return conf.Waifu.Items.Select(x => new WaifuItemModel(x.ItemEmoji, - (int)(x.Price * conf.Waifu.Multipliers.AllGiftPrices), + (long)(x.Price * conf.Waifu.Multipliers.AllGiftPrices), x.Name, x.Negative)) .ToList(); } - public class FullWaifuInfo + public async Task OnReadyAsync() { - public WaifuInfo Waifu { get; set; } - public IEnumerable Claims { get; set; } - public int Divorces { get; set; } + // only decay waifu values from shard 0 + if (_client.ShardId != 0) + return; + + var redisKey = $"{_creds.RedisKey()}_last_waifu_decay"; + while (true) + { + try + { + var multi = _gss.Data.Waifu.Decay.Percent / 100f; + var minPrice = _gss.Data.Waifu.Decay.MinPrice; + var decayInterval = _gss.Data.Waifu.Decay.HourInterval; + + if (multi is < 0f or > 1f || decayInterval < 0) + { + continue; + } + + var val = await _cache.Redis.GetDatabase().StringGetAsync(redisKey); + if (val != default) + { + var lastDecay = DateTime.FromBinary((long)val); + var toWait = decayInterval.Hours() - (DateTime.UtcNow - lastDecay); + + if (toWait > 0.Hours()) + { + continue; + } + } + + await _cache.Redis.GetDatabase().StringSetAsync(redisKey, DateTime.UtcNow.ToBinary()); + + await using var uow = _db.GetDbContext(); + + await uow.WaifuInfo + .Where(x => x.Price > minPrice && x.ClaimerId == null) + .UpdateAsync(old => new() + { + Price = (long)(old.Price * multi) + }); + + await uow.SaveChangesAsync(); + } + catch (Exception ex) + { + Log.Error(ex, "Unexpected error occured in waifu decay loop: {ErrorMessage}", ex.Message); + } + finally + { + await Task.Delay(1.Hours()); + } + } } } \ No newline at end of file diff --git a/src/NadekoBot/data/gambling.yml b/src/NadekoBot/data/gambling.yml index c3303485b..2e9aa2e39 100644 --- a/src/NadekoBot/data/gambling.yml +++ b/src/NadekoBot/data/gambling.yml @@ -1,5 +1,5 @@ # DO NOT CHANGE -version: 4 +version: 5 # Currency settings currency: # What is the emoji/character which represents the currency @@ -34,28 +34,28 @@ generation: # in order to get it hasPassword: true # Every message sent has a certain % chance to generate the currency -# specify the percentage here (1 being 100%, 0 being 0% - for example -# default is 0.02, which is 2% + # specify the percentage here (1 being 100%, 0 being 0% - for example + # default is 0.02, which is 2% chance: 0.02 # How many seconds have to pass for the next message to have a chance to spawn currency genCooldown: 10 # Minimum amount of currency that can spawn minAmount: 1 # Maximum amount of currency that can spawn. -# Set to the same value as MinAmount to always spawn the same amount + # Set to the same value as MinAmount to always spawn the same amount maxAmount: 1 -# Settings for timely command +# Settings for timely command # (letting people claim X amount of currency every Y hours) timely: # How much currency will the users get every time they run .timely command # setting to 0 or less will disable this feature amount: 0 # How often (in hours) can users claim currency with .timely command -# setting to 0 or less will disable this feature + # setting to 0 or less will disable this feature cooldown: 24 # How much will each user's owned currency decay over time. decay: -# Percentage of user's current currency which will be deducted every 24h. +# Percentage of user's current currency which will be deducted every 24h. # 0 - 1 (1 is 100%, 0.5 50%, 0 disabled) percent: 0 # Maximum amount of user's currency that can decay at each interval. 0 for unlimited. @@ -82,37 +82,49 @@ waifu: minPrice: 50 multipliers: # Multiplier for waifureset. Default 150. -# Formula (at the time of writing this): -# price = (waifu_price * 1.25f) + ((number_of_divorces + changes_of_heart + 2) * WaifuReset) rounded up + # Formula (at the time of writing this): + # price = (waifu_price * 1.25f) + ((number_of_divorces + changes_of_heart + 2) * WaifuReset) rounded up waifuReset: 150 - # The minimum amount of currency that you have to pay -# in order to buy a waifu who doesn't have a crush on you. -# Default is 1.1 -# Example: If a waifu is worth 100, you will have to pay at least 100 * NormalClaim currency to claim her. -# (100 * 1.1 = 110) + # The minimum amount of currency that you have to pay + # in order to buy a waifu who doesn't have a crush on you. + # Default is 1.1 + # Example: If a waifu is worth 100, you will have to pay at least 100 * NormalClaim currency to claim her. + # (100 * 1.1 = 110) normalClaim: 1.1 - # The minimum amount of currency that you have to pay -# in order to buy a waifu that has a crush on you. -# Default is 0.88 -# Example: If a waifu is worth 100, you will have to pay at least 100 * CrushClaim currency to claim her. -# (100 * 0.88 = 88) + # The minimum amount of currency that you have to pay + # in order to buy a waifu that has a crush on you. + # Default is 0.88 + # Example: If a waifu is worth 100, you will have to pay at least 100 * CrushClaim currency to claim her. + # (100 * 0.88 = 88) crushClaim: 0.88 # When divorcing a waifu, her new value will be her current value multiplied by this number. -# Default 0.75 (meaning will lose 25% of her value) + # Default 0.75 (meaning will lose 25% of her value) divorceNewValue: 0.75 # All gift prices will be multiplied by this number. -# Default 1 (meaning no effect) + # Default 1 (meaning no effect) allGiftPrices: 1.0 # What percentage of the value of the gift will a waifu gain when she's gifted. -# Default 0.95 (meaning 95%) -# Example: If a waifu is worth 1000, and she receives a gift worth 100, her new value will be 1095) + # Default 0.95 (meaning 95%) + # Example: If a waifu is worth 1000, and she receives a gift worth 100, her new value will be 1095) giftEffect: 0.95 # What percentage of the value of the gift will a waifu lose when she's gifted a gift marked as 'negative'. -# Default 0.5 (meaning 50%) -# Example: If a waifu is worth 1000, and she receives a negative gift worth 100, her new value will be 950) + # Default 0.5 (meaning 50%) + # Example: If a waifu is worth 1000, and she receives a negative gift worth 100, her new value will be 950) negativeGiftEffect: 0.50 + # Settings for periodic waifu price decay. + # Waifu price decays only if the waifu has no claimer. + decay: + # Percentage (0 - 100) of the waifu value to reduce. + # Set 0 to disable + # For example if a waifu has a price of 500$, setting this value to 10 would reduce the waifu value by 10% (50$) + percent: 0 + # How often to decay waifu values, in hours + hourInterval: 24 + # Minimum waifu price required for the decay to be applied. + # For example if this value is set to 300, any waifu with the price 300 or less will not experience decay. + minPrice: 300 # List of items available for gifting. -# If negative is true, gift will instead reduce waifu value. + # If negative is true, gift will instead reduce waifu value. items: - itemEmoji: "๐Ÿฅ”" price: 5