WIP db provider support for Mysql and Postgres

This commit is contained in:
Kwoth
2022-04-11 10:41:26 +00:00
parent 8e1ec2ed9e
commit e23233ee06
66 changed files with 21891 additions and 382 deletions

View File

@@ -5,7 +5,23 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
## 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

View File

@@ -41,10 +41,7 @@ public sealed class Bot
_credsProvider = new BotCredsProvider(totalShards);
_creds = _credsProvider.GetCreds();
_db = new(_creds);
if (shardId == 0)
_db.Setup();
_db = new(_credsProvider);
var messageCacheSize =
#if GLOBAL_NADEKO
@@ -286,6 +283,9 @@ public sealed class Bot
public async Task RunAsync()
{
if (ShardId == 0)
await _db.SetupAsync();
var sw = Stopwatch.StartNew();
await LoginAsync(_creds.Token);

View File

@@ -99,7 +99,7 @@ Windows default
public Creds()
{
Version = 4;
Version = 5;
Token = string.Empty;
UsePrivilegedIntents = true;
OwnerIds = new List<ulong>();
@@ -124,10 +124,15 @@ Windows default
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; }
[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; }
}

View File

@@ -12,7 +12,7 @@ public static class ClubExtensions
.ThenInclude(x => x.User)
.Include(x => x.Bans)
.ThenInclude(x => x.User)
.Include(x => x.Users)
.Include(x => x.Members)
.AsQueryable();
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)
=> 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)
=> 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)
.FirstOrDefault(c => EF.Functions.Collate(c.Name, "NOCASE") == EF.Functions.Collate(name, "NOCASE")
&& 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;
.FirstOrDefault(c => c.Name == name);
public static List<ClubInfo> GetClubLeaderboardPage(this DbSet<ClubInfo> clubs, int page)
=> clubs.AsNoTracking().OrderByDescending(x => x.Xp).Skip(page * 9).Take(9).ToList();

View File

@@ -9,6 +9,11 @@ namespace NadekoBot.Db;
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(
this NadekoContext ctx,
ulong userId,
@@ -37,20 +42,49 @@ public static class DiscordUserExtensions
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
public static DiscordUser GetOrCreateUser(
this NadekoContext ctx,
ulong userId,
string username,
string discrim,
string avatarId)
string avatarId,
Func<IQueryable<DiscordUser>, IQueryable<DiscordUser>> includes = null)
{
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)
=> ctx.GetOrCreateUser(original.Id, original.Username, original.Discriminator, original.AvatarId);
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, includes);
public static int GetUserGlobalRank(this DbSet<DiscordUser> users, ulong id)
=> users.AsQueryable()

View File

@@ -73,7 +73,6 @@ public static class GuildConfigExtensions
{
GuildConfig config;
// todo linq2db
if (includes is null)
config = ctx.GuildConfigs.IncludeEverything().FirstOrDefault(c => c.GuildId == guildId);
else

View File

@@ -8,24 +8,19 @@ public class ClubInfo : DbEntity
{
[MaxLength(20)]
public string Name { get; set; }
public int Discrim { get; set; }
public string Description { get; set; }
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 List<DiscordUser> Users { get; set; } = new();
public List<DiscordUser> Members { get; set; } = new();
public List<ClubApplicants> Applicants { get; set; } = new();
public List<ClubBans> Bans { get; set; } = new();
public string Description { get; set; }
public override string ToString()
=> Name + "#" + Discrim;
=> Name;
}
public class ClubApplicants

View File

@@ -10,6 +10,7 @@ public class DiscordUser : DbEntity
public string Discriminator { get; set; }
public string AvatarId { get; set; }
public int? ClubId { get; set; }
public ClubInfo Club { get; set; }
public bool IsClubAdmin { get; set; }

View 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");
}
}

View File

