mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	WIP db provider support for Mysql and Postgres
This commit is contained in:
		
							
								
								
									
										18
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -5,7 +5,23 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Unreleased
 | 
					## Unreleased
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- More cool stuff coming soon
 | 
					### Added
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- NadekoBot now supports mysql, postgresql and sqlite
 | 
				
			||||||
 | 
					  - To change the db nadeko will use, simply change the `db type` in `creds.yml`
 | 
				
			||||||
 | 
					  - There is no migration code right now, which means that if you want to switch to another system you'll either have to manually export/import your database or start fresh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Changed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Minor club rework
 | 
				
			||||||
 | 
					  - Clubs names are now case sensitive (owo and OwO can be 2 different clubs)
 | 
				
			||||||
 | 
					  - Removed discriminators
 | 
				
			||||||
 | 
					    - Current discriminators which are greater than 1 are appended to clubnames to avoid duplicates, you can rename your club with `.clubrename` to remove it
 | 
				
			||||||
 | 
					    - Most of the clubs with #1 discriminator no longer have it (For example MyClub#1 will now just be MyClub)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Removed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - Removed `.clublevelreq` command as it doesn't serve much purpose
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## [4.0.6] - 21.03.2022
 | 
					## [4.0.6] - 21.03.2022
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,10 +41,7 @@ public sealed class Bot
 | 
				
			|||||||
        _credsProvider = new BotCredsProvider(totalShards);
 | 
					        _credsProvider = new BotCredsProvider(totalShards);
 | 
				
			||||||
        _creds = _credsProvider.GetCreds();
 | 
					        _creds = _credsProvider.GetCreds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _db = new(_creds);
 | 
					        _db = new(_credsProvider);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (shardId == 0)
 | 
					 | 
				
			||||||
            _db.Setup();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var messageCacheSize =
 | 
					        var messageCacheSize =
 | 
				
			||||||
#if GLOBAL_NADEKO
 | 
					#if GLOBAL_NADEKO
 | 
				
			||||||
@@ -286,6 +283,9 @@ public sealed class Bot
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public async Task RunAsync()
 | 
					    public async Task RunAsync()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        if (ShardId == 0)
 | 
				
			||||||
 | 
					            await _db.SetupAsync();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        var sw = Stopwatch.StartNew();
 | 
					        var sw = Stopwatch.StartNew();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await LoginAsync(_creds.Token);
 | 
					        await LoginAsync(_creds.Token);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -99,7 +99,7 @@ Windows default
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public Creds()
 | 
					    public Creds()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Version = 4;
 | 
					        Version = 5;
 | 
				
			||||||
        Token = string.Empty;
 | 
					        Token = string.Empty;
 | 
				
			||||||
        UsePrivilegedIntents = true;
 | 
					        UsePrivilegedIntents = true;
 | 
				
			||||||
        OwnerIds = new List<ulong>();
 | 
					        OwnerIds = new List<ulong>();
 | 
				
			||||||
@@ -124,10 +124,15 @@ Windows default
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public class DbOptions
 | 
					    public class DbOptions
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [Comment(@"Database type. Only sqlite supported atm")]
 | 
					        [Comment(@"Database type. ""sqlite"", ""mysql"" and ""postgresql"" are supported.
 | 
				
			||||||
 | 
					Default is ""sqlite""")]
 | 
				
			||||||
        public string Type { get; set; }
 | 
					        public string Type { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Comment(@"Connection string. Will default to ""Data Source=data/NadekoBot.db""")]
 | 
					        [Comment(@"Database connection string.
 | 
				
			||||||
 | 
					You MUST change this if you're not using ""sqlite"" type.
 | 
				
			||||||
 | 
					Default is ""Data Source=data/NadekoBot.db""
 | 
				
			||||||
 | 
					Example for mysql: ""Server=localhost;Port=3306;Uid=root;Pwd=my_super_secret_mysql_password;Database=nadeko""
 | 
				
			||||||
 | 
					Example for postgresql: ""Server=localhost;Port=5432;User Id=postgres;Password=my_super_secret_postgres_password;Database=nadeko;""")]
 | 
				
			||||||
        public string ConnectionString { get; set; }
 | 
					        public string ConnectionString { get; set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,7 @@ public static class ClubExtensions
 | 
				
			|||||||
                .ThenInclude(x => x.User)
 | 
					                .ThenInclude(x => x.User)
 | 
				
			||||||
                .Include(x => x.Bans)
 | 
					                .Include(x => x.Bans)
 | 
				
			||||||
                .ThenInclude(x => x.User)
 | 
					                .ThenInclude(x => x.User)
 | 
				
			||||||
                .Include(x => x.Users)
 | 
					                .Include(x => x.Members)
 | 
				
			||||||
                .AsQueryable();
 | 
					                .AsQueryable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static ClubInfo GetByOwner(this DbSet<ClubInfo> clubs, ulong userId)
 | 
					    public static ClubInfo GetByOwner(this DbSet<ClubInfo> clubs, ulong userId)
 | 
				
			||||||
@@ -20,24 +20,14 @@ public static class ClubExtensions
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public static ClubInfo GetByOwnerOrAdmin(this DbSet<ClubInfo> clubs, ulong userId)
 | 
					    public static ClubInfo GetByOwnerOrAdmin(this DbSet<ClubInfo> clubs, ulong userId)
 | 
				
			||||||
        => Include(clubs)
 | 
					        => Include(clubs)
 | 
				
			||||||
            .FirstOrDefault(c => c.Owner.UserId == userId || c.Users.Any(u => u.UserId == userId && u.IsClubAdmin));
 | 
					            .FirstOrDefault(c => c.Owner.UserId == userId || c.Members.Any(u => u.UserId == userId && u.IsClubAdmin));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static ClubInfo GetByMember(this DbSet<ClubInfo> clubs, ulong userId)
 | 
					    public static ClubInfo GetByMember(this DbSet<ClubInfo> clubs, ulong userId)
 | 
				
			||||||
        => Include(clubs).FirstOrDefault(c => c.Users.Any(u => u.UserId == userId));
 | 
					        => Include(clubs).FirstOrDefault(c => c.Members.Any(u => u.UserId == userId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static ClubInfo GetByName(this DbSet<ClubInfo> clubs, string name, int discrim)
 | 
					    public static ClubInfo GetByName(this DbSet<ClubInfo> clubs, string name)
 | 
				
			||||||
        => Include(clubs)
 | 
					        => Include(clubs)
 | 
				
			||||||
            .FirstOrDefault(c => EF.Functions.Collate(c.Name, "NOCASE") == EF.Functions.Collate(name, "NOCASE")
 | 
					            .FirstOrDefault(c => c.Name == name);
 | 
				
			||||||
                                 && c.Discrim == discrim);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static int GetNextDiscrim(this DbSet<ClubInfo> clubs, string name)
 | 
					 | 
				
			||||||
        => Include(clubs)
 | 
					 | 
				
			||||||
           .Where(x =>
 | 
					 | 
				
			||||||
               EF.Functions.Collate(x.Name, "NOCASE") == EF.Functions.Collate(name, "NOCASE"))
 | 
					 | 
				
			||||||
           .Select(x => x.Discrim)
 | 
					 | 
				
			||||||
           .DefaultIfEmpty()
 | 
					 | 
				
			||||||
           .Max()
 | 
					 | 
				
			||||||
           + 1;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static List<ClubInfo> GetClubLeaderboardPage(this DbSet<ClubInfo> clubs, int page)
 | 
					    public static List<ClubInfo> GetClubLeaderboardPage(this DbSet<ClubInfo> clubs, int page)
 | 
				
			||||||
        => clubs.AsNoTracking().OrderByDescending(x => x.Xp).Skip(page * 9).Take(9).ToList();
 | 
					        => clubs.AsNoTracking().OrderByDescending(x => x.Xp).Skip(page * 9).Take(9).ToList();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,11 @@ namespace NadekoBot.Db;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public static class DiscordUserExtensions
 | 
					public static class DiscordUserExtensions
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    public static Task<DiscordUser> GetByUserIdAsync(
 | 
				
			||||||
 | 
					        this IQueryable<DiscordUser> set,
 | 
				
			||||||
 | 
					        ulong userId)
 | 
				
			||||||
 | 
					        => set.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    public static void EnsureUserCreated(
 | 
					    public static void EnsureUserCreated(
 | 
				
			||||||
        this NadekoContext ctx,
 | 
					        this NadekoContext ctx,
 | 
				
			||||||
        ulong userId,
 | 
					        ulong userId,
 | 
				
			||||||
@@ -37,20 +42,49 @@ public static class DiscordUserExtensions
 | 
				
			|||||||
                      UserId = userId
 | 
					                      UserId = userId
 | 
				
			||||||
                  });
 | 
					                  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Task EnsureUserCreatedAsync(
 | 
				
			||||||
 | 
					        this NadekoContext 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
 | 
					    //temp is only used in updatecurrencystate, so that i don't overwrite real usernames/discrims with Unknown
 | 
				
			||||||
    public static DiscordUser GetOrCreateUser(
 | 
					    public static DiscordUser GetOrCreateUser(
 | 
				
			||||||
        this NadekoContext ctx,
 | 
					        this NadekoContext ctx,
 | 
				
			||||||
        ulong userId,
 | 
					        ulong userId,
 | 
				
			||||||
        string username,
 | 
					        string username,
 | 
				
			||||||
        string discrim,
 | 
					        string discrim,
 | 
				
			||||||
        string avatarId)
 | 
					        string avatarId,
 | 
				
			||||||
 | 
					        Func<IQueryable<DiscordUser>, IQueryable<DiscordUser>> includes = null)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        ctx.EnsureUserCreated(userId, username, discrim, avatarId);
 | 
					        ctx.EnsureUserCreated(userId, username, discrim, avatarId);
 | 
				
			||||||
        return ctx.DiscordUser.Include(x => x.Club).First(u => u.UserId == userId);
 | 
					
 | 
				
			||||||
 | 
					        IQueryable<DiscordUser> queryable = ctx.DiscordUser;
 | 
				
			||||||
 | 
					        if (includes is not null)
 | 
				
			||||||
 | 
					            queryable = includes(queryable);
 | 
				
			||||||
 | 
					        return queryable.First(u => u.UserId == userId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static DiscordUser GetOrCreateUser(this NadekoContext ctx, IUser original)
 | 
					    public static DiscordUser GetOrCreateUser(this NadekoContext ctx, IUser original, Func<IQueryable<DiscordUser>, IQueryable<DiscordUser>> includes = null)
 | 
				
			||||||
        => ctx.GetOrCreateUser(original.Id, original.Username, original.Discriminator, original.AvatarId);
 | 
					        => ctx.GetOrCreateUser(original.Id, original.Username, original.Discriminator, original.AvatarId, includes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static int GetUserGlobalRank(this DbSet<DiscordUser> users, ulong id)
 | 
					    public static int GetUserGlobalRank(this DbSet<DiscordUser> users, ulong id)
 | 
				
			||||||
        => users.AsQueryable()
 | 
					        => users.AsQueryable()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,7 +73,6 @@ public static class GuildConfigExtensions
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        GuildConfig config;
 | 
					        GuildConfig config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // todo linq2db
 | 
					 | 
				
			||||||
        if (includes is null)
 | 
					        if (includes is null)
 | 
				
			||||||
            config = ctx.GuildConfigs.IncludeEverything().FirstOrDefault(c => c.GuildId == guildId);
 | 
					            config = ctx.GuildConfigs.IncludeEverything().FirstOrDefault(c => c.GuildId == guildId);
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,24 +8,19 @@ public class ClubInfo : DbEntity
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    [MaxLength(20)]
 | 
					    [MaxLength(20)]
 | 
				
			||||||
    public string Name { get; set; }
 | 
					    public string Name { get; set; }
 | 
				
			||||||
 | 
					    public string Description { get; set; }
 | 
				
			||||||
    public int Discrim { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public string ImageUrl { get; set; } = string.Empty;
 | 
					    public string ImageUrl { get; set; } = string.Empty;
 | 
				
			||||||
    public int MinimumLevelReq { get; set; } = 5;
 | 
					 | 
				
			||||||
    public int Xp { get; set; } = 0;
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    public int OwnerId { get; set; }
 | 
					    public int Xp { get; set; } = 0;
 | 
				
			||||||
 | 
					    public int? OwnerId { get; set; }
 | 
				
			||||||
    public DiscordUser Owner { get; set; }
 | 
					    public DiscordUser Owner { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public List<DiscordUser> Users { get; set; } = new();
 | 
					    public List<DiscordUser> Members { get; set; } = new();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public List<ClubApplicants> Applicants { get; set; } = new();
 | 
					    public List<ClubApplicants> Applicants { get; set; } = new();
 | 
				
			||||||
    public List<ClubBans> Bans { get; set; } = new();
 | 
					    public List<ClubBans> Bans { get; set; } = new();
 | 
				
			||||||
    public string Description { get; set; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public override string ToString()
 | 
					    public override string ToString()
 | 
				
			||||||
        => Name + "#" + Discrim;
 | 
					        => Name;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ClubApplicants
 | 
					public class ClubApplicants
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ public class DiscordUser : DbEntity
 | 
				
			|||||||
    public string Discriminator { get; set; }
 | 
					    public string Discriminator { get; set; }
 | 
				
			||||||
    public string AvatarId { get; set; }
 | 
					    public string AvatarId { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int? ClubId { get; set; }
 | 
				
			||||||
    public ClubInfo Club { get; set; }
 | 
					    public ClubInfo Club { get; set; }
 | 
				
			||||||
    public bool IsClubAdmin { get; set; }
 | 
					    public bool IsClubAdmin { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										42
									
								
								src/NadekoBot/Db/MysqlContext.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/NadekoBot/Db/MysqlContext.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					using NadekoBot.Db.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.Services.Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public sealed class MysqlContext : NadekoContext
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private readonly string _connStr;
 | 
				
			||||||
 | 
					    private readonly string _version;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected override string CurrencyTransactionOtherIdDefaultValue
 | 
				
			||||||
 | 
					        => "NULL";
 | 
				
			||||||
 | 
					    protected override string DiscordUserLastXpGainDefaultValue
 | 
				
			||||||
 | 
					        => "(UTC_TIMESTAMP - INTERVAL 1 year)";
 | 
				
			||||||
 | 
					    protected override string LastLevelUpDefaultValue
 | 
				
			||||||
 | 
					        => "(UTC_TIMESTAMP)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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<ClubInfo>()
 | 
				
			||||||
 | 
					                    .Property(x => x.Name)
 | 
				
			||||||
 | 
					                    .UseCollation("utf8mb4_bin");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
#nullable disable
 | 
					#nullable disable
 | 
				
			||||||
using Microsoft.Data.Sqlite;
 | 
					 | 
				
			||||||
using Microsoft.EntityFrameworkCore;
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
using Microsoft.EntityFrameworkCore.Design;
 | 
					using Microsoft.EntityFrameworkCore.Design;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
@@ -10,23 +9,7 @@ using NadekoBot.Services.Database.Models;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Services.Database;
 | 
					namespace NadekoBot.Services.Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class NadekoContextFactory : IDesignTimeDbContextFactory<NadekoContext>
 | 
					public abstract class NadekoContext : DbContext
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public NadekoContext CreateDbContext(string[] args)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        LogSetup.SetupLogger(-2);
 | 
					 | 
				
			||||||
        var optionsBuilder = new DbContextOptionsBuilder<NadekoContext>();
 | 
					 | 
				
			||||||
        var creds = new BotCredsProvider().GetCreds();
 | 
					 | 
				
			||||||
        var builder = new SqliteConnectionStringBuilder(creds.Db.ConnectionString);
 | 
					 | 
				
			||||||
        builder.DataSource = Path.Combine(AppContext.BaseDirectory, builder.DataSource);
 | 
					 | 
				
			||||||
        optionsBuilder.UseSqlite(builder.ToString());
 | 
					 | 
				
			||||||
        var ctx = new NadekoContext(optionsBuilder.Options);
 | 
					 | 
				
			||||||
        ctx.Database.SetCommandTimeout(60);
 | 
					 | 
				
			||||||
        return ctx;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class NadekoContext : DbContext
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public DbSet<GuildConfig> GuildConfigs { get; set; }
 | 
					    public DbSet<GuildConfig> GuildConfigs { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -69,10 +52,13 @@ public class NadekoContext : DbContext
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public DbSet<Permissionv2> Permissions { get; set; }
 | 
					    public DbSet<Permissionv2> Permissions { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public NadekoContext(DbContextOptions<NadekoContext> options)
 | 
					    #region Mandatory Provider-Specific Values
 | 
				
			||||||
        : base(options)
 | 
					
 | 
				
			||||||
    {
 | 
					    protected abstract string CurrencyTransactionOtherIdDefaultValue { get; }
 | 
				
			||||||
    }
 | 
					    protected abstract string DiscordUserLastXpGainDefaultValue { get; }
 | 
				
			||||||
 | 
					    protected abstract string LastLevelUpDefaultValue { get; }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    #endregion
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    protected override void OnModelCreating(ModelBuilder modelBuilder)
 | 
					    protected override void OnModelCreating(ModelBuilder modelBuilder)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -167,10 +153,10 @@ public class NadekoContext : DbContext
 | 
				
			|||||||
              .HasDefaultValue(XpNotificationLocation.None);
 | 
					              .HasDefaultValue(XpNotificationLocation.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            du.Property(x => x.LastXpGain)
 | 
					            du.Property(x => x.LastXpGain)
 | 
				
			||||||
              .HasDefaultValueSql("datetime('now', '-1 years')");
 | 
					              .HasDefaultValueSql(DiscordUserLastXpGainDefaultValue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            du.Property(x => x.LastLevelUp)
 | 
					            du.Property(x => x.LastLevelUp)
 | 
				
			||||||
              .HasDefaultValueSql("datetime('now')");
 | 
					              .HasDefaultValueSql(LastLevelUpDefaultValue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            du.Property(x => x.TotalXp)
 | 
					            du.Property(x => x.TotalXp)
 | 
				
			||||||
              .HasDefaultValue(0);
 | 
					              .HasDefaultValue(0);
 | 
				
			||||||
@@ -179,7 +165,10 @@ public class NadekoContext : DbContext
 | 
				
			|||||||
              .HasDefaultValue(0);
 | 
					              .HasDefaultValue(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            du.HasAlternateKey(w => w.UserId);
 | 
					            du.HasAlternateKey(w => w.UserId);
 | 
				
			||||||
            du.HasOne(x => x.Club).WithMany(x => x.Users).IsRequired(false);
 | 
					            du.HasOne(x => x.Club)
 | 
				
			||||||
 | 
					              .WithMany(x => x.Members)
 | 
				
			||||||
 | 
					              .IsRequired(false)
 | 
				
			||||||
 | 
					              .OnDelete(DeleteBehavior.NoAction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            du.HasIndex(x => x.TotalXp);
 | 
					            du.HasIndex(x => x.TotalXp);
 | 
				
			||||||
            du.HasIndex(x => x.CurrencyAmount);
 | 
					            du.HasIndex(x => x.CurrencyAmount);
 | 
				
			||||||
@@ -218,7 +207,7 @@ public class NadekoContext : DbContext
 | 
				
			|||||||
           .IsUnique();
 | 
					           .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        xps.Property(x => x.LastLevelUp)
 | 
					        xps.Property(x => x.LastLevelUp)
 | 
				
			||||||
           .HasDefaultValue(new DateTime(2017, 9, 21, 20, 53, 13, 307, DateTimeKind.Local));
 | 
					           .HasDefaultValueSql(LastLevelUpDefaultValue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        xps.HasIndex(x => x.UserId);
 | 
					        xps.HasIndex(x => x.UserId);
 | 
				
			||||||
        xps.HasIndex(x => x.GuildId);
 | 
					        xps.HasIndex(x => x.GuildId);
 | 
				
			||||||
@@ -248,13 +237,14 @@ public class NadekoContext : DbContext
 | 
				
			|||||||
        #region Club
 | 
					        #region Club
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var ci = modelBuilder.Entity<ClubInfo>();
 | 
					        var ci = modelBuilder.Entity<ClubInfo>();
 | 
				
			||||||
        ci.HasOne(x => x.Owner).WithOne().HasForeignKey<ClubInfo>(x => x.OwnerId);
 | 
					        ci.HasOne(x => x.Owner)
 | 
				
			||||||
 | 
					          .WithOne()
 | 
				
			||||||
 | 
					          .HasForeignKey<ClubInfo>(x => x.OwnerId)
 | 
				
			||||||
 | 
					          .OnDelete(DeleteBehavior.SetNull);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ci.HasAlternateKey(x => new
 | 
					        ci.HasAlternateKey(x => new
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            x.Name,
 | 
					            x.Name
 | 
				
			||||||
            x.Discrim
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					        #endregion
 | 
				
			||||||
@@ -268,9 +258,13 @@ public class NadekoContext : DbContext
 | 
				
			|||||||
                        t.UserId
 | 
					                        t.UserId
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        modelBuilder.Entity<ClubApplicants>().HasOne(pt => pt.User).WithMany();
 | 
					        modelBuilder.Entity<ClubApplicants>()
 | 
				
			||||||
 | 
					                    .HasOne(pt => pt.User)
 | 
				
			||||||
 | 
					                    .WithMany();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        modelBuilder.Entity<ClubApplicants>().HasOne(pt => pt.Club).WithMany(x => x.Applicants);
 | 
					        modelBuilder.Entity<ClubApplicants>()
 | 
				
			||||||
 | 
					                    .HasOne(pt => pt.Club)
 | 
				
			||||||
 | 
					                    .WithMany(x => x.Applicants);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        modelBuilder.Entity<ClubBans>()
 | 
					        modelBuilder.Entity<ClubBans>()
 | 
				
			||||||
                    .HasKey(t => new
 | 
					                    .HasKey(t => new
 | 
				
			||||||
@@ -279,9 +273,13 @@ public class NadekoContext : DbContext
 | 
				
			|||||||
                        t.UserId
 | 
					                        t.UserId
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        modelBuilder.Entity<ClubBans>().HasOne(pt => pt.User).WithMany();
 | 
					        modelBuilder.Entity<ClubBans>()
 | 
				
			||||||
 | 
					                    .HasOne(pt => pt.User)
 | 
				
			||||||
 | 
					                    .WithMany();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        modelBuilder.Entity<ClubBans>().HasOne(pt => pt.Club).WithMany(x => x.Bans);
 | 
					        modelBuilder.Entity<ClubBans>()
 | 
				
			||||||
 | 
					                    .HasOne(pt => pt.Club)
 | 
				
			||||||
 | 
					                    .WithMany(x => x.Bans);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -299,7 +297,7 @@ public class NadekoContext : DbContext
 | 
				
			|||||||
             .IsUnique(false);
 | 
					             .IsUnique(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            e.Property(x => x.OtherId)
 | 
					            e.Property(x => x.OtherId)
 | 
				
			||||||
             .HasDefaultValueSql("NULL");
 | 
					             .HasDefaultValueSql(CurrencyTransactionOtherIdDefaultValue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            e.Property(x => x.Type)
 | 
					            e.Property(x => x.Type)
 | 
				
			||||||
             .IsRequired();
 | 
					             .IsRequired();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								src/NadekoBot/Db/PostgreSqlContext.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/NadekoBot/Db/PostgreSqlContext.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.Services.Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public sealed class PostgreSqlContext : NadekoContext
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private readonly string _connStr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected override string CurrencyTransactionOtherIdDefaultValue
 | 
				
			||||||
 | 
					        => "NULL";
 | 
				
			||||||
 | 
					    protected override string DiscordUserLastXpGainDefaultValue
 | 
				
			||||||
 | 
					        => "timezone('utc', now()) - interval '-1 year'";
 | 
				
			||||||
 | 
					    protected override string LastLevelUpDefaultValue
 | 
				
			||||||
 | 
					        => "timezone('utc', now())";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public PostgreSqlContext(string connStr = "Host=localhost")
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _connStr = connStr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        base.OnConfiguring(optionsBuilder);
 | 
				
			||||||
 | 
					        optionsBuilder
 | 
				
			||||||
 | 
					            .UseLowerCaseNamingConvention()
 | 
				
			||||||
 | 
					            .UseNpgsql(_connStr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/NadekoBot/Db/SqliteContext.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/NadekoBot/Db/SqliteContext.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					using Microsoft.Data.Sqlite;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.Services.Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public sealed class SqliteContext : NadekoContext
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private readonly string _connectionString;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected override string CurrencyTransactionOtherIdDefaultValue
 | 
				
			||||||
 | 
					        => "NULL";
 | 
				
			||||||
 | 
					    protected override string DiscordUserLastXpGainDefaultValue
 | 
				
			||||||
 | 
					        => "datetime('now', '-1 years')";
 | 
				
			||||||
 | 
					    protected override string LastLevelUpDefaultValue
 | 
				
			||||||
 | 
					        => "datetime('now')";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public SqliteContext(string connectionString = "Data Source=data/NadekoBot.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());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3425
									
								
								src/NadekoBot/Migrations/MySql/20220409170652_mysql-init.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										3425
									
								
								src/NadekoBot/Migrations/MySql/20220409170652_mysql-init.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2343
									
								
								src/NadekoBot/Migrations/MySql/20220409170652_mysql-init.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2343
									
								
								src/NadekoBot/Migrations/MySql/20220409170652_mysql-init.cs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										3423
									
								
								src/NadekoBot/Migrations/MySql/MysqlContextModelSnapshot.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3423
									
								
								src/NadekoBot/Migrations/MySql/MysqlContextModelSnapshot.cs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										3565
									
								
								src/NadekoBot/Migrations/Postgresql/20220409170719_mysql-init.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										3565
									
								
								src/NadekoBot/Migrations/Postgresql/20220409170719_mysql-init.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2201
									
								
								src/NadekoBot/Migrations/Postgresql/20220409170719_mysql-init.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2201
									
								
								src/NadekoBot/Migrations/Postgresql/20220409170719_mysql-init.cs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20210621042359_squash")]
 | 
					    [Migration("20210621042359_squash")]
 | 
				
			||||||
    partial class squash
 | 
					    partial class squash
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20210707002343_cleanup")]
 | 
					    [Migration("20210707002343_cleanup")]
 | 
				
			||||||
    partial class cleanup
 | 
					    partial class cleanup
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20210911225622_rero-cascade")]
 | 
					    [Migration("20210911225622_rero-cascade")]
 | 
				
			||||||
    partial class rerocascade
 | 
					    partial class rerocascade
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20210912182515_boost-messages")]
 | 
					    [Migration("20210912182515_boost-messages")]
 | 
				
			||||||
    partial class boostmessages
 | 
					    partial class boostmessages
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20210912200106_logsettings-independence")]
 | 
					    [Migration("20210912200106_logsettings-independence")]
 | 
				
			||||||
    partial class logsettingsindependence
 | 
					    partial class logsettingsindependence
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20210914180026_image-only-channels")]
 | 
					    [Migration("20210914180026_image-only-channels")]
 | 
				
			||||||
    partial class imageonlychannels
 | 
					    partial class imageonlychannels
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20210921204645_logignore-user-channel")]
 | 
					    [Migration("20210921204645_logignore-user-channel")]
 | 
				
			||||||
    partial class logignoreuserchannel
 | 
					    partial class logignoreuserchannel
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20211015232708_nsfw-blacklist-tags")]
 | 
					    [Migration("20211015232708_nsfw-blacklist-tags")]
 | 
				
			||||||
    partial class nsfwblacklisttags
 | 
					    partial class nsfwblacklisttags
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20211121002508_weighted-warnings")]
 | 
					    [Migration("20211121002508_weighted-warnings")]
 | 
				
			||||||
    partial class weightedwarnings
 | 
					    partial class weightedwarnings
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20211213145407_atl-rework")]
 | 
					    [Migration("20211213145407_atl-rework")]
 | 
				
			||||||
    partial class atlrework
 | 
					    partial class atlrework
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -10,7 +10,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20220102102344_crs-rename-to-expressions-perm-rename")]
 | 
					    [Migration("20220102102344_crs-rename-to-expressions-perm-rename")]
 | 
				
			||||||
    partial class crsrenametoexpressionspermrename
 | 
					    partial class crsrenametoexpressionspermrename
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -10,7 +10,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20220110105942_filter-settings-cleanup")]
 | 
					    [Migration("20220110105942_filter-settings-cleanup")]
 | 
				
			||||||
    partial class filtersettingscleanup
 | 
					    partial class filtersettingscleanup
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -10,7 +10,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20220125044401_curtrs-rework-discorduser-defaults")]
 | 
					    [Migration("20220125044401_curtrs-rework-discorduser-defaults")]
 | 
				
			||||||
    partial class curtrsreworkdiscorduserdefaults
 | 
					    partial class curtrsreworkdiscorduserdefaults
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -10,7 +10,7 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    [Migration("20220213123633_music-autoplay")]
 | 
					    [Migration("20220213123633_music-autoplay")]
 | 
				
			||||||
    partial class musicautoplay
 | 
					    partial class musicautoplay
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
							
								
								
									
										2756
									
								
								src/NadekoBot/Migrations/Sqlite/20220409170828_clubs-refactor.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										2756
									
								
								src/NadekoBot/Migrations/Sqlite/20220409170828_clubs-refactor.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										132
									
								
								src/NadekoBot/Migrations/Sqlite/20220409170828_clubs-refactor.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/NadekoBot/Migrations/Sqlite/20220409170828_clubs-refactor.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore.Migrations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#nullable disable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public partial class clubsrefactor : Migration
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        protected override void Up(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            migrationBuilder.Sql(@"UPDATE Clubs
 | 
				
			||||||
 | 
					SET Name = Name || '#' || Discrim
 | 
				
			||||||
 | 
					WHERE Discrim <> 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					UPDATE Clubs as co
 | 
				
			||||||
 | 
					SET Name =
 | 
				
			||||||
 | 
						CASE (select count(*) from Clubs as ci where co.Name == ci.Name) = 1
 | 
				
			||||||
 | 
							WHEN true
 | 
				
			||||||
 | 
								THEN Name
 | 
				
			||||||
 | 
							ELSE
 | 
				
			||||||
 | 
								Name || '#' || Discrim
 | 
				
			||||||
 | 
					    END
 | 
				
			||||||
 | 
					 WHERE Discrim = 1;");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            migrationBuilder.DropForeignKey(
 | 
				
			||||||
 | 
					                name: "FK_Clubs_DiscordUser_OwnerId",
 | 
				
			||||||
 | 
					                table: "Clubs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropUniqueConstraint(
 | 
				
			||||||
 | 
					                name: "AK_Clubs_Name_Discrim",
 | 
				
			||||||
 | 
					                table: "Clubs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "Discrim",
 | 
				
			||||||
 | 
					                table: "Clubs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropColumn(
 | 
				
			||||||
 | 
					                name: "MinimumLevelReq",
 | 
				
			||||||
 | 
					                table: "Clubs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AlterColumn<DateTime>(
 | 
				
			||||||
 | 
					                name: "LastLevelUp",
 | 
				
			||||||
 | 
					                table: "UserXpStats",
 | 
				
			||||||
 | 
					                type: "TEXT",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValueSql: "datetime('now')",
 | 
				
			||||||
 | 
					                oldClrType: typeof(DateTime),
 | 
				
			||||||
 | 
					                oldType: "TEXT",
 | 
				
			||||||
 | 
					                oldDefaultValue: new DateTime(2017, 9, 21, 20, 53, 13, 307, DateTimeKind.Local));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AlterColumn<int>(
 | 
				
			||||||
 | 
					                name: "OwnerId",
 | 
				
			||||||
 | 
					                table: "Clubs",
 | 
				
			||||||
 | 
					                type: "INTEGER",
 | 
				
			||||||
 | 
					                nullable: true,
 | 
				
			||||||
 | 
					                oldClrType: typeof(int),
 | 
				
			||||||
 | 
					                oldType: "INTEGER");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddUniqueConstraint(
 | 
				
			||||||
 | 
					                name: "AK_Clubs_Name",
 | 
				
			||||||
 | 
					                table: "Clubs",
 | 
				
			||||||
 | 
					                column: "Name");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddForeignKey(
 | 
				
			||||||
 | 
					                name: "FK_Clubs_DiscordUser_OwnerId",
 | 
				
			||||||
 | 
					                table: "Clubs",
 | 
				
			||||||
 | 
					                column: "OwnerId",
 | 
				
			||||||
 | 
					                principalTable: "DiscordUser",
 | 
				
			||||||
 | 
					                principalColumn: "Id",
 | 
				
			||||||
 | 
					                onDelete: ReferentialAction.SetNull);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected override void Down(MigrationBuilder migrationBuilder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            migrationBuilder.DropForeignKey(
 | 
				
			||||||
 | 
					                name: "FK_Clubs_DiscordUser_OwnerId",
 | 
				
			||||||
 | 
					                table: "Clubs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.DropUniqueConstraint(
 | 
				
			||||||
 | 
					                name: "AK_Clubs_Name",
 | 
				
			||||||
 | 
					                table: "Clubs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AlterColumn<DateTime>(
 | 
				
			||||||
 | 
					                name: "LastLevelUp",
 | 
				
			||||||
 | 
					                table: "UserXpStats",
 | 
				
			||||||
 | 
					                type: "TEXT",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValue: new DateTime(2017, 9, 21, 20, 53, 13, 307, DateTimeKind.Local),
 | 
				
			||||||
 | 
					                oldClrType: typeof(DateTime),
 | 
				
			||||||
 | 
					                oldType: "TEXT",
 | 
				
			||||||
 | 
					                oldDefaultValueSql: "datetime('now')");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AlterColumn<int>(
 | 
				
			||||||
 | 
					                name: "OwnerId",
 | 
				
			||||||
 | 
					                table: "Clubs",
 | 
				
			||||||
 | 
					                type: "INTEGER",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValue: 0,
 | 
				
			||||||
 | 
					                oldClrType: typeof(int),
 | 
				
			||||||
 | 
					                oldType: "INTEGER",
 | 
				
			||||||
 | 
					                oldNullable: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<int>(
 | 
				
			||||||
 | 
					                name: "Discrim",
 | 
				
			||||||
 | 
					                table: "Clubs",
 | 
				
			||||||
 | 
					                type: "INTEGER",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValue: 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddColumn<int>(
 | 
				
			||||||
 | 
					                name: "MinimumLevelReq",
 | 
				
			||||||
 | 
					                table: "Clubs",
 | 
				
			||||||
 | 
					                type: "INTEGER",
 | 
				
			||||||
 | 
					                nullable: false,
 | 
				
			||||||
 | 
					                defaultValue: 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddUniqueConstraint(
 | 
				
			||||||
 | 
					                name: "AK_Clubs_Name_Discrim",
 | 
				
			||||||
 | 
					                table: "Clubs",
 | 
				
			||||||
 | 
					                columns: new[] { "Name", "Discrim" });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            migrationBuilder.AddForeignKey(
 | 
				
			||||||
 | 
					                name: "FK_Clubs_DiscordUser_OwnerId",
 | 
				
			||||||
 | 
					                table: "Clubs",
 | 
				
			||||||
 | 
					                column: "OwnerId",
 | 
				
			||||||
 | 
					                principalTable: "DiscordUser",
 | 
				
			||||||
 | 
					                principalColumn: "Id",
 | 
				
			||||||
 | 
					                onDelete: ReferentialAction.Cascade);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -9,13 +9,13 @@ using NadekoBot.Services.Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Migrations
 | 
					namespace NadekoBot.Migrations
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [DbContext(typeof(NadekoContext))]
 | 
					    [DbContext(typeof(SqliteContext))]
 | 
				
			||||||
    partial class NadekoContextModelSnapshot : ModelSnapshot
 | 
					    partial class NadekoSqliteContextModelSnapshot : ModelSnapshot
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        protected override void BuildModel(ModelBuilder modelBuilder)
 | 
					        protected override void BuildModel(ModelBuilder modelBuilder)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
#pragma warning disable 612, 618
 | 
					#pragma warning disable 612, 618
 | 
				
			||||||
            modelBuilder.HasAnnotation("ProductVersion", "6.0.2");
 | 
					            modelBuilder.HasAnnotation("ProductVersion", "6.0.3");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -29,7 +29,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("UserId");
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("ClubApplicants", (string)null);
 | 
					                    b.ToTable("ClubApplicants");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Db.Models.ClubBans", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Db.Models.ClubBans", b =>
 | 
				
			||||||
@@ -44,7 +44,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("UserId");
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("ClubBans", (string)null);
 | 
					                    b.ToTable("ClubBans");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Db.Models.ClubInfo", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Db.Models.ClubInfo", b =>
 | 
				
			||||||
@@ -59,21 +59,15 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.Property<string>("Description")
 | 
					                    b.Property<string>("Description")
 | 
				
			||||||
                        .HasColumnType("TEXT");
 | 
					                        .HasColumnType("TEXT");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<int>("Discrim")
 | 
					 | 
				
			||||||
                        .HasColumnType("INTEGER");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    b.Property<string>("ImageUrl")
 | 
					                    b.Property<string>("ImageUrl")
 | 
				
			||||||
                        .HasColumnType("TEXT");
 | 
					                        .HasColumnType("TEXT");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<int>("MinimumLevelReq")
 | 
					 | 
				
			||||||
                        .HasColumnType("INTEGER");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    b.Property<string>("Name")
 | 
					                    b.Property<string>("Name")
 | 
				
			||||||
                        .IsRequired()
 | 
					                        .IsRequired()
 | 
				
			||||||
                        .HasMaxLength(20)
 | 
					                        .HasMaxLength(20)
 | 
				
			||||||
                        .HasColumnType("TEXT");
 | 
					                        .HasColumnType("TEXT");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<int>("OwnerId")
 | 
					                    b.Property<int?>("OwnerId")
 | 
				
			||||||
                        .HasColumnType("INTEGER");
 | 
					                        .HasColumnType("INTEGER");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<int>("Xp")
 | 
					                    b.Property<int>("Xp")
 | 
				
			||||||
@@ -81,12 +75,12 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasKey("Id");
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.HasAlternateKey("Name", "Discrim");
 | 
					                    b.HasAlternateKey("Name");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("OwnerId")
 | 
					                    b.HasIndex("OwnerId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("Clubs", (string)null);
 | 
					                    b.ToTable("Clubs");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b =>
 | 
				
			||||||
@@ -155,7 +149,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("UserId");
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("DiscordUser", (string)null);
 | 
					                    b.ToTable("DiscordUser");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b =>
 | 
				
			||||||
@@ -189,7 +183,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("FollowedStream", (string)null);
 | 
					                    b.ToTable("FollowedStream");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b =>
 | 
				
			||||||
@@ -218,7 +212,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("GuildConfigId")
 | 
					                    b.HasIndex("GuildConfigId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("AntiAltSetting", (string)null);
 | 
					                    b.ToTable("AntiAltSetting");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
 | 
				
			||||||
@@ -250,7 +244,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("GuildConfigId")
 | 
					                    b.HasIndex("GuildConfigId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("AntiRaidSetting", (string)null);
 | 
					                    b.ToTable("AntiRaidSetting");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b =>
 | 
				
			||||||
@@ -272,7 +266,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("AntiSpamSettingId");
 | 
					                    b.HasIndex("AntiSpamSettingId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("AntiSpamIgnore", (string)null);
 | 
					                    b.ToTable("AntiSpamIgnore");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b =>
 | 
				
			||||||
@@ -304,7 +298,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("GuildConfigId")
 | 
					                    b.HasIndex("GuildConfigId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("AntiSpamSetting", (string)null);
 | 
					                    b.ToTable("AntiSpamSetting");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoCommand", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoCommand", b =>
 | 
				
			||||||
@@ -342,7 +336,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasKey("Id");
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("AutoCommands", (string)null);
 | 
					                    b.ToTable("AutoCommands");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
 | 
				
			||||||
@@ -370,7 +364,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildId");
 | 
					                    b.HasIndex("GuildId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("AutoTranslateChannels", (string)null);
 | 
					                    b.ToTable("AutoTranslateChannels");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
 | 
				
			||||||
@@ -398,7 +392,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasAlternateKey("ChannelId", "UserId");
 | 
					                    b.HasAlternateKey("ChannelId", "UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("AutoTranslateUsers", (string)null);
 | 
					                    b.ToTable("AutoTranslateUsers");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b =>
 | 
				
			||||||
@@ -421,7 +415,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("GuildId")
 | 
					                    b.HasIndex("GuildId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("BanTemplates", (string)null);
 | 
					                    b.ToTable("BanTemplates");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistEntry", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistEntry", b =>
 | 
				
			||||||
@@ -441,7 +435,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasKey("Id");
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("Blacklist", (string)null);
 | 
					                    b.ToTable("Blacklist");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
 | 
				
			||||||
@@ -466,7 +460,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("CommandAlias", (string)null);
 | 
					                    b.ToTable("CommandAlias");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
 | 
				
			||||||
@@ -491,7 +485,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("CommandCooldown", (string)null);
 | 
					                    b.ToTable("CommandCooldown");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b =>
 | 
				
			||||||
@@ -529,7 +523,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("UserId");
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("CurrencyTransactions", (string)null);
 | 
					                    b.ToTable("CurrencyTransactions");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.DelMsgOnCmdChannel", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.DelMsgOnCmdChannel", b =>
 | 
				
			||||||
@@ -554,7 +548,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("DelMsgOnCmdChannel", (string)null);
 | 
					                    b.ToTable("DelMsgOnCmdChannel");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordPermOverride", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordPermOverride", b =>
 | 
				
			||||||
@@ -580,7 +574,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("GuildId", "Command")
 | 
					                    b.HasIndex("GuildId", "Command")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("DiscordPermOverrides", (string)null);
 | 
					                    b.ToTable("DiscordPermOverrides");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
 | 
				
			||||||
@@ -605,7 +599,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("XpSettingsId");
 | 
					                    b.HasIndex("XpSettingsId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("ExcludedItem", (string)null);
 | 
					                    b.ToTable("ExcludedItem");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.FeedSub", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.FeedSub", b =>
 | 
				
			||||||
@@ -631,7 +625,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasAlternateKey("GuildConfigId", "Url");
 | 
					                    b.HasAlternateKey("GuildConfigId", "Url");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("FeedSub", (string)null);
 | 
					                    b.ToTable("FeedSub");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
 | 
				
			||||||
@@ -653,7 +647,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("FilterChannelId", (string)null);
 | 
					                    b.ToTable("FilterChannelId");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
 | 
				
			||||||
@@ -675,7 +669,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("FilteredWord", (string)null);
 | 
					                    b.ToTable("FilteredWord");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterLinksChannelId", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterLinksChannelId", b =>
 | 
				
			||||||
@@ -697,7 +691,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("FilterLinksChannelId", (string)null);
 | 
					                    b.ToTable("FilterLinksChannelId");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterWordsChannelId", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterWordsChannelId", b =>
 | 
				
			||||||
@@ -719,7 +713,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("FilterWordsChannelId", (string)null);
 | 
					                    b.ToTable("FilterWordsChannelId");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
 | 
				
			||||||
@@ -741,7 +735,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("GCChannelId", (string)null);
 | 
					                    b.ToTable("GCChannelId");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.GroupName", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.GroupName", b =>
 | 
				
			||||||
@@ -767,7 +761,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("GuildConfigId", "Number")
 | 
					                    b.HasIndex("GuildConfigId", "Number")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("GroupName", (string)null);
 | 
					                    b.ToTable("GroupName");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
 | 
				
			||||||
@@ -891,7 +885,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("WarnExpireHours");
 | 
					                    b.HasIndex("WarnExpireHours");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("GuildConfigs", (string)null);
 | 
					                    b.ToTable("GuildConfigs");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogItem", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogItem", b =>
 | 
				
			||||||
@@ -917,7 +911,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("LogSettingId", "LogItemId", "ItemType")
 | 
					                    b.HasIndex("LogSettingId", "LogItemId", "ItemType")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("IgnoredLogChannels", (string)null);
 | 
					                    b.ToTable("IgnoredLogChannels");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
 | 
				
			||||||
@@ -939,7 +933,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("LogSettingId");
 | 
					                    b.HasIndex("LogSettingId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("IgnoredVoicePresenceCHannels", (string)null);
 | 
					                    b.ToTable("IgnoredVoicePresenceCHannels");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.ImageOnlyChannel", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.ImageOnlyChannel", b =>
 | 
				
			||||||
@@ -962,7 +956,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("ChannelId")
 | 
					                    b.HasIndex("ChannelId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("ImageOnlyChannels", (string)null);
 | 
					                    b.ToTable("ImageOnlyChannels");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
 | 
				
			||||||
@@ -1027,7 +1021,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("GuildId")
 | 
					                    b.HasIndex("GuildId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("LogSettings", (string)null);
 | 
					                    b.ToTable("LogSettings");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlayerSettings", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlayerSettings", b =>
 | 
				
			||||||
@@ -1064,7 +1058,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("GuildId")
 | 
					                    b.HasIndex("GuildId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("MusicPlayerSettings", (string)null);
 | 
					                    b.ToTable("MusicPlayerSettings");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b =>
 | 
				
			||||||
@@ -1087,7 +1081,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasKey("Id");
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("MusicPlaylists", (string)null);
 | 
					                    b.ToTable("MusicPlaylists");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
 | 
				
			||||||
@@ -1109,7 +1103,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("MutedUserId", (string)null);
 | 
					                    b.ToTable("MutedUserId");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.NadekoExpression", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.NadekoExpression", b =>
 | 
				
			||||||
@@ -1147,7 +1141,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasKey("Id");
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("Expressions", (string)null);
 | 
					                    b.ToTable("Expressions");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklistedTag", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklistedTag", b =>
 | 
				
			||||||
@@ -1169,7 +1163,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildId");
 | 
					                    b.HasIndex("GuildId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("NsfwBlacklistedTags", (string)null);
 | 
					                    b.ToTable("NsfwBlacklistedTags");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b =>
 | 
				
			||||||
@@ -1209,7 +1203,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("Permissions", (string)null);
 | 
					                    b.ToTable("Permissions");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.PlantedCurrency", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.PlantedCurrency", b =>
 | 
				
			||||||
@@ -1246,7 +1240,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("MessageId")
 | 
					                    b.HasIndex("MessageId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("PlantedCurrency", (string)null);
 | 
					                    b.ToTable("PlantedCurrency");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
 | 
				
			||||||
@@ -1280,7 +1274,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("MusicPlaylistId");
 | 
					                    b.HasIndex("MusicPlaylistId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("PlaylistSong", (string)null);
 | 
					                    b.ToTable("PlaylistSong");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.Poll", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.Poll", b =>
 | 
				
			||||||
@@ -1306,7 +1300,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("GuildId")
 | 
					                    b.HasIndex("GuildId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("Poll", (string)null);
 | 
					                    b.ToTable("Poll");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.PollAnswer", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.PollAnswer", b =>
 | 
				
			||||||
@@ -1331,7 +1325,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("PollId");
 | 
					                    b.HasIndex("PollId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("PollAnswer", (string)null);
 | 
					                    b.ToTable("PollAnswer");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.PollVote", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.PollVote", b =>
 | 
				
			||||||
@@ -1356,7 +1350,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("PollId");
 | 
					                    b.HasIndex("PollId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("PollVote", (string)null);
 | 
					                    b.ToTable("PollVote");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b =>
 | 
				
			||||||
@@ -1392,7 +1386,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("Keyword");
 | 
					                    b.HasIndex("Keyword");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("Quotes", (string)null);
 | 
					                    b.ToTable("Quotes");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRole", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRole", b =>
 | 
				
			||||||
@@ -1417,7 +1411,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("ReactionRoleMessageId");
 | 
					                    b.HasIndex("ReactionRoleMessageId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("ReactionRole", (string)null);
 | 
					                    b.ToTable("ReactionRole");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRoleMessage", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRoleMessage", b =>
 | 
				
			||||||
@@ -1448,7 +1442,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("ReactionRoleMessage", (string)null);
 | 
					                    b.ToTable("ReactionRoleMessage");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b =>
 | 
				
			||||||
@@ -1482,7 +1476,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("When");
 | 
					                    b.HasIndex("When");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("Reminders", (string)null);
 | 
					                    b.ToTable("Reminders");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b =>
 | 
				
			||||||
@@ -1517,7 +1511,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasKey("Id");
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("Repeaters", (string)null);
 | 
					                    b.ToTable("Repeaters");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.RewardedUser", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.RewardedUser", b =>
 | 
				
			||||||
@@ -1546,7 +1540,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("PatreonUserId")
 | 
					                    b.HasIndex("PatreonUserId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("RewardedUsers", (string)null);
 | 
					                    b.ToTable("RewardedUsers");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.RotatingPlayingStatus", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.RotatingPlayingStatus", b =>
 | 
				
			||||||
@@ -1566,7 +1560,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasKey("Id");
 | 
					                    b.HasKey("Id");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("RotatingStatus", (string)null);
 | 
					                    b.ToTable("RotatingStatus");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b =>
 | 
				
			||||||
@@ -1597,7 +1591,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("GuildId", "RoleId")
 | 
					                    b.HasIndex("GuildId", "RoleId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("SelfAssignableRoles", (string)null);
 | 
					                    b.ToTable("SelfAssignableRoles");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntry", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntry", b =>
 | 
				
			||||||
@@ -1637,7 +1631,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("ShopEntry", (string)null);
 | 
					                    b.ToTable("ShopEntry");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntryItem", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntryItem", b =>
 | 
				
			||||||
@@ -1659,7 +1653,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("ShopEntryId");
 | 
					                    b.HasIndex("ShopEntryId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("ShopEntryItem", (string)null);
 | 
					                    b.ToTable("ShopEntryItem");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredRole", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredRole", b =>
 | 
				
			||||||
@@ -1681,7 +1675,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("SlowmodeIgnoredRole", (string)null);
 | 
					                    b.ToTable("SlowmodeIgnoredRole");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredUser", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredUser", b =>
 | 
				
			||||||
@@ -1703,7 +1697,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("SlowmodeIgnoredUser", (string)null);
 | 
					                    b.ToTable("SlowmodeIgnoredUser");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b =>
 | 
				
			||||||
@@ -1728,7 +1722,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("StreamRoleSettingsId");
 | 
					                    b.HasIndex("StreamRoleSettingsId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("StreamRoleBlacklistedUser", (string)null);
 | 
					                    b.ToTable("StreamRoleBlacklistedUser");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b =>
 | 
				
			||||||
@@ -1760,7 +1754,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("GuildConfigId")
 | 
					                    b.HasIndex("GuildConfigId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("StreamRoleSettings", (string)null);
 | 
					                    b.ToTable("StreamRoleSettings");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b =>
 | 
				
			||||||
@@ -1785,7 +1779,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("StreamRoleSettingsId");
 | 
					                    b.HasIndex("StreamRoleSettingsId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("StreamRoleWhitelistedUser", (string)null);
 | 
					                    b.ToTable("StreamRoleWhitelistedUser");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.UnbanTimer", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.UnbanTimer", b =>
 | 
				
			||||||
@@ -1810,7 +1804,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("UnbanTimer", (string)null);
 | 
					                    b.ToTable("UnbanTimer");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b =>
 | 
				
			||||||
@@ -1835,7 +1829,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("UnmuteTimer", (string)null);
 | 
					                    b.ToTable("UnmuteTimer");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.UnroleTimer", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.UnroleTimer", b =>
 | 
				
			||||||
@@ -1863,7 +1857,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("UnroleTimer", (string)null);
 | 
					                    b.ToTable("UnroleTimer");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b =>
 | 
				
			||||||
@@ -1884,7 +1878,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.Property<DateTime>("LastLevelUp")
 | 
					                    b.Property<DateTime>("LastLevelUp")
 | 
				
			||||||
                        .ValueGeneratedOnAdd()
 | 
					                        .ValueGeneratedOnAdd()
 | 
				
			||||||
                        .HasColumnType("TEXT")
 | 
					                        .HasColumnType("TEXT")
 | 
				
			||||||
                        .HasDefaultValue(new DateTime(2017, 9, 21, 20, 53, 13, 307, DateTimeKind.Local));
 | 
					                        .HasDefaultValueSql("datetime('now')");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Property<int>("NotifyOnLevelUp")
 | 
					                    b.Property<int>("NotifyOnLevelUp")
 | 
				
			||||||
                        .HasColumnType("INTEGER");
 | 
					                        .HasColumnType("INTEGER");
 | 
				
			||||||
@@ -1908,7 +1902,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("UserId", "GuildId")
 | 
					                    b.HasIndex("UserId", "GuildId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("UserXpStats", (string)null);
 | 
					                    b.ToTable("UserXpStats");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b =>
 | 
				
			||||||
@@ -1933,7 +1927,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("VcRoleInfo", (string)null);
 | 
					                    b.ToTable("VcRoleInfo");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b =>
 | 
				
			||||||
@@ -1968,7 +1962,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("WaifuId")
 | 
					                    b.HasIndex("WaifuId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("WaifuInfo", (string)null);
 | 
					                    b.ToTable("WaifuInfo");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b =>
 | 
				
			||||||
@@ -1993,7 +1987,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("WaifuInfoId");
 | 
					                    b.HasIndex("WaifuInfoId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("WaifuItem", (string)null);
 | 
					                    b.ToTable("WaifuItem");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
 | 
				
			||||||
@@ -2025,7 +2019,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("UserId");
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("WaifuUpdates", (string)null);
 | 
					                    b.ToTable("WaifuUpdates");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.Warning", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.Warning", b =>
 | 
				
			||||||
@@ -2068,7 +2062,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("UserId");
 | 
					                    b.HasIndex("UserId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("Warnings", (string)null);
 | 
					                    b.ToTable("Warnings");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.WarningPunishment", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.WarningPunishment", b =>
 | 
				
			||||||
@@ -2099,7 +2093,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("GuildConfigId");
 | 
					                    b.HasIndex("GuildConfigId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("WarningPunishment", (string)null);
 | 
					                    b.ToTable("WarningPunishment");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.XpCurrencyReward", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.XpCurrencyReward", b =>
 | 
				
			||||||
@@ -2124,7 +2118,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.HasIndex("XpSettingsId");
 | 
					                    b.HasIndex("XpSettingsId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("XpCurrencyReward", (string)null);
 | 
					                    b.ToTable("XpCurrencyReward");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b =>
 | 
				
			||||||
@@ -2153,7 +2147,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("XpSettingsId", "Level")
 | 
					                    b.HasIndex("XpSettingsId", "Level")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("XpRoleReward", (string)null);
 | 
					                    b.ToTable("XpRoleReward");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b =>
 | 
				
			||||||
@@ -2176,7 +2170,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasIndex("GuildConfigId")
 | 
					                    b.HasIndex("GuildConfigId")
 | 
				
			||||||
                        .IsUnique();
 | 
					                        .IsUnique();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.ToTable("XpSettings", (string)null);
 | 
					                    b.ToTable("XpSettings");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
 | 
				
			||||||
@@ -2222,8 +2216,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
                    b.HasOne("NadekoBot.Db.Models.DiscordUser", "Owner")
 | 
					                    b.HasOne("NadekoBot.Db.Models.DiscordUser", "Owner")
 | 
				
			||||||
                        .WithOne()
 | 
					                        .WithOne()
 | 
				
			||||||
                        .HasForeignKey("NadekoBot.Db.Models.ClubInfo", "OwnerId")
 | 
					                        .HasForeignKey("NadekoBot.Db.Models.ClubInfo", "OwnerId")
 | 
				
			||||||
                        .OnDelete(DeleteBehavior.Cascade)
 | 
					                        .OnDelete(DeleteBehavior.SetNull);
 | 
				
			||||||
                        .IsRequired();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Navigation("Owner");
 | 
					                    b.Navigation("Owner");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
@@ -2231,8 +2224,9 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
            modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    b.HasOne("NadekoBot.Db.Models.ClubInfo", "Club")
 | 
					                    b.HasOne("NadekoBot.Db.Models.ClubInfo", "Club")
 | 
				
			||||||
                        .WithMany("Users")
 | 
					                        .WithMany("Members")
 | 
				
			||||||
                        .HasForeignKey("ClubId");
 | 
					                        .HasForeignKey("ClubId")
 | 
				
			||||||
 | 
					                        .OnDelete(DeleteBehavior.NoAction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Navigation("Club");
 | 
					                    b.Navigation("Club");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
@@ -2561,9 +2555,11 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    b.HasOne("NadekoBot.Services.Database.Models.WaifuInfo", null)
 | 
					                    b.HasOne("NadekoBot.Services.Database.Models.WaifuInfo", "WaifuInfo")
 | 
				
			||||||
                        .WithMany("Items")
 | 
					                        .WithMany("Items")
 | 
				
			||||||
                        .HasForeignKey("WaifuInfoId");
 | 
					                        .HasForeignKey("WaifuInfoId");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    b.Navigation("WaifuInfo");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
 | 
				
			||||||
@@ -2635,7 +2631,7 @@ namespace NadekoBot.Migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    b.Navigation("Bans");
 | 
					                    b.Navigation("Bans");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    b.Navigation("Users");
 | 
					                    b.Navigation("Members");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b =>
 | 
					            modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b =>
 | 
				
			||||||
@@ -20,7 +20,7 @@ public class DangerousCommandsService : INService
 | 
				
			|||||||
        await ctx.DiscordUser.UpdateAsync(_ => new DiscordUser()
 | 
					        await ctx.DiscordUser.UpdateAsync(_ => new DiscordUser()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Club = null,
 | 
					            Club = null,
 | 
				
			||||||
            IsClubAdmin = false,
 | 
					            // IsClubAdmin = false,
 | 
				
			||||||
            TotalXp = 0
 | 
					            TotalXp = 0
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        await ctx.ClubApplicants.DeleteAsync();
 | 
					        await ctx.ClubApplicants.DeleteAsync();
 | 
				
			||||||
@@ -97,6 +97,7 @@ public class DangerousCommandsService : INService
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        using var uow = _db.GetDbContext();
 | 
					        using var uow = _db.GetDbContext();
 | 
				
			||||||
        var conn = uow.Database.GetDbConnection();
 | 
					        var conn = uow.Database.GetDbConnection();
 | 
				
			||||||
 | 
					        conn.Open();
 | 
				
			||||||
        using var cmd = conn.CreateCommand();
 | 
					        using var cmd = conn.CreateCommand();
 | 
				
			||||||
        cmd.CommandText = sql;
 | 
					        cmd.CommandText = sql;
 | 
				
			||||||
        using var reader = cmd.ExecuteReader();
 | 
					        using var reader = cmd.ExecuteReader();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -280,7 +280,7 @@ public partial class Utility : NadekoModule
 | 
				
			|||||||
                                            $"{_stats.MessageCounter} ({_stats.MessagesPerSecond:F2}/sec)",
 | 
					                                            $"{_stats.MessageCounter} ({_stats.MessagesPerSecond:F2}/sec)",
 | 
				
			||||||
                                            true)
 | 
					                                            true)
 | 
				
			||||||
                                        .AddField(GetText(strs.memory),
 | 
					                                        .AddField(GetText(strs.memory),
 | 
				
			||||||
                                            FormattableString.Invariant($"{_stats.GetPrivateMemory():F2} MB"),
 | 
					                                            FormattableString.Invariant($"{_stats.GetPrivateMemoryMegabytes():F2} MB"),
 | 
				
			||||||
                                            true)
 | 
					                                            true)
 | 
				
			||||||
                                        .AddField(GetText(strs.owner_ids), ownerIds, true)
 | 
					                                        .AddField(GetText(strs.owner_ids), ownerIds, true)
 | 
				
			||||||
                                        .AddField(GetText(strs.uptime), _stats.GetUptimeString("\n"), true)
 | 
					                                        .AddField(GetText(strs.uptime), _stats.GetUptimeString("\n"), true)
 | 
				
			||||||
@@ -337,7 +337,7 @@ public partial class Utility : NadekoModule
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        using var http = _httpFactory.CreateClient();
 | 
					        using var http = _httpFactory.CreateClient();
 | 
				
			||||||
        using var res = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
 | 
					        using var res = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
 | 
				
			||||||
        if (!res.IsImage() || res.GetImageSize() is null or > 262_144)
 | 
					        if (!res.IsImage() || res.GetContentLength() > 262_144)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await ReplyErrorLocalizedAsync(strs.invalid_emoji_link);
 | 
					            await ReplyErrorLocalizedAsync(strs.invalid_emoji_link);
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace NadekoBot.Modules.Xp;
 | 
				
			|||||||
public partial class Xp
 | 
					public partial class Xp
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [Group]
 | 
					    [Group]
 | 
				
			||||||
    public partial class Club : NadekoModule<ClubService>
 | 
					    public partial class Club : NadekoModule<IClubService>
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly XpService _xps;
 | 
					        private readonly XpService _xps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -19,35 +19,33 @@ public partial class Xp
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var club = _service.TransferClub(ctx.User, newOwner);
 | 
					            var club = _service.TransferClub(ctx.User, newOwner);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (club is not null)
 | 
					            if (club is null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await ReplyConfirmLocalizedAsync(strs.club_transfered(Format.Bold(club.Name),
 | 
					                await ReplyErrorLocalizedAsync(strs.club_transfer_failed);
 | 
				
			||||||
                    Format.Bold(newOwner.ToString())));
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                await ReplyErrorLocalizedAsync(strs.club_transfer_failed);
 | 
					            {
 | 
				
			||||||
 | 
					                await ReplyConfirmLocalizedAsync(
 | 
				
			||||||
 | 
					                    strs.club_transfered(
 | 
				
			||||||
 | 
					                        Format.Bold(club.Name),
 | 
				
			||||||
 | 
					                        Format.Bold(newOwner.ToString())
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Cmd]
 | 
					        [Cmd]
 | 
				
			||||||
        public async partial Task ClubAdmin([Leftover] IUser toAdmin)
 | 
					        public async partial Task ClubAdmin([Leftover] IUser toAdmin)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            bool admin;
 | 
					            var admin = await _service.ToggleAdminAsync(ctx.User, toAdmin);
 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                admin = _service.ToggleAdmin(ctx.User, toAdmin);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (InvalidOperationException)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await ReplyErrorLocalizedAsync(strs.club_admin_error);
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
            if (admin)
 | 
					            if (admin is null)
 | 
				
			||||||
 | 
					                await ReplyErrorLocalizedAsync(strs.club_admin_error);
 | 
				
			||||||
 | 
					            else if (admin is true)
 | 
				
			||||||
                await ReplyConfirmLocalizedAsync(strs.club_admin_add(Format.Bold(toAdmin.ToString())));
 | 
					                await ReplyConfirmLocalizedAsync(strs.club_admin_add(Format.Bold(toAdmin.ToString())));
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                await ReplyConfirmLocalizedAsync(strs.club_admin_remove(Format.Bold(toAdmin.ToString())));
 | 
					                await ReplyConfirmLocalizedAsync(strs.club_admin_remove(Format.Bold(toAdmin.ToString())));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        [Cmd]
 | 
					        [Cmd]
 | 
				
			||||||
        public async partial Task ClubCreate([Leftover] string clubName)
 | 
					        public async partial Task ClubCreate([Leftover] string clubName)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -57,20 +55,28 @@ public partial class Xp
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!_service.CreateClub(ctx.User, clubName, out var club))
 | 
					            var succ = await _service.CreateClubAsync(ctx.User, clubName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (succ is null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await ReplyErrorLocalizedAsync(strs.club_create_error_name);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (succ is false)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await ReplyErrorLocalizedAsync(strs.club_create_error);
 | 
					                await ReplyErrorLocalizedAsync(strs.club_create_error);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await ReplyConfirmLocalizedAsync(strs.club_created(Format.Bold(club.ToString())));
 | 
					            await ReplyConfirmLocalizedAsync(strs.club_created(Format.Bold(clubName)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Cmd]
 | 
					        [Cmd]
 | 
				
			||||||
        public async partial Task ClubIcon([Leftover] string url = null)
 | 
					        public async partial Task ClubIcon([Leftover] string url = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if ((!Uri.IsWellFormedUriString(url, UriKind.Absolute) && url is not null)
 | 
					            if ((!Uri.IsWellFormedUriString(url, UriKind.Absolute) && url is not null)
 | 
				
			||||||
                || !await _service.SetClubIcon(ctx.User.Id, url is null ? null : new Uri(url)))
 | 
					                || !await _service.SetClubIconAsync(ctx.User.Id, url))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await ReplyErrorLocalizedAsync(strs.club_icon_error);
 | 
					                await ReplyErrorLocalizedAsync(strs.club_icon_error);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
@@ -82,7 +88,7 @@ public partial class Xp
 | 
				
			|||||||
        private async Task InternalClubInfoAsync(ClubInfo club)
 | 
					        private async Task InternalClubInfoAsync(ClubInfo club)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var lvl = new LevelStats(club.Xp);
 | 
					            var lvl = new LevelStats(club.Xp);
 | 
				
			||||||
            var users = club.Users.OrderByDescending(x =>
 | 
					            var users = club.Members.OrderByDescending(x =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var l = new LevelStats(x.TotalXp).Level;
 | 
					                var l = new LevelStats(x.TotalXp).Level;
 | 
				
			||||||
                if (club.OwnerId == x.Id)
 | 
					                if (club.OwnerId == x.Id)
 | 
				
			||||||
@@ -102,7 +108,7 @@ public partial class Xp
 | 
				
			|||||||
                                   .AddField(GetText(strs.desc),
 | 
					                                   .AddField(GetText(strs.desc),
 | 
				
			||||||
                                       string.IsNullOrWhiteSpace(club.Description) ? "-" : club.Description)
 | 
					                                       string.IsNullOrWhiteSpace(club.Description) ? "-" : club.Description)
 | 
				
			||||||
                                   .AddField(GetText(strs.owner), club.Owner.ToString(), true)
 | 
					                                   .AddField(GetText(strs.owner), club.Owner.ToString(), true)
 | 
				
			||||||
                                   .AddField(GetText(strs.level_req), club.MinimumLevelReq.ToString(), true)
 | 
					                                   // .AddField(GetText(strs.level_req), club.MinimumLevelReq.ToString(), true)
 | 
				
			||||||
                                   .AddField(GetText(strs.members),
 | 
					                                   .AddField(GetText(strs.members),
 | 
				
			||||||
                                       string.Join("\n",
 | 
					                                       string.Join("\n",
 | 
				
			||||||
                                           users.Skip(page * 10)
 | 
					                                           users.Skip(page * 10)
 | 
				
			||||||
@@ -123,7 +129,7 @@ public partial class Xp
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    return embed;
 | 
					                    return embed;
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                club.Users.Count,
 | 
					                club.Members.Count,
 | 
				
			||||||
                10);
 | 
					                10);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -187,7 +193,6 @@ public partial class Xp
 | 
				
			|||||||
                10);
 | 
					                10);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        [Cmd]
 | 
					        [Cmd]
 | 
				
			||||||
        public partial Task ClubApps(int page = 1)
 | 
					        public partial Task ClubApps(int page = 1)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -310,19 +315,10 @@ public partial class Xp
 | 
				
			|||||||
            return ReplyErrorLocalizedAsync(strs.club_user_unban_fail);
 | 
					            return ReplyErrorLocalizedAsync(strs.club_user_unban_fail);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Cmd]
 | 
					 | 
				
			||||||
        public async partial Task ClubLevelReq(int level)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_service.ChangeClubLevelReq(ctx.User.Id, level))
 | 
					 | 
				
			||||||
                await ReplyConfirmLocalizedAsync(strs.club_level_req_changed(Format.Bold(level.ToString())));
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                await ReplyErrorLocalizedAsync(strs.club_level_req_change_error);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [Cmd]
 | 
					        [Cmd]
 | 
				
			||||||
        public async partial Task ClubDescription([Leftover] string desc = null)
 | 
					        public async partial Task ClubDescription([Leftover] string desc = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_service.ChangeClubDescription(ctx.User.Id, desc))
 | 
					            if (_service.SetDescription(ctx.User.Id, desc))
 | 
				
			||||||
                await ReplyConfirmLocalizedAsync(strs.club_desc_updated(Format.Bold(desc ?? "-")));
 | 
					                await ReplyConfirmLocalizedAsync(strs.club_desc_updated(Format.Bold(desc ?? "-")));
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                await ReplyErrorLocalizedAsync(strs.club_desc_update_failed);
 | 
					                await ReplyErrorLocalizedAsync(strs.club_desc_update_failed);
 | 
				
			||||||
@@ -332,7 +328,7 @@ public partial class Xp
 | 
				
			|||||||
        public async partial Task ClubDisband()
 | 
					        public async partial Task ClubDisband()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_service.Disband(ctx.User.Id, out var club))
 | 
					            if (_service.Disband(ctx.User.Id, out var club))
 | 
				
			||||||
                await ReplyConfirmLocalizedAsync(strs.club_disbanded(Format.Bold(club.ToString())));
 | 
					                await ReplyConfirmLocalizedAsync(strs.club_disbanded(Format.Bold(club.Name)));
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                await ReplyErrorLocalizedAsync(strs.club_disband_error);
 | 
					                await ReplyErrorLocalizedAsync(strs.club_disband_error);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,13 @@
 | 
				
			|||||||
#nullable disable
 | 
					#nullable disable
 | 
				
			||||||
 | 
					using LinqToDB;
 | 
				
			||||||
 | 
					using LinqToDB.EntityFrameworkCore;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
using NadekoBot.Db;
 | 
					using NadekoBot.Db;
 | 
				
			||||||
using NadekoBot.Db.Models;
 | 
					using NadekoBot.Db.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace NadekoBot.Modules.Xp.Services;
 | 
					namespace NadekoBot.Modules.Xp.Services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ClubService : INService
 | 
					public class ClubService : INService, IClubService
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private readonly DbService _db;
 | 
					    private readonly DbService _db;
 | 
				
			||||||
    private readonly IHttpClientFactory _httpFactory;
 | 
					    private readonly IHttpClientFactory _httpFactory;
 | 
				
			||||||
@@ -15,46 +18,42 @@ public class ClubService : INService
 | 
				
			|||||||
        _httpFactory = httpFactory;
 | 
					        _httpFactory = httpFactory;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public bool CreateClub(IUser user, string clubName, out ClubInfo club)
 | 
					    public async Task<bool?> CreateClubAsync(IUser user, string clubName)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        //must be lvl 5 and must not be in a club already
 | 
					        //must be lvl 5 and must not be in a club already
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        club = null;
 | 
					        await using var uow = _db.GetDbContext();
 | 
				
			||||||
        using var uow = _db.GetDbContext();
 | 
					 | 
				
			||||||
        var du = uow.GetOrCreateUser(user);
 | 
					        var du = uow.GetOrCreateUser(user);
 | 
				
			||||||
        uow.SaveChanges();
 | 
					 | 
				
			||||||
        var xp = new LevelStats(du.TotalXp);
 | 
					        var xp = new LevelStats(du.TotalXp);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if (xp.Level >= 5 && du.Club is null)
 | 
					        if (xp.Level < 5 || du.ClubId is not null)
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            du.IsClubAdmin = true;
 | 
					 | 
				
			||||||
            du.Club = new()
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Name = clubName,
 | 
					 | 
				
			||||||
                Discrim = uow.Clubs.GetNextDiscrim(clubName),
 | 
					 | 
				
			||||||
                Owner = du
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
            uow.Clubs.Add(du.Club);
 | 
					 | 
				
			||||||
            uow.SaveChanges();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        uow.Set<ClubApplicants>().RemoveRange(uow.Set<ClubApplicants>().AsQueryable().Where(x => x.UserId == du.Id));
 | 
					        if (await uow.Clubs.AnyAsyncEF(x => x.Name == clubName))
 | 
				
			||||||
        club = du.Club;
 | 
					            return null;
 | 
				
			||||||
        uow.SaveChanges();
 | 
					        
 | 
				
			||||||
 | 
					        du.IsClubAdmin = true;
 | 
				
			||||||
 | 
					        du.Club = new()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Name = clubName,
 | 
				
			||||||
 | 
					            Owner = du
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        uow.Clubs.Add(du.Club);
 | 
				
			||||||
 | 
					        await uow.SaveChangesAsync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await uow.GetTable<ClubApplicants>()
 | 
				
			||||||
 | 
					                 .DeleteAsync(x => x.UserId == du.Id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public ClubInfo TransferClub(IUser from, IUser newOwner)
 | 
					    public ClubInfo TransferClub(IUser from, IUser newOwner)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        ClubInfo club;
 | 
					 | 
				
			||||||
        using var uow = _db.GetDbContext();
 | 
					        using var uow = _db.GetDbContext();
 | 
				
			||||||
        club = uow.Clubs.GetByOwner(from.Id);
 | 
					        var club = uow.Clubs.GetByOwner(@from.Id);
 | 
				
			||||||
        var newOwnerUser = uow.GetOrCreateUser(newOwner);
 | 
					        var newOwnerUser = uow.GetOrCreateUser(newOwner);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (club is null || club.Owner.UserId != from.Id || !club.Users.Contains(newOwnerUser))
 | 
					        if (club is null || club.Owner.UserId != from.Id || !club.Members.Contains(newOwnerUser))
 | 
				
			||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        club.Owner.IsClubAdmin = true; // old owner will stay as admin
 | 
					        club.Owner.IsClubAdmin = true; // old owner will stay as admin
 | 
				
			||||||
@@ -64,21 +63,20 @@ public class ClubService : INService
 | 
				
			|||||||
        return club;
 | 
					        return club;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public bool ToggleAdmin(IUser owner, IUser toAdmin)
 | 
					    public async Task<bool?> ToggleAdminAsync(IUser owner, IUser toAdmin)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        bool newState;
 | 
					        await using var uow = _db.GetDbContext();
 | 
				
			||||||
        using var uow = _db.GetDbContext();
 | 
					 | 
				
			||||||
        var club = uow.Clubs.GetByOwner(owner.Id);
 | 
					        var club = uow.Clubs.GetByOwner(owner.Id);
 | 
				
			||||||
        var adminUser = uow.GetOrCreateUser(toAdmin);
 | 
					        var adminUser = uow.GetOrCreateUser(toAdmin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (club is null || club.Owner.UserId != owner.Id || !club.Users.Contains(adminUser))
 | 
					        if (club is null || club.Owner.UserId != owner.Id || !club.Members.Contains(adminUser))
 | 
				
			||||||
            throw new InvalidOperationException();
 | 
					            return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (club.OwnerId == adminUser.Id)
 | 
					        if (club.OwnerId == adminUser.Id)
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        newState = adminUser.IsClubAdmin = !adminUser.IsClubAdmin;
 | 
					        var newState = adminUser.IsClubAdmin = !adminUser.IsClubAdmin;
 | 
				
			||||||
        uow.SaveChanges();
 | 
					        await uow.SaveChangesAsync();
 | 
				
			||||||
        return newState;
 | 
					        return newState;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -89,13 +87,13 @@ public class ClubService : INService
 | 
				
			|||||||
        return member;
 | 
					        return member;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public async Task<bool> SetClubIcon(ulong ownerUserId, Uri url)
 | 
					    public async Task<bool> SetClubIconAsync(ulong ownerUserId, string url)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (url is not null)
 | 
					        if (url is not null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            using var http = _httpFactory.CreateClient();
 | 
					            using var http = _httpFactory.CreateClient();
 | 
				
			||||||
            using var temp = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
 | 
					            using var temp = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
 | 
				
			||||||
            if (!temp.IsImage() || temp.GetImageSize() > 11)
 | 
					            if (!temp.IsImage() || temp.GetContentLength() > 5.Megabytes().Bytes)
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -105,30 +103,18 @@ public class ClubService : INService
 | 
				
			|||||||
        if (club is null)
 | 
					        if (club is null)
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        club.ImageUrl = url.ToString();
 | 
					        club.ImageUrl = url;
 | 
				
			||||||
        uow.SaveChanges();
 | 
					        await uow.SaveChangesAsync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public bool GetClubByName(string clubName, out ClubInfo club)
 | 
					    public bool GetClubByName(string clubName, out ClubInfo club)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        club = null;
 | 
					 | 
				
			||||||
        var arr = clubName.Split('#');
 | 
					 | 
				
			||||||
        if (arr.Length < 2 || !int.TryParse(arr[arr.Length - 1], out var discrim))
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //incase club has # in it
 | 
					 | 
				
			||||||
        var name = string.Concat(arr.Except(new[] { arr[arr.Length - 1] }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (string.IsNullOrWhiteSpace(name))
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        using var uow = _db.GetDbContext();
 | 
					        using var uow = _db.GetDbContext();
 | 
				
			||||||
        club = uow.Clubs.GetByName(name, discrim);
 | 
					        club = uow.Clubs.GetByName(clubName);
 | 
				
			||||||
        if (club is null)
 | 
					
 | 
				
			||||||
            return false;
 | 
					        return club is not null;
 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public bool ApplyToClub(IUser user, ClubInfo club)
 | 
					    public bool ApplyToClub(IUser user, ClubInfo club)
 | 
				
			||||||
@@ -138,7 +124,6 @@ public class ClubService : INService
 | 
				
			|||||||
        uow.SaveChanges();
 | 
					        uow.SaveChanges();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (du.Club is not null
 | 
					        if (du.Club is not null
 | 
				
			||||||
            || new LevelStats(du.TotalXp).Level < club.MinimumLevelReq
 | 
					 | 
				
			||||||
            || club.Bans.Any(x => x.UserId == du.Id)
 | 
					            || club.Bans.Any(x => x.UserId == du.Id)
 | 
				
			||||||
            || club.Applicants.Any(x => x.UserId == du.Id))
 | 
					            || club.Applicants.Any(x => x.UserId == du.Id))
 | 
				
			||||||
            //user banned or a member of a club, or already applied,
 | 
					            //user banned or a member of a club, or already applied,
 | 
				
			||||||
@@ -202,23 +187,7 @@ public class ClubService : INService
 | 
				
			|||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public bool ChangeClubLevelReq(ulong userId, int level)
 | 
					    public bool SetDescription(ulong userId, string desc)
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        if (level < 5)
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        using var uow = _db.GetDbContext();
 | 
					 | 
				
			||||||
        var club = uow.Clubs.GetByOwner(userId);
 | 
					 | 
				
			||||||
        if (club is null)
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        club.MinimumLevelReq = level;
 | 
					 | 
				
			||||||
        uow.SaveChanges();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public bool ChangeClubDescription(ulong userId, string desc)
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        using var uow = _db.GetDbContext();
 | 
					        using var uow = _db.GetDbContext();
 | 
				
			||||||
        var club = uow.Clubs.GetByOwner(userId);
 | 
					        var club = uow.Clubs.GetByOwner(userId);
 | 
				
			||||||
@@ -250,7 +219,7 @@ public class ClubService : INService
 | 
				
			|||||||
        if (club is null)
 | 
					        if (club is null)
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var usr = club.Users.FirstOrDefault(x => x.ToString().ToUpperInvariant() == userName.ToUpperInvariant())
 | 
					        var usr = club.Members.FirstOrDefault(x => x.ToString().ToUpperInvariant() == userName.ToUpperInvariant())
 | 
				
			||||||
                  ?? club.Applicants
 | 
					                  ?? club.Applicants
 | 
				
			||||||
                         .FirstOrDefault(x => x.User.ToString().ToUpperInvariant() == userName.ToUpperInvariant())
 | 
					                         .FirstOrDefault(x => x.User.ToString().ToUpperInvariant() == userName.ToUpperInvariant())
 | 
				
			||||||
                         ?.User;
 | 
					                         ?.User;
 | 
				
			||||||
@@ -266,7 +235,7 @@ public class ClubService : INService
 | 
				
			|||||||
            Club = club,
 | 
					            Club = club,
 | 
				
			||||||
            User = usr
 | 
					            User = usr
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        club.Users.Remove(usr);
 | 
					        club.Members.Remove(usr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var app = club.Applicants.FirstOrDefault(x => x.UserId == usr.Id);
 | 
					        var app = club.Applicants.FirstOrDefault(x => x.UserId == usr.Id);
 | 
				
			||||||
        if (app is not null)
 | 
					        if (app is not null)
 | 
				
			||||||
@@ -301,14 +270,14 @@ public class ClubService : INService
 | 
				
			|||||||
        if (club is null)
 | 
					        if (club is null)
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var usr = club.Users.FirstOrDefault(x => x.ToString().ToUpperInvariant() == userName.ToUpperInvariant());
 | 
					        var usr = club.Members.FirstOrDefault(x => x.ToString().ToUpperInvariant() == userName.ToUpperInvariant());
 | 
				
			||||||
        if (usr is null)
 | 
					        if (usr is null)
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (club.OwnerId == usr.Id || (usr.IsClubAdmin && club.Owner.UserId != kickerId))
 | 
					        if (club.OwnerId == usr.Id || (usr.IsClubAdmin && club.Owner.UserId != kickerId))
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        club.Users.Remove(usr);
 | 
					        club.Members.Remove(usr);
 | 
				
			||||||
        var app = club.Applicants.FirstOrDefault(x => x.UserId == usr.Id);
 | 
					        var app = club.Applicants.FirstOrDefault(x => x.UserId == usr.Id);
 | 
				
			||||||
        if (app is not null)
 | 
					        if (app is not null)
 | 
				
			||||||
            club.Applicants.Remove(app);
 | 
					            club.Applicants.Remove(app);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								src/NadekoBot/Modules/Xp/Club/IClubService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/NadekoBot/Modules/Xp/Club/IClubService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					using NadekoBot.Db.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NadekoBot.Modules.Xp.Services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface IClubService
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Task<bool?> CreateClubAsync(IUser user, string clubName);
 | 
				
			||||||
 | 
					    ClubInfo? TransferClub(IUser from, IUser newOwner);
 | 
				
			||||||
 | 
					    Task<bool?> ToggleAdminAsync(IUser owner, IUser toAdmin);
 | 
				
			||||||
 | 
					    ClubInfo? GetClubByMember(IUser user);
 | 
				
			||||||
 | 
					    Task<bool> SetClubIconAsync(ulong ownerUserId, string? url);
 | 
				
			||||||
 | 
					    bool GetClubByName(string clubName, out ClubInfo club);
 | 
				
			||||||
 | 
					    bool ApplyToClub(IUser user, ClubInfo club);
 | 
				
			||||||
 | 
					    bool AcceptApplication(ulong clubOwnerUserId, string userName, out DiscordUser discordUser);
 | 
				
			||||||
 | 
					    ClubInfo? GetClubWithBansAndApplications(ulong ownerUserId);
 | 
				
			||||||
 | 
					    bool LeaveClub(IUser user);
 | 
				
			||||||
 | 
					    bool SetDescription(ulong userId, string? desc);
 | 
				
			||||||
 | 
					    bool Disband(ulong userId, out ClubInfo club);
 | 
				
			||||||
 | 
					    bool Ban(ulong bannerId, string userName, out ClubInfo club);
 | 
				
			||||||
 | 
					    bool UnBan(ulong ownerUserId, string userName, out ClubInfo club);
 | 
				
			||||||
 | 
					    bool Kick(ulong kickerId, string userName, out ClubInfo club);
 | 
				
			||||||
 | 
					    List<ClubInfo> GetClubLeaderboardPage(int page);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -647,7 +647,7 @@ public class XpService : INService, IReadyExecutor
 | 
				
			|||||||
        int guildRank;
 | 
					        int guildRank;
 | 
				
			||||||
        await using (var uow = _db.GetDbContext())
 | 
					        await using (var uow = _db.GetDbContext())
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            du = uow.GetOrCreateUser(user);
 | 
					            du = uow.GetOrCreateUser(user, set => set.Include(x => x.Club));
 | 
				
			||||||
            totalXp = du.TotalXp;
 | 
					            totalXp = du.TotalXp;
 | 
				
			||||||
            globalRank = uow.DiscordUser.GetUserGlobalRank(user.Id);
 | 
					            globalRank = uow.DiscordUser.GetUserGlobalRank(user.Id);
 | 
				
			||||||
            guildRank = uow.UserXpStats.GetUserGuildRanking(user.Id, user.GuildId);
 | 
					            guildRank = uow.UserXpStats.GetUserGuildRanking(user.Id, user.GuildId);
 | 
				
			||||||
@@ -1021,8 +1021,9 @@ public class XpService : INService, IReadyExecutor
 | 
				
			|||||||
                    using (var http = _httpFactory.CreateClient())
 | 
					                    using (var http = _httpFactory.CreateClient())
 | 
				
			||||||
                    using (var temp = await http.GetAsync(imgUrl, HttpCompletionOption.ResponseHeadersRead))
 | 
					                    using (var temp = await http.GetAsync(imgUrl, HttpCompletionOption.ResponseHeadersRead))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        if (!temp.IsImage() || temp.GetImageSize() > 11)
 | 
					                        if (!temp.IsImage() || temp.GetContentLength() > 11.Megabytes().Bytes)
 | 
				
			||||||
                            return;
 | 
					                            return;
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
                        var imgData = await temp.Content.ReadAsByteArrayAsync();
 | 
					                        var imgData = await temp.Content.ReadAsByteArrayAsync();
 | 
				
			||||||
                        using (var tempDraw = Image.Load(imgData))
 | 
					                        using (var tempDraw = Image.Load(imgData))
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,6 +84,9 @@
 | 
				
			|||||||
<!--            <PrivateAssets>all</PrivateAssets>-->
 | 
					<!--            <PrivateAssets>all</PrivateAssets>-->
 | 
				
			||||||
<!--            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
 | 
					<!--            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
 | 
				
			||||||
<!--        </PackageReference>-->
 | 
					<!--        </PackageReference>-->
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        <PackageReference Include="EFCore.NamingConventions" Version="6.0.0" />
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
    </ItemGroup>
 | 
					    </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <ItemGroup>
 | 
					    <ItemGroup>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
#nullable disable
 | 
					#nullable disable
 | 
				
			||||||
using LinqToDB.Common;
 | 
					using LinqToDB.Common;
 | 
				
			||||||
using LinqToDB.EntityFrameworkCore;
 | 
					using LinqToDB.EntityFrameworkCore;
 | 
				
			||||||
using Microsoft.Data.Sqlite;
 | 
					 | 
				
			||||||
using Microsoft.EntityFrameworkCore;
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
using NadekoBot.Services.Database;
 | 
					using NadekoBot.Services.Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -9,49 +8,68 @@ namespace NadekoBot.Services;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public class DbService
 | 
					public class DbService
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private readonly DbContextOptions<NadekoContext> _options;
 | 
					    private readonly IBotCredsProvider _creds;
 | 
				
			||||||
    private readonly DbContextOptions<NadekoContext> _migrateOptions;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public DbService(IBotCredentials creds)
 | 
					    // these are props because creds can change at runtime
 | 
				
			||||||
 | 
					    private string DbType => _creds.GetCreds().Db.Type.ToLowerInvariant().Trim();
 | 
				
			||||||
 | 
					    private string ConnString => _creds.GetCreds().Db.ConnectionString;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public DbService(IBotCredsProvider creds)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        LinqToDBForEFTools.Initialize();
 | 
					        LinqToDBForEFTools.Initialize();
 | 
				
			||||||
        Configuration.Linq.DisableQueryCache = true;
 | 
					        Configuration.Linq.DisableQueryCache = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var builder = new SqliteConnectionStringBuilder(creds.Db.ConnectionString);
 | 
					        _creds = creds;
 | 
				
			||||||
        builder.DataSource = Path.Combine(AppContext.BaseDirectory, builder.DataSource);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var optionsBuilder = new DbContextOptionsBuilder<NadekoContext>();
 | 
					 | 
				
			||||||
        optionsBuilder.UseSqlite(builder.ToString());
 | 
					 | 
				
			||||||
        _options = optionsBuilder.Options;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        optionsBuilder = new();
 | 
					 | 
				
			||||||
        optionsBuilder.UseSqlite(builder.ToString());
 | 
					 | 
				
			||||||
        _migrateOptions = optionsBuilder.Options;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void Setup()
 | 
					    public async Task SetupAsync()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        using var context = new NadekoContext(_options);
 | 
					        var dbType = DbType;
 | 
				
			||||||
        if (context.Database.GetPendingMigrations().Any())
 | 
					        var connString = ConnString;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await using var context = CreateRawDbContext(dbType, connString);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // make sure sqlite db is in wal journal mode
 | 
				
			||||||
 | 
					        if (context is SqliteContext)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            using var mContext = new NadekoContext(_migrateOptions);
 | 
					            await context.Database.ExecuteSqlRawAsync("PRAGMA journal_mode=WAL");
 | 
				
			||||||
            mContext.Database.Migrate();
 | 
					 | 
				
			||||||
            mContext.SaveChanges();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        context.Database.ExecuteSqlRaw("PRAGMA journal_mode=WAL");
 | 
					        await context.Database.MigrateAsync();
 | 
				
			||||||
        context.SaveChanges();
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static NadekoContext CreateRawDbContext(string dbType, string connString)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        switch (dbType)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            case "postgresql":
 | 
				
			||||||
 | 
					            case "postgres":
 | 
				
			||||||
 | 
					            case "pgsql":
 | 
				
			||||||
 | 
					                return new PostgreSqlContext(connString);
 | 
				
			||||||
 | 
					            case "mysql":
 | 
				
			||||||
 | 
					                return new MysqlContext(connString);
 | 
				
			||||||
 | 
					            case "sqlite":
 | 
				
			||||||
 | 
					                return new SqliteContext(connString);
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                throw new NotSupportedException($"The database provide type of '{dbType}' is not supported.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    private NadekoContext GetDbContextInternal()
 | 
					    private NadekoContext GetDbContextInternal()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var context = new NadekoContext(_options);
 | 
					        var dbType = DbType;
 | 
				
			||||||
        context.Database.SetCommandTimeout(60);
 | 
					        var connString = ConnString;
 | 
				
			||||||
        var conn = context.Database.GetDbConnection();
 | 
					
 | 
				
			||||||
        conn.Open();
 | 
					        var context = CreateRawDbContext(dbType, connString);
 | 
				
			||||||
        using var com = conn.CreateCommand();
 | 
					        if (context is SqliteContext)
 | 
				
			||||||
        com.CommandText = "PRAGMA journal_mode=WAL; PRAGMA synchronous=OFF";
 | 
					        {
 | 
				
			||||||
        com.ExecuteNonQuery();
 | 
					            var conn = context.Database.GetDbConnection();
 | 
				
			||||||
 | 
					            conn.Open();
 | 
				
			||||||
 | 
					            using var com = conn.CreateCommand();
 | 
				
			||||||
 | 
					            com.CommandText = "PRAGMA synchronous=OFF";
 | 
				
			||||||
 | 
					            com.ExecuteNonQuery();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return context;
 | 
					        return context;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,5 +52,5 @@ public interface IStatsService
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    ///     Gets total amount of private memory currently in use by the bot, in Megabytes.
 | 
					    ///     Gets total amount of private memory currently in use by the bot, in Megabytes.
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    double GetPrivateMemory();
 | 
					    double GetPrivateMemoryMegabytes();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -166,9 +166,9 @@ public sealed class BotCredsProvider : IBotCredsProvider
 | 
				
			|||||||
        if (File.Exists(CREDS_FILE_NAME))
 | 
					        if (File.Exists(CREDS_FILE_NAME))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var creds = Yaml.Deserializer.Deserialize<Creds>(File.ReadAllText(CREDS_FILE_NAME));
 | 
					            var creds = Yaml.Deserializer.Deserialize<Creds>(File.ReadAllText(CREDS_FILE_NAME));
 | 
				
			||||||
            if (creds.Version <= 3)
 | 
					            if (creds.Version <= 4)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                creds.Version = 4;
 | 
					                creds.Version = 5;
 | 
				
			||||||
                File.WriteAllText(CREDS_FILE_NAME, Yaml.Serializer.Serialize(creds));
 | 
					                File.WriteAllText(CREDS_FILE_NAME, Yaml.Serializer.Serialize(creds));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -183,9 +183,9 @@ public sealed class StatsService : IStatsService, IReadyExecutor, INService
 | 
				
			|||||||
        return time.Humanize(3, maxUnit: TimeUnit.Day, minUnit: TimeUnit.Minute);
 | 
					        return time.Humanize(3, maxUnit: TimeUnit.Day, minUnit: TimeUnit.Minute);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public double GetPrivateMemory()
 | 
					    public double GetPrivateMemoryMegabytes()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _currentProcess.Refresh();
 | 
					        _currentProcess.Refresh();
 | 
				
			||||||
        return _currentProcess.PrivateMemorySize64 / (double)1.MiB();
 | 
					        return _currentProcess.PrivateMemorySize64 / 1.Megabytes().Bytes;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -190,13 +190,10 @@ public static class Extensions
 | 
				
			|||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static long? GetImageSize(this HttpResponseMessage msg)
 | 
					    public static long GetContentLength(this HttpResponseMessage msg)
 | 
				
			||||||
    {
 | 
					        => msg.Content.Headers.ContentLength is long length
 | 
				
			||||||
        if (msg.Content.Headers.ContentLength is null)
 | 
					            ? length
 | 
				
			||||||
            return null;
 | 
					            : long.MaxValue;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return msg.Content.Headers.ContentLength.Value / 1.Mb();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static string GetText(this IBotStrings strings, in LocStr str, ulong? guildId = null)
 | 
					    public static string GetText(this IBotStrings strings, in LocStr str, ulong? guildId = null)
 | 
				
			||||||
        => strings.GetText(str.Key, guildId, str.Params);
 | 
					        => strings.GetText(str.Key, guildId, str.Params);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,45 +2,6 @@ namespace NadekoBot.Extensions;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public static class NumberExtensions
 | 
					public static class NumberExtensions
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public static int KiB(this int value)
 | 
					 | 
				
			||||||
        => value * 1024;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static int Kb(this int value)
 | 
					 | 
				
			||||||
        => value * 1000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static int MiB(this int value)
 | 
					 | 
				
			||||||
        => value.KiB() * 1024;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static int Mb(this int value)
 | 
					 | 
				
			||||||
        => value.Kb() * 1000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static int GiB(this int value)
 | 
					 | 
				
			||||||
        => value.MiB() * 1024;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static int Gb(this int value)
 | 
					 | 
				
			||||||
        => value.Mb() * 1000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static ulong KiB(this ulong value)
 | 
					 | 
				
			||||||
        => value * 1024;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static ulong Kb(this ulong value)
 | 
					 | 
				
			||||||
        => value * 1000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static ulong MiB(this ulong value)
 | 
					 | 
				
			||||||
        => value.KiB() * 1024;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static ulong Mb(this ulong value)
 | 
					 | 
				
			||||||
        => value.Kb() * 1000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static ulong GiB(this ulong value)
 | 
					 | 
				
			||||||
        => value.MiB() * 1024;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static ulong Gb(this ulong value)
 | 
					 | 
				
			||||||
        => value.Mb() * 1000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static bool IsInteger(this decimal number)
 | 
					 | 
				
			||||||
        => number == Math.Truncate(number);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static DateTimeOffset ToUnixTimestamp(this double number)
 | 
					    public static DateTimeOffset ToUnixTimestamp(this double number)
 | 
				
			||||||
        => new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).AddSeconds(number);
 | 
					        => new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).AddSeconds(number);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
# DO NOT CHANGE
 | 
					# DO NOT CHANGE
 | 
				
			||||||
version: 4
 | 
					version: 5
 | 
				
			||||||
# Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/
 | 
					# Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/
 | 
				
			||||||
token: ''
 | 
					token: ''
 | 
				
			||||||
# List of Ids of the users who have bot owner permissions
 | 
					# List of Ids of the users who have bot owner permissions
 | 
				
			||||||
@@ -47,9 +47,14 @@ cleverbotApiKey: ''
 | 
				
			|||||||
redisOptions: localhost:6379,syncTimeout=30000,responseTimeout=30000,allowAdmin=true,password=
 | 
					redisOptions: localhost:6379,syncTimeout=30000,responseTimeout=30000,allowAdmin=true,password=
 | 
				
			||||||
# Database options. Don't change if you don't know what you're doing. Leave null for default values
 | 
					# Database options. Don't change if you don't know what you're doing. Leave null for default values
 | 
				
			||||||
db:
 | 
					db:
 | 
				
			||||||
# Database type. Only sqlite supported atm
 | 
					# Database type. "sqlite", "mysql" and "postgresql" are supported.
 | 
				
			||||||
 | 
					# Default is "sqlite"
 | 
				
			||||||
  type: sqlite
 | 
					  type: sqlite
 | 
				
			||||||
  # Connection string. Will default to "Data Source=data/NadekoBot.db"
 | 
					  # Database connection string.
 | 
				
			||||||
 | 
					  # You MUST change this if you're not using "sqlite" type.
 | 
				
			||||||
 | 
					  # Default is "Data Source=data/NadekoBot.db"
 | 
				
			||||||
 | 
					  # Example for mysql: "Server=localhost;Port=3306;Uid=root;Pwd=my_super_secret_mysql_password;Database=nadeko"
 | 
				
			||||||
 | 
					  # Example for postgresql: "Server=localhost;Port=5432;User Id=postgres;Password=my_super_secret_postgres_password;Database=nadeko;"
 | 
				
			||||||
  connectionString: Data Source=data/NadekoBot.db
 | 
					  connectionString: Data Source=data/NadekoBot.db
 | 
				
			||||||
# Address and port of the coordinator endpoint. Leave empty for default.
 | 
					# Address and port of the coordinator endpoint. Leave empty for default.
 | 
				
			||||||
# Change only if you've changed the coordinator address or port.
 | 
					# Change only if you've changed the coordinator address or port.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1074,8 +1074,6 @@ clubban:
 | 
				
			|||||||
- clubban
 | 
					- clubban
 | 
				
			||||||
clubunban:
 | 
					clubunban:
 | 
				
			||||||
- clubunban
 | 
					- clubunban
 | 
				
			||||||
clublevelreq:
 | 
					 | 
				
			||||||
- clublevelreq
 | 
					 | 
				
			||||||
clubdescription:
 | 
					clubdescription:
 | 
				
			||||||
- clubdesc
 | 
					- clubdesc
 | 
				
			||||||
clubicon:
 | 
					clubicon:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1891,10 +1891,6 @@ clubunban:
 | 
				
			|||||||
  desc: "Unbans the previously banned user from the club. You must be the club owner."
 | 
					  desc: "Unbans the previously banned user from the club. You must be the club owner."
 | 
				
			||||||
  args:
 | 
					  args:
 | 
				
			||||||
    - "user#1337"
 | 
					    - "user#1337"
 | 
				
			||||||
clublevelreq:
 | 
					 | 
				
			||||||
  desc: "Sets the club required level to apply to join the club. You must be club owner. You can't set this number below 5."
 | 
					 | 
				
			||||||
  args:
 | 
					 | 
				
			||||||
    - "7"
 | 
					 | 
				
			||||||
clubdescription:
 | 
					clubdescription:
 | 
				
			||||||
  desc: "Sets the club description. Maximum 150 characters. Club owner only."
 | 
					  desc: "Sets the club description. Maximum 150 characters. Club owner only."
 | 
				
			||||||
  args:
 | 
					  args:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -818,6 +818,7 @@
 | 
				
			|||||||
  "server_leaderboard": "Server XP Leaderboard",
 | 
					  "server_leaderboard": "Server XP Leaderboard",
 | 
				
			||||||
  "global_leaderboard": "Global XP Leaderboard",
 | 
					  "global_leaderboard": "Global XP Leaderboard",
 | 
				
			||||||
  "modified": "Modified server XP of the user {0} by {1}",
 | 
					  "modified": "Modified server XP of the user {0} by {1}",
 | 
				
			||||||
 | 
					  "club_create_error_name": "Failed creating the club. A club with that name already exists.",
 | 
				
			||||||
  "club_create_error": "Failed creating the club. Make sure you're above level 5 and not a member of a club already.",
 | 
					  "club_create_error": "Failed creating the club. Make sure you're above level 5 and not a member of a club already.",
 | 
				
			||||||
  "club_name_too_long": "Club name is too long.",
 | 
					  "club_name_too_long": "Club name is too long.",
 | 
				
			||||||
  "club_created": "Club {0} successfully created!",
 | 
					  "club_created": "Club {0} successfully created!",
 | 
				
			||||||
@@ -835,8 +836,6 @@
 | 
				
			|||||||
  "club_user_ban_fail": "Failed to ban. You're either not the club owner, or that user is not in your club or applied to it.",
 | 
					  "club_user_ban_fail": "Failed to ban. You're either not the club owner, or that user is not in your club or applied to it.",
 | 
				
			||||||
  "club_user_unbanned": "Unbanned user {0} in {1} club.",
 | 
					  "club_user_unbanned": "Unbanned user {0} in {1} club.",
 | 
				
			||||||
  "club_user_unban_fail": "Failed to unban. You're either not the club owner, or that user is not in your club or applied to it.",
 | 
					  "club_user_unban_fail": "Failed to unban. You're either not the club owner, or that user is not in your club or applied to it.",
 | 
				
			||||||
  "club_level_req_changed": "Changed club's level requirement to {0}",
 | 
					 | 
				
			||||||
  "club_level_req_change_error": "Failed changing level requirement.",
 | 
					 | 
				
			||||||
  "club_desc_updated": "Updated club description to {0}",
 | 
					  "club_desc_updated": "Updated club description to {0}",
 | 
				
			||||||
  "club_desc_update_failed": "Failed changing club description.",
 | 
					  "club_desc_update_failed": "Failed changing club description.",
 | 
				
			||||||
  "club_disbanded": "Club {0} has been disbanded",
 | 
					  "club_disbanded": "Club {0} has been disbanded",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user