diff --git a/Ellie.sln b/Ellie.sln
index 6b140ba..fc04b66 100644
--- a/Ellie.sln
+++ b/Ellie.sln
@@ -34,6 +34,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ellise.Common", "src\Ellise
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ellie.Econ", "src\Ellie.Econ\Ellie.Econ.csproj", "{5E69237D-37C5-4EDF-9037-9C8FDF23FD86}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ellie.Bot.Modules.Expresssions", "src\Ellie.Bot.Modules.Expresssions\Ellie.Bot.Modules.Expresssions.csproj", "{F4D3A613-320A-40DD-975A-247186A20173}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ellie.Bot.Common", "src\Ellie.Bot.Common\Ellie.Bot.Common.csproj", "{3EC0F005-560F-4E90-88CF-199520133BBA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ellie.Bot.Db", "src\Ellie.Bot.Db\Ellie.Bot.Db.csproj", "{D3411F6C-320C-456D-BA86-24481EB000EA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ellie.Core.Generators.Cloneable", "src\Ellie.Core.Generators.Cloneable\Ellie.Core.Generators.Cloneable.csproj", "{AFA3DD12-0F98-4754-ADD7-9FF3C1A37C90}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -76,6 +84,22 @@ Global
{5E69237D-37C5-4EDF-9037-9C8FDF23FD86}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E69237D-37C5-4EDF-9037-9C8FDF23FD86}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E69237D-37C5-4EDF-9037-9C8FDF23FD86}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F4D3A613-320A-40DD-975A-247186A20173}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F4D3A613-320A-40DD-975A-247186A20173}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F4D3A613-320A-40DD-975A-247186A20173}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F4D3A613-320A-40DD-975A-247186A20173}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3EC0F005-560F-4E90-88CF-199520133BBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3EC0F005-560F-4E90-88CF-199520133BBA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3EC0F005-560F-4E90-88CF-199520133BBA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3EC0F005-560F-4E90-88CF-199520133BBA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D3411F6C-320C-456D-BA86-24481EB000EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D3411F6C-320C-456D-BA86-24481EB000EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D3411F6C-320C-456D-BA86-24481EB000EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D3411F6C-320C-456D-BA86-24481EB000EA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AFA3DD12-0F98-4754-ADD7-9FF3C1A37C90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AFA3DD12-0F98-4754-ADD7-9FF3C1A37C90}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AFA3DD12-0F98-4754-ADD7-9FF3C1A37C90}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AFA3DD12-0F98-4754-ADD7-9FF3C1A37C90}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -91,6 +115,10 @@ Global
{D6CF9ABE-205E-4699-90CA-0F18ED236490} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}
{227F78CC-633E-4B1F-A12B-DF8BFF30549C} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}
{5E69237D-37C5-4EDF-9037-9C8FDF23FD86} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}
+ {F4D3A613-320A-40DD-975A-247186A20173} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}
+ {3EC0F005-560F-4E90-88CF-199520133BBA} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}
+ {D3411F6C-320C-456D-BA86-24481EB000EA} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}
+ {AFA3DD12-0F98-4754-ADD7-9FF3C1A37C90} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {878761F1-C7B5-4D38-A00D-3377D703EBBA}
diff --git a/src/Ellie.Bot.Db/Ellie.Bot.Db.csproj b/src/Ellie.Bot.Db/Ellie.Bot.Db.csproj
new file mode 100644
index 0000000..d4079e8
--- /dev/null
+++ b/src/Ellie.Bot.Db/Ellie.Bot.Db.csproj
@@ -0,0 +1,31 @@
+
+
+
+ net7.0
+ enable
+ enable
+ Ellie.Db
+ false
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ellie.Bot.Db/Extensions/ClubExtensions.cs b/src/Ellie.Bot.Db/Extensions/ClubExtensions.cs
new file mode 100644
index 0000000..be40d67
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/ClubExtensions.cs
@@ -0,0 +1,34 @@
+#nullable disable
+using Microsoft.EntityFrameworkCore;
+using Ellie.Db.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Extensions/CurrencyTransactionExtensions.cs b/src/Ellie.Bot.Db/Extensions/CurrencyTransactionExtensions.cs
new file mode 100644
index 0000000..72426da
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/CurrencyTransactionExtensions.cs
@@ -0,0 +1,20 @@
+#nullable disable
+using LinqToDB.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Extensions/DbExtensions.cs b/src/Ellie.Bot.Db/Extensions/DbExtensions.cs
new file mode 100644
index 0000000..5c7c843
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/DbExtensions.cs
@@ -0,0 +1,12 @@
+#nullable disable
+using Microsoft.EntityFrameworkCore;
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Extensions/DiscordUserExtensions.cs b/src/Ellie.Bot.Db/Extensions/DiscordUserExtensions.cs
new file mode 100644
index 0000000..2ffb6da
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/DiscordUserExtensions.cs
@@ -0,0 +1,126 @@
+#nullable disable
+using LinqToDB;
+using LinqToDB.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
+using Ellie.Db.Models;
+
+namespace Ellie.Db;
+
+public static class DiscordUserExtensions
+{
+ public static Task GetByUserIdAsync(
+ this IQueryable set,
+ ulong userId)
+ => set.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId);
+
+ public static void EnsureUserCreated(
+ this DbContext ctx,
+ ulong userId,
+ string username,
+ string discrim,
+ string avatarId)
+ => ctx.GetTable()
+ .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 DbContext ctx,
+ ulong userId)
+ => ctx.GetTable()
+ .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 DbContext ctx,
+ ulong userId,
+ string username,
+ string discrim,
+ string avatarId,
+ Func, IQueryable> includes = null)
+ {
+ ctx.EnsureUserCreated(userId, username, discrim, avatarId);
+
+ IQueryable queryable = ctx.Set();
+ if (includes is not null)
+ queryable = includes(queryable);
+ return queryable.First(u => u.UserId == userId);
+ }
+
+
+ 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/Ellie.Bot.Db/Extensions/EllieExpressionExtensions.cs b/src/Ellie.Bot.Db/Extensions/EllieExpressionExtensions.cs
new file mode 100644
index 0000000..79b8f1e
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/EllieExpressionExtensions.cs
@@ -0,0 +1,15 @@
+#nullable disable
+using LinqToDB;
+using Microsoft.EntityFrameworkCore;
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Extensions/GuildConfigExtensions.cs b/src/Ellie.Bot.Db/Extensions/GuildConfigExtensions.cs
new file mode 100644
index 0000000..da7ff8f
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/GuildConfigExtensions.cs
@@ -0,0 +1,229 @@
+#nullable disable
+using Microsoft.EntityFrameworkCore;
+using Ellie.Db.Models;
+using Ellie.Services.Database;
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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 EllieBaseContext 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,
+ IReadOnlyList 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 DbContext ctx,
+ ulong guildId,
+ Func, IQueryable> includes)
+ {
+ GuildConfig config;
+
+ if (includes is null)
+ config = ctx.Set().IncludeEverything().FirstOrDefault(c => c.GuildId == guildId);
+ else
+ {
+ var set = includes(ctx.Set());
+ config = set.FirstOrDefault(c => c.GuildId == guildId);
+ }
+
+ if (config is null)
+ {
+ ctx.Set().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 EllieBaseContext ctx, ulong guildId)
+ {
+ var logSetting = ctx.Set()
+ .AsQueryable()
+ .Include(x => x.LogIgnores)
+ .Where(x => x.GuildId == guildId)
+ .FirstOrDefault();
+
+ if (logSetting is null)
+ {
+ ctx.Set()
+ .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 EllieBaseContext ctx, ulong guildId)
+ {
+ var config = ctx.Set().AsQueryable()
+ .Where(gc => gc.GuildId == guildId)
+ .Include(gc => gc.Permissions)
+ .FirstOrDefault();
+
+ if (config is null) // if there is no guildconfig, create new one
+ {
+ ctx.Set().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 DbContext 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/Ellie.Bot.Db/Extensions/MusicPlayerSettingsExtensions.cs b/src/Ellie.Bot.Db/Extensions/MusicPlayerSettingsExtensions.cs
new file mode 100644
index 0000000..5fa94ac
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/MusicPlayerSettingsExtensions.cs
@@ -0,0 +1,27 @@
+#nullable disable
+using Microsoft.EntityFrameworkCore;
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Extensions/MusicPlaylistExtensions.cs b/src/Ellie.Bot.Db/Extensions/MusicPlaylistExtensions.cs
new file mode 100644
index 0000000..c140b4b
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/MusicPlaylistExtensions.cs
@@ -0,0 +1,19 @@
+#nullable disable
+using Microsoft.EntityFrameworkCore;
+using Ellie.Services.Database.Models;
+
+namespace Ellie.Db;
+
+public static class MusicPlaylistExtensions
+{
+ public static List GetPlaylistsOnPage(this DbSet playlists, int num)
+ {
+ if (num < 1)
+ 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/Ellie.Bot.Db/Extensions/QuoteExtensions.cs b/src/Ellie.Bot.Db/Extensions/QuoteExtensions.cs
new file mode 100644
index 0000000..6f6bb6a
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/QuoteExtensions.cs
@@ -0,0 +1,57 @@
+#nullable disable
+using Microsoft.EntityFrameworkCore;
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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)
+ {
+ // todo figure shuffle out
+ return (await quotes.AsQueryable().Where(q => q.GuildId == guildId && q.Keyword == keyword).ToListAsync())
+ .Shuffle()
+ .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/Ellie.Bot.Db/Extensions/ReminderExtensions.cs b/src/Ellie.Bot.Db/Extensions/ReminderExtensions.cs
new file mode 100644
index 0000000..cd64d29
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/ReminderExtensions.cs
@@ -0,0 +1,23 @@
+#nullable disable
+using Microsoft.EntityFrameworkCore;
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Extensions/SelfAssignableRolesExtensions.cs b/src/Ellie.Bot.Db/Extensions/SelfAssignableRolesExtensions.cs
new file mode 100644
index 0000000..2d3e54e
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/SelfAssignableRolesExtensions.cs
@@ -0,0 +1,22 @@
+#nullable disable
+using Microsoft.EntityFrameworkCore;
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Extensions/UserXpExtensions.cs b/src/Ellie.Bot.Db/Extensions/UserXpExtensions.cs
new file mode 100644
index 0000000..4951b5c
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/UserXpExtensions.cs
@@ -0,0 +1,72 @@
+#nullable disable
+using LinqToDB;
+using LinqToDB.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
+using Ellie.Services.Database;
+using Ellie.Services.Database.Models;
+
+namespace Ellie.Db;
+
+public static class UserXpExtensions
+{
+ public static UserXpStats GetOrCreateUserXpStats(this DbContext ctx, ulong guildId, ulong userId)
+ {
+ var usr = ctx.Set().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);
+
+ public static async Task GetLevelDataFor(this ITable userXp, ulong guildId, ulong userId)
+ => await userXp
+ .Where(x => x.GuildId == guildId && x.UserId == userId)
+ .FirstOrDefaultAsyncLinqToDB() is UserXpStats uxs
+ ? new(uxs.Xp + uxs.AwardedXp)
+ : new(0);
+
+}
\ No newline at end of file
diff --git a/src/Ellie.Bot.Db/Extensions/WaifuExtensions.cs b/src/Ellie.Bot.Db/Extensions/WaifuExtensions.cs
new file mode 100644
index 0000000..1f3e73f
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/WaifuExtensions.cs
@@ -0,0 +1,145 @@
+#nullable disable
+using LinqToDB;
+using LinqToDB.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
+using Ellie.Db.Models;
+using Ellie.Services.Database;
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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 DbContext ctx, ulong userId)
+ {
+ await ctx.Set()
+ .ToLinqToDBTable()
+ .InsertOrUpdateAsync(() => new()
+ {
+ AffinityId = null,
+ ClaimerId = null,
+ Price = 1,
+ WaifuId = ctx.Set().Where(x => x.UserId == userId).Select(x => x.Id).First()
+ },
+ _ => new(),
+ () => new()
+ {
+ WaifuId = ctx.Set().Where(x => x.UserId == userId).Select(x => x.Id).First()
+ });
+
+ var toReturn = ctx.Set().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.Set().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/Ellie.Bot.Db/Extensions/WarningExtensions.cs b/src/Ellie.Bot.Db/Extensions/WarningExtensions.cs
new file mode 100644
index 0000000..31275b9
--- /dev/null
+++ b/src/Ellie.Bot.Db/Extensions/WarningExtensions.cs
@@ -0,0 +1,60 @@
+#nullable disable
+using Microsoft.EntityFrameworkCore;
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/GlobalUsings.cs b/src/Ellie.Bot.Db/GlobalUsings.cs
new file mode 100644
index 0000000..7621f40
--- /dev/null
+++ b/src/Ellie.Bot.Db/GlobalUsings.cs
@@ -0,0 +1,31 @@
+// // global using System.Collections.Concurrent;
+// global using NonBlocking;
+//
+// // packages
+// global using Serilog;
+// global using Humanizer;
+//
+// // nadekobot
+global using Ellie;
+global using Ellie.Services;
+global using Ellise.Common;
+// global using NadekoBot.Common; // old + nadekobot specific things
+// global using NadekoBot.Common.Attributes;
+// global using NadekoBot.Extensions;
+// global using Nadeko.Snake;
+
+// // discord
+// global using Discord;
+// global using Discord.Commands;
+// global using Discord.Net;
+// global using Discord.WebSocket;
+//
+// // aliases
+// global using GuildPerm = Discord.GuildPermission;
+// global using ChannelPerm = Discord.ChannelPermission;
+// global using BotPermAttribute = Discord.Commands.RequireBotPermissionAttribute;
+// global using LeftoverAttribute = Discord.Commands.RemainderAttribute;
+// global using TypeReaderResult = NadekoBot.Common.TypeReaders.TypeReaderResult;
+
+// non-essential
+// global using JetBrains.Annotations;
\ No newline at end of file
diff --git a/src/Ellie.Bot.Db/Helpers/ActivityType.cs b/src/Ellie.Bot.Db/Helpers/ActivityType.cs
new file mode 100644
index 0000000..6850963
--- /dev/null
+++ b/src/Ellie.Bot.Db/Helpers/ActivityType.cs
@@ -0,0 +1,6 @@
+namespace Ellie.Bot.Db;
+
+public enum ActivityType
+{
+
+}
\ No newline at end of file
diff --git a/src/Ellie.Bot.Db/Helpers/GuildPerm.cs b/src/Ellie.Bot.Db/Helpers/GuildPerm.cs
new file mode 100644
index 0000000..5ab60e9
--- /dev/null
+++ b/src/Ellie.Bot.Db/Helpers/GuildPerm.cs
@@ -0,0 +1,47 @@
+namespace Ellie.Bot.Db;
+
+[Flags]
+public enum GuildPerm : ulong
+{
+ CreateInstantInvite = 1,
+ KickMembers = 2,
+ BanMembers = 4,
+ Administrator = 8,
+ ManageChannels = 16, // 0x0000000000000010
+ ManageGuild = 32, // 0x0000000000000020
+ ViewGuildInsights = 524288, // 0x0000000000080000
+ AddReactions = 64, // 0x0000000000000040
+ ViewAuditLog = 128, // 0x0000000000000080
+ ViewChannel = 1024, // 0x0000000000000400
+ SendMessages = 2048, // 0x0000000000000800
+ SendTTSMessages = 4096, // 0x0000000000001000
+ ManageMessages = 8192, // 0x0000000000002000
+ EmbedLinks = 16384, // 0x0000000000004000
+ AttachFiles = 32768, // 0x0000000000008000
+ ReadMessageHistory = 65536, // 0x0000000000010000
+ MentionEveryone = 131072, // 0x0000000000020000
+ UseExternalEmojis = 262144, // 0x0000000000040000
+ Connect = 1048576, // 0x0000000000100000
+ Speak = 2097152, // 0x0000000000200000
+ MuteMembers = 4194304, // 0x0000000000400000
+ DeafenMembers = 8388608, // 0x0000000000800000
+ MoveMembers = 16777216, // 0x0000000001000000
+ UseVAD = 33554432, // 0x0000000002000000
+ PrioritySpeaker = 256, // 0x0000000000000100
+ Stream = 512, // 0x0000000000000200
+ ChangeNickname = 67108864, // 0x0000000004000000
+ ManageNicknames = 134217728, // 0x0000000008000000
+ ManageRoles = 268435456, // 0x0000000010000000
+ ManageWebhooks = 536870912, // 0x0000000020000000
+ ManageEmojisAndStickers = 1073741824, // 0x0000000040000000
+ UseApplicationCommands = 2147483648, // 0x0000000080000000
+ RequestToSpeak = 4294967296, // 0x0000000100000000
+ ManageEvents = 8589934592, // 0x0000000200000000
+ ManageThreads = 17179869184, // 0x0000000400000000
+ CreatePublicThreads = 34359738368, // 0x0000000800000000
+ CreatePrivateThreads = 68719476736, // 0x0000001000000000
+ UseExternalStickers = 137438953472, // 0x0000002000000000
+ SendMessagesInThreads = 274877906944, // 0x0000004000000000
+ StartEmbeddedActivities = 549755813888, // 0x0000008000000000
+ ModerateMembers = 1099511627776, // 0x0000010000000000
+}
\ No newline at end of file
diff --git a/src/Ellie.Bot.Db/LevelStats.cs b/src/Ellie.Bot.Db/LevelStats.cs
new file mode 100644
index 0000000..5ae7349
--- /dev/null
+++ b/src/Ellie.Bot.Db/LevelStats.cs
@@ -0,0 +1,41 @@
+#nullable disable
+
+namespace Ellie.Db;
+
+public readonly struct LevelStats
+{
+ public const int XP_REQUIRED_LVL_1 = 36;
+
+ public long Level { get; }
+ public long LevelXp { get; }
+ public long RequiredXp { get; }
+ public long TotalXp { get; }
+
+ public LevelStats(long xp)
+ {
+ if (xp < 0)
+ xp = 0;
+
+ TotalXp = xp;
+
+ const int baseXp = XP_REQUIRED_LVL_1;
+
+ var required = baseXp;
+ var totalXp = 0;
+ var lvl = 1;
+ while (true)
+ {
+ required = (int)(baseXp + (baseXp / 4.0 * (lvl - 1)));
+
+ if (required + totalXp > xp)
+ break;
+
+ totalXp += required;
+ lvl++;
+ }
+
+ Level = lvl - 1;
+ LevelXp = xp - totalXp;
+ RequiredXp = required;
+ }
+}
diff --git a/src/Ellie.Bot.Db/Models/AntiProtection.cs b/src/Ellie.Bot.Db/Models/AntiProtection.cs
new file mode 100644
index 0000000..fe27693
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/AntiProtection.cs
@@ -0,0 +1,67 @@
+#nullable disable
+namespace Ellie.Services.Database.Models;
+
+
+// todo db required, nullable?
+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 for supported Actions, like:
+ /// Mute, Chatmute, Voicemute, etc...
+ ///
+ public int PunishDuration { 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/Ellie.Bot.Db/Models/AutoCommand.cs b/src/Ellie.Bot.Db/Models/AutoCommand.cs
new file mode 100644
index 0000000..7968d69
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/AutoCommand.cs
@@ -0,0 +1,14 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/AutoPublishChannel.cs b/src/Ellie.Bot.Db/Models/AutoPublishChannel.cs
new file mode 100644
index 0000000..7ad898d
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/AutoPublishChannel.cs
@@ -0,0 +1,9 @@
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Models/AutoTranslateChannel.cs b/src/Ellie.Bot.Db/Models/AutoTranslateChannel.cs
new file mode 100644
index 0000000..1b1f177
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/AutoTranslateChannel.cs
@@ -0,0 +1,10 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/AutoTranslateUser.cs b/src/Ellie.Bot.Db/Models/AutoTranslateUser.cs
new file mode 100644
index 0000000..b106f7e
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/AutoTranslateUser.cs
@@ -0,0 +1,11 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/BanTemplate.cs b/src/Ellie.Bot.Db/Models/BanTemplate.cs
new file mode 100644
index 0000000..2ccdb4c
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/BanTemplate.cs
@@ -0,0 +1,9 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/BankUser.cs b/src/Ellie.Bot.Db/Models/BankUser.cs
new file mode 100644
index 0000000..5ad0846
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/BankUser.cs
@@ -0,0 +1,9 @@
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Models/BlacklistEntry.cs b/src/Ellie.Bot.Db/Models/BlacklistEntry.cs
new file mode 100644
index 0000000..74dcd0b
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/BlacklistEntry.cs
@@ -0,0 +1,15 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/ClubInfo.cs b/src/Ellie.Bot.Db/Models/ClubInfo.cs
new file mode 100644
index 0000000..6cefb77
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/ClubInfo.cs
@@ -0,0 +1,42 @@
+#nullable disable
+using Ellie.Services.Database.Models;
+using System.ComponentModel.DataAnnotations;
+
+namespace Ellie.Db.Models;
+
+public class ClubInfo : DbEntity
+{
+ [MaxLength(20)]
+ public string Name { get; set; }
+ public string Description { get; set; }
+ public string ImageUrl { get; set; } = string.Empty;
+
+ 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/Ellie.Bot.Db/Models/CommandAlias.cs b/src/Ellie.Bot.Db/Models/CommandAlias.cs
new file mode 100644
index 0000000..f83fed5
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/CommandAlias.cs
@@ -0,0 +1,8 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/CommandCooldown.cs b/src/Ellie.Bot.Db/Models/CommandCooldown.cs
new file mode 100644
index 0000000..438ed4a
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/CommandCooldown.cs
@@ -0,0 +1,8 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/CurrencyTransaction.cs b/src/Ellie.Bot.Db/Models/CurrencyTransaction.cs
new file mode 100644
index 0000000..9fced0c
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/CurrencyTransaction.cs
@@ -0,0 +1,12 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/DbEntity.cs b/src/Ellie.Bot.Db/Models/DbEntity.cs
new file mode 100644
index 0000000..095516c
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/DbEntity.cs
@@ -0,0 +1,12 @@
+#nullable disable
+using System.ComponentModel.DataAnnotations;
+
+namespace Ellie.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/Ellie.Bot.Db/Models/DelMsgOnCmdChannel.cs b/src/Ellie.Bot.Db/Models/DelMsgOnCmdChannel.cs
new file mode 100644
index 0000000..564da2f
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/DelMsgOnCmdChannel.cs
@@ -0,0 +1,14 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/DiscordPemOverride.cs b/src/Ellie.Bot.Db/Models/DiscordPemOverride.cs
new file mode 100644
index 0000000..3edb006
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/DiscordPemOverride.cs
@@ -0,0 +1,12 @@
+#nullable disable
+using Ellie.Bot.Db;
+
+namespace Ellie.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/Ellie.Bot.Db/Models/DiscordUser.cs b/src/Ellie.Bot.Db/Models/DiscordUser.cs
new file mode 100644
index 0000000..c3fa32b
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/DiscordUser.cs
@@ -0,0 +1,32 @@
+#nullable disable
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Models/EllieExpression.cs b/src/Ellie.Bot.Db/Models/EllieExpression.cs
new file mode 100644
index 0000000..49ad645
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/EllieExpression.cs
@@ -0,0 +1,27 @@
+#nullable disable
+namespace Ellie.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 Reactions { get; set; }
+
+ public string[] GetReactions()
+ => string.IsNullOrWhiteSpace(Reactions) ? Array.Empty() : Reactions.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/Ellie.Bot.Db/Models/Event.cs b/src/Ellie.Bot.Db/Models/Event.cs
new file mode 100644
index 0000000..c81cc2b
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/Event.cs
@@ -0,0 +1,49 @@
+#nullable disable
+namespace Ellie.Services.Database.Models;
+
+public class CurrencyEvent
+{
+ public enum Type
+ {
+ Reaction,
+
+ GameStatus
+ //NotRaid,
+ }
+
+ public ulong ServerId { get; set; }
+ public ulong ChannelId { get; set; }
+ public ulong MessageId { get; set; }
+ public Type EventType { get; set; }
+
+ ///
+ /// Amount of currency that the user will be rewarded.
+ ///
+ public long Amount { get; set; }
+
+ ///
+ /// Maximum amount of currency that can be handed out.
+ ///
+ public long PotSize { get; set; }
+
+ public List AwardedUsers { get; set; }
+
+ ///
+ /// Used as extra data storage for events which need it.
+ ///
+ public ulong ExtraId { get; set; }
+
+ ///
+ /// May be used for some future event.
+ ///
+ public ulong ExtraId2 { get; set; }
+
+ ///
+ /// May be used for some future event.
+ ///
+ public string ExtraString { get; set; }
+}
+
+public class AwardedUser
+{
+}
\ No newline at end of file
diff --git a/src/Ellie.Bot.Db/Models/FeedSub.cs b/src/Ellie.Bot.Db/Models/FeedSub.cs
new file mode 100644
index 0000000..fabac6e
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/FeedSub.cs
@@ -0,0 +1,19 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/FilterChannelId.cs b/src/Ellie.Bot.Db/Models/FilterChannelId.cs
new file mode 100644
index 0000000..7b64da6
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/FilterChannelId.cs
@@ -0,0 +1,30 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/FilterLinksChannelId.cs b/src/Ellie.Bot.Db/Models/FilterLinksChannelId.cs
new file mode 100644
index 0000000..e044ddf
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/FilterLinksChannelId.cs
@@ -0,0 +1,13 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/FilteredWord.cs b/src/Ellie.Bot.Db/Models/FilteredWord.cs
new file mode 100644
index 0000000..28bace4
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/FilteredWord.cs
@@ -0,0 +1,7 @@
+#nullable disable
+namespace Ellie.Services.Database.Models;
+
+public class FilteredWord : DbEntity
+{
+ public string Word { get; set; }
+}
\ No newline at end of file
diff --git a/src/Ellie.Bot.Db/Models/FollowedStream.cs b/src/Ellie.Bot.Db/Models/FollowedStream.cs
new file mode 100644
index 0000000..2772a20
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/FollowedStream.cs
@@ -0,0 +1,35 @@
+#nullable disable
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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);
+
+
+}
\ No newline at end of file
diff --git a/src/Ellie.Bot.Db/Models/GCChannelId.cs b/src/Ellie.Bot.Db/Models/GCChannelId.cs
new file mode 100644
index 0000000..3322dd5
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/GCChannelId.cs
@@ -0,0 +1,14 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/GamblingStats.cs b/src/Ellie.Bot.Db/Models/GamblingStats.cs
new file mode 100644
index 0000000..ca9a7a5
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/GamblingStats.cs
@@ -0,0 +1,9 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/GroupName.cs b/src/Ellie.Bot.Db/Models/GroupName.cs
new file mode 100644
index 0000000..9c4c831
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/GroupName.cs
@@ -0,0 +1,11 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/GuildConfig.cs b/src/Ellie.Bot.Db/Models/GuildConfig.cs
new file mode 100644
index 0000000..afa1edd
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/GuildConfig.cs
@@ -0,0 +1,108 @@
+#nullable disable
+using Ellie.Db.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Models/IgnoredLogItem.cs b/src/Ellie.Bot.Db/Models/IgnoredLogItem.cs
new file mode 100644
index 0000000..254ac46
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/IgnoredLogItem.cs
@@ -0,0 +1,16 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/IgnoredVoicePresenceChannel.cs b/src/Ellie.Bot.Db/Models/IgnoredVoicePresenceChannel.cs
new file mode 100644
index 0000000..f21fe6a
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/IgnoredVoicePresenceChannel.cs
@@ -0,0 +1,8 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/ImageOnlyChannel.cs b/src/Ellie.Bot.Db/Models/ImageOnlyChannel.cs
new file mode 100644
index 0000000..3e68b94
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/ImageOnlyChannel.cs
@@ -0,0 +1,15 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/LogSetting.cs b/src/Ellie.Bot.Db/Models/LogSetting.cs
new file mode 100644
index 0000000..ec417a8
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/LogSetting.cs
@@ -0,0 +1,37 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/MusicPlaylist.cs b/src/Ellie.Bot.Db/Models/MusicPlaylist.cs
new file mode 100644
index 0000000..178c09e
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/MusicPlaylist.cs
@@ -0,0 +1,10 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/MusicSettings.cs b/src/Ellie.Bot.Db/Models/MusicSettings.cs
new file mode 100644
index 0000000..37d195a
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/MusicSettings.cs
@@ -0,0 +1,61 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/MutedUserId.cs b/src/Ellie.Bot.Db/Models/MutedUserId.cs
new file mode 100644
index 0000000..23f703d
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/MutedUserId.cs
@@ -0,0 +1,13 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/NsfwBlacklistedTag.cs b/src/Ellie.Bot.Db/Models/NsfwBlacklistedTag.cs
new file mode 100644
index 0000000..6e74b43
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/NsfwBlacklistedTag.cs
@@ -0,0 +1,14 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/PatronQuota.cs b/src/Ellie.Bot.Db/Models/PatronQuota.cs
new file mode 100644
index 0000000..c4ca0fc
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/PatronQuota.cs
@@ -0,0 +1,48 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/Permission.cs b/src/Ellie.Bot.Db/Models/Permission.cs
new file mode 100644
index 0000000..92530b4
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/Permission.cs
@@ -0,0 +1,55 @@
+#nullable disable
+using System.ComponentModel.DataAnnotations.Schema;
+using System.Diagnostics;
+
+namespace Ellie.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/Ellie.Bot.Db/Models/PlantedCurrency.cs b/src/Ellie.Bot.Db/Models/PlantedCurrency.cs
new file mode 100644
index 0000000..fcc6c19
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/PlantedCurrency.cs
@@ -0,0 +1,12 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/PlaylistSong.cs b/src/Ellie.Bot.Db/Models/PlaylistSong.cs
new file mode 100644
index 0000000..5c2dd78
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/PlaylistSong.cs
@@ -0,0 +1,19 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/Poll.cs b/src/Ellie.Bot.Db/Models/Poll.cs
new file mode 100644
index 0000000..9c63e53
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/Poll.cs
@@ -0,0 +1,17 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/PollVote.cs b/src/Ellie.Bot.Db/Models/PollVote.cs
new file mode 100644
index 0000000..c701945
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/PollVote.cs
@@ -0,0 +1,14 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/Quote.cs b/src/Ellie.Bot.Db/Models/Quote.cs
new file mode 100644
index 0000000..6ec60d7
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/Quote.cs
@@ -0,0 +1,26 @@
+#nullable disable
+using System.ComponentModel.DataAnnotations;
+
+namespace Ellie.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/Ellie.Bot.Db/Models/ReactionRole.cs b/src/Ellie.Bot.Db/Models/ReactionRole.cs
new file mode 100644
index 0000000..a73bbdd
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/ReactionRole.cs
@@ -0,0 +1,18 @@
+#nullable disable
+using System.ComponentModel.DataAnnotations;
+
+namespace Ellie.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/Ellie.Bot.Db/Models/Reminder.cs b/src/Ellie.Bot.Db/Models/Reminder.cs
new file mode 100644
index 0000000..e497b23
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/Reminder.cs
@@ -0,0 +1,12 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/Repeater.cs b/src/Ellie.Bot.Db/Models/Repeater.cs
new file mode 100644
index 0000000..afad1c7
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/Repeater.cs
@@ -0,0 +1,15 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/RewardedUser.cs b/src/Ellie.Bot.Db/Models/RewardedUser.cs
new file mode 100644
index 0000000..b95e700
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/RewardedUser.cs
@@ -0,0 +1,10 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/RotatingPlayingStatus.cs b/src/Ellie.Bot.Db/Models/RotatingPlayingStatus.cs
new file mode 100644
index 0000000..4e831bf
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/RotatingPlayingStatus.cs
@@ -0,0 +1,10 @@
+#nullable disable
+using Ellie.Bot.Db;
+
+namespace Ellie.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/Ellie.Bot.Db/Models/SelfAssignableRole.cs b/src/Ellie.Bot.Db/Models/SelfAssignableRole.cs
new file mode 100644
index 0000000..9f2af77
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/SelfAssignableRole.cs
@@ -0,0 +1,11 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/ShopEntry.cs b/src/Ellie.Bot.Db/Models/ShopEntry.cs
new file mode 100644
index 0000000..8a8063b
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/ShopEntry.cs
@@ -0,0 +1,43 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/SlowmodeIgnoredRole.cs b/src/Ellie.Bot.Db/Models/SlowmodeIgnoredRole.cs
new file mode 100644
index 0000000..6ab743b
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/SlowmodeIgnoredRole.cs
@@ -0,0 +1,20 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/SlowmodeIgnoredUser.cs b/src/Ellie.Bot.Db/Models/SlowmodeIgnoredUser.cs
new file mode 100644
index 0000000..0cbb143
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/SlowmodeIgnoredUser.cs
@@ -0,0 +1,20 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/StreamOnlineMessage.cs b/src/Ellie.Bot.Db/Models/StreamOnlineMessage.cs
new file mode 100644
index 0000000..6b15750
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/StreamOnlineMessage.cs
@@ -0,0 +1,13 @@
+#nullable disable
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Models/StreamRoleSettings.cs b/src/Ellie.Bot.Db/Models/StreamRoleSettings.cs
new file mode 100644
index 0000000..d406f70
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/StreamRoleSettings.cs
@@ -0,0 +1,68 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/UnbanTimer.cs b/src/Ellie.Bot.Db/Models/UnbanTimer.cs
new file mode 100644
index 0000000..86c12b8
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/UnbanTimer.cs
@@ -0,0 +1,14 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/UnmuteTimer.cs b/src/Ellie.Bot.Db/Models/UnmuteTimer.cs
new file mode 100644
index 0000000..16e88a9
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/UnmuteTimer.cs
@@ -0,0 +1,14 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/UnroleTimer.cs b/src/Ellie.Bot.Db/Models/UnroleTimer.cs
new file mode 100644
index 0000000..92ff05f
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/UnroleTimer.cs
@@ -0,0 +1,15 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/UserXpStats.cs b/src/Ellie.Bot.Db/Models/UserXpStats.cs
new file mode 100644
index 0000000..fe9bd31
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/UserXpStats.cs
@@ -0,0 +1,13 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/VcRoleInfo.cs b/src/Ellie.Bot.Db/Models/VcRoleInfo.cs
new file mode 100644
index 0000000..6463622
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/VcRoleInfo.cs
@@ -0,0 +1,8 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/Waifu.cs b/src/Ellie.Bot.Db/Models/Waifu.cs
new file mode 100644
index 0000000..b357499
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/Waifu.cs
@@ -0,0 +1,33 @@
+#nullable disable
+using Ellie.Db.Models;
+
+namespace Ellie.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 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; }
+}
\ No newline at end of file
diff --git a/src/Ellie.Bot.Db/Models/WaifuItem.cs b/src/Ellie.Bot.Db/Models/WaifuItem.cs
new file mode 100644
index 0000000..95491c8
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/WaifuItem.cs
@@ -0,0 +1,10 @@
+#nullable disable
+namespace Ellie.Services.Database.Models;
+
+public class WaifuItem : DbEntity
+{
+ public WaifuInfo WaifuInfo { get; set; }
+ public int? WaifuInfoId { get; set; }
+ public string ItemEmoji { get; set; }
+ public string Name { get; set; }
+}
diff --git a/src/Ellie.Bot.Db/Models/WaifuUpdate.cs b/src/Ellie.Bot.Db/Models/WaifuUpdate.cs
new file mode 100644
index 0000000..28255db
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/WaifuUpdate.cs
@@ -0,0 +1,23 @@
+#nullable disable
+using Ellie.Db.Models;
+
+namespace Ellie.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/Ellie.Bot.Db/Models/WarnExpireAction.cs b/src/Ellie.Bot.Db/Models/WarnExpireAction.cs
new file mode 100644
index 0000000..7937ed3
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/WarnExpireAction.cs
@@ -0,0 +1,8 @@
+#nullable disable
+namespace Ellie.Services.Database.Models;
+
+public enum WarnExpireAction
+{
+ Clear,
+ Delete
+}
\ No newline at end of file
diff --git a/src/Ellie.Bot.Db/Models/Warning.cs b/src/Ellie.Bot.Db/Models/Warning.cs
new file mode 100644
index 0000000..b41e4ed
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/Warning.cs
@@ -0,0 +1,13 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/WarningPunishment.cs b/src/Ellie.Bot.Db/Models/WarningPunishment.cs
new file mode 100644
index 0000000..a4242fd
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/WarningPunishment.cs
@@ -0,0 +1,10 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/XpSettings.cs b/src/Ellie.Bot.Db/Models/XpSettings.cs
new file mode 100644
index 0000000..c2b4346
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/XpSettings.cs
@@ -0,0 +1,62 @@
+#nullable disable
+namespace Ellie.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/Ellie.Bot.Db/Models/XpShopOwnedItem.cs b/src/Ellie.Bot.Db/Models/XpShopOwnedItem.cs
new file mode 100644
index 0000000..affd086
--- /dev/null
+++ b/src/Ellie.Bot.Db/Models/XpShopOwnedItem.cs
@@ -0,0 +1,18 @@
+#nullable disable warnings
+using Ellie.Services.Database.Models;
+
+namespace Ellie.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