From b8df0d8b063edf8b8a43d2d26a6d095eef1a226d Mon Sep 17 00:00:00 2001 From: Toastie Date: Sat, 6 Apr 2024 23:24:57 +1300 Subject: [PATCH] Finally finished the Db potion of the EllieBot code --- src/EllieBot/Db/EllieContext.cs | 491 ++++++++++++++++++ src/EllieBot/Db/Extensions/ClubExtensions.cs | 34 ++ .../CurrencyTransactionExtensions.cs | 20 + src/EllieBot/Db/Extensions/DbExtensions.cs | 12 + .../Db/Extensions/DiscordUserExtensions.cs | 179 +++++++ .../Extensions/EllieExpressionExtensions.cs | 15 + .../Db/Extensions/GuildConfigExtensions.cs | 227 ++++++++ .../MusicPlayerSettingsExtensions.cs | 27 + .../Db/Extensions/MusicPlaylistExtensions.cs | 19 + src/EllieBot/Db/Extensions/PollExtensions.cs | 28 + src/EllieBot/Db/Extensions/QuoteExtensions.cs | 57 ++ .../Db/Extensions/ReminderExtensions.cs | 23 + .../SelfAssignableRolesExtensions.cs | 22 + .../Db/Extensions/UserXpExtensions.cs | 63 +++ src/EllieBot/Db/Extensions/WaifuExtensions.cs | 145 ++++++ .../Db/Extensions/WarningExtensions.cs | 60 +++ src/EllieBot/Db/Models/AntiProtection.cs | 65 +++ src/EllieBot/Db/Models/AutoCommand.cs | 14 + src/EllieBot/Db/Models/AutoPublishChannel.cs | 9 + .../Db/Models/AutoTranslateChannel.cs | 10 + src/EllieBot/Db/Models/AutoTranslateUser.cs | 11 + src/EllieBot/Db/Models/BanTemplate.cs | 9 + src/EllieBot/Db/Models/BankUser.cs | 9 + src/EllieBot/Db/Models/BlacklistEntry.cs | 14 + src/EllieBot/Db/Models/ClubInfo.cs | 42 ++ src/EllieBot/Db/Models/CommandAlias.cs | 8 + src/EllieBot/Db/Models/CommandCooldown.cs | 8 + src/EllieBot/Db/Models/CurrencyTransaction.cs | 12 + src/EllieBot/Db/Models/DbEntity.cs | 12 + src/EllieBot/Db/Models/DelMsgOnCmdChannel.cs | 14 + src/EllieBot/Db/Models/DiscordPermOverride.cs | 10 + src/EllieBot/Db/Models/DiscordUser.cs | 31 ++ src/EllieBot/Db/Models/EllieExpression.cs | 26 + src/EllieBot/Db/Models/FeedSub.cs | 19 + src/EllieBot/Db/Models/FilterChannelId.cs | 30 ++ .../Db/Models/FilterLinksChannelId.cs | 13 + src/EllieBot/Db/Models/FilteredWord.cs | 7 + src/EllieBot/Db/Models/FollowedStream.cs | 37 ++ src/EllieBot/Db/Models/GCChannelId.cs | 14 + src/EllieBot/Db/Models/GamblingStats.cs | 9 + src/EllieBot/Db/Models/GroupName.cs | 11 + src/EllieBot/Db/Models/GuildConfig.cs | 108 ++++ src/EllieBot/Db/Models/IgnoredLogItem.cs | 16 + .../Db/Models/IgnoredVoicePresenceChannel.cs | 8 + src/EllieBot/Db/Models/ImageOnlyChannel.cs | 15 + src/EllieBot/Db/Models/LogSetting.cs | 37 ++ src/EllieBot/Db/Models/MusicPlaylist.cs | 10 + src/EllieBot/Db/Models/MusicSettings.cs | 61 +++ src/EllieBot/Db/Models/MutedUserId.cs | 13 + src/EllieBot/Db/Models/NsfwBlacklistedTag.cs | 14 + src/EllieBot/Db/Models/PatronQuota.cs | 48 ++ src/EllieBot/Db/Models/Permission.cs | 56 ++ src/EllieBot/Db/Models/PlantedCurrency.cs | 12 + src/EllieBot/Db/Models/PlaylistSong.cs | 19 + src/EllieBot/Db/Models/Poll.cs | 17 + src/EllieBot/Db/Models/PollVote.cs | 14 + src/EllieBot/Db/Models/Quote.cs | 26 + src/EllieBot/Db/Models/ReactionRole.cs | 18 + src/EllieBot/Db/Models/Reminder.cs | 12 + src/EllieBot/Db/Models/Repeater.cs | 15 + src/EllieBot/Db/Models/RewardedUser.cs | 10 + .../Db/Models/RotatingPlayingStatus.cs | 8 + src/EllieBot/Db/Models/SelfAssignableRole.cs | 11 + src/EllieBot/Db/Models/ShopEntry.cs | 43 ++ src/EllieBot/Db/Models/SlowmodeIgnoredRole.cs | 20 + src/EllieBot/Db/Models/SlowmodeIgnoredUser.cs | 20 + src/EllieBot/Db/Models/StreamOnlineMessage.cs | 13 + src/EllieBot/Db/Models/StreamRoleSettings.cs | 68 +++ src/EllieBot/Db/Models/UnbanTimer.cs | 14 + src/EllieBot/Db/Models/UnmuteTimer.cs | 14 + src/EllieBot/Db/Models/UnroleTimer.cs | 15 + src/EllieBot/Db/Models/UserXpStats.cs | 13 + src/EllieBot/Db/Models/VcRoleInfo.cs | 8 + src/EllieBot/Db/Models/Waifu.cs | 75 +++ src/EllieBot/Db/Models/WaifuItem.cs | 10 + src/EllieBot/Db/Models/WaifuUpdate.cs | 23 + src/EllieBot/Db/Models/WarnExpireAction.cs | 8 + src/EllieBot/Db/Models/Warning.cs | 13 + src/EllieBot/Db/Models/WarningPunishment.cs | 10 + src/EllieBot/Db/Models/XpSettings.cs | 62 +++ src/EllieBot/Db/Models/XpShotOwnedItem.cs | 18 + src/EllieBot/Db/MysqlContext.cs | 38 ++ src/EllieBot/Db/PostgreSqlContext.cs | 26 + src/EllieBot/Db/SqliteContext.cs | 26 + 84 files changed, 2951 insertions(+) create mode 100644 src/EllieBot/Db/EllieContext.cs create mode 100644 src/EllieBot/Db/Extensions/ClubExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/CurrencyTransactionExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/DbExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/DiscordUserExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/EllieExpressionExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/GuildConfigExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/MusicPlayerSettingsExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/MusicPlaylistExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/PollExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/QuoteExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/ReminderExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/SelfAssignableRolesExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/UserXpExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/WaifuExtensions.cs create mode 100644 src/EllieBot/Db/Extensions/WarningExtensions.cs create mode 100644 src/EllieBot/Db/Models/AntiProtection.cs create mode 100644 src/EllieBot/Db/Models/AutoCommand.cs create mode 100644 src/EllieBot/Db/Models/AutoPublishChannel.cs create mode 100644 src/EllieBot/Db/Models/AutoTranslateChannel.cs create mode 100644 src/EllieBot/Db/Models/AutoTranslateUser.cs create mode 100644 src/EllieBot/Db/Models/BanTemplate.cs create mode 100644 src/EllieBot/Db/Models/BankUser.cs create mode 100644 src/EllieBot/Db/Models/BlacklistEntry.cs create mode 100644 src/EllieBot/Db/Models/ClubInfo.cs create mode 100644 src/EllieBot/Db/Models/CommandAlias.cs create mode 100644 src/EllieBot/Db/Models/CommandCooldown.cs create mode 100644 src/EllieBot/Db/Models/CurrencyTransaction.cs create mode 100644 src/EllieBot/Db/Models/DbEntity.cs create mode 100644 src/EllieBot/Db/Models/DelMsgOnCmdChannel.cs create mode 100644 src/EllieBot/Db/Models/DiscordPermOverride.cs create mode 100644 src/EllieBot/Db/Models/DiscordUser.cs create mode 100644 src/EllieBot/Db/Models/EllieExpression.cs create mode 100644 src/EllieBot/Db/Models/FeedSub.cs create mode 100644 src/EllieBot/Db/Models/FilterChannelId.cs create mode 100644 src/EllieBot/Db/Models/FilterLinksChannelId.cs create mode 100644 src/EllieBot/Db/Models/FilteredWord.cs create mode 100644 src/EllieBot/Db/Models/FollowedStream.cs create mode 100644 src/EllieBot/Db/Models/GCChannelId.cs create mode 100644 src/EllieBot/Db/Models/GamblingStats.cs create mode 100644 src/EllieBot/Db/Models/GroupName.cs create mode 100644 src/EllieBot/Db/Models/GuildConfig.cs create mode 100644 src/EllieBot/Db/Models/IgnoredLogItem.cs create mode 100644 src/EllieBot/Db/Models/IgnoredVoicePresenceChannel.cs create mode 100644 src/EllieBot/Db/Models/ImageOnlyChannel.cs create mode 100644 src/EllieBot/Db/Models/LogSetting.cs create mode 100644 src/EllieBot/Db/Models/MusicPlaylist.cs create mode 100644 src/EllieBot/Db/Models/MusicSettings.cs create mode 100644 src/EllieBot/Db/Models/MutedUserId.cs create mode 100644 src/EllieBot/Db/Models/NsfwBlacklistedTag.cs create mode 100644 src/EllieBot/Db/Models/PatronQuota.cs create mode 100644 src/EllieBot/Db/Models/Permission.cs create mode 100644 src/EllieBot/Db/Models/PlantedCurrency.cs create mode 100644 src/EllieBot/Db/Models/PlaylistSong.cs create mode 100644 src/EllieBot/Db/Models/Poll.cs create mode 100644 src/EllieBot/Db/Models/PollVote.cs create mode 100644 src/EllieBot/Db/Models/Quote.cs create mode 100644 src/EllieBot/Db/Models/ReactionRole.cs create mode 100644 src/EllieBot/Db/Models/Reminder.cs create mode 100644 src/EllieBot/Db/Models/Repeater.cs create mode 100644 src/EllieBot/Db/Models/RewardedUser.cs create mode 100644 src/EllieBot/Db/Models/RotatingPlayingStatus.cs create mode 100644 src/EllieBot/Db/Models/SelfAssignableRole.cs create mode 100644 src/EllieBot/Db/Models/ShopEntry.cs create mode 100644 src/EllieBot/Db/Models/SlowmodeIgnoredRole.cs create mode 100644 src/EllieBot/Db/Models/SlowmodeIgnoredUser.cs create mode 100644 src/EllieBot/Db/Models/StreamOnlineMessage.cs create mode 100644 src/EllieBot/Db/Models/StreamRoleSettings.cs create mode 100644 src/EllieBot/Db/Models/UnbanTimer.cs create mode 100644 src/EllieBot/Db/Models/UnmuteTimer.cs create mode 100644 src/EllieBot/Db/Models/UnroleTimer.cs create mode 100644 src/EllieBot/Db/Models/UserXpStats.cs create mode 100644 src/EllieBot/Db/Models/VcRoleInfo.cs create mode 100644 src/EllieBot/Db/Models/Waifu.cs create mode 100644 src/EllieBot/Db/Models/WaifuItem.cs create mode 100644 src/EllieBot/Db/Models/WaifuUpdate.cs create mode 100644 src/EllieBot/Db/Models/WarnExpireAction.cs create mode 100644 src/EllieBot/Db/Models/Warning.cs create mode 100644 src/EllieBot/Db/Models/WarningPunishment.cs create mode 100644 src/EllieBot/Db/Models/XpSettings.cs create mode 100644 src/EllieBot/Db/Models/XpShotOwnedItem.cs create mode 100644 src/EllieBot/Db/MysqlContext.cs create mode 100644 src/EllieBot/Db/PostgreSqlContext.cs create mode 100644 src/EllieBot/Db/SqliteContext.cs diff --git a/src/EllieBot/Db/EllieContext.cs b/src/EllieBot/Db/EllieContext.cs new file mode 100644 index 0000000..f995daa --- /dev/null +++ b/src/EllieBot/Db/EllieContext.cs @@ -0,0 +1,491 @@ +#nullable disable +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using EllieBot.Db.Models; +using EllieBot.Services.Database.Models; + +// ReSharper disable UnusedAutoPropertyAccessor.Global + +namespace EllieBot.Services.Database; + +public abstract class EllieContext : DbContext +{ + public DbSet GuildConfigs { get; set; } + + public DbSet Quotes { get; set; } + public DbSet Reminders { get; set; } + public DbSet SelfAssignableRoles { get; set; } + public DbSet MusicPlaylists { get; set; } + public DbSet Expressions { get; set; } + public DbSet CurrencyTransactions { get; set; } + public DbSet WaifuUpdates { get; set; } + public DbSet WaifuItem { get; set; } + public DbSet Warnings { get; set; } + public DbSet UserXpStats { get; set; } + public DbSet Clubs { get; set; } + public DbSet ClubBans { get; set; } + public DbSet ClubApplicants { get; set; } + + + //logging + public DbSet LogSettings { get; set; } + public DbSet IgnoredVoicePresenceCHannels { get; set; } + public DbSet IgnoredLogChannels { get; set; } + + public DbSet RotatingStatus { get; set; } + public DbSet Blacklist { get; set; } + public DbSet AutoCommands { get; set; } + public DbSet RewardedUsers { get; set; } + public DbSet PlantedCurrency { get; set; } + public DbSet BanTemplates { get; set; } + public DbSet DiscordPermOverrides { get; set; } + public DbSet DiscordUser { get; set; } + public DbSet MusicPlayerSettings { get; set; } + public DbSet Repeaters { get; set; } + public DbSet Poll { get; set; } + public DbSet WaifuInfo { get; set; } + public DbSet ImageOnlyChannels { get; set; } + public DbSet NsfwBlacklistedTags { get; set; } + public DbSet AutoTranslateChannels { get; set; } + public DbSet AutoTranslateUsers { get; set; } + + public DbSet Permissions { get; set; } + + public DbSet BankUsers { get; set; } + + public DbSet ReactionRoles { get; set; } + + public DbSet Patrons { get; set; } + + public DbSet PatronQuotas { get; set; } + + public DbSet StreamOnlineMessages { get; set; } + + + #region Mandatory Provider-Specific Values + + protected abstract string CurrencyTransactionOtherIdDefaultValue { get; } + + #endregion + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + #region QUOTES + + var quoteEntity = modelBuilder.Entity(); + quoteEntity.HasIndex(x => x.GuildId); + quoteEntity.HasIndex(x => x.Keyword); + + #endregion + + #region GuildConfig + + var configEntity = modelBuilder.Entity(); + configEntity.HasIndex(c => c.GuildId) + .IsUnique(); + + configEntity.Property(x => x.VerboseErrors) + .HasDefaultValue(true); + + modelBuilder.Entity().HasOne(x => x.GuildConfig).WithOne(x => x.AntiSpamSetting); + + modelBuilder.Entity().HasOne(x => x.GuildConfig).WithOne(x => x.AntiRaidSetting); + + modelBuilder.Entity() + .HasOne(x => x.AntiAltSetting) + .WithOne() + .HasForeignKey(x => x.GuildConfigId) + .OnDelete(DeleteBehavior.Cascade); + + modelBuilder.Entity() + .HasAlternateKey(x => new + { + x.GuildConfigId, + x.Url + }); + + modelBuilder.Entity().HasIndex(x => x.MessageId).IsUnique(); + + modelBuilder.Entity().HasIndex(x => x.ChannelId); + + configEntity.HasIndex(x => x.WarnExpireHours).IsUnique(false); + + #endregion + + #region streamrole + + modelBuilder.Entity().HasOne(x => x.GuildConfig).WithOne(x => x.StreamRole); + + #endregion + + #region Self Assignable Roles + + var selfassignableRolesEntity = modelBuilder.Entity(); + + selfassignableRolesEntity.HasIndex(s => new + { + s.GuildId, + s.RoleId + }) + .IsUnique(); + + selfassignableRolesEntity.Property(x => x.Group).HasDefaultValue(0); + + #endregion + + #region MusicPlaylists + + var musicPlaylistEntity = modelBuilder.Entity(); + + musicPlaylistEntity.HasMany(p => p.Songs).WithOne().OnDelete(DeleteBehavior.Cascade); + + #endregion + + #region Waifus + + var wi = modelBuilder.Entity(); + wi.HasOne(x => x.Waifu).WithOne(); + + wi.HasIndex(x => x.Price); + wi.HasIndex(x => x.ClaimerId); + // wi.HasMany(x => x.Items) + // .WithOne() + // .OnDelete(DeleteBehavior.Cascade); + + #endregion + + #region DiscordUser + + modelBuilder.Entity(du => + { + du.Property(x => x.IsClubAdmin) + .HasDefaultValue(false); + + du.Property(x => x.NotifyOnLevelUp) + .HasDefaultValue(XpNotificationLocation.None); + + du.Property(x => x.TotalXp) + .HasDefaultValue(0); + + du.Property(x => x.CurrencyAmount) + .HasDefaultValue(0); + + du.HasAlternateKey(w => w.UserId); + du.HasOne(x => x.Club) + .WithMany(x => x.Members) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + + du.HasIndex(x => x.TotalXp); + du.HasIndex(x => x.CurrencyAmount); + du.HasIndex(x => x.UserId); + }); + + #endregion + + #region Warnings + + modelBuilder.Entity(warn => + { + warn.HasIndex(x => x.GuildId); + warn.HasIndex(x => x.UserId); + warn.HasIndex(x => x.DateAdded); + warn.Property(x => x.Weight).HasDefaultValue(1); + }); + + #endregion + + #region XpStats + + var xps = modelBuilder.Entity(); + xps.HasIndex(x => new + { + x.UserId, + x.GuildId + }) + .IsUnique(); + + xps.HasIndex(x => x.UserId); + xps.HasIndex(x => x.GuildId); + xps.HasIndex(x => x.Xp); + xps.HasIndex(x => x.AwardedXp); + + #endregion + + #region XpSettings + + modelBuilder.Entity().HasOne(x => x.GuildConfig).WithOne(x => x.XpSettings); + + #endregion + + #region XpRoleReward + + modelBuilder.Entity() + .HasIndex(x => new + { + x.XpSettingsId, + x.Level + }) + .IsUnique(); + + #endregion + + #region Club + + var ci = modelBuilder.Entity(); + ci.HasOne(x => x.Owner) + .WithOne() + .HasForeignKey(x => x.OwnerId) + .OnDelete(DeleteBehavior.SetNull); + + ci.HasAlternateKey(x => new + { + x.Name + }); + + #endregion + + #region ClubManytoMany + + modelBuilder.Entity() + .HasKey(t => new + { + t.ClubId, + t.UserId + }); + + modelBuilder.Entity() + .HasOne(pt => pt.User) + .WithMany(); + + modelBuilder.Entity() + .HasOne(pt => pt.Club) + .WithMany(x => x.Applicants); + + modelBuilder.Entity() + .HasKey(t => new + { + t.ClubId, + t.UserId + }); + + modelBuilder.Entity() + .HasOne(pt => pt.User) + .WithMany(); + + modelBuilder.Entity() + .HasOne(pt => pt.Club) + .WithMany(x => x.Bans); + + #endregion + + #region Polls + + modelBuilder.Entity().HasIndex(x => x.GuildId).IsUnique(); + + #endregion + + #region CurrencyTransactions + + modelBuilder.Entity(e => + { + e.HasIndex(x => x.UserId) + .IsUnique(false); + + e.Property(x => x.OtherId) + .HasDefaultValueSql(CurrencyTransactionOtherIdDefaultValue); + + e.Property(x => x.Type) + .IsRequired(); + + e.Property(x => x.Extra) + .IsRequired(); + }); + + #endregion + + #region Reminders + + modelBuilder.Entity().HasIndex(x => x.When); + + #endregion + + #region GroupName + + modelBuilder.Entity() + .HasIndex(x => new + { + x.GuildConfigId, + x.Number + }) + .IsUnique(); + + modelBuilder.Entity() + .HasOne(x => x.GuildConfig) + .WithMany(x => x.SelfAssignableRoleGroupNames) + .IsRequired(); + + #endregion + + #region BanTemplate + + modelBuilder.Entity().HasIndex(x => x.GuildId).IsUnique(); + modelBuilder.Entity() + .Property(x => x.PruneDays) + .HasDefaultValue(null) + .IsRequired(false); + + #endregion + + #region Perm Override + + modelBuilder.Entity() + .HasIndex(x => new + { + x.GuildId, + x.Command + }) + .IsUnique(); + + #endregion + + #region Music + + modelBuilder.Entity().HasIndex(x => x.GuildId).IsUnique(); + + modelBuilder.Entity().Property(x => x.Volume).HasDefaultValue(100); + + #endregion + + #region Reaction roles + + modelBuilder.Entity(rr2 => + { + rr2.HasIndex(x => x.GuildId) + .IsUnique(false); + + rr2.HasIndex(x => new + { + x.MessageId, + x.Emote + }) + .IsUnique(); + }); + + #endregion + + #region LogSettings + + modelBuilder.Entity(ls => ls.HasIndex(x => x.GuildId).IsUnique()); + + modelBuilder.Entity(ls => ls + .HasMany(x => x.LogIgnores) + .WithOne(x => x.LogSetting) + .OnDelete(DeleteBehavior.Cascade)); + + modelBuilder.Entity(ili => ili + .HasIndex(x => new + { + x.LogSettingId, + x.LogItemId, + x.ItemType + }) + .IsUnique()); + + #endregion + + modelBuilder.Entity(ioc => ioc.HasIndex(x => x.ChannelId).IsUnique()); + + modelBuilder.Entity(nbt => nbt.HasIndex(x => x.GuildId).IsUnique(false)); + + var atch = modelBuilder.Entity(); + atch.HasIndex(x => x.GuildId).IsUnique(false); + + atch.HasIndex(x => x.ChannelId).IsUnique(); + + atch.HasMany(x => x.Users).WithOne(x => x.Channel).OnDelete(DeleteBehavior.Cascade); + + modelBuilder.Entity(atu => atu.HasAlternateKey(x => new + { + x.ChannelId, + x.UserId + })); + + #region BANK + + modelBuilder.Entity(bu => bu.HasIndex(x => x.UserId).IsUnique()); + + #endregion + + + #region Patron + + // currency rewards + var pr = modelBuilder.Entity(); + pr.HasIndex(x => x.PlatformUserId).IsUnique(); + + // patrons + // patrons are not identified by their user id, but by their platform user id + // as multiple accounts (even maybe on different platforms) could have + // the same account connected to them + modelBuilder.Entity(pu => + { + pu.HasIndex(x => x.UniquePlatformUserId).IsUnique(); + pu.HasKey(x => x.UserId); + }); + + // quotes are per user id + modelBuilder.Entity(pq => + { + pq.HasIndex(x => x.UserId).IsUnique(false); + pq.HasKey(x => new + { + x.UserId, + x.FeatureType, + x.Feature + }); + }); + + #endregion + + #region Xp Item Shop + + modelBuilder.Entity( + x => + { + // user can own only one of each item + x.HasIndex(model => new + { + model.UserId, + model.ItemType, + model.ItemKey + }) + .IsUnique(); + }); + + #endregion + + #region AutoPublish + + modelBuilder.Entity(apc => apc + .HasIndex(x => x.GuildId) + .IsUnique()); + + #endregion + + #region GamblingStats + + modelBuilder.Entity(gs => gs + .HasIndex(x => x.Feature) + .IsUnique()); + + #endregion + } + +#if DEBUG + private static readonly ILoggerFactory _debugLoggerFactory = LoggerFactory.Create(x => x.AddConsole()); + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseLoggerFactory(_debugLoggerFactory); +#endif +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/ClubExtensions.cs b/src/EllieBot/Db/Extensions/ClubExtensions.cs new file mode 100644 index 0000000..d8183fc --- /dev/null +++ b/src/EllieBot/Db/Extensions/ClubExtensions.cs @@ -0,0 +1,34 @@ +#nullable disable +using Microsoft.EntityFrameworkCore; +using EllieBot.Db.Models; + +namespace EllieBot.Db; + +public static class ClubExtensions +{ + private static IQueryable Include(this DbSet clubs) + => clubs.Include(x => x.Owner) + .Include(x => x.Applicants) + .ThenInclude(x => x.User) + .Include(x => x.Bans) + .ThenInclude(x => x.User) + .Include(x => x.Members) + .AsQueryable(); + + public static ClubInfo GetByOwner(this DbSet clubs, ulong userId) + => Include(clubs).FirstOrDefault(c => c.Owner.UserId == userId); + + public static ClubInfo GetByOwnerOrAdmin(this DbSet clubs, ulong userId) + => Include(clubs) + .FirstOrDefault(c => c.Owner.UserId == userId || c.Members.Any(u => u.UserId == userId && u.IsClubAdmin)); + + public static ClubInfo GetByMember(this DbSet clubs, ulong userId) + => Include(clubs).FirstOrDefault(c => c.Members.Any(u => u.UserId == userId)); + + public static ClubInfo GetByName(this DbSet clubs, string name) + => Include(clubs) + .FirstOrDefault(c => c.Name == name); + + public static List GetClubLeaderboardPage(this DbSet clubs, int page) + => clubs.AsNoTracking().OrderByDescending(x => x.Xp).Skip(page * 9).Take(9).ToList(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/CurrencyTransactionExtensions.cs b/src/EllieBot/Db/Extensions/CurrencyTransactionExtensions.cs new file mode 100644 index 0000000..c6659ca --- /dev/null +++ b/src/EllieBot/Db/Extensions/CurrencyTransactionExtensions.cs @@ -0,0 +1,20 @@ +#nullable disable +using LinqToDB.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public static class CurrencyTransactionExtensions +{ + public static Task> GetPageFor( + this DbSet set, + ulong userId, + int page) + => set.ToLinqToDBTable() + .Where(x => x.UserId == userId) + .OrderByDescending(x => x.DateAdded) + .Skip(15 * page) + .Take(15) + .ToListAsyncLinqToDB(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/DbExtensions.cs b/src/EllieBot/Db/Extensions/DbExtensions.cs new file mode 100644 index 0000000..6878b31 --- /dev/null +++ b/src/EllieBot/Db/Extensions/DbExtensions.cs @@ -0,0 +1,12 @@ +#nullable disable +using Microsoft.EntityFrameworkCore; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public static class DbExtensions +{ + public static T GetById(this DbSet set, int id) + where T : DbEntity + => set.FirstOrDefault(x => x.Id == id); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/DiscordUserExtensions.cs b/src/EllieBot/Db/Extensions/DiscordUserExtensions.cs new file mode 100644 index 0000000..fc165c5 --- /dev/null +++ b/src/EllieBot/Db/Extensions/DiscordUserExtensions.cs @@ -0,0 +1,179 @@ +#nullable disable +using LinqToDB; +using LinqToDB.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using EllieBot.Db.Models; +using EllieBot.Services.Database; +using System.Collections.Immutable; + +namespace EllieBot.Db; + +public static class DiscordUserExtensions +{ + /// + /// Adds the specified to the database. If a database user with placeholder name + /// and discriminator is present in , their name and discriminator get updated accordingly. + /// + /// This database context. + /// The users to add or update in the database. + /// A tuple with the amount of new users added and old users updated. + public static async Task<(long UsersAdded, long UsersUpdated)> RefreshUsersAsync(this EllieContext ctx, List users) + { + var presentDbUsers = await ctx.DiscordUser + .Select(x => new { x.UserId, x.Username, x.Discriminator }) + .Where(x => users.Select(y => y.Id).Contains(x.UserId)) + .ToArrayAsyncEF(); + + var usersToAdd = users + .Where(x => !presentDbUsers.Select(x => x.UserId).Contains(x.Id)) + .Select(x => new DiscordUser() + { + UserId = x.Id, + AvatarId = x.AvatarId, + Username = x.Username, + Discriminator = x.Discriminator + }); + + var added = (await ctx.BulkCopyAsync(usersToAdd)).RowsCopied; + var toUpdateUserIds = presentDbUsers + .Where(x => x.Username == "Unknown" && x.Discriminator == "????") + .Select(x => x.UserId) + .ToArray(); + + foreach (var user in users.Where(x => toUpdateUserIds.Contains(x.Id))) + { + await ctx.DiscordUser + .Where(x => x.UserId == user.Id) + .UpdateAsync(x => new DiscordUser() + { + Username = user.Username, + Discriminator = user.Discriminator, + + // .award tends to set AvatarId and DateAdded to NULL, so account for that. + AvatarId = user.AvatarId, + DateAdded = x.DateAdded ?? DateTime.UtcNow + }); + } + + return (added, toUpdateUserIds.Length); + } + + public static Task GetByUserIdAsync( + this IQueryable set, + ulong userId) + => set.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId); + + public static void EnsureUserCreated( + this EllieContext ctx, + ulong userId, + string username, + string discrim, + string avatarId) + => ctx.DiscordUser.ToLinqToDBTable() + .InsertOrUpdate( + () => new() + { + UserId = userId, + Username = username, + Discriminator = discrim, + AvatarId = avatarId, + TotalXp = 0, + CurrencyAmount = 0 + }, + old => new() + { + Username = username, + Discriminator = discrim, + AvatarId = avatarId + }, + () => new() + { + UserId = userId + }); + + public static Task EnsureUserCreatedAsync( + this EllieContext ctx, + ulong userId) + => ctx.DiscordUser + .ToLinqToDBTable() + .InsertOrUpdateAsync( + () => new() + { + UserId = userId, + Username = "Unknown", + Discriminator = "????", + AvatarId = string.Empty, + TotalXp = 0, + CurrencyAmount = 0 + }, + old => new() + { + + }, + () => new() + { + UserId = userId + }); + + //temp is only used in updatecurrencystate, so that i don't overwrite real usernames/discrims with Unknown + public static DiscordUser GetOrCreateUser( + this EllieContext ctx, + ulong userId, + string username, + string discrim, + string avatarId, + Func, IQueryable> includes = null) + { + ctx.EnsureUserCreated(userId, username, discrim, avatarId); + + IQueryable queryable = ctx.DiscordUser; + if (includes is not null) + queryable = includes(queryable); + return queryable.First(u => u.UserId == userId); + } + + public static DiscordUser GetOrCreateUser(this EllieContext ctx, IUser original, Func, IQueryable> includes = null) + => ctx.GetOrCreateUser(original.Id, original.Username, original.Discriminator, original.AvatarId, includes); + + public static int GetUserGlobalRank(this DbSet users, ulong id) + => users.AsQueryable() + .Where(x => x.TotalXp + > users.AsQueryable().Where(y => y.UserId == id).Select(y => y.TotalXp).FirstOrDefault()) + .Count() + + 1; + + public static DiscordUser[] GetUsersXpLeaderboardFor(this DbSet users, int page) + => users.AsQueryable().OrderByDescending(x => x.TotalXp).Skip(page * 9).Take(9).AsEnumerable().ToArray(); + + public static List GetTopRichest( + this DbSet users, + ulong botId, + int count, + int page = 0) + => users.AsQueryable() + .Where(c => c.CurrencyAmount > 0 && botId != c.UserId) + .OrderByDescending(c => c.CurrencyAmount) + .Skip(page * 9) + .Take(count) + .ToList(); + + public static async Task GetUserCurrencyAsync(this DbSet users, ulong userId) + => (await users.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId))?.CurrencyAmount ?? 0; + + public static void RemoveFromMany(this DbSet users, IEnumerable ids) + { + var items = users.AsQueryable().Where(x => ids.Contains(x.UserId)); + foreach (var item in items) + item.CurrencyAmount = 0; + } + + public static decimal GetTotalCurrency(this DbSet users) + => users.Sum((Func)(x => x.CurrencyAmount)); + + public static decimal GetTopOnePercentCurrency(this DbSet users, ulong botId) + => users.AsQueryable() + .Where(x => x.UserId != botId) + .OrderByDescending(x => x.CurrencyAmount) + .Take(users.Count() / 100 == 0 ? 1 : users.Count() / 100) + .Sum(x => x.CurrencyAmount); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/EllieExpressionExtensions.cs b/src/EllieBot/Db/Extensions/EllieExpressionExtensions.cs new file mode 100644 index 0000000..7eaf707 --- /dev/null +++ b/src/EllieBot/Db/Extensions/EllieExpressionExtensions.cs @@ -0,0 +1,15 @@ +#nullable disable +using LinqToDB; +using Microsoft.EntityFrameworkCore; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public static class EllieExpressionExtensions +{ + public static int ClearFromGuild(this DbSet exprs, ulong guildId) + => exprs.Delete(x => x.GuildId == guildId); + + public static IEnumerable ForId(this DbSet exprs, ulong id) + => exprs.AsNoTracking().AsQueryable().Where(x => x.GuildId == id).ToList(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/GuildConfigExtensions.cs b/src/EllieBot/Db/Extensions/GuildConfigExtensions.cs new file mode 100644 index 0000000..2b6272a --- /dev/null +++ b/src/EllieBot/Db/Extensions/GuildConfigExtensions.cs @@ -0,0 +1,227 @@ +#nullable disable +using Microsoft.EntityFrameworkCore; +using EllieBot.Db.Models; +using EllieBot.Services.Database; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public static class GuildConfigExtensions +{ + private static List DefaultWarnPunishments + => new() + { + new() + { + Count = 3, + Punishment = PunishmentAction.Kick + }, + new() + { + Count = 5, + Punishment = PunishmentAction.Ban + } + }; + + /// + /// Gets full stream role settings for the guild with the specified id. + /// + /// Db Context + /// Id of the guild to get stream role settings for. + /// Guild'p stream role settings + public static StreamRoleSettings GetStreamRoleSettings(this EllieContext ctx, ulong guildId) + { + var conf = ctx.GuildConfigsForId(guildId, + set => set.Include(y => y.StreamRole) + .Include(y => y.StreamRole.Whitelist) + .Include(y => y.StreamRole.Blacklist)); + + if (conf.StreamRole is null) + conf.StreamRole = new(); + + return conf.StreamRole; + } + + private static IQueryable IncludeEverything(this DbSet configs) + => configs.AsQueryable() + .AsSplitQuery() + .Include(gc => gc.CommandCooldowns) + .Include(gc => gc.FollowedStreams) + .Include(gc => gc.StreamRole) + .Include(gc => gc.XpSettings) + .ThenInclude(x => x.ExclusionList) + .Include(gc => gc.DelMsgOnCmdChannels); + + public static IEnumerable GetAllGuildConfigs( + this DbSet configs, + List availableGuilds) + => configs.IncludeEverything().AsNoTracking().Where(x => availableGuilds.Contains(x.GuildId)).ToList(); + + /// + /// Gets and creates if it doesn't exist a config for a guild. + /// + /// Context + /// Id of the guide + /// Use to manipulate the set however you want. Pass null to include everything + /// Config for the guild + public static GuildConfig GuildConfigsForId( + this EllieContext ctx, + ulong guildId, + Func, IQueryable> includes) + { + GuildConfig config; + + if (includes is null) + config = ctx.GuildConfigs.IncludeEverything().FirstOrDefault(c => c.GuildId == guildId); + else + { + var set = includes(ctx.GuildConfigs); + config = set.FirstOrDefault(c => c.GuildId == guildId); + } + + if (config is null) + { + ctx.GuildConfigs.Add(config = new() + { + GuildId = guildId, + Permissions = Permissionv2.GetDefaultPermlist, + WarningsInitialized = true, + WarnPunishments = DefaultWarnPunishments + }); + ctx.SaveChanges(); + } + + if (!config.WarningsInitialized) + { + config.WarningsInitialized = true; + config.WarnPunishments = DefaultWarnPunishments; + } + + return config; + + // ctx.GuildConfigs + // .ToLinqToDBTable() + // .InsertOrUpdate(() => new() + // { + // GuildId = guildId, + // Permissions = Permissionv2.GetDefaultPermlist, + // WarningsInitialized = true, + // WarnPunishments = DefaultWarnPunishments + // }, + // _ => new(), + // () => new() + // { + // GuildId = guildId + // }); + // + // if(includes is null) + // return ctx.GuildConfigs + // .ToLinqToDBTable() + // .First(x => x.GuildId == guildId); + } + + public static LogSetting LogSettingsFor(this EllieContext ctx, ulong guildId) + { + var logSetting = ctx.LogSettings.AsQueryable() + .Include(x => x.LogIgnores) + .Where(x => x.GuildId == guildId) + .FirstOrDefault(); + + if (logSetting is null) + { + ctx.LogSettings.Add(logSetting = new() + { + GuildId = guildId + }); + ctx.SaveChanges(); + } + + return logSetting; + } + + public static IEnumerable PermissionsForAll(this DbSet configs, List include) + { + var query = configs.AsQueryable().Where(x => include.Contains(x.GuildId)).Include(gc => gc.Permissions); + + return query.ToList(); + } + + public static GuildConfig GcWithPermissionsFor(this EllieContext ctx, ulong guildId) + { + var config = ctx.GuildConfigs.AsQueryable() + .Where(gc => gc.GuildId == guildId) + .Include(gc => gc.Permissions) + .FirstOrDefault(); + + if (config is null) // if there is no guildconfig, create new one + { + ctx.GuildConfigs.Add(config = new() + { + GuildId = guildId, + Permissions = Permissionv2.GetDefaultPermlist + }); + ctx.SaveChanges(); + } + else if (config.Permissions is null || !config.Permissions.Any()) // if no perms, add default ones + { + config.Permissions = Permissionv2.GetDefaultPermlist; + ctx.SaveChanges(); + } + + return config; + } + + public static IEnumerable GetFollowedStreams(this DbSet configs) + => configs.AsQueryable().Include(x => x.FollowedStreams).SelectMany(gc => gc.FollowedStreams).ToArray(); + + public static IEnumerable GetFollowedStreams(this DbSet configs, List included) + => configs.AsQueryable() + .Where(gc => included.Contains(gc.GuildId)) + .Include(gc => gc.FollowedStreams) + .SelectMany(gc => gc.FollowedStreams) + .ToList(); + + public static void SetCleverbotEnabled(this DbSet configs, ulong id, bool cleverbotEnabled) + { + var conf = configs.FirstOrDefault(gc => gc.GuildId == id); + + if (conf is null) + return; + + conf.CleverbotEnabled = cleverbotEnabled; + } + + public static XpSettings XpSettingsFor(this EllieContext ctx, ulong guildId) + { + var gc = ctx.GuildConfigsForId(guildId, + set => set.Include(x => x.XpSettings) + .ThenInclude(x => x.RoleRewards) + .Include(x => x.XpSettings) + .ThenInclude(x => x.CurrencyRewards) + .Include(x => x.XpSettings) + .ThenInclude(x => x.ExclusionList)); + + if (gc.XpSettings is null) + gc.XpSettings = new(); + + return gc.XpSettings; + } + + public static IEnumerable GetGeneratingChannels(this DbSet configs) + => configs.AsQueryable() + .Include(x => x.GenerateCurrencyChannelIds) + .Where(x => x.GenerateCurrencyChannelIds.Any()) + .SelectMany(x => x.GenerateCurrencyChannelIds) + .Select(x => new GeneratingChannel + { + ChannelId = x.ChannelId, + GuildId = x.GuildConfig.GuildId + }) + .ToArray(); + + public class GeneratingChannel + { + public ulong GuildId { get; set; } + public ulong ChannelId { get; set; } + } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/MusicPlayerSettingsExtensions.cs b/src/EllieBot/Db/Extensions/MusicPlayerSettingsExtensions.cs new file mode 100644 index 0000000..bbefa11 --- /dev/null +++ b/src/EllieBot/Db/Extensions/MusicPlayerSettingsExtensions.cs @@ -0,0 +1,27 @@ +#nullable disable +using Microsoft.EntityFrameworkCore; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public static class MusicPlayerSettingsExtensions +{ + public static async Task ForGuildAsync(this DbSet settings, ulong guildId) + { + var toReturn = await settings.AsQueryable().FirstOrDefaultAsync(x => x.GuildId == guildId); + + if (toReturn is null) + { + var newSettings = new MusicPlayerSettings + { + GuildId = guildId, + PlayerRepeat = PlayerRepeatType.Queue + }; + + await settings.AddAsync(newSettings); + return newSettings; + } + + return toReturn; + } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/MusicPlaylistExtensions.cs b/src/EllieBot/Db/Extensions/MusicPlaylistExtensions.cs new file mode 100644 index 0000000..747d218 --- /dev/null +++ b/src/EllieBot/Db/Extensions/MusicPlaylistExtensions.cs @@ -0,0 +1,19 @@ +#nullable disable +using Microsoft.EntityFrameworkCore; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public static class MusicPlaylistExtensions +{ + public static List GetPlaylistsOnPage(this DbSet playlists, int num) + { + if (num < 0) + throw new ArgumentOutOfRangeException(nameof(num)); + + return playlists.AsQueryable().Skip((num - 1) * 20).Take(20).Include(pl => pl.Songs).ToList(); + } + + public static MusicPlaylist GetWithSongs(this DbSet playlists, int id) + => playlists.Include(mpl => mpl.Songs).FirstOrDefault(mpl => mpl.Id == id); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/PollExtensions.cs b/src/EllieBot/Db/Extensions/PollExtensions.cs new file mode 100644 index 0000000..abbf992 --- /dev/null +++ b/src/EllieBot/Db/Extensions/PollExtensions.cs @@ -0,0 +1,28 @@ +#nullable disable +using Microsoft.EntityFrameworkCore; +using EllieBot.Services.Database; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public static class PollExtensions +{ + public static IEnumerable GetAllPolls(this DbSet polls) + => polls.Include(x => x.Answers).Include(x => x.Votes).ToArray(); + + public static void RemovePoll(this EllieContext ctx, int id) + { + var p = ctx.Poll.Include(x => x.Answers).Include(x => x.Votes).FirstOrDefault(x => x.Id == id); + + if (p is null) + return; + + if (p.Votes is not null) + { + ctx.RemoveRange(p.Votes); + p.Answers.Clear(); + } + + ctx.Poll.Remove(p); + } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/QuoteExtensions.cs b/src/EllieBot/Db/Extensions/QuoteExtensions.cs new file mode 100644 index 0000000..59cae90 --- /dev/null +++ b/src/EllieBot/Db/Extensions/QuoteExtensions.cs @@ -0,0 +1,57 @@ +#nullable disable +using Microsoft.EntityFrameworkCore; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public static class QuoteExtensions +{ + public static IEnumerable GetForGuild(this DbSet quotes, ulong guildId) + => quotes.AsQueryable().Where(x => x.GuildId == guildId); + + public static IReadOnlyCollection GetGroup( + this DbSet quotes, + ulong guildId, + int page, + OrderType order) + { + var q = quotes.AsQueryable().Where(x => x.GuildId == guildId); + if (order == OrderType.Keyword) + q = q.OrderBy(x => x.Keyword); + else + q = q.OrderBy(x => x.Id); + + return q.Skip(15 * page).Take(15).ToArray(); + } + + public static async Task GetRandomQuoteByKeywordAsync( + this DbSet quotes, + ulong guildId, + string keyword) + { + var rng = new EllieRandom(); + return (await quotes.AsQueryable().Where(q => q.GuildId == guildId && q.Keyword == keyword).ToListAsync()) + .OrderBy(_ => rng.Next()) + .FirstOrDefault(); + } + + public static async Task SearchQuoteKeywordTextAsync( + this DbSet quotes, + ulong guildId, + string keyword, + string text) + { + var rngk = new EllieRandom(); + return (await quotes.AsQueryable() + .Where(q => q.GuildId == guildId + && (keyword == null || q.Keyword == keyword) + && (EF.Functions.Like(q.Text.ToUpper(), $"%{text.ToUpper()}%") + || EF.Functions.Like(q.AuthorName, text))) + .ToListAsync()) + .OrderBy(_ => rngk.Next()) + .FirstOrDefault(); + } + + public static void RemoveAllByKeyword(this DbSet quotes, ulong guildId, string keyword) + => quotes.RemoveRange(quotes.AsQueryable().Where(x => x.GuildId == guildId && x.Keyword.ToUpper() == keyword)); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/ReminderExtensions.cs b/src/EllieBot/Db/Extensions/ReminderExtensions.cs new file mode 100644 index 0000000..33a79ca --- /dev/null +++ b/src/EllieBot/Db/Extensions/ReminderExtensions.cs @@ -0,0 +1,23 @@ +#nullable disable +using Microsoft.EntityFrameworkCore; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public static class ReminderExtensions +{ + public static IEnumerable GetIncludedReminders( + this DbSet reminders, + IEnumerable guildIds) + => reminders.AsQueryable().Where(x => guildIds.Contains(x.ServerId) || x.ServerId == 0).ToList(); + + public static IEnumerable RemindersFor(this DbSet reminders, ulong userId, int page) + => reminders.AsQueryable().Where(x => x.UserId == userId).OrderBy(x => x.DateAdded).Skip(page * 10).Take(10); + + public static IEnumerable RemindersForServer(this DbSet reminders, ulong serverId, int page) + => reminders.AsQueryable() + .Where(x => x.ServerId == serverId) + .OrderBy(x => x.DateAdded) + .Skip(page * 10) + .Take(10); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/SelfAssignableRolesExtensions.cs b/src/EllieBot/Db/Extensions/SelfAssignableRolesExtensions.cs new file mode 100644 index 0000000..ee6ef7b --- /dev/null +++ b/src/EllieBot/Db/Extensions/SelfAssignableRolesExtensions.cs @@ -0,0 +1,22 @@ +#nullable disable +using Microsoft.EntityFrameworkCore; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public static class SelfAssignableRolesExtensions +{ + public static bool DeleteByGuildAndRoleId(this DbSet roles, ulong guildId, ulong roleId) + { + var role = roles.FirstOrDefault(s => s.GuildId == guildId && s.RoleId == roleId); + + if (role is null) + return false; + + roles.Remove(role); + return true; + } + + public static IReadOnlyCollection GetFromGuild(this DbSet roles, ulong guildId) + => roles.AsQueryable().Where(s => s.GuildId == guildId).ToArray(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/UserXpExtensions.cs b/src/EllieBot/Db/Extensions/UserXpExtensions.cs new file mode 100644 index 0000000..be1d97f --- /dev/null +++ b/src/EllieBot/Db/Extensions/UserXpExtensions.cs @@ -0,0 +1,63 @@ +#nullable disable +using LinqToDB; +using Microsoft.EntityFrameworkCore; +using EllieBot.Services.Database; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public static class UserXpExtensions +{ + public static UserXpStats GetOrCreateUserXpStats(this EllieContext ctx, ulong guildId, ulong userId) + { + var usr = ctx.UserXpStats.FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId); + + if (usr is null) + { + ctx.Add(usr = new() + { + Xp = 0, + UserId = userId, + NotifyOnLevelUp = XpNotificationLocation.None, + GuildId = guildId + }); + } + + return usr; + } + + public static List GetUsersFor(this DbSet xps, ulong guildId, int page) + => xps.AsQueryable() + .AsNoTracking() + .Where(x => x.GuildId == guildId) + .OrderByDescending(x => x.Xp + x.AwardedXp) + .Skip(page * 9) + .Take(9) + .ToList(); + + public static List GetTopUserXps(this DbSet xps, ulong guildId, int count) + => xps.AsQueryable() + .AsNoTracking() + .Where(x => x.GuildId == guildId) + .OrderByDescending(x => x.Xp + x.AwardedXp) + .Take(count) + .ToList(); + + public static int GetUserGuildRanking(this DbSet xps, ulong userId, ulong guildId) + => xps.AsQueryable() + .AsNoTracking() + .Where(x => x.GuildId == guildId + && x.Xp + x.AwardedXp + > xps.AsQueryable() + .Where(y => y.UserId == userId && y.GuildId == guildId) + .Select(y => y.Xp + y.AwardedXp) + .FirstOrDefault()) + .Count() + + 1; + + public static void ResetGuildUserXp(this DbSet xps, ulong userId, ulong guildId) + => xps.Delete(x => x.UserId == userId && x.GuildId == guildId); + + public static void ResetGuildXp(this DbSet xps, ulong guildId) + => xps.Delete(x => x.GuildId == guildId); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/WaifuExtensions.cs b/src/EllieBot/Db/Extensions/WaifuExtensions.cs new file mode 100644 index 0000000..ee1fddf --- /dev/null +++ b/src/EllieBot/Db/Extensions/WaifuExtensions.cs @@ -0,0 +1,145 @@ +#nullable disable +using LinqToDB; +using LinqToDB.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using EllieBot.Db.Models; +using EllieBot.Services.Database; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public class WaifuInfoStats +{ + public int WaifuId { get; init; } + public string FullName { get; init; } + public long Price { get; init; } + public string ClaimerName { get; init; } + public string AffinityName { get; init; } + public int AffinityCount { get; init; } + public int DivorceCount { get; init; } + public int ClaimCount { get; init; } +} + +public static class WaifuExtensions +{ + public static WaifuInfo ByWaifuUserId( + this DbSet waifus, + ulong userId, + Func, IQueryable> includes = null) + { + if (includes is null) + { + return waifus.Include(wi => wi.Waifu) + .Include(wi => wi.Affinity) + .Include(wi => wi.Claimer) + .Include(wi => wi.Items) + .FirstOrDefault(wi => wi.Waifu.UserId == userId); + } + + return includes(waifus).AsQueryable().FirstOrDefault(wi => wi.Waifu.UserId == userId); + } + + public static IEnumerable GetTop(this DbSet waifus, int count, int skip = 0) + { + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + if (count == 0) + return new List(); + + return waifus.Include(wi => wi.Waifu) + .Include(wi => wi.Affinity) + .Include(wi => wi.Claimer) + .OrderByDescending(wi => wi.Price) + .Skip(skip) + .Take(count) + .Select(x => new WaifuLbResult + { + Affinity = x.Affinity == null ? null : x.Affinity.Username, + AffinityDiscrim = x.Affinity == null ? null : x.Affinity.Discriminator, + Claimer = x.Claimer == null ? null : x.Claimer.Username, + ClaimerDiscrim = x.Claimer == null ? null : x.Claimer.Discriminator, + Username = x.Waifu.Username, + Discrim = x.Waifu.Discriminator, + Price = x.Price + }) + .ToList(); + } + + public static decimal GetTotalValue(this DbSet waifus) + => waifus.AsQueryable().Where(x => x.ClaimerId != null).Sum(x => x.Price); + + public static ulong GetWaifuUserId(this DbSet waifus, ulong ownerId, string name) + => waifus.AsQueryable() + .AsNoTracking() + .Where(x => x.Claimer.UserId == ownerId && x.Waifu.Username + "#" + x.Waifu.Discriminator == name) + .Select(x => x.Waifu.UserId) + .FirstOrDefault(); + + public static async Task GetWaifuInfoAsync(this EllieContext ctx, ulong userId) + { + await ctx.WaifuInfo + .ToLinqToDBTable() + .InsertOrUpdateAsync(() => new() + { + AffinityId = null, + ClaimerId = null, + Price = 1, + WaifuId = ctx.DiscordUser.Where(x => x.UserId == userId).Select(x => x.Id).First() + }, + _ => new(), + () => new() + { + WaifuId = ctx.DiscordUser.Where(x => x.UserId == userId).Select(x => x.Id).First() + }); + + var toReturn = ctx.WaifuInfo.AsQueryable() + .Where(w => w.WaifuId + == ctx.Set() + .AsQueryable() + .Where(u => u.UserId == userId) + .Select(u => u.Id) + .FirstOrDefault()) + .Select(w => new WaifuInfoStats + { + WaifuId = w.WaifuId, + FullName = + ctx.Set() + .AsQueryable() + .Where(u => u.UserId == userId) + .Select(u => u.Username + "#" + u.Discriminator) + .FirstOrDefault(), + AffinityCount = + ctx.Set() + .AsQueryable() + .Count(x => x.UserId == w.WaifuId + && x.UpdateType == WaifuUpdateType.AffinityChanged + && x.NewId != null), + AffinityName = + ctx.Set() + .AsQueryable() + .Where(u => u.Id == w.AffinityId) + .Select(u => u.Username + "#" + u.Discriminator) + .FirstOrDefault(), + ClaimCount = ctx.WaifuInfo.AsQueryable().Count(x => x.ClaimerId == w.WaifuId), + ClaimerName = + ctx.Set() + .AsQueryable() + .Where(u => u.Id == w.ClaimerId) + .Select(u => u.Username + "#" + u.Discriminator) + .FirstOrDefault(), + DivorceCount = + ctx.Set() + .AsQueryable() + .Count(x => x.OldId == w.WaifuId + && x.NewId == null + && x.UpdateType == WaifuUpdateType.Claimed), + Price = w.Price, + }) + .FirstOrDefault(); + + if (toReturn is null) + return null; + + return toReturn; + } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Extensions/WarningExtensions.cs b/src/EllieBot/Db/Extensions/WarningExtensions.cs new file mode 100644 index 0000000..59474d2 --- /dev/null +++ b/src/EllieBot/Db/Extensions/WarningExtensions.cs @@ -0,0 +1,60 @@ +#nullable disable +using Microsoft.EntityFrameworkCore; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db; + +public static class WarningExtensions +{ + public static Warning[] ForId(this DbSet warnings, ulong guildId, ulong userId) + { + var query = warnings.AsQueryable() + .Where(x => x.GuildId == guildId && x.UserId == userId) + .OrderByDescending(x => x.DateAdded); + + return query.ToArray(); + } + + public static bool Forgive( + this DbSet warnings, + ulong guildId, + ulong userId, + string mod, + int index) + { + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index)); + + var warn = warnings.AsQueryable() + .Where(x => x.GuildId == guildId && x.UserId == userId) + .OrderByDescending(x => x.DateAdded) + .Skip(index) + .FirstOrDefault(); + + if (warn is null || warn.Forgiven) + return false; + + warn.Forgiven = true; + warn.ForgivenBy = mod; + return true; + } + + public static async Task ForgiveAll( + this DbSet warnings, + ulong guildId, + ulong userId, + string mod) + => await warnings.AsQueryable() + .Where(x => x.GuildId == guildId && x.UserId == userId) + .ForEachAsync(x => + { + if (x.Forgiven != true) + { + x.Forgiven = true; + x.ForgivenBy = mod; + } + }); + + public static Warning[] GetForGuild(this DbSet warnings, ulong id) + => warnings.AsQueryable().Where(x => x.GuildId == id).ToArray(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/AntiProtection.cs b/src/EllieBot/Db/Models/AntiProtection.cs new file mode 100644 index 0000000..2301b42 --- /dev/null +++ b/src/EllieBot/Db/Models/AntiProtection.cs @@ -0,0 +1,65 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class AntiRaidSetting : DbEntity +{ + public int GuildConfigId { get; set; } + public GuildConfig GuildConfig { get; set; } + + public int UserThreshold { get; set; } + public int Seconds { get; set; } + public PunishmentAction Action { get; set; } + + /// + /// Duration of the punishment, in minutes. This works only in supported Actions, like: + /// Mute, ChatMute, VoiceMute, etc... + /// + public int PunishmentDuration { get; set; } +} + +public class AntiSpamSetting : DbEntity +{ + public int GuildConfigId { get; set; } + public GuildConfig GuildConfig { get; set; } + + public PunishmentAction Action { get; set; } + public int MessageThreshold { get; set; } = 3; + public int MuteTime { get; set; } + public ulong? RoleId { get; set; } + public HashSet IgnoredChannels { get; set; } = new(); +} + +public class AntiAltSetting +{ + public int Id { get; set; } + public int GuildConfigId { get; set; } + public TimeSpan MinAge { get; set; } + public PunishmentAction Action { get; set; } + public int ActionDurationMinutes { get; set; } + public ulong? RoleId { get; set; } +} + +public enum PunishmentAction +{ + Mute, + Kick, + Ban, + Softban, + RemoveRoles, + ChatMute, + VoiceMute, + AddRole, + Warn, + TimeOut +} + +public class AntiSpamIgnore : DbEntity +{ + public ulong ChannelId { get; set; } + + public override int GetHashCode() + => ChannelId.GetHashCode(); + + public override bool Equals(object obj) + => obj is AntiSpamIgnore inst ? inst.ChannelId == ChannelId : false; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/AutoCommand.cs b/src/EllieBot/Db/Models/AutoCommand.cs new file mode 100644 index 0000000..3738c37 --- /dev/null +++ b/src/EllieBot/Db/Models/AutoCommand.cs @@ -0,0 +1,14 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class AutoCommand : DbEntity +{ + public string CommandText { get; set; } + public ulong ChannelId { get; set; } + public string ChannelName { get; set; } + public ulong? GuildId { get; set; } + public string GuildName { get; set; } + public ulong? VoiceChannelId { get; set; } + public string VoiceChannelName { get; set; } + public int Interval { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/AutoPublishChannel.cs b/src/EllieBot/Db/Models/AutoPublishChannel.cs new file mode 100644 index 0000000..fa81769 --- /dev/null +++ b/src/EllieBot/Db/Models/AutoPublishChannel.cs @@ -0,0 +1,9 @@ +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db.Models; + +public class AutoPublishChannel : DbEntity +{ + public ulong GuildId { get; set; } + public ulong ChannelId { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/AutoTranslateChannel.cs b/src/EllieBot/Db/Models/AutoTranslateChannel.cs new file mode 100644 index 0000000..a90df1e --- /dev/null +++ b/src/EllieBot/Db/Models/AutoTranslateChannel.cs @@ -0,0 +1,10 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class AutoTranslateChannel : DbEntity +{ + public ulong GuildId { get; set; } + public ulong ChannelId { get; set; } + public bool AutoDelete { get; set; } + public IList Users { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/AutoTranslateUser.cs b/src/EllieBot/Db/Models/AutoTranslateUser.cs new file mode 100644 index 0000000..ebe6754 --- /dev/null +++ b/src/EllieBot/Db/Models/AutoTranslateUser.cs @@ -0,0 +1,11 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class AutoTranslateUser : DbEntity +{ + public int ChannelId { get; set; } + public AutoTranslateChannel Channel { get; set; } + public ulong UserId { get; set; } + public string Source { get; set; } + public string Target { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/BanTemplate.cs b/src/EllieBot/Db/Models/BanTemplate.cs new file mode 100644 index 0000000..cc72275 --- /dev/null +++ b/src/EllieBot/Db/Models/BanTemplate.cs @@ -0,0 +1,9 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class BanTemplate : DbEntity +{ + public ulong GuildId { get; set; } + public string Text { get; set; } + public int? PruneDays { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/BankUser.cs b/src/EllieBot/Db/Models/BankUser.cs new file mode 100644 index 0000000..7fa6418 --- /dev/null +++ b/src/EllieBot/Db/Models/BankUser.cs @@ -0,0 +1,9 @@ +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db.Models; + +public class BankUser : DbEntity +{ + public ulong UserId { get; set; } + public long Balance { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/BlacklistEntry.cs b/src/EllieBot/Db/Models/BlacklistEntry.cs new file mode 100644 index 0000000..849c3f7 --- /dev/null +++ b/src/EllieBot/Db/Models/BlacklistEntry.cs @@ -0,0 +1,14 @@ +namespace EllieBot.Services.Database.Models; + +public class BlacklistEntry : DbEntity +{ + public ulong ItemId { get; set; } + public BlacklistType Type { get; set; } +} + +public enum BlacklistType +{ + Server, + Channel, + User +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/ClubInfo.cs b/src/EllieBot/Db/Models/ClubInfo.cs new file mode 100644 index 0000000..75d2d1b --- /dev/null +++ b/src/EllieBot/Db/Models/ClubInfo.cs @@ -0,0 +1,42 @@ +#nullable disable +using EllieBot.Services.Database.Models; +using System.ComponentModel.DataAnnotations; + +namespace EllieBot.Db.Models; + +public class ClubInfo : DbEntity +{ + [MaxLength(20)] + public string Name { get; set; } + public string Description { get; set; } + public string ImageUrl { get; set; } + + public int Xp { get; set; } = 0; + public int? OwnerId { get; set; } + public DiscordUser Owner { get; set; } + + public List Members { get; set; } = new(); + public List Applicants { get; set; } = new(); + public List Bans { get; set; } = new(); + + public override string ToString() + => Name; +} + +public class ClubApplicants +{ + public int ClubId { get; set; } + public ClubInfo Club { get; set; } + + public int UserId { get; set; } + public DiscordUser User { get; set; } +} + +public class ClubBans +{ + public int ClubId { get; set; } + public ClubInfo Club { get; set; } + + public int UserId { get; set; } + public DiscordUser User { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/CommandAlias.cs b/src/EllieBot/Db/Models/CommandAlias.cs new file mode 100644 index 0000000..be2a6f1 --- /dev/null +++ b/src/EllieBot/Db/Models/CommandAlias.cs @@ -0,0 +1,8 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class CommandAlias : DbEntity +{ + public string Trigger { get; set; } + public string Mapping { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/CommandCooldown.cs b/src/EllieBot/Db/Models/CommandCooldown.cs new file mode 100644 index 0000000..2b824ef --- /dev/null +++ b/src/EllieBot/Db/Models/CommandCooldown.cs @@ -0,0 +1,8 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class CommandCooldown : DbEntity +{ + public int Seconds { get; set; } + public string CommandName { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/CurrencyTransaction.cs b/src/EllieBot/Db/Models/CurrencyTransaction.cs new file mode 100644 index 0000000..9153520 --- /dev/null +++ b/src/EllieBot/Db/Models/CurrencyTransaction.cs @@ -0,0 +1,12 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class CurrencyTransaction : DbEntity +{ + public long Amount { get; set; } + public string Note { get; set; } + public ulong UserId { get; set; } + public string Type { get; set; } + public string Extra { get; set; } + public ulong? OtherId { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/DbEntity.cs b/src/EllieBot/Db/Models/DbEntity.cs new file mode 100644 index 0000000..035ff76 --- /dev/null +++ b/src/EllieBot/Db/Models/DbEntity.cs @@ -0,0 +1,12 @@ +#nullable disable +using System.ComponentModel.DataAnnotations; + +namespace EllieBot.Services.Database.Models; + +public class DbEntity +{ + [Key] + public int Id { get; set; } + + public DateTime? DateAdded { get; set; } = DateTime.UtcNow; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/DelMsgOnCmdChannel.cs b/src/EllieBot/Db/Models/DelMsgOnCmdChannel.cs new file mode 100644 index 0000000..59ac41d --- /dev/null +++ b/src/EllieBot/Db/Models/DelMsgOnCmdChannel.cs @@ -0,0 +1,14 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class DelMsgOnCmdChannel : DbEntity +{ + public ulong ChannelId { get; set; } + public bool State { get; set; } + + public override int GetHashCode() + => ChannelId.GetHashCode(); + + public override bool Equals(object obj) + => obj is DelMsgOnCmdChannel x && x.ChannelId == ChannelId; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/DiscordPermOverride.cs b/src/EllieBot/Db/Models/DiscordPermOverride.cs new file mode 100644 index 0000000..461dcc4 --- /dev/null +++ b/src/EllieBot/Db/Models/DiscordPermOverride.cs @@ -0,0 +1,10 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class DiscordPermOverride : DbEntity +{ + public GuildPerm Perm { get; set; } + + public ulong? GuildId { get; set; } + public string Command { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/DiscordUser.cs b/src/EllieBot/Db/Models/DiscordUser.cs new file mode 100644 index 0000000..aa52e19 --- /dev/null +++ b/src/EllieBot/Db/Models/DiscordUser.cs @@ -0,0 +1,31 @@ +#nullable disable +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db.Models; + +// FUTURE remove LastLevelUp from here and UserXpStats +public class DiscordUser : DbEntity +{ + public ulong UserId { get; set; } + public string Username { get; set; } + public string Discriminator { get; set; } + public string AvatarId { get; set; } + + public int? ClubId { get; set; } + public ClubInfo Club { get; set; } + public bool IsClubAdmin { get; set; } + + public long TotalXp { get; set; } + public XpNotificationLocation NotifyOnLevelUp { get; set; } + + public long CurrencyAmount { get; set; } + + public override bool Equals(object obj) + => obj is DiscordUser du ? du.UserId == UserId : false; + + public override int GetHashCode() + => UserId.GetHashCode(); + + public override string ToString() + => Username + "#" + Discriminator; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/EllieExpression.cs b/src/EllieBot/Db/Models/EllieExpression.cs new file mode 100644 index 0000000..9218e0c --- /dev/null +++ b/src/EllieBot/Db/Models/EllieExpression.cs @@ -0,0 +1,26 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class EllieExpression : DbEntity +{ + public ulong? GuildId { get; set; } + public string Response { get; set; } + public string Trigger { get; set; } + + public bool AutoDeleteTrigger { get; set; } + public bool DmResponse { get; set; } + public bool ContainsAnywhere { get; set; } + public bool AllowTarget { get; set; } + + public string[] GetReactions() + => string.IsNullOrWhiteSpace(Reaactions) ? Array.Empty() : Reaactions.Split("@@@"); + + public bool IsGlobal() + => GuildId is null or 0; +} + +public class ReactionResponse : DbEntity +{ + public bool OwnerOnly { get; set; } + public string Text { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/FeedSub.cs b/src/EllieBot/Db/Models/FeedSub.cs new file mode 100644 index 0000000..f15aa81 --- /dev/null +++ b/src/EllieBot/Db/Models/FeedSub.cs @@ -0,0 +1,19 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class FeedSub : DbEntity +{ + public int GuildConfigId { get; set; } + public GuildConfig GuildConfig { get; set; } + + public ulong ChannelId { get; set; } + public string Url { get; set; } + + public string Message { get; set; } + + public override int GetHashCode() + => Url.GetHashCode(StringComparison.InvariantCulture) ^ GuildConfigId.GetHashCode(); + + public override bool Equals(object obj) + => obj is FeedSub s && s.Url.ToLower() == Url.ToLower() && s.GuildConfigId == GuildConfigId; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/FilterChannelId.cs b/src/EllieBot/Db/Models/FilterChannelId.cs new file mode 100644 index 0000000..e674ec2 --- /dev/null +++ b/src/EllieBot/Db/Models/FilterChannelId.cs @@ -0,0 +1,30 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class FilterChannelId : DbEntity +{ + public ulong ChannelId { get; set; } + + public bool Equals(FilterChannelId other) + => ChannelId == other.ChannelId; + + public override bool Equals(object obj) + => obj is FilterChannelId fci && Equals(fci); + + public override int GetHashCode() + => ChannelId.GetHashCode(); +} + +public class FilterWordsChannelId : DbEntity +{ + public ulong ChannelId { get; set; } + + public bool Equals(FilterWordsChannelId other) + => ChannelId == other.ChannelId; + + public override bool Equals(object obj) + => obj is FilterWordsChannelId fci && Equals(fci); + + public override int GetHashCode() + => ChannelId.GetHashCode(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/FilterLinksChannelId.cs b/src/EllieBot/Db/Models/FilterLinksChannelId.cs new file mode 100644 index 0000000..440085e --- /dev/null +++ b/src/EllieBot/Db/Models/FilterLinksChannelId.cs @@ -0,0 +1,13 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class FilterLinksChannelId : DbEntity +{ + public ulong ChannelId { get; set; } + + public override bool Equals(object obj) + => obj is FilterLinksChannelId f && f.ChannelId == ChannelId; + + public override int GetHashCode() + => ChannelId.GetHashCode(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/FilteredWord.cs b/src/EllieBot/Db/Models/FilteredWord.cs new file mode 100644 index 0000000..5a5ee93 --- /dev/null +++ b/src/EllieBot/Db/Models/FilteredWord.cs @@ -0,0 +1,7 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class FilteredWord : DbEntity +{ + public string Word { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/FollowedStream.cs b/src/EllieBot/Db/Models/FollowedStream.cs new file mode 100644 index 0000000..3a96846 --- /dev/null +++ b/src/EllieBot/Db/Models/FollowedStream.cs @@ -0,0 +1,37 @@ +#nullable disable +using EllieBot.Modules.Searches.Common; +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db.Models; + +public class FollowedStream : DbEntity +{ + public enum FType + { + Twitch = 0, + Picarto = 3, + Youtube = 4, + Facebook = 5, + Trovo = 6 + } + + public ulong GuildId { get; set; } + public ulong ChannelId { get; set; } + public string Username { get; set; } + public FType Type { get; set; } + public string Message { get; set; } + + protected bool Equals(FollowedStream other) + => ChannelId == other.ChannelId + && Username.Trim().ToUpperInvariant() == other.Username.Trim().ToUpperInvariant() + && Type == other.Type; + + public override int GetHashCode() + => HashCode.Combine(ChannelId, Username, (int)Type); + + public override bool Equals(object obj) + => obj is FollowedStream fs && Equals(fs); + + public StreamDataKey CreateKey() + => new(Type, Username.ToLower()); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/GCChannelId.cs b/src/EllieBot/Db/Models/GCChannelId.cs new file mode 100644 index 0000000..387605c --- /dev/null +++ b/src/EllieBot/Db/Models/GCChannelId.cs @@ -0,0 +1,14 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class GCChannelId : DbEntity +{ + public GuildConfig GuildConfig { get; set; } + public ulong ChannelId { get; set; } + + public override bool Equals(object obj) + => obj is GCChannelId gc && gc.ChannelId == ChannelId; + + public override int GetHashCode() + => ChannelId.GetHashCode(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/GamblingStats.cs b/src/EllieBot/Db/Models/GamblingStats.cs new file mode 100644 index 0000000..8a4654e --- /dev/null +++ b/src/EllieBot/Db/Models/GamblingStats.cs @@ -0,0 +1,9 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class GamblingStats : DbEntity +{ + public string Feature { get; set; } + public decimal Bet { get; set; } + public decimal PaidOut { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/GroupName.cs b/src/EllieBot/Db/Models/GroupName.cs new file mode 100644 index 0000000..52ef07b --- /dev/null +++ b/src/EllieBot/Db/Models/GroupName.cs @@ -0,0 +1,11 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class GroupName : DbEntity +{ + public int GuildConfigId { get; set; } + public GuildConfig GuildConfig { get; set; } + + public int Number { get; set; } + public string Name { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/GuildConfig.cs b/src/EllieBot/Db/Models/GuildConfig.cs new file mode 100644 index 0000000..5346489 --- /dev/null +++ b/src/EllieBot/Db/Models/GuildConfig.cs @@ -0,0 +1,108 @@ +#nullable disable +using EllieBot.Db.Models; + +namespace EllieBot.Services.Database.Models; + +public class GuildConfig : DbEntity +{ + public ulong GuildId { get; set; } + + public string Prefix { get; set; } + + public bool DeleteMessageOnCommand { get; set; } + public HashSet DelMsgOnCmdChannels { get; set; } = new(); + + public string AutoAssignRoleIds { get; set; } + + //greet stuff + public int AutoDeleteGreetMessagesTimer { get; set; } = 30; + public int AutoDeleteByeMessagesTimer { get; set; } = 30; + + public ulong GreetMessageChannelId { get; set; } + public ulong ByeMessageChannelId { get; set; } + + public bool SendDmGreetMessage { get; set; } + public string DmGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!"; + + public bool SendChannelGreetMessage { get; set; } + public string ChannelGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!"; + + public bool SendChannelByeMessage { get; set; } + public string ChannelByeMessageText { get; set; } = "%user% has left!"; + + //self assignable roles + public bool ExclusiveSelfAssignedRoles { get; set; } + public bool AutoDeleteSelfAssignedRoleMessages { get; set; } + + //stream notifications + public HashSet FollowedStreams { get; set; } = new(); + + //currencyGeneration + public HashSet GenerateCurrencyChannelIds { get; set; } = new(); + + public List Permissions { get; set; } + public bool VerbosePermissions { get; set; } = true; + public string PermissionRole { get; set; } + + public HashSet CommandCooldowns { get; set; } = new(); + + //filtering + public bool FilterInvites { get; set; } + public bool FilterLinks { get; set; } + public HashSet FilterInvitesChannelIds { get; set; } = new(); + public HashSet FilterLinksChannelIds { get; set; } = new(); + + //public bool FilterLinks { get; set; } + //public HashSet FilterLinksChannels { get; set; } = new HashSet(); + + public bool FilterWords { get; set; } + public HashSet FilteredWords { get; set; } = new(); + public HashSet FilterWordsChannelIds { get; set; } = new(); + + public HashSet MutedUsers { get; set; } = new(); + + public string MuteRoleName { get; set; } + public bool CleverbotEnabled { get; set; } + + public AntiRaidSetting AntiRaidSetting { get; set; } + public AntiSpamSetting AntiSpamSetting { get; set; } + public AntiAltSetting AntiAltSetting { get; set; } + + public string Locale { get; set; } + public string TimeZoneId { get; set; } + + public HashSet UnmuteTimers { get; set; } = new(); + public HashSet UnbanTimer { get; set; } = new(); + public HashSet UnroleTimer { get; set; } = new(); + public HashSet VcRoleInfos { get; set; } + public HashSet CommandAliases { get; set; } = new(); + public List WarnPunishments { get; set; } = new(); + public bool WarningsInitialized { get; set; } + public HashSet SlowmodeIgnoredUsers { get; set; } + public HashSet SlowmodeIgnoredRoles { get; set; } + + public List ShopEntries { get; set; } + public ulong? GameVoiceChannel { get; set; } + public bool VerboseErrors { get; set; } = true; + + public StreamRoleSettings StreamRole { get; set; } + + public XpSettings XpSettings { get; set; } + public List FeedSubs { get; set; } = new(); + public bool NotifyStreamOffline { get; set; } + public bool DeleteStreamOnlineMessage { get; set; } + public List SelfAssignableRoleGroupNames { get; set; } + public int WarnExpireHours { get; set; } + public WarnExpireAction WarnExpireAction { get; set; } = WarnExpireAction.Clear; + + public bool DisableGlobalExpressions { get; set; } = false; + + #region Boost Message + + public bool SendBoostMessage { get; set; } + public string BoostMessage { get; set; } = "%user% just boosted this server!"; + public ulong BoostMessageChannelId { get; set; } + public int BoostMessageDeleteAfter { get; set; } + + #endregion +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/IgnoredLogItem.cs b/src/EllieBot/Db/Models/IgnoredLogItem.cs new file mode 100644 index 0000000..060ef32 --- /dev/null +++ b/src/EllieBot/Db/Models/IgnoredLogItem.cs @@ -0,0 +1,16 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class IgnoredLogItem : DbEntity +{ + public int LogSettingId { get; set; } + public LogSetting LogSetting { get; set; } + public ulong LogItemId { get; set; } + public IgnoredItemType ItemType { get; set; } +} + +public enum IgnoredItemType +{ + Channel, + User +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/IgnoredVoicePresenceChannel.cs b/src/EllieBot/Db/Models/IgnoredVoicePresenceChannel.cs new file mode 100644 index 0000000..e25c38a --- /dev/null +++ b/src/EllieBot/Db/Models/IgnoredVoicePresenceChannel.cs @@ -0,0 +1,8 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class IgnoredVoicePresenceChannel : DbEntity +{ + public LogSetting LogSetting { get; set; } + public ulong ChannelId { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/ImageOnlyChannel.cs b/src/EllieBot/Db/Models/ImageOnlyChannel.cs new file mode 100644 index 0000000..2fce8d1 --- /dev/null +++ b/src/EllieBot/Db/Models/ImageOnlyChannel.cs @@ -0,0 +1,15 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class ImageOnlyChannel : DbEntity +{ + public ulong GuildId { get; set; } + public ulong ChannelId { get; set; } + public OnlyChannelType Type { get; set; } +} + +public enum OnlyChannelType +{ + Image, + Link +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/LogSetting.cs b/src/EllieBot/Db/Models/LogSetting.cs new file mode 100644 index 0000000..b09371a --- /dev/null +++ b/src/EllieBot/Db/Models/LogSetting.cs @@ -0,0 +1,37 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class LogSetting : DbEntity +{ + public List LogIgnores { get; set; } = new(); + + public ulong GuildId { get; set; } + public ulong? LogOtherId { get; set; } + public ulong? MessageUpdatedId { get; set; } + public ulong? MessageDeletedId { get; set; } + + public ulong? UserJoinedId { get; set; } + public ulong? UserLeftId { get; set; } + public ulong? UserBannedId { get; set; } + public ulong? UserUnbannedId { get; set; } + public ulong? UserUpdatedId { get; set; } + + public ulong? ChannelCreatedId { get; set; } + public ulong? ChannelDestroyedId { get; set; } + public ulong? ChannelUpdatedId { get; set; } + + + public ulong? ThreadDeletedId { get; set; } + public ulong? ThreadCreatedId { get; set; } + + public ulong? UserMutedId { get; set; } + + // userpresence + public ulong? LogUserPresenceId { get; set; } + + // voicepresence + + public ulong? LogVoicePresenceId { get; set; } + public ulong? LogVoicePresenceTTSId { get; set; } + public ulong? LogWarnsId { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/MusicPlaylist.cs b/src/EllieBot/Db/Models/MusicPlaylist.cs new file mode 100644 index 0000000..3f96d05 --- /dev/null +++ b/src/EllieBot/Db/Models/MusicPlaylist.cs @@ -0,0 +1,10 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class MusicPlaylist : DbEntity +{ + public string Name { get; set; } + public string Author { get; set; } + public ulong AuthorId { get; set; } + public List Songs { get; set; } = new(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/MusicSettings.cs b/src/EllieBot/Db/Models/MusicSettings.cs new file mode 100644 index 0000000..b81d433 --- /dev/null +++ b/src/EllieBot/Db/Models/MusicSettings.cs @@ -0,0 +1,61 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class MusicPlayerSettings +{ + /// + /// Auto generated Id + /// + public int Id { get; set; } + + /// + /// Id of the guild + /// + public ulong GuildId { get; set; } + + /// + /// Queue repeat type + /// + public PlayerRepeatType PlayerRepeat { get; set; } = PlayerRepeatType.Queue; + + /// + /// Channel id the bot will always try to send track related messages to + /// + public ulong? MusicChannelId { get; set; } + + /// + /// Default volume player will be created with + /// + public int Volume { get; set; } = 100; + + /// + /// Whether the bot should auto disconnect from the voice channel once the queue is done + /// This only has effect if + /// + public bool AutoDisconnect { get; set; } + + /// + /// Selected quality preset for the music player + /// + public QualityPreset QualityPreset { get; set; } + + /// + /// Whether the bot will automatically queue related songs + /// + public bool AutoPlay { get; set; } +} + +public enum QualityPreset +{ + Highest, + High, + Medium, + Low +} + +public enum PlayerRepeatType +{ + None, + Track, + Queue +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/MutedUserId.cs b/src/EllieBot/Db/Models/MutedUserId.cs new file mode 100644 index 0000000..2d752f6 --- /dev/null +++ b/src/EllieBot/Db/Models/MutedUserId.cs @@ -0,0 +1,13 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class MutedUserId : DbEntity +{ + public ulong UserId { get; set; } + + public override int GetHashCode() + => UserId.GetHashCode(); + + public override bool Equals(object obj) + => obj is MutedUserId mui ? mui.UserId == UserId : false; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/NsfwBlacklistedTag.cs b/src/EllieBot/Db/Models/NsfwBlacklistedTag.cs new file mode 100644 index 0000000..3617132 --- /dev/null +++ b/src/EllieBot/Db/Models/NsfwBlacklistedTag.cs @@ -0,0 +1,14 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class NsfwBlacklistedTag : DbEntity +{ + public ulong GuildId { get; set; } + public string Tag { get; set; } + + public override int GetHashCode() + => Tag.GetHashCode(StringComparison.InvariantCulture); + + public override bool Equals(object obj) + => obj is NsfwBlacklistedTag x && x.Tag == Tag; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/PatronQuota.cs b/src/EllieBot/Db/Models/PatronQuota.cs new file mode 100644 index 0000000..ba5129d --- /dev/null +++ b/src/EllieBot/Db/Models/PatronQuota.cs @@ -0,0 +1,48 @@ +#nullable disable +namespace EllieBot.Db.Models; + +/// +/// Contains data about usage of Patron-Only commands per user +/// in order to provide support for quota limitations +/// (allow user x who is pledging amount y to use the specified command only +/// x amount of times in the specified time period) +/// +public class PatronQuota +{ + public ulong UserId { get; set; } + public FeatureType FeatureType { get; set; } + public string Feature { get; set; } + public uint HourlyCount { get; set; } + public uint DailyCount { get; set; } + public uint MonthlyCount { get; set; } +} + +public enum FeatureType +{ + Command, + Group, + Module, + Limit +} + +public class PatronUser +{ + public string UniquePlatformUserId { get; set; } + public ulong UserId { get; set; } + public int AmountCents { get; set; } + + public DateTime LastCharge { get; set; } + + // Date Only component + public DateTime ValidThru { get; set; } + + public PatronUser Clone() + => new PatronUser() + { + UniquePlatformUserId = this.UniquePlatformUserId, + UserId = this.UserId, + AmountCents = this.AmountCents, + LastCharge = this.LastCharge, + ValidThru = this.ValidThru + }; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/Permission.cs b/src/EllieBot/Db/Models/Permission.cs new file mode 100644 index 0000000..61089f6 --- /dev/null +++ b/src/EllieBot/Db/Models/Permission.cs @@ -0,0 +1,56 @@ +#nullable disable +using System.ComponentModel.DataAnnotations.Schema; +using System.Diagnostics; + +namespace EllieBot.Services.Database.Models; + + +[DebuggerDisplay("{PrimaryTarget}{SecondaryTarget} {SecondaryTargetName} {State} {PrimaryTargetId}")] +public class Permissionv2 : DbEntity, IIndexed +{ + public int? GuildConfigId { get; set; } + public int Index { get; set; } + + public PrimaryPermissionType PrimaryTarget { get; set; } + public ulong PrimaryTargetId { get; set; } + + public SecondaryPermissionType SecondaryTarget { get; set; } + public string SecondaryTargetName { get; set; } + + public bool IsCustomCommand { get; set; } + + public bool State { get; set; } + + [NotMapped] + public static Permissionv2 AllowAllPerm + => new() + { + PrimaryTarget = PrimaryPermissionType.Server, + PrimaryTargetId = 0, + SecondaryTarget = SecondaryPermissionType.AllModules, + SecondaryTargetName = "*", + State = true, + Index = 0 + }; + + public static List GetDefaultPermlist + => new() + { + AllowAllPerm + }; +} + +public enum PrimaryPermissionType +{ + User, + Channel, + Role, + Server +} + +public enum SecondaryPermissionType +{ + Module, + Command, + AllModules +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/PlantedCurrency.cs b/src/EllieBot/Db/Models/PlantedCurrency.cs new file mode 100644 index 0000000..3741530 --- /dev/null +++ b/src/EllieBot/Db/Models/PlantedCurrency.cs @@ -0,0 +1,12 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class PlantedCurrency : DbEntity +{ + public long Amount { get; set; } + public string Password { get; set; } + public ulong GuildId { get; set; } + public ulong ChannelId { get; set; } + public ulong UserId { get; set; } + public ulong MessageId { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/PlaylistSong.cs b/src/EllieBot/Db/Models/PlaylistSong.cs new file mode 100644 index 0000000..7a2078d --- /dev/null +++ b/src/EllieBot/Db/Models/PlaylistSong.cs @@ -0,0 +1,19 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class PlaylistSong : DbEntity +{ + public string Provider { get; set; } + public MusicType ProviderType { get; set; } + public string Title { get; set; } + public string Uri { get; set; } + public string Query { get; set; } +} + +public enum MusicType +{ + Radio, + YouTube, + Local, + Soundcloud +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/Poll.cs b/src/EllieBot/Db/Models/Poll.cs new file mode 100644 index 0000000..10e988c --- /dev/null +++ b/src/EllieBot/Db/Models/Poll.cs @@ -0,0 +1,17 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class Poll : DbEntity +{ + public ulong GuildId { get; set; } + public ulong ChannelId { get; set; } + public string Question { get; set; } + public IndexedCollection Answers { get; set; } + public HashSet Votes { get; set; } = new(); +} + +public class PollAnswer : DbEntity, IIndexed +{ + public int Index { get; set; } + public string Text { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/PollVote.cs b/src/EllieBot/Db/Models/PollVote.cs new file mode 100644 index 0000000..e78dadf --- /dev/null +++ b/src/EllieBot/Db/Models/PollVote.cs @@ -0,0 +1,14 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class PollVote : DbEntity +{ + public ulong UserId { get; set; } + public int VoteIndex { get; set; } + + public override int GetHashCode() + => UserId.GetHashCode(); + + public override bool Equals(object obj) + => obj is PollVote p ? p.UserId == UserId : false; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/Quote.cs b/src/EllieBot/Db/Models/Quote.cs new file mode 100644 index 0000000..529dc46 --- /dev/null +++ b/src/EllieBot/Db/Models/Quote.cs @@ -0,0 +1,26 @@ +#nullable disable +using System.ComponentModel.DataAnnotations; + +namespace EllieBot.Services.Database.Models; + +public class Quote : DbEntity +{ + public ulong GuildId { get; set; } + + [Required] + public string Keyword { get; set; } + + [Required] + public string AuthorName { get; set; } + + public ulong AuthorId { get; set; } + + [Required] + public string Text { get; set; } +} + +public enum OrderType +{ + Id = -1, + Keyword = -2 +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/ReactionRole.cs b/src/EllieBot/Db/Models/ReactionRole.cs new file mode 100644 index 0000000..4e1e4f6 --- /dev/null +++ b/src/EllieBot/Db/Models/ReactionRole.cs @@ -0,0 +1,18 @@ +#nullable disable +using System.ComponentModel.DataAnnotations; + +namespace EllieBot.Services.Database.Models; + +public class ReactionRoleV2 : DbEntity +{ + public ulong GuildId { get; set; } + public ulong ChannelId { get; set; } + + public ulong MessageId { get; set; } + + [MaxLength(100)] + public string Emote { get; set; } + public ulong RoleId { get; set; } + public int Group { get; set; } + public int LevelReq { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/Reminder.cs b/src/EllieBot/Db/Models/Reminder.cs new file mode 100644 index 0000000..df51f6d --- /dev/null +++ b/src/EllieBot/Db/Models/Reminder.cs @@ -0,0 +1,12 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class Reminder : DbEntity +{ + public DateTime When { get; set; } + public ulong ChannelId { get; set; } + public ulong ServerId { get; set; } + public ulong UserId { get; set; } + public string Message { get; set; } + public bool IsPrivate { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/Repeater.cs b/src/EllieBot/Db/Models/Repeater.cs new file mode 100644 index 0000000..7ac971e --- /dev/null +++ b/src/EllieBot/Db/Models/Repeater.cs @@ -0,0 +1,15 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class Repeater +{ + public int Id { get; set; } + public ulong GuildId { get; set; } + public ulong ChannelId { get; set; } + public ulong? LastMessageId { get; set; } + public string Message { get; set; } + public TimeSpan Interval { get; set; } + public TimeSpan? StartTimeOfDay { get; set; } + public bool NoRedundant { get; set; } + public DateTime DateAdded { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/RewardedUser.cs b/src/EllieBot/Db/Models/RewardedUser.cs new file mode 100644 index 0000000..9ac6e78 --- /dev/null +++ b/src/EllieBot/Db/Models/RewardedUser.cs @@ -0,0 +1,10 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class RewardedUser : DbEntity +{ + public ulong UserId { get; set; } + public string PlatformUserId { get; set; } + public long AmountRewardedThisMonth { get; set; } + public DateTime LastReward { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/RotatingPlayingStatus.cs b/src/EllieBot/Db/Models/RotatingPlayingStatus.cs new file mode 100644 index 0000000..a76c38f --- /dev/null +++ b/src/EllieBot/Db/Models/RotatingPlayingStatus.cs @@ -0,0 +1,8 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class RotatingPlayingStatus : DbEntity +{ + public string Status { get; set; } + public ActivityType Type { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/SelfAssignableRole.cs b/src/EllieBot/Db/Models/SelfAssignableRole.cs new file mode 100644 index 0000000..df6cd48 --- /dev/null +++ b/src/EllieBot/Db/Models/SelfAssignableRole.cs @@ -0,0 +1,11 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class SelfAssignedRole : DbEntity +{ + public ulong GuildId { get; set; } + public ulong RoleId { get; set; } + + public int Group { get; set; } + public int LevelRequirement { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/ShopEntry.cs b/src/EllieBot/Db/Models/ShopEntry.cs new file mode 100644 index 0000000..ed83cb1 --- /dev/null +++ b/src/EllieBot/Db/Models/ShopEntry.cs @@ -0,0 +1,43 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public enum ShopEntryType +{ + Role, + + List + //Infinite_List, +} + +public class ShopEntry : DbEntity, IIndexed +{ + public int Index { get; set; } + public int Price { get; set; } + public string Name { get; set; } + public ulong AuthorId { get; set; } + + public ShopEntryType Type { get; set; } + + //role + public string RoleName { get; set; } + public ulong RoleId { get; set; } + + //list + public HashSet Items { get; set; } = new(); + public ulong? RoleRequirement { get; set; } +} + +public class ShopEntryItem : DbEntity +{ + public string Text { get; set; } + + public override bool Equals(object obj) + { + if (obj is null || GetType() != obj.GetType()) + return false; + return ((ShopEntryItem)obj).Text == Text; + } + + public override int GetHashCode() + => Text.GetHashCode(StringComparison.InvariantCulture); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/SlowmodeIgnoredRole.cs b/src/EllieBot/Db/Models/SlowmodeIgnoredRole.cs new file mode 100644 index 0000000..a2d6bdd --- /dev/null +++ b/src/EllieBot/Db/Models/SlowmodeIgnoredRole.cs @@ -0,0 +1,20 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class SlowmodeIgnoredRole : DbEntity +{ + public ulong RoleId { get; set; } + + // override object.Equals + public override bool Equals(object obj) + { + if (obj is null || GetType() != obj.GetType()) + return false; + + return ((SlowmodeIgnoredRole)obj).RoleId == RoleId; + } + + // override object.GetHashCode + public override int GetHashCode() + => RoleId.GetHashCode(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/SlowmodeIgnoredUser.cs b/src/EllieBot/Db/Models/SlowmodeIgnoredUser.cs new file mode 100644 index 0000000..92597c3 --- /dev/null +++ b/src/EllieBot/Db/Models/SlowmodeIgnoredUser.cs @@ -0,0 +1,20 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class SlowmodeIgnoredUser : DbEntity +{ + public ulong UserId { get; set; } + + // override object.Equals + public override bool Equals(object obj) + { + if (obj is null || GetType() != obj.GetType()) + return false; + + return ((SlowmodeIgnoredUser)obj).UserId == UserId; + } + + // override object.GetHashCode + public override int GetHashCode() + => UserId.GetHashCode(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/StreamOnlineMessage.cs b/src/EllieBot/Db/Models/StreamOnlineMessage.cs new file mode 100644 index 0000000..e9b1e24 --- /dev/null +++ b/src/EllieBot/Db/Models/StreamOnlineMessage.cs @@ -0,0 +1,13 @@ +#nullable disable +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db.Models; + +public class StreamOnlineMessage : DbEntity +{ + public ulong ChannelId { get; set; } + public ulong MessageId { get; set; } + + public FollowedStream.FType Type { get; set; } + public string Name { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/StreamRoleSettings.cs b/src/EllieBot/Db/Models/StreamRoleSettings.cs new file mode 100644 index 0000000..dbab2d0 --- /dev/null +++ b/src/EllieBot/Db/Models/StreamRoleSettings.cs @@ -0,0 +1,68 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class StreamRoleSettings : DbEntity +{ + public int GuildConfigId { get; set; } + public GuildConfig GuildConfig { get; set; } + + /// + /// Whether the feature is enabled in the guild. + /// + public bool Enabled { get; set; } + + /// + /// Id of the role to give to the users in the role 'FromRole' when they start streaming + /// + public ulong AddRoleId { get; set; } + + /// + /// Id of the role whose users are eligible to get the 'AddRole' + /// + public ulong FromRoleId { get; set; } + + /// + /// If set, feature will only apply to users who have this keyword in their streaming status. + /// + public string Keyword { get; set; } + + /// + /// A collection of whitelisted users' IDs. Whitelisted users don't require 'keyword' in + /// order to get the stream role. + /// + public HashSet Whitelist { get; set; } = new(); + + /// + /// A collection of blacklisted users' IDs. Blacklisted useres will never get the stream role. + /// + public HashSet Blacklist { get; set; } = new(); +} + +public class StreamRoleBlacklistedUser : DbEntity +{ + public ulong UserId { get; set; } + public string Username { get; set; } + + public override bool Equals(object obj) + { + if (obj is not StreamRoleBlacklistedUser x) + return false; + + return x.UserId == UserId; + } + + public override int GetHashCode() + => UserId.GetHashCode(); +} + +public class StreamRoleWhitelistedUser : DbEntity +{ + public ulong UserId { get; set; } + public string Username { get; set; } + + public override bool Equals(object obj) + => obj is StreamRoleWhitelistedUser x ? x.UserId == UserId : false; + + public override int GetHashCode() + => UserId.GetHashCode(); +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/UnbanTimer.cs b/src/EllieBot/Db/Models/UnbanTimer.cs new file mode 100644 index 0000000..b017624 --- /dev/null +++ b/src/EllieBot/Db/Models/UnbanTimer.cs @@ -0,0 +1,14 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class UnbanTimer : DbEntity +{ + public ulong UserId { get; set; } + public DateTime UnbanAt { get; set; } + + public override int GetHashCode() + => UserId.GetHashCode(); + + public override bool Equals(object obj) + => obj is UnbanTimer ut ? ut.UserId == UserId : false; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/UnmuteTimer.cs b/src/EllieBot/Db/Models/UnmuteTimer.cs new file mode 100644 index 0000000..4619b68 --- /dev/null +++ b/src/EllieBot/Db/Models/UnmuteTimer.cs @@ -0,0 +1,14 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class UnmuteTimer : DbEntity +{ + public ulong UserId { get; set; } + public DateTime UnmuteAt { get; set; } + + public override int GetHashCode() + => UserId.GetHashCode(); + + public override bool Equals(object obj) + => obj is UnmuteTimer ut ? ut.UserId == UserId : false; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/UnroleTimer.cs b/src/EllieBot/Db/Models/UnroleTimer.cs new file mode 100644 index 0000000..f7c7f8d --- /dev/null +++ b/src/EllieBot/Db/Models/UnroleTimer.cs @@ -0,0 +1,15 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class UnroleTimer : DbEntity +{ + public ulong UserId { get; set; } + public ulong RoleId { get; set; } + public DateTime UnbanAt { get; set; } + + public override int GetHashCode() + => UserId.GetHashCode() ^ RoleId.GetHashCode(); + + public override bool Equals(object obj) + => obj is UnroleTimer ut ? ut.UserId == UserId && ut.RoleId == RoleId : false; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/UserXpStats.cs b/src/EllieBot/Db/Models/UserXpStats.cs new file mode 100644 index 0000000..e79f735 --- /dev/null +++ b/src/EllieBot/Db/Models/UserXpStats.cs @@ -0,0 +1,13 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class UserXpStats : DbEntity +{ + public ulong UserId { get; set; } + public ulong GuildId { get; set; } + public long Xp { get; set; } + public long AwardedXp { get; set; } + public XpNotificationLocation NotifyOnLevelUp { get; set; } +} + +public enum XpNotificationLocation { None, Dm, Channel } \ No newline at end of file diff --git a/src/EllieBot/Db/Models/VcRoleInfo.cs b/src/EllieBot/Db/Models/VcRoleInfo.cs new file mode 100644 index 0000000..b80fc17 --- /dev/null +++ b/src/EllieBot/Db/Models/VcRoleInfo.cs @@ -0,0 +1,8 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class VcRoleInfo : DbEntity +{ + public ulong VoiceChannelId { get; set; } + public ulong RoleId { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/Waifu.cs b/src/EllieBot/Db/Models/Waifu.cs new file mode 100644 index 0000000..a140be5 --- /dev/null +++ b/src/EllieBot/Db/Models/Waifu.cs @@ -0,0 +1,75 @@ +#nullable disable +using EllieBot.Db.Models; + +namespace EllieBot.Services.Database.Models; + +public class WaifuInfo : DbEntity +{ + public int WaifuId { get; set; } + public DiscordUser Waifu { get; set; } + + public int? ClaimerId { get; set; } + public DiscordUser Claimer { get; set; } + + public int? AffinityId { get; set; } + public DiscordUser Affinity { get; set; } + + public long Price { get; set; } + public List Items { get; set; } = new(); + + public override string ToString() + { + var claimer = "no one"; + var status = string.Empty; + + var waifuUsername = Waifu.Username.TrimTo(20); + var claimerUsername = Claimer?.Username.TrimTo(20); + + if (ClaimerId is not null) + claimer = $"{claimerUsername}#{Claimer.Discriminator}"; + if (AffinityId is null) + status = $"... but {waifuUsername}'s heart is empty"; + else if (AffinityId == ClaimerId) + status = $"... and {waifuUsername} likes {claimerUsername} too <3"; + else + { + status = + $"... but {waifuUsername}'s heart belongs to {Affinity.Username.TrimTo(20)}#{Affinity.Discriminator}"; + } + + return $"**{waifuUsername}#{Waifu.Discriminator}** - claimed by **{claimer}**\n\t{status}"; + } +} + +public class WaifuLbResult +{ + public string Username { get; set; } + public string Discrim { get; set; } + + public string Claimer { get; set; } + public string ClaimerDiscrim { get; set; } + + public string Affinity { get; set; } + public string AffinityDiscrim { get; set; } + + public long Price { get; set; } + + public override string ToString() + { + var claimer = "no one"; + var status = string.Empty; + + var waifuUsername = Username.TrimTo(20); + var claimerUsername = Claimer?.TrimTo(20); + + if (Claimer is not null) + claimer = $"{claimerUsername}#{ClaimerDiscrim}"; + if (Affinity is null) + status = $"... but {waifuUsername}'s heart is empty"; + else if (Affinity + AffinityDiscrim == Claimer + ClaimerDiscrim) + status = $"... and {waifuUsername} likes {claimerUsername} too <3"; + else + status = $"... but {waifuUsername}'s heart belongs to {Affinity.TrimTo(20)}#{AffinityDiscrim}"; + return $"**{waifuUsername}#{Discrim}** - claimed by **{claimer}**\n\t{status}"; + } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/WaifuItem.cs b/src/EllieBot/Db/Models/WaifuItem.cs new file mode 100644 index 0000000..32df5ba --- /dev/null +++ b/src/EllieBot/Db/Models/WaifuItem.cs @@ -0,0 +1,10 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class WaifuItem +{ + public WaifuInfo WaifuInfo { get; set; } + public int? WaifuInfoId { get; set; } + public string ItemEmoji { get; set; } + public string Name { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/WaifuUpdate.cs b/src/EllieBot/Db/Models/WaifuUpdate.cs new file mode 100644 index 0000000..70e8960 --- /dev/null +++ b/src/EllieBot/Db/Models/WaifuUpdate.cs @@ -0,0 +1,23 @@ +#nullable disable +using EllieBot.Db.Models; + +namespace EllieBot.Services.Database.Models; + +public class WaifuUpdate : DbEntity +{ + public int UserId { get; set; } + public DiscordUser User { get; set; } + public WaifuUpdateType UpdateType { get; set; } + + public int? OldId { get; set; } + public DiscordUser Old { get; set; } + + public int? NewId { get; set; } + public DiscordUser New { get; set; } +} + +public enum WaifuUpdateType +{ + AffinityChanged, + Claimed +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/WarnExpireAction.cs b/src/EllieBot/Db/Models/WarnExpireAction.cs new file mode 100644 index 0000000..caa595f --- /dev/null +++ b/src/EllieBot/Db/Models/WarnExpireAction.cs @@ -0,0 +1,8 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public enum WarnExpireAction +{ + Clear, + Delete +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/Warning.cs b/src/EllieBot/Db/Models/Warning.cs new file mode 100644 index 0000000..9bd9b41 --- /dev/null +++ b/src/EllieBot/Db/Models/Warning.cs @@ -0,0 +1,13 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class Warning : DbEntity +{ + public ulong GuildId { get; set; } + public ulong UserId { get; set; } + public string Reason { get; set; } + public bool Forgiven { get; set; } + public string ForgivenBy { get; set; } + public string Moderator { get; set; } + public long Weight { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/WarningPunishment.cs b/src/EllieBot/Db/Models/WarningPunishment.cs new file mode 100644 index 0000000..02590cd --- /dev/null +++ b/src/EllieBot/Db/Models/WarningPunishment.cs @@ -0,0 +1,10 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class WarningPunishment : DbEntity +{ + public int Count { get; set; } + public PunishmentAction Punishment { get; set; } + public int Time { get; set; } + public ulong? RoleId { get; set; } +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/XpSettings.cs b/src/EllieBot/Db/Models/XpSettings.cs new file mode 100644 index 0000000..066cd07 --- /dev/null +++ b/src/EllieBot/Db/Models/XpSettings.cs @@ -0,0 +1,62 @@ +#nullable disable +namespace EllieBot.Services.Database.Models; + +public class XpSettings : DbEntity +{ + public int GuildConfigId { get; set; } + public GuildConfig GuildConfig { get; set; } + + public HashSet RoleRewards { get; set; } = new(); + public HashSet CurrencyRewards { get; set; } = new(); + public HashSet ExclusionList { get; set; } = new(); + public bool ServerExcluded { get; set; } +} + +public enum ExcludedItemType { Channel, Role } + +public class XpRoleReward : DbEntity +{ + public int XpSettingsId { get; set; } + public XpSettings XpSettings { get; set; } + + public int Level { get; set; } + public ulong RoleId { get; set; } + + /// + /// Whether the role should be removed (true) or added (false) + /// + public bool Remove { get; set; } + + public override int GetHashCode() + => Level.GetHashCode() ^ XpSettingsId.GetHashCode(); + + public override bool Equals(object obj) + => obj is XpRoleReward xrr && xrr.Level == Level && xrr.XpSettingsId == XpSettingsId; +} + +public class XpCurrencyReward : DbEntity +{ + public int XpSettingsId { get; set; } + public XpSettings XpSettings { get; set; } + + public int Level { get; set; } + public int Amount { get; set; } + + public override int GetHashCode() + => Level.GetHashCode() ^ XpSettingsId.GetHashCode(); + + public override bool Equals(object obj) + => obj is XpCurrencyReward xrr && xrr.Level == Level && xrr.XpSettingsId == XpSettingsId; +} + +public class ExcludedItem : DbEntity +{ + public ulong ItemId { get; set; } + public ExcludedItemType ItemType { get; set; } + + public override int GetHashCode() + => ItemId.GetHashCode() ^ ItemType.GetHashCode(); + + public override bool Equals(object obj) + => obj is ExcludedItem ei && ei.ItemId == ItemId && ei.ItemType == ItemType; +} \ No newline at end of file diff --git a/src/EllieBot/Db/Models/XpShotOwnedItem.cs b/src/EllieBot/Db/Models/XpShotOwnedItem.cs new file mode 100644 index 0000000..aaba2f6 --- /dev/null +++ b/src/EllieBot/Db/Models/XpShotOwnedItem.cs @@ -0,0 +1,18 @@ +#nullable disable +using EllieBot.Services.Database.Models; + +namespace EllieBot.Db.Models; + +public class XpShopOwnedItem : DbEntity +{ + public ulong UserId { get; set; } + public XpShopItemType ItemType { get; set; } + public bool IsUsing { get; set; } + public string ItemKey { get; set; } +} + +public enum XpShopItemType +{ + Background = 0, + Frame = 1, +} \ No newline at end of file diff --git a/src/EllieBot/Db/MysqlContext.cs b/src/EllieBot/Db/MysqlContext.cs new file mode 100644 index 0000000..5406c1d --- /dev/null +++ b/src/EllieBot/Db/MysqlContext.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore; +using EllieBot.Db.Models; + +namespace EllieBot.Services.Database; + +public sealed class MysqlContext : EllieContext +{ + private readonly string _connStr; + private readonly string _version; + + protected override string CurrencyTransactionOtherIdDefaultValue + => "NULL"; + + public MysqlContext(string connStr = "Server=localhost", string version = "8.0") + { + _connStr = connStr; + _version = version; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + optionsBuilder + .UseLowerCaseNamingConvention() + .UseMySql(_connStr, ServerVersion.Parse(_version)); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + // mysql is case insensitive by default + // we can set binary collation to change that + modelBuilder.Entity() + .Property(x => x.Name) + .UseCollation("utf8mb4_bin"); + } +} \ No newline at end of file diff --git a/src/EllieBot/Db/PostgreSqlContext.cs b/src/EllieBot/Db/PostgreSqlContext.cs new file mode 100644 index 0000000..f254aaf --- /dev/null +++ b/src/EllieBot/Db/PostgreSqlContext.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore; + +namespace EllieBot.Services.Database; + +public class PostgreSqlContext : EllieContext +{ + private readonly string _connStr; + + protected override string CurrencyTransactionOtherIdDefaultValue + => "NULL"; + + public PostgreSqlContext(string connStr = "Host=localhost") + { + _connStr = connStr; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); + + base.OnConfiguring(optionsBuilder); + optionsBuilder + .UseLowerCaseNamingConvention() + .UseNpgsql(_connStr); + } +} \ No newline at end of file diff --git a/src/EllieBot/Db/SqliteContext.cs b/src/EllieBot/Db/SqliteContext.cs new file mode 100644 index 0000000..6744423 --- /dev/null +++ b/src/EllieBot/Db/SqliteContext.cs @@ -0,0 +1,26 @@ +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; + +namespace EllieBot.Services.Database; + +public sealed class SqliteContext : EllieContext +{ + private readonly string _connectionString; + + protected override string CurrencyTransactionOtherIdDefaultValue + => "NULL"; + + public SqliteContext(string connectionString = "Data Source=data/EllieBot.db", int commandTimeout = 60) + { + _connectionString = connectionString; + Database.SetCommandTimeout(commandTimeout); + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + var builder = new SqliteConnectionStringBuilder(_connectionString); + builder.DataSource = Path.Combine(AppContext.BaseDirectory, builder.DataSource); + optionsBuilder.UseSqlite(builder.ToString()); + } +} \ No newline at end of file