@@ -1,5 +1,4 @@
#nullable disable
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Logging;
@@ -10,23 +9,7 @@ using NadekoBot.Services.Database.Models;
namespace NadekoBot.Services.Database;
public class NadekoContextFactory : IDesignTimeDbContextFactory<NadekoContext>
{
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 abstract class NadekoContext : DbContext
{
public DbSet<GuildConfig> GuildConfigs { get; set; }
@@ -69,10 +52,13 @@ public class NadekoContext : DbContext
public DbSet<Permissionv2> Permissions { get; set; }
public NadekoContext(DbContextOptions<NadekoContext> options)
: base(options)
{
}
#region Mandatory Provider-Specific Values
protected abstract string CurrencyTransactionOtherIdDefaultValue { get; }
protected abstract string DiscordUserLastXpGainDefaultValue { get; }
protected abstract string LastLevelUpDefaultValue { get; }
#endregion
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@@ -167,10 +153,10 @@ public class NadekoContext : DbContext
.HasDefaultValue(XpNotificationLocation.None);
du.Property(x => x.LastXpGain)
.HasDefaultValueSql("datetime('now', '-1 years')");
.HasDefaultValueSql(DiscordUserLastXpGainDefaultValue);
du.Property(x => x.LastLevelUp)
.HasDefaultValueSql("datetime('now')");
.HasDefaultValueSql(LastLevelUpDefaultValue);
du.Property(x => x.TotalXp)
.HasDefaultValue(0);
@@ -179,7 +165,10 @@ public class NadekoContext : DbContext
.HasDefaultValue(0);
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.CurrencyAmount);
@@ -218,7 +207,7 @@ public class NadekoContext : DbContext
.IsUnique();
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.GuildId);
@@ -248,13 +237,14 @@ public class NadekoContext : DbContext
#region Club
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
{
x.Name,
x.Discrim
x.Name
});
#endregion
@@ -268,9 +258,13 @@ public class NadekoContext : DbContext
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>()
.HasKey(t => new
@@ -279,9 +273,13 @@ public class NadekoContext : DbContext
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
@@ -299,7 +297,7 @@ public class NadekoContext : DbContext
.IsUnique(false);
e.Property(x => x.OtherId)
.HasDefaultValueSql("NULL");
.HasDefaultValueSql(CurrencyTransactionOtherIdDefaultValue);
e.Property(x => x.Type)
.IsRequired();

View 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);
}
}

View 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());
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20210621042359_squash")]
partial class squash
{

View File

@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20210707002343_cleanup")]
partial class cleanup
{

View File

@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20210911225622_rero-cascade")]
partial class rerocascade
{

View File

@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20210912182515_boost-messages")]
partial class boostmessages
{

View File

@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20210912200106_logsettings-independence")]
partial class logsettingsindependence
{

View File

@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20210914180026_image-only-channels")]
partial class imageonlychannels
{

View File

@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20210921204645_logignore-user-channel")]
partial class logignoreuserchannel
{

View File

@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20211015232708_nsfw-blacklist-tags")]
partial class nsfwblacklisttags
{

View File

@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20211121002508_weighted-warnings")]
partial class weightedwarnings
{

View File

@@ -8,7 +8,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20211213145407_atl-rework")]
partial class atlrework
{

View File

@@ -10,7 +10,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20220102102344_crs-rename-to-expressions-perm-rename")]
partial class crsrenametoexpressionspermrename
{

View File

@@ -10,7 +10,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20220110105942_filter-settings-cleanup")]
partial class filtersettingscleanup
{

View File

@@ -10,7 +10,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20220125044401_curtrs-rework-discorduser-defaults")]
partial class curtrsreworkdiscorduserdefaults
{

View File

@@ -10,7 +10,7 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[DbContext(typeof(SqliteContext))]
[Migration("20220213123633_music-autoplay")]
partial class musicautoplay
{

File diff suppressed because it is too large Load Diff

View 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);
}
}
}

View File

@@ -9,13 +9,13 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
partial class NadekoContextModelSnapshot : ModelSnapshot
[DbContext(typeof(SqliteContext))]
partial class NadekoSqliteContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.2");
modelBuilder.HasAnnotation("ProductVersion", "6.0.3");
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
{
@@ -29,7 +29,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("ClubApplicants", (string)null);
b.ToTable("ClubApplicants");
});
modelBuilder.Entity("NadekoBot.Db.Models.ClubBans", b =>
@@ -44,7 +44,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("ClubBans", (string)null);
b.ToTable("ClubBans");
});
modelBuilder.Entity("NadekoBot.Db.Models.ClubInfo", b =>
@@ -59,21 +59,15 @@ namespace NadekoBot.Migrations
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<int>("Discrim")
.HasColumnType("INTEGER");
b.Property<string>("ImageUrl")
.HasColumnType("TEXT");
b.Property<int>("MinimumLevelReq")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("TEXT");
b.Property<int>("OwnerId")
b.Property<int?>("OwnerId")
.HasColumnType("INTEGER");
b.Property<int>("Xp")
@@ -81,12 +75,12 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.HasAlternateKey("Name", "Discrim");
b.HasAlternateKey("Name");
b.HasIndex("OwnerId")
.IsUnique();
b.ToTable("Clubs", (string)null);
b.ToTable("Clubs");
});
modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b =>
@@ -155,7 +149,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("DiscordUser", (string)null);
b.ToTable("DiscordUser");
});
modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b =>
@@ -189,7 +183,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("FollowedStream", (string)null);
b.ToTable("FollowedStream");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b =>
@@ -218,7 +212,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId")
.IsUnique();
b.ToTable("AntiAltSetting", (string)null);
b.ToTable("AntiAltSetting");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
@@ -250,7 +244,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId")
.IsUnique();
b.ToTable("AntiRaidSetting", (string)null);
b.ToTable("AntiRaidSetting");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b =>
@@ -272,7 +266,7 @@ namespace NadekoBot.Migrations
b.HasIndex("AntiSpamSettingId");
b.ToTable("AntiSpamIgnore", (string)null);
b.ToTable("AntiSpamIgnore");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b =>
@@ -304,7 +298,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId")
.IsUnique();
b.ToTable("AntiSpamSetting", (string)null);
b.ToTable("AntiSpamSetting");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoCommand", b =>
@@ -342,7 +336,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("AutoCommands", (string)null);
b.ToTable("AutoCommands");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
@@ -370,7 +364,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId");
b.ToTable("AutoTranslateChannels", (string)null);
b.ToTable("AutoTranslateChannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
@@ -398,7 +392,7 @@ namespace NadekoBot.Migrations
b.HasAlternateKey("ChannelId", "UserId");
b.ToTable("AutoTranslateUsers", (string)null);
b.ToTable("AutoTranslateUsers");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b =>
@@ -421,7 +415,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId")
.IsUnique();
b.ToTable("BanTemplates", (string)null);
b.ToTable("BanTemplates");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistEntry", b =>
@@ -441,7 +435,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("Blacklist", (string)null);
b.ToTable("Blacklist");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
@@ -466,7 +460,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("CommandAlias", (string)null);
b.ToTable("CommandAlias");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
@@ -491,7 +485,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("CommandCooldown", (string)null);
b.ToTable("CommandCooldown");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b =>
@@ -529,7 +523,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("CurrencyTransactions", (string)null);
b.ToTable("CurrencyTransactions");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.DelMsgOnCmdChannel", b =>
@@ -554,7 +548,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("DelMsgOnCmdChannel", (string)null);
b.ToTable("DelMsgOnCmdChannel");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordPermOverride", b =>
@@ -580,7 +574,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId", "Command")
.IsUnique();
b.ToTable("DiscordPermOverrides", (string)null);
b.ToTable("DiscordPermOverrides");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
@@ -605,7 +599,7 @@ namespace NadekoBot.Migrations
b.HasIndex("XpSettingsId");
b.ToTable("ExcludedItem", (string)null);
b.ToTable("ExcludedItem");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FeedSub", b =>
@@ -631,7 +625,7 @@ namespace NadekoBot.Migrations
b.HasAlternateKey("GuildConfigId", "Url");
b.ToTable("FeedSub", (string)null);
b.ToTable("FeedSub");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
@@ -653,7 +647,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("FilterChannelId", (string)null);
b.ToTable("FilterChannelId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
@@ -675,7 +669,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("FilteredWord", (string)null);
b.ToTable("FilteredWord");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterLinksChannelId", b =>
@@ -697,7 +691,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("FilterLinksChannelId", (string)null);
b.ToTable("FilterLinksChannelId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterWordsChannelId", b =>
@@ -719,7 +713,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("FilterWordsChannelId", (string)null);
b.ToTable("FilterWordsChannelId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
@@ -741,7 +735,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("GCChannelId", (string)null);
b.ToTable("GCChannelId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GroupName", b =>
@@ -767,7 +761,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId", "Number")
.IsUnique();
b.ToTable("GroupName", (string)null);
b.ToTable("GroupName");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
@@ -891,7 +885,7 @@ namespace NadekoBot.Migrations
b.HasIndex("WarnExpireHours");
b.ToTable("GuildConfigs", (string)null);
b.ToTable("GuildConfigs");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogItem", b =>
@@ -917,7 +911,7 @@ namespace NadekoBot.Migrations
b.HasIndex("LogSettingId", "LogItemId", "ItemType")
.IsUnique();
b.ToTable("IgnoredLogChannels", (string)null);
b.ToTable("IgnoredLogChannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
@@ -939,7 +933,7 @@ namespace NadekoBot.Migrations
b.HasIndex("LogSettingId");
b.ToTable("IgnoredVoicePresenceCHannels", (string)null);
b.ToTable("IgnoredVoicePresenceCHannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ImageOnlyChannel", b =>
@@ -962,7 +956,7 @@ namespace NadekoBot.Migrations
b.HasIndex("ChannelId")
.IsUnique();
b.ToTable("ImageOnlyChannels", (string)null);
b.ToTable("ImageOnlyChannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
@@ -1027,7 +1021,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId")
.IsUnique();
b.ToTable("LogSettings", (string)null);
b.ToTable("LogSettings");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlayerSettings", b =>
@@ -1064,7 +1058,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId")
.IsUnique();
b.ToTable("MusicPlayerSettings", (string)null);
b.ToTable("MusicPlayerSettings");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b =>
@@ -1087,7 +1081,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("MusicPlaylists", (string)null);
b.ToTable("MusicPlaylists");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
@@ -1109,7 +1103,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("MutedUserId", (string)null);
b.ToTable("MutedUserId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.NadekoExpression", b =>
@@ -1147,7 +1141,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("Expressions", (string)null);
b.ToTable("Expressions");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklistedTag", b =>
@@ -1169,7 +1163,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId");
b.ToTable("NsfwBlacklistedTags", (string)null);
b.ToTable("NsfwBlacklistedTags");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b =>
@@ -1209,7 +1203,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("Permissions", (string)null);
b.ToTable("Permissions");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlantedCurrency", b =>
@@ -1246,7 +1240,7 @@ namespace NadekoBot.Migrations
b.HasIndex("MessageId")
.IsUnique();
b.ToTable("PlantedCurrency", (string)null);
b.ToTable("PlantedCurrency");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
@@ -1280,7 +1274,7 @@ namespace NadekoBot.Migrations
b.HasIndex("MusicPlaylistId");
b.ToTable("PlaylistSong", (string)null);
b.ToTable("PlaylistSong");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Poll", b =>
@@ -1306,7 +1300,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId")
.IsUnique();
b.ToTable("Poll", (string)null);
b.ToTable("Poll");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PollAnswer", b =>
@@ -1331,7 +1325,7 @@ namespace NadekoBot.Migrations
b.HasIndex("PollId");
b.ToTable("PollAnswer", (string)null);
b.ToTable("PollAnswer");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PollVote", b =>
@@ -1356,7 +1350,7 @@ namespace NadekoBot.Migrations
b.HasIndex("PollId");
b.ToTable("PollVote", (string)null);
b.ToTable("PollVote");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b =>
@@ -1392,7 +1386,7 @@ namespace NadekoBot.Migrations
b.HasIndex("Keyword");
b.ToTable("Quotes", (string)null);
b.ToTable("Quotes");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRole", b =>
@@ -1417,7 +1411,7 @@ namespace NadekoBot.Migrations
b.HasIndex("ReactionRoleMessageId");
b.ToTable("ReactionRole", (string)null);
b.ToTable("ReactionRole");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRoleMessage", b =>
@@ -1448,7 +1442,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("ReactionRoleMessage", (string)null);
b.ToTable("ReactionRoleMessage");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b =>
@@ -1482,7 +1476,7 @@ namespace NadekoBot.Migrations
b.HasIndex("When");
b.ToTable("Reminders", (string)null);
b.ToTable("Reminders");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b =>
@@ -1517,7 +1511,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("Repeaters", (string)null);
b.ToTable("Repeaters");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.RewardedUser", b =>
@@ -1546,7 +1540,7 @@ namespace NadekoBot.Migrations
b.HasIndex("PatreonUserId")
.IsUnique();
b.ToTable("RewardedUsers", (string)null);
b.ToTable("RewardedUsers");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.RotatingPlayingStatus", b =>
@@ -1566,7 +1560,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("RotatingStatus", (string)null);
b.ToTable("RotatingStatus");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b =>
@@ -1597,7 +1591,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId", "RoleId")
.IsUnique();
b.ToTable("SelfAssignableRoles", (string)null);
b.ToTable("SelfAssignableRoles");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntry", b =>
@@ -1637,7 +1631,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("ShopEntry", (string)null);
b.ToTable("ShopEntry");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntryItem", b =>
@@ -1659,7 +1653,7 @@ namespace NadekoBot.Migrations
b.HasIndex("ShopEntryId");
b.ToTable("ShopEntryItem", (string)null);
b.ToTable("ShopEntryItem");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredRole", b =>
@@ -1681,7 +1675,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("SlowmodeIgnoredRole", (string)null);
b.ToTable("SlowmodeIgnoredRole");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredUser", b =>
@@ -1703,7 +1697,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("SlowmodeIgnoredUser", (string)null);
b.ToTable("SlowmodeIgnoredUser");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b =>
@@ -1728,7 +1722,7 @@ namespace NadekoBot.Migrations
b.HasIndex("StreamRoleSettingsId");
b.ToTable("StreamRoleBlacklistedUser", (string)null);
b.ToTable("StreamRoleBlacklistedUser");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b =>
@@ -1760,7 +1754,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId")
.IsUnique();
b.ToTable("StreamRoleSettings", (string)null);
b.ToTable("StreamRoleSettings");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b =>
@@ -1785,7 +1779,7 @@ namespace NadekoBot.Migrations
b.HasIndex("StreamRoleSettingsId");
b.ToTable("StreamRoleWhitelistedUser", (string)null);
b.ToTable("StreamRoleWhitelistedUser");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnbanTimer", b =>
@@ -1810,7 +1804,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("UnbanTimer", (string)null);
b.ToTable("UnbanTimer");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b =>
@@ -1835,7 +1829,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("UnmuteTimer", (string)null);
b.ToTable("UnmuteTimer");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnroleTimer", b =>
@@ -1863,7 +1857,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("UnroleTimer", (string)null);
b.ToTable("UnroleTimer");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b =>
@@ -1884,7 +1878,7 @@ namespace NadekoBot.Migrations
b.Property<DateTime>("LastLevelUp")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT")
.HasDefaultValue(new DateTime(2017, 9, 21, 20, 53, 13, 307, DateTimeKind.Local));
.HasDefaultValueSql("datetime('now')");
b.Property<int>("NotifyOnLevelUp")
.HasColumnType("INTEGER");
@@ -1908,7 +1902,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId", "GuildId")
.IsUnique();
b.ToTable("UserXpStats", (string)null);
b.ToTable("UserXpStats");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b =>
@@ -1933,7 +1927,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("VcRoleInfo", (string)null);
b.ToTable("VcRoleInfo");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b =>
@@ -1968,7 +1962,7 @@ namespace NadekoBot.Migrations
b.HasIndex("WaifuId")
.IsUnique();
b.ToTable("WaifuInfo", (string)null);
b.ToTable("WaifuInfo");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b =>
@@ -1993,7 +1987,7 @@ namespace NadekoBot.Migrations
b.HasIndex("WaifuInfoId");
b.ToTable("WaifuItem", (string)null);
b.ToTable("WaifuItem");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
@@ -2025,7 +2019,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("WaifuUpdates", (string)null);
b.ToTable("WaifuUpdates");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Warning", b =>
@@ -2068,7 +2062,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("Warnings", (string)null);
b.ToTable("Warnings");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WarningPunishment", b =>
@@ -2099,7 +2093,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("WarningPunishment", (string)null);
b.ToTable("WarningPunishment");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpCurrencyReward", b =>
@@ -2124,7 +2118,7 @@ namespace NadekoBot.Migrations
b.HasIndex("XpSettingsId");
b.ToTable("XpCurrencyReward", (string)null);
b.ToTable("XpCurrencyReward");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b =>
@@ -2153,7 +2147,7 @@ namespace NadekoBot.Migrations
b.HasIndex("XpSettingsId", "Level")
.IsUnique();
b.ToTable("XpRoleReward", (string)null);
b.ToTable("XpRoleReward");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b =>
@@ -2176,7 +2170,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId")
.IsUnique();
b.ToTable("XpSettings", (string)null);
b.ToTable("XpSettings");
});
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
@@ -2222,8 +2216,7 @@ namespace NadekoBot.Migrations
b.HasOne("NadekoBot.Db.Models.DiscordUser", "Owner")
.WithOne()
.HasForeignKey("NadekoBot.Db.Models.ClubInfo", "OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("Owner");
});
@@ -2231,8 +2224,9 @@ namespace NadekoBot.Migrations
modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b =>
{
b.HasOne("NadekoBot.Db.Models.ClubInfo", "Club")
.WithMany("Users")
.HasForeignKey("ClubId");
.WithMany("Members")
.HasForeignKey("ClubId")
.OnDelete(DeleteBehavior.NoAction);
b.Navigation("Club");
});
@@ -2561,9 +2555,11 @@ namespace NadekoBot.Migrations
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")
.HasForeignKey("WaifuInfoId");
b.Navigation("WaifuInfo");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
@@ -2635,7 +2631,7 @@ namespace NadekoBot.Migrations
b.Navigation("Bans");
b.Navigation("Users");
b.Navigation("Members");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b =>

View File

@@ -20,7 +20,7 @@ public class DangerousCommandsService : INService
await ctx.DiscordUser.UpdateAsync(_ => new DiscordUser()
{
Club = null,
IsClubAdmin = false,
// IsClubAdmin = false,
TotalXp = 0
});
await ctx.ClubApplicants.DeleteAsync();
@@ -97,6 +97,7 @@ public class DangerousCommandsService : INService
using var uow = _db.GetDbContext();
var conn = uow.Database.GetDbConnection();
conn.Open();
using var cmd = conn.CreateCommand();
cmd.CommandText = sql;
using var reader = cmd.ExecuteReader();

View File

@@ -280,7 +280,7 @@ public partial class Utility : NadekoModule
$"{_stats.MessageCounter} ({_stats.MessagesPerSecond:F2}/sec)",
true)
.AddField(GetText(strs.memory),
FormattableString.Invariant($"{_stats.GetPrivateMemory():F2} MB"),
FormattableString.Invariant($"{_stats.GetPrivateMemoryMegabytes():F2} MB"),
true)
.AddField(GetText(strs.owner_ids), ownerIds, 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 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);
return;

View File

@@ -7,7 +7,7 @@ namespace NadekoBot.Modules.Xp;
public partial class Xp
{
[Group]
public partial class Club : NadekoModule<ClubService>
public partial class Club : NadekoModule<IClubService>
{
private readonly XpService _xps;
@@ -19,35 +19,33 @@ public partial class Xp
{
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),
Format.Bold(newOwner.ToString())));
await ReplyErrorLocalizedAsync(strs.club_transfer_failed);
}
else
await ReplyErrorLocalizedAsync(strs.club_transfer_failed);
{
await ReplyConfirmLocalizedAsync(
strs.club_transfered(
Format.Bold(club.Name),
Format.Bold(newOwner.ToString())
)
);
}
}
[Cmd]
public async partial Task ClubAdmin([Leftover] IUser toAdmin)
{
bool admin;
try
{
admin = _service.ToggleAdmin(ctx.User, toAdmin);
}
catch (InvalidOperationException)
{
await ReplyErrorLocalizedAsync(strs.club_admin_error);
return;
}
var admin = await _service.ToggleAdminAsync(ctx.User, toAdmin);
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())));
else
await ReplyConfirmLocalizedAsync(strs.club_admin_remove(Format.Bold(toAdmin.ToString())));
}
[Cmd]
public async partial Task ClubCreate([Leftover] string clubName)
{
@@ -57,20 +55,28 @@ public partial class Xp
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);
return;
}
await ReplyConfirmLocalizedAsync(strs.club_created(Format.Bold(club.ToString())));
await ReplyConfirmLocalizedAsync(strs.club_created(Format.Bold(clubName)));
}
[Cmd]
public async partial Task ClubIcon([Leftover] string url = 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);
return;
@@ -82,7 +88,7 @@ public partial class Xp
private async Task InternalClubInfoAsync(ClubInfo club)
{
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;
if (club.OwnerId == x.Id)
@@ -102,7 +108,7 @@ public partial class Xp
.AddField(GetText(strs.desc),
string.IsNullOrWhiteSpace(club.Description) ? "-" : club.Description)
.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),
string.Join("\n",
users.Skip(page * 10)
@@ -123,7 +129,7 @@ public partial class Xp
return embed;
},
club.Users.Count,
club.Members.Count,
10);
}
@@ -187,7 +193,6 @@ public partial class Xp
10);
}
[Cmd]
public partial Task ClubApps(int page = 1)
{
@@ -310,19 +315,10 @@ public partial class Xp
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]
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 ?? "-")));
else
await ReplyErrorLocalizedAsync(strs.club_desc_update_failed);
@@ -332,7 +328,7 @@ public partial class Xp
public async partial Task ClubDisband()
{
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
await ReplyErrorLocalizedAsync(strs.club_disband_error);
}

View File

@@ -1,10 +1,13 @@
#nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Db;
using NadekoBot.Db.Models;
namespace NadekoBot.Modules.Xp.Services;
public class ClubService : INService
public class ClubService : INService, IClubService
{
private readonly DbService _db;
private readonly IHttpClientFactory _httpFactory;
@@ -15,46 +18,42 @@ public class ClubService : INService
_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
club = null;
using var uow = _db.GetDbContext();
await using var uow = _db.GetDbContext();
var du = uow.GetOrCreateUser(user);
uow.SaveChanges();
var xp = new LevelStats(du.TotalXp);
if (xp.Level >= 5 && du.Club is null)
{
du.IsClubAdmin = true;
du.Club = new()
{
Name = clubName,
Discrim = uow.Clubs.GetNextDiscrim(clubName),
Owner = du
};
uow.Clubs.Add(du.Club);
uow.SaveChanges();
}
else
if (xp.Level < 5 || du.ClubId is not null)
return false;
uow.Set<ClubApplicants>().RemoveRange(uow.Set<ClubApplicants>().AsQueryable().Where(x => x.UserId == du.Id));
club = du.Club;
uow.SaveChanges();
if (await uow.Clubs.AnyAsyncEF(x => x.Name == clubName))
return null;
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;
}
public ClubInfo TransferClub(IUser from, IUser newOwner)
{
ClubInfo club;
using var uow = _db.GetDbContext();
club = uow.Clubs.GetByOwner(from.Id);
var club = uow.Clubs.GetByOwner(@from.Id);
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;
club.Owner.IsClubAdmin = true; // old owner will stay as admin
@@ -64,21 +63,20 @@ public class ClubService : INService
return club;
}
public bool ToggleAdmin(IUser owner, IUser toAdmin)
public async Task<bool?> ToggleAdminAsync(IUser owner, IUser toAdmin)
{
bool newState;
using var uow = _db.GetDbContext();
await using var uow = _db.GetDbContext();
var club = uow.Clubs.GetByOwner(owner.Id);
var adminUser = uow.GetOrCreateUser(toAdmin);
if (club is null || club.Owner.UserId != owner.Id || !club.Users.Contains(adminUser))
throw new InvalidOperationException();
if (club is null || club.Owner.UserId != owner.Id || !club.Members.Contains(adminUser))
return null;
if (club.OwnerId == adminUser.Id)
return true;
newState = adminUser.IsClubAdmin = !adminUser.IsClubAdmin;
uow.SaveChanges();
var newState = adminUser.IsClubAdmin = !adminUser.IsClubAdmin;
await uow.SaveChangesAsync();
return newState;
}
@@ -89,13 +87,13 @@ public class ClubService : INService
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)
{
using var http = _httpFactory.CreateClient();
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;
}
@@ -105,30 +103,18 @@ public class ClubService : INService
if (club is null)
return false;
club.ImageUrl = url.ToString();
uow.SaveChanges();
club.ImageUrl = url;
await uow.SaveChangesAsync();
return true;
}
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();
club = uow.Clubs.GetByName(name, discrim);
if (club is null)
return false;
return true;
club = uow.Clubs.GetByName(clubName);
return club is not null;
}
public bool ApplyToClub(IUser user, ClubInfo club)
@@ -138,7 +124,6 @@ public class ClubService : INService
uow.SaveChanges();
if (du.Club is not null
|| new LevelStats(du.TotalXp).Level < club.MinimumLevelReq
|| club.Bans.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,
@@ -202,23 +187,7 @@ public class ClubService : INService
return true;
}
public bool ChangeClubLevelReq(ulong userId, int level)
{
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)
public bool SetDescription(ulong userId, string desc)
{
using var uow = _db.GetDbContext();
var club = uow.Clubs.GetByOwner(userId);
@@ -250,7 +219,7 @@ public class ClubService : INService
if (club is null)
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
.FirstOrDefault(x => x.User.ToString().ToUpperInvariant() == userName.ToUpperInvariant())
?.User;
@@ -266,7 +235,7 @@ public class ClubService : INService
Club = club,
User = usr
});
club.Users.Remove(usr);
club.Members.Remove(usr);
var app = club.Applicants.FirstOrDefault(x => x.UserId == usr.Id);
if (app is not null)
@@ -301,14 +270,14 @@ public class ClubService : INService
if (club is null)
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)
return false;
if (club.OwnerId == usr.Id || (usr.IsClubAdmin && club.Owner.UserId != kickerId))
return false;
club.Users.Remove(usr);
club.Members.Remove(usr);
var app = club.Applicants.FirstOrDefault(x => x.UserId == usr.Id);
if (app is not null)
club.Applicants.Remove(app);

View 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);
}

View File

@@ -647,7 +647,7 @@ public class XpService : INService, IReadyExecutor
int guildRank;
await using (var uow = _db.GetDbContext())
{
du = uow.GetOrCreateUser(user);
du = uow.GetOrCreateUser(user, set => set.Include(x => x.Club));
totalXp = du.TotalXp;
globalRank = uow.DiscordUser.GetUserGlobalRank(user.Id);
guildRank = uow.UserXpStats.GetUserGuildRanking(user.Id, user.GuildId);
@@ -1021,8 +1021,9 @@ public class XpService : INService, IReadyExecutor
using (var http = _httpFactory.CreateClient())
using (var temp = await http.GetAsync(imgUrl, HttpCompletionOption.ResponseHeadersRead))
{
if (!temp.IsImage() || temp.GetImageSize() > 11)
if (!temp.IsImage() || temp.GetContentLength() > 11.Megabytes().Bytes)
return;
var imgData = await temp.Content.ReadAsByteArrayAsync();
using (var tempDraw = Image.Load(imgData))
{

View File

@@ -84,6 +84,9 @@
<!-- <PrivateAssets>all</PrivateAssets>-->
<!-- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
<!-- </PackageReference>-->
<PackageReference Include="EFCore.NamingConventions" Version="6.0.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,7 +1,6 @@
#nullable disable
using LinqToDB.Common;
using LinqToDB.EntityFrameworkCore;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database;
@@ -9,49 +8,68 @@ namespace NadekoBot.Services;
public class DbService
{
private readonly DbContextOptions<NadekoContext> _options;
private readonly DbContextOptions<NadekoContext> _migrateOptions;
private readonly IBotCredsProvider _creds;
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();
Configuration.Linq.DisableQueryCache = true;
var builder = new SqliteConnectionStringBuilder(creds.Db.ConnectionString);
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;
_creds = creds;
}
public void Setup()
public async Task SetupAsync()
{
using var context = new NadekoContext(_options);
if (context.Database.GetPendingMigrations().Any())
var dbType = DbType;
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);
mContext.Database.Migrate();
mContext.SaveChanges();
await context.Database.ExecuteSqlRawAsync("PRAGMA journal_mode=WAL");
}
context.Database.ExecuteSqlRaw("PRAGMA journal_mode=WAL");
context.SaveChanges();
await context.Database.MigrateAsync();
}
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()
{
var context = new NadekoContext(_options);
context.Database.SetCommandTimeout(60);
var conn = context.Database.GetDbConnection();
conn.Open();
using var com = conn.CreateCommand();
com.CommandText = "PRAGMA journal_mode=WAL; PRAGMA synchronous=OFF";
com.ExecuteNonQuery();
var dbType = DbType;
var connString = ConnString;
var context = CreateRawDbContext(dbType, connString);
if (context is SqliteContext)
{
var conn = context.Database.GetDbConnection();
conn.Open();
using var com = conn.CreateCommand();
com.CommandText = "PRAGMA synchronous=OFF";
com.ExecuteNonQuery();
}
return context;
}

View File

@@ -52,5 +52,5 @@ public interface IStatsService
/// <summary>
/// Gets total amount of private memory currently in use by the bot, in Megabytes.
/// </summary>
double GetPrivateMemory();
double GetPrivateMemoryMegabytes();
}

View File

@@ -166,9 +166,9 @@ public sealed class BotCredsProvider : IBotCredsProvider
if (File.Exists(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));
}
}

View File

@@ -183,9 +183,9 @@ public sealed class StatsService : IStatsService, IReadyExecutor, INService
return time.Humanize(3, maxUnit: TimeUnit.Day, minUnit: TimeUnit.Minute);
}
public double GetPrivateMemory()
public double GetPrivateMemoryMegabytes()
{
_currentProcess.Refresh();
return _currentProcess.PrivateMemorySize64 / (double)1.MiB();
return _currentProcess.PrivateMemorySize64 / 1.Megabytes().Bytes;
}
}

View File

@@ -190,13 +190,10 @@ public static class Extensions
return false;
}
public static long? GetImageSize(this HttpResponseMessage msg)
{
if (msg.Content.Headers.ContentLength is null)
return null;
return msg.Content.Headers.ContentLength.Value / 1.Mb();
}
public static long GetContentLength(this HttpResponseMessage msg)
=> msg.Content.Headers.ContentLength is long length
? length
: long.MaxValue;
public static string GetText(this IBotStrings strings, in LocStr str, ulong? guildId = null)
=> strings.GetText(str.Key, guildId, str.Params);

View File

@@ -2,45 +2,6 @@ namespace NadekoBot.Extensions;
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)
=> new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).AddSeconds(number);
}

View File

@@ -1,5 +1,5 @@
# DO NOT CHANGE
version: 4
version: 5
# Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/
token: ''
# 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=
# Database options. Don't change if you don't know what you're doing. Leave null for default values
db:
# Database type. Only sqlite supported atm
# Database type. "sqlite", "mysql" and "postgresql" are supported.
# Default is "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
# Address and port of the coordinator endpoint. Leave empty for default.
# Change only if you've changed the coordinator address or port.

View File

@@ -1074,8 +1074,6 @@ clubban:
- clubban
clubunban:
- clubunban
clublevelreq:
- clublevelreq
clubdescription:
- clubdesc
clubicon:

View File

@@ -1891,10 +1891,6 @@ clubunban:
desc: "Unbans the previously banned user from the club. You must be the club owner."
args:
- "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:
desc: "Sets the club description. Maximum 150 characters. Club owner only."
args:

View File

@@ -818,6 +818,7 @@
"server_leaderboard": "Server XP Leaderboard",
"global_leaderboard": "Global XP Leaderboard",
"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_name_too_long": "Club name is too long.",
"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_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_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_update_failed": "Failed changing club description.",
"club_disbanded": "Club {0} has been disbanded",