merged gc rework branch, but deleted all migrations as they're incompatible

This commit is contained in:
Toastie 2025-01-29 22:51:59 +13:00
parent e97fbe64c5
commit 5450d40bae
Signed by: toastie_t0ast
GPG key ID: 0861BE54AD481DC7
114 changed files with 9639 additions and 9175 deletions
CHANGELOG.md
src/EllieBot
Bot.cs
Db
Migrations
Modules

View file

@ -188,7 +188,7 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
- Self Assigned Roles reworked! Use `.h .sar` for the list of commands - Self Assigned Roles reworked! Use `.h .sar` for the list of commands
- `.sar autodel` - `.sar autodel`
- Toggles the automatic deletion of the user's message and Nadeko's confirmations for .iam and .iamn commands. - Toggles the automatic deletion of the user's message and Ellie's confirmations for .iam and .iamn commands.
- `.sar ad` - `.sar ad`
- Adds a role to the list of self-assignable roles. You can also specify a group. - Adds a role to the list of self-assignable roles. You can also specify a group.
- If 'Exclusive self-assignable roles' feature is enabled (.sar exclusive), users will be able to pick one role - If 'Exclusive self-assignable roles' feature is enabled (.sar exclusive), users will be able to pick one role

View file

@ -1,12 +1,9 @@
#nullable disable #nullable disable
using DryIoc; using DryIoc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using EllieBot.Common.Configs; using EllieBot.Common.Configs;
using EllieBot.Common.ModuleBehaviors; using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
using RunMode = Discord.Commands.RunMode; using RunMode = Discord.Commands.RunMode;
@ -15,10 +12,7 @@ namespace EllieBot;
public sealed class Bot : IBot public sealed class Bot : IBot
{ {
public event Func<GuildConfig, Task> JoinedGuild = delegate { return Task.CompletedTask; };
public DiscordSocketClient Client { get; } public DiscordSocketClient Client { get; }
public IReadOnlyCollection<GuildConfig> AllGuildConfigs { get; private set; }
private IContainer Services { get; set; } private IContainer Services { get; set; }
@ -102,7 +96,6 @@ public sealed class Bot : IBot
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
AllGuildConfigs = await uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList);
uow.EnsureUserCreated(bot.Id, bot.Username, bot.Discriminator, bot.AvatarId); uow.EnsureUserCreated(bot.Id, bot.Username, bot.Discriminator, bot.AvatarId);
} }
@ -114,10 +107,6 @@ public sealed class Bot : IBot
var svcs = new Container(); var svcs = new Container();
// this is required in order for marmalade unloading to work
// svcs.Components.Remove<IPlanner, Planner>();
// svcs.Components.Add<IPlanner, RemovablePlanner>();
svcs.AddSingleton<IBotCreds>(_ => _credsProvider.GetCreds()); svcs.AddSingleton<IBotCreds>(_ => _credsProvider.GetCreds());
svcs.AddSingleton<DbService, DbService>(_db); svcs.AddSingleton<DbService, DbService>(_db);
svcs.AddSingleton<IBotCredsProvider>(_credsProvider); svcs.AddSingleton<IBotCredsProvider>(_credsProvider);
@ -245,16 +234,6 @@ public sealed class Bot : IBot
private Task Client_JoinedGuild(SocketGuild arg) private Task Client_JoinedGuild(SocketGuild arg)
{ {
Log.Information("Joined server: {GuildName} [{GuildId}]", arg.Name, arg.Id); Log.Information("Joined server: {GuildName} [{GuildId}]", arg.Name, arg.Id);
_ = Task.Run(async () =>
{
GuildConfig gc;
await using (var uow = _db.GetDbContext())
{
gc = uow.GuildConfigsForId(arg.Id, null);
}
await JoinedGuild.Invoke(gc);
});
return Task.CompletedTask; return Task.CompletedTask;
} }

View file

@ -1,8 +1,8 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using EllieBot.Modules.Administration.Services;
// ReSharper disable UnusedAutoPropertyAccessor.Global // ReSharper disable UnusedAutoPropertyAccessor.Global
@ -11,6 +11,11 @@ namespace EllieBot.Db;
public abstract class EllieContext : DbContext public abstract class EllieContext : DbContext
{ {
public DbSet<GuildConfig> GuildConfigs { get; set; } public DbSet<GuildConfig> GuildConfigs { get; set; }
public DbSet<Permissionv2> Permissions { get; set; }
//new
public DbSet<XpSettings> XpSettings { get; set; }
public DbSet<GreetSettings> GreetSettings { get; set; } public DbSet<GreetSettings> GreetSettings { get; set; }
public DbSet<Quote> Quotes { get; set; } public DbSet<Quote> Quotes { get; set; }
@ -46,7 +51,6 @@ public abstract class EllieContext : DbContext
public DbSet<AutoTranslateChannel> AutoTranslateChannels { get; set; } public DbSet<AutoTranslateChannel> AutoTranslateChannels { get; set; }
public DbSet<AutoTranslateUser> AutoTranslateUsers { get; set; } public DbSet<AutoTranslateUser> AutoTranslateUsers { get; set; }
public DbSet<Permissionv2> Permissions { get; set; }
public DbSet<BankUser> BankUsers { get; set; } public DbSet<BankUser> BankUsers { get; set; }
@ -76,7 +80,7 @@ public abstract class EllieContext : DbContext
{ {
// load all entities from current assembly // load all entities from current assembly
modelBuilder.ApplyConfigurationsFromAssembly(typeof(EllieContext).Assembly); modelBuilder.ApplyConfigurationsFromAssembly(typeof(EllieContext).Assembly);
#region Notify #region Notify
modelBuilder.Entity<Notify>(e => modelBuilder.Entity<Notify>(e =>
@ -170,10 +174,10 @@ public abstract class EllieContext : DbContext
modelBuilder.Entity<UserBetStats>(ubs => modelBuilder.Entity<UserBetStats>(ubs =>
{ {
ubs.HasIndex(x => new ubs.HasIndex(x => new
{ {
x.UserId, x.UserId,
x.Game x.Game
}) })
.IsUnique(); .IsUnique();
ubs.HasIndex(x => x.MaxWin) ubs.HasIndex(x => x.MaxWin)
@ -221,174 +225,8 @@ public abstract class EllieContext : DbContext
configEntity.Property(x => x.VerboseErrors) configEntity.Property(x => x.VerboseErrors)
.HasDefaultValue(true); .HasDefaultValue(true);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.DelMsgOnCmdChannels)
.WithOne()
.HasForeignKey(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FollowedStreams)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.GenerateCurrencyChannelIds)
.WithOne(x => x.GuildConfig)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.Permissions)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.CommandCooldowns)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FilterInvitesChannelIds)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FilterLinksChannelIds)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FilteredWords)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FilterWordsChannelIds)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.MutedUsers)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.AntiRaidSetting)
.WithOne()
.HasForeignKey<AntiRaidSetting>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
// start antispam
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.AntiSpamSetting)
.WithOne()
.HasForeignKey<AntiSpamSetting>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<AntiSpamSetting>()
.HasMany(x => x.IgnoredChannels)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
// end antispam
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.AntiAltSetting)
.WithOne()
.HasForeignKey<AntiAltSetting>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.UnmuteTimers)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.UnbanTimer)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.UnroleTimer)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.VcRoleInfos)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.CommandAliases)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.SlowmodeIgnoredRoles)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.SlowmodeIgnoredUsers)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
// start shop
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.ShopEntries)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ShopEntry>()
.HasMany(x => x.Items)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
// end shop // end shop
// start streamrole
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.StreamRole)
.WithOne(x => x.GuildConfig)
.HasForeignKey<StreamRoleSettings>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<StreamRoleSettings>()
.HasMany(x => x.Whitelist)
.WithOne(x => x.StreamRoleSettings)
.HasForeignKey(x => x.StreamRoleSettingsId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<StreamRoleSettings>()
.HasMany(x => x.Blacklist)
.WithOne(x => x.StreamRoleSettings)
.HasForeignKey(x => x.StreamRoleSettingsId)
.OnDelete(DeleteBehavior.Cascade);
// end streamrole
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.XpSettings)
.WithOne(x => x.GuildConfig)
.HasForeignKey<XpSettings>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FeedSubs)
.WithOne(x => x.GuildConfig)
.HasForeignKey(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<FeedSub>()
.HasAlternateKey(x => new
{
x.GuildConfigId,
x.Url
});
modelBuilder.Entity<PlantedCurrency>().HasIndex(x => x.MessageId).IsUnique(); modelBuilder.Entity<PlantedCurrency>().HasIndex(x => x.MessageId).IsUnique();
modelBuilder.Entity<PlantedCurrency>().HasIndex(x => x.ChannelId); modelBuilder.Entity<PlantedCurrency>().HasIndex(x => x.ChannelId);
@ -397,9 +235,7 @@ public abstract class EllieContext : DbContext
#endregion #endregion
#region WarningPunishments modelBuilder.Entity<WarningPunishment>(b =>
var warnpunishmentEntity = modelBuilder.Entity<WarningPunishment>(b =>
{ {
b.HasAlternateKey(x => new b.HasAlternateKey(x => new
{ {
@ -408,8 +244,6 @@ public abstract class EllieContext : DbContext
}); });
}); });
#endregion
#region MusicPlaylists #region MusicPlaylists
var musicPlaylistEntity = modelBuilder.Entity<MusicPlaylist>(); var musicPlaylistEntity = modelBuilder.Entity<MusicPlaylist>();
@ -489,33 +323,6 @@ public abstract class EllieContext : DbContext
#endregion #endregion
#region XpRoleReward
modelBuilder.Entity<XpRoleReward>()
.HasIndex(x => new
{
x.XpSettingsId,
x.Level
})
.IsUnique();
modelBuilder.Entity<XpSettings>()
.HasMany(x => x.RoleRewards)
.WithOne(x => x.XpSettings)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<XpSettings>()
.HasMany(x => x.CurrencyRewards)
.WithOne(x => x.XpSettings)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<XpSettings>()
.HasMany(x => x.ExclusionList)
.WithOne(x => x.XpSettings)
.OnDelete(DeleteBehavior.Cascade);
#endregion
#region Club #region Club
var ci = modelBuilder.Entity<ClubInfo>(); var ci = modelBuilder.Entity<ClubInfo>();
@ -806,8 +613,15 @@ public abstract class EllieContext : DbContext
#if DEBUG #if DEBUG
private static readonly ILoggerFactory _debugLoggerFactory = LoggerFactory.Create(x => x.AddConsole()); private static readonly ILoggerFactory _debugLoggerFactory = LoggerFactory.Create(x => x.AddConsole());
#endif
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseLoggerFactory(_debugLoggerFactory); {
#if DEBUG
optionsBuilder.UseLoggerFactory(_debugLoggerFactory);
#endif #endif
optionsBuilder.ConfigureWarnings(x => x.Log(RelationalEventId.PendingModelChangesWarning)
.Ignore());
}
} }

View file

@ -2,7 +2,6 @@
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using EllieBot.Migrations;
namespace EllieBot.Db; namespace EllieBot.Db;
@ -28,14 +27,14 @@ public sealed class EllieDbService : DbService
public override async Task SetupAsync() public override async Task SetupAsync()
{ {
await using var context = CreateRawDbContext(DbType, ConnString); await using var context = CreateRawDbContext(DbType, ConnString);
await RunMigration(context);
// make sure sqlite db is in wal journal mode // make sure sqlite db is in wal journal mode
if (context is SqliteContext) if (context is SqliteContext)
{ {
await context.Database.ExecuteSqlRawAsync("PRAGMA journal_mode=WAL"); await context.Database.ExecuteSqlRawAsync("PRAGMA journal_mode=WAL");
} }
await RunMigration(context);
} }
public override EllieContext CreateRawDbContext(string dbType, string connString) public override EllieContext CreateRawDbContext(string dbType, string connString)

View file

@ -9,4 +9,40 @@ public static class DbExtensions
public static T GetById<T>(this DbSet<T> set, int id) public static T GetById<T>(this DbSet<T> set, int id)
where T : DbEntity where T : DbEntity
=> set.FirstOrDefault(x => x.Id == id); => set.FirstOrDefault(x => x.Id == id);
public static GuildConfig GuildConfigsForId(this DbContext ctx, ulong guildId)
{
var query = ctx.Set<GuildConfig>().Where(gc => gc.GuildId == guildId);
return query.FirstOrDefault();
}
public static GuildFilterConfig FilterConfigForId(
this DbContext ctx,
ulong guildId,
Func<IQueryable<GuildFilterConfig>, IQueryable<GuildFilterConfig>> includes = default)
{
includes ??= static set => set;
var gfc = includes(ctx.Set<GuildFilterConfig>()
.Where(gc => gc.GuildId == guildId))
.FirstOrDefault();
if (gfc is null)
{
ctx.Add(gfc = new GuildFilterConfig()
{
});
}
return gfc;
}
public static GuildConfig GuildConfigsForId(
this DbContext ctx,
ulong guildId,
Func<IQueryable<GuildConfig>, IQueryable<GuildConfig>> set)
{
var query = ctx.Set<GuildConfig>().Where(gc => gc.GuildId == guildId);
return query.FirstOrDefault();
}
} }

View file

@ -84,7 +84,7 @@ public static class DiscordUserExtensions
> users.AsQueryable().Where(y => y.UserId == id).Select(y => y.TotalXp).FirstOrDefault()) > users.AsQueryable().Where(y => y.UserId == id).Select(y => y.TotalXp).FirstOrDefault())
.Count() .Count()
+ 1; + 1;
public static Task<List<DiscordUser>> GetTopRichest( public static Task<List<DiscordUser>> GetTopRichest(
this DbSet<DiscordUser> users, this DbSet<DiscordUser> users,
ulong botId, ulong botId,

View file

@ -1,4 +1,5 @@
#nullable disable #nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EllieBot.Db.Models; using EllieBot.Db.Models;
@ -29,102 +30,23 @@ public static class GuildConfigExtensions
/// <param name="ctx">Db Context</param> /// <param name="ctx">Db Context</param>
/// <param name="guildId">Id of the guild to get stream role settings for.</param> /// <param name="guildId">Id of the guild to get stream role settings for.</param>
/// <returns>Guild'p stream role settings</returns> /// <returns>Guild'p stream role settings</returns>
public static StreamRoleSettings GetStreamRoleSettings(this DbContext ctx, ulong guildId) public static async Task<StreamRoleSettings> GetOrCreateStreamRoleSettings(this DbContext ctx, ulong guildId)
{ {
var conf = ctx.GuildConfigsForId(guildId, var srs = await ctx.GetTable<StreamRoleSettings>()
set => set.Include(y => y.StreamRole) .Where(x => x.GuildId == guildId)
.Include(y => y.StreamRole.Whitelist) .FirstOrDefaultAsyncEF();
.Include(y => y.StreamRole.Blacklist));
if (conf.StreamRole is null) if (srs is not null)
conf.StreamRole = new(); return srs;
return conf.StreamRole; srs = new()
}
private static IQueryable<GuildConfig> IncludeEverything(this DbSet<GuildConfig> configs)
=> configs
.AsSplitQuery()
.Include(gc => gc.CommandCooldowns)
.Include(gc => gc.FollowedStreams)
.Include(gc => gc.StreamRole)
.Include(gc => gc.DelMsgOnCmdChannels)
.Include(gc => gc.XpSettings)
.ThenInclude(x => x.ExclusionList);
public static async Task<GuildConfig[]> GetAllGuildConfigs(
this DbSet<GuildConfig> configs,
List<ulong> availableGuilds)
{
var result = await configs
.IncludeEverything()
.Where(x => availableGuilds.Contains(x.GuildId))
.AsNoTracking()
.ToArrayAsync();
return result;
}
/// <summary>
/// Gets and creates if it doesn't exist a config for a guild.
/// </summary>
/// <param name="ctx">Context</param>
/// <param name="guildId">Id of the guide</param>
/// <param name="includes">Use to manipulate the set however you want. Pass null to include everything</param>
/// <returns>Config for the guild</returns>
public static GuildConfig GuildConfigsForId(
this DbContext ctx,
ulong guildId,
Func<DbSet<GuildConfig>, IQueryable<GuildConfig>> includes)
{
GuildConfig config;
if (includes is null)
config = ctx.Set<GuildConfig>().IncludeEverything().FirstOrDefault(c => c.GuildId == guildId);
else
{ {
var set = includes(ctx.Set<GuildConfig>()); GuildId = guildId,
config = set.FirstOrDefault(c => c.GuildId == guildId); };
}
if (config is null) ctx.Set<StreamRoleSettings>().Add(srs);
{
ctx.Set<GuildConfig>()
.Add(config = new()
{
GuildId = guildId,
Permissions = Permissionv2.GetDefaultPermlist,
WarningsInitialized = true,
});
ctx.SaveChanges();
}
if (!config.WarningsInitialized) return srs;
{
config.WarningsInitialized = true;
}
return config;
// ctx.GuildConfigs
// .ToLinqToDBTable()
// .InsertOrUpdate(() => new()
// {
// GuildId = guildId,
// Permissions = Permissionv2.GetDefaultPermlist,
// WarningsInitialized = true,
// WarnPunishments = DefaultWarnPunishments
// },
// _ => new(),
// () => new()
// {
// GuildId = guildId
// });
//
// if(includes is null)
// return ctx.GuildConfigs
// .ToLinqToDBTable()
// .First(x => x.GuildId == guildId);
} }
public static LogSetting LogSettingsFor(this DbContext ctx, ulong guildId) public static LogSetting LogSettingsFor(this DbContext ctx, ulong guildId)
@ -148,6 +70,8 @@ public static class GuildConfigExtensions
return logSetting; return logSetting;
} }
public static IEnumerable<GuildConfig> PermissionsForAll(this DbSet<GuildConfig> configs, List<ulong> include) public static IEnumerable<GuildConfig> PermissionsForAll(this DbSet<GuildConfig> configs, List<ulong> include)
{ {
var query = configs.AsQueryable().Where(x => include.Contains(x.GuildId)).Include(gc => gc.Permissions); var query = configs.AsQueryable().Where(x => include.Contains(x.GuildId)).Include(gc => gc.Permissions);
@ -182,45 +106,24 @@ public static class GuildConfigExtensions
return config; return config;
} }
public static IEnumerable<FollowedStream> GetFollowedStreams(this DbSet<GuildConfig> configs) public static async Task<XpSettings> XpSettingsFor(this DbContext ctx, ulong guildId)
=> configs.AsQueryable().Include(x => x.FollowedStreams).SelectMany(gc => gc.FollowedStreams).ToArray();
public static IEnumerable<FollowedStream> GetFollowedStreams(this DbSet<GuildConfig> configs, List<ulong> included)
=> configs.AsQueryable()
.Where(gc => included.Contains(gc.GuildId))
.Include(gc => gc.FollowedStreams)
.SelectMany(gc => gc.FollowedStreams)
.ToList();
public static XpSettings XpSettingsFor(this DbContext ctx, ulong guildId)
{ {
var gc = ctx.GuildConfigsForId(guildId, var srs = await ctx.GetTable<XpSettings>()
set => set.Include(x => x.XpSettings) .Where(x => x.GuildId == guildId)
.ThenInclude(x => x.RoleRewards) .FirstOrDefaultAsyncLinqToDB();
.Include(x => x.XpSettings)
.ThenInclude(x => x.CurrencyRewards)
.Include(x => x.XpSettings)
.ThenInclude(x => x.ExclusionList));
if (gc.XpSettings is null) if (srs is not null)
gc.XpSettings = new(); return srs;
return gc.XpSettings; srs = await ctx.GetTable<XpSettings>()
.InsertWithOutputAsync(() => new()
{
GuildId = guildId,
});
return srs;
} }
public static IEnumerable<GeneratingChannel> GetGeneratingChannels(this DbSet<GuildConfig> configs)
=> configs.AsQueryable()
.Include(x => x.GenerateCurrencyChannelIds)
.Where(x => x.GenerateCurrencyChannelIds.Any())
.SelectMany(x => x.GenerateCurrencyChannelIds)
.Select(x => new GeneratingChannel
{
ChannelId = x.ChannelId,
GuildId = x.GuildConfig.GuildId
})
.ToArray();
public class GeneratingChannel public class GeneratingChannel
{ {
public ulong GuildId { get; set; } public ulong GuildId { get; set; }

View file

@ -9,7 +9,7 @@ public static class UserXpExtensions
{ {
public static async Task<UserXpStats?> GetGuildUserXp(this ITable<UserXpStats> table, ulong guildId, ulong userId) public static async Task<UserXpStats?> GetGuildUserXp(this ITable<UserXpStats> table, ulong guildId, ulong userId)
=> await table.FirstOrDefaultAsyncLinqToDB(x => x.GuildId == guildId && x.UserId == userId); => await table.FirstOrDefaultAsyncLinqToDB(x => x.GuildId == guildId && x.UserId == userId);
public static UserXpStats GetOrCreateUserXpStats(this DbContext ctx, ulong guildId, ulong userId) public static UserXpStats GetOrCreateUserXpStats(this DbContext ctx, ulong guildId, ulong userId)
{ {
var usr = ctx.Set<UserXpStats>().FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId); var usr = ctx.Set<UserXpStats>().FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId);

View file

@ -1,8 +1,21 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class CommandAlias : DbEntity public class CommandAlias : DbEntity
{ {
public ulong GuildId { get; set; }
public string Trigger { get; set; } public string Trigger { get; set; }
public string Mapping { get; set; } public string Mapping { get; set; }
}
public class CommandAliasEntityConfiguration : IEntityTypeConfiguration<CommandAlias>
{
public void Configure(EntityTypeBuilder<CommandAlias> builder)
{
builder.HasIndex(x => x.GuildId);
}
} }

View file

@ -1,8 +1,25 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class CommandCooldown : DbEntity public class CommandCooldown : DbEntity
{ {
public ulong GuildId { get; set; }
public int Seconds { get; set; } public int Seconds { get; set; }
public string CommandName { get; set; } public string CommandName { get; set; }
}
public class CommandCooldownEntityConfiguration : IEntityTypeConfiguration<CommandCooldown>
{
public void Configure(EntityTypeBuilder<CommandCooldown> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.CommandName
})
.IsUnique();
}
} }

View file

@ -1,16 +1,25 @@
#nullable disable using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class DelMsgOnCmdChannel : DbEntity public class DelMsgOnCmdChannel : DbEntity
{ {
public int GuildConfigId { get; set; } public ulong GuildId { get; set; }
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
public bool State { get; set; } public bool State { get; set; }
}
public override int GetHashCode() public class DelMsgOnCmdChannelEntityConfiguration : IEntityTypeConfiguration<DelMsgOnCmdChannel>
=> ChannelId.GetHashCode(); {
public void Configure(EntityTypeBuilder<DelMsgOnCmdChannel> builder)
public override bool Equals(object obj) {
=> obj is DelMsgOnCmdChannel x && x.ChannelId == ChannelId; builder.HasIndex(x => new
} {
x.GuildId,
x.ChannelId
}).IsUnique();
}
}

View file

@ -1,6 +1,7 @@
#nullable disable #nullable disable
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
// FUTURE remove LastLevelUp from here and UserXpStats // FUTURE remove LastLevelUp from here and UserXpStats
public class DiscordUser : DbEntity public class DiscordUser : DbEntity
{ {

View file

@ -1,19 +1,29 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class FeedSub : DbEntity public class FeedSub : DbEntity
{ {
public int GuildConfigId { get; set; } public ulong GuildId { get; set; }
public GuildConfig GuildConfig { get; set; }
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
public string Url { get; set; } public string Url { get; set; }
public string Message { get; set; } public string Message { get; set; }
}
public override int GetHashCode() public sealed class FeedSubEntityConfiguration : IEntityTypeConfiguration<FeedSub>
=> Url.GetHashCode(StringComparison.InvariantCulture) ^ GuildConfigId.GetHashCode(); {
public void Configure(EntityTypeBuilder<FeedSub> builder)
public override bool Equals(object obj) {
=> obj is FeedSub s && s.Url.ToLower() == Url.ToLower() && s.GuildConfigId == GuildConfigId; builder
.HasIndex(x => new
{
x.GuildId,
x.Url
})
.IsUnique();
}
} }

View file

@ -1,4 +1,4 @@
#nullable disable #nullable disable
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class FlagTranslateChannel : DbEntity public class FlagTranslateChannel : DbEntity

View file

@ -1,7 +1,10 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class FollowedStream : DbEntity public class FollowedStream
{ {
public enum FType public enum FType
{ {
@ -12,7 +15,7 @@ public class FollowedStream : DbEntity
Trovo = 6, Trovo = 6,
Kick = 7, Kick = 7,
} }
public int Id { get; set; }
public ulong GuildId { get; set; } public ulong GuildId { get; set; }
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
public string Username { get; set; } public string Username { get; set; }
@ -29,6 +32,17 @@ public class FollowedStream : DbEntity
public override bool Equals(object obj) public override bool Equals(object obj)
=> obj is FollowedStream fs && Equals(fs); => obj is FollowedStream fs && Equals(fs);
}
public sealed class FollowedStreamEntityConfig : IEntityTypeConfiguration<FollowedStream>
{
public void Configure(EntityTypeBuilder<FollowedStream> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.Username,
x.Type,
});
}
} }

View file

@ -1,14 +1,24 @@
#nullable disable using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class GCChannelId : DbEntity public class GCChannelId : DbEntity
{ {
public GuildConfig GuildConfig { get; set; } public ulong GuildId { get; set; }
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
}
public override bool Equals(object obj) public class GCChannelIdEntityConfiguration : IEntityTypeConfiguration<GCChannelId>
=> obj is GCChannelId gc && gc.ChannelId == ChannelId; {
public void Configure(EntityTypeBuilder<GCChannelId> builder)
public override int GetHashCode() {
=> ChannelId.GetHashCode(); builder.HasIndex(x => new
{
x.GuildId,
x.ChannelId
})
.IsUnique();
}
} }

View file

@ -1,85 +1,59 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class GuildFilterConfig
{
[Key]
public int Id { get; set; }
public ulong GuildId { get; set; }
public bool FilterInvites { get; set; }
public bool FilterLinks { get; set; }
public bool FilterWords { get; set; }
public HashSet<FilterChannelId> FilterInvitesChannelIds { get; set; } = new();
public HashSet<FilterLinksChannelId> FilterLinksChannelIds { get; set; } = new();
public HashSet<FilteredWord> FilteredWords { get; set; } = new();
public HashSet<FilterWordsChannelId> FilterWordsChannelIds { get; set; } = new();
}
public sealed class GuildFilterConfigEntityConfiguration : IEntityTypeConfiguration<GuildFilterConfig>
{
public void Configure(EntityTypeBuilder<GuildFilterConfig> builder)
{
builder.HasIndex(x => x.GuildId);
}
}
public class GuildConfig : DbEntity public class GuildConfig : DbEntity
{ {
// public bool Keep { get; set; }
public ulong GuildId { get; set; } public ulong GuildId { get; set; }
public string Prefix { get; set; } public string Prefix { get; set; }
public bool DeleteMessageOnCommand { get; set; } public bool DeleteMessageOnCommand { get; set; }
public HashSet<DelMsgOnCmdChannel> DelMsgOnCmdChannels { get; set; } = new();
public string AutoAssignRoleIds { get; set; } public string AutoAssignRoleIds { get; set; }
//todo FUTURE: DELETE, UNUSED
public bool ExclusiveSelfAssignedRoles { get; set; }
public bool AutoDeleteSelfAssignedRoleMessages { get; set; }
//stream notifications
public HashSet<FollowedStream> FollowedStreams { get; set; } = new();
//currencyGeneration
public HashSet<GCChannelId> GenerateCurrencyChannelIds { get; set; } = new();
public List<Permissionv2> Permissions { get; set; }
public bool VerbosePermissions { get; set; } = true; public bool VerbosePermissions { get; set; } = true;
public string PermissionRole { get; set; } public string PermissionRole { get; set; }
public HashSet<CommandCooldown> CommandCooldowns { get; set; } = new();
//filtering //filtering
public bool FilterInvites { get; set; }
public bool FilterLinks { get; set; }
public HashSet<FilterChannelId> FilterInvitesChannelIds { get; set; } = new();
public HashSet<FilterLinksChannelId> FilterLinksChannelIds { get; set; } = new();
public bool FilterWords { get; set; }
public HashSet<FilteredWord> FilteredWords { get; set; } = new();
public HashSet<FilterWordsChannelId> FilterWordsChannelIds { get; set; } = new();
// mute
public HashSet<MutedUserId> MutedUsers { get; set; } = new();
public string MuteRoleName { get; set; } public string MuteRoleName { get; set; }
// chatterbot // chatterbot
public bool CleverbotEnabled { get; set; } public bool CleverbotEnabled { get; set; }
// protection
public AntiRaidSetting AntiRaidSetting { get; set; }
public AntiSpamSetting AntiSpamSetting { get; set; }
public AntiAltSetting AntiAltSetting { get; set; }
// time
public string Locale { get; set; }
public string TimeZoneId { get; set; }
// timers
public HashSet<UnmuteTimer> UnmuteTimers { get; set; } = new();
public HashSet<UnbanTimer> UnbanTimer { get; set; } = new();
public HashSet<UnroleTimer> UnroleTimer { get; set; } = new();
// vcrole
public HashSet<VcRoleInfo> VcRoleInfos { get; set; }
// aliases // aliases
public HashSet<CommandAlias> CommandAliases { get; set; } = new();
public bool WarningsInitialized { get; set; } public bool WarningsInitialized { get; set; }
public HashSet<SlowmodeIgnoredUser> SlowmodeIgnoredUsers { get; set; }
public HashSet<SlowmodeIgnoredRole> SlowmodeIgnoredRoles { get; set; }
public List<ShopEntry> ShopEntries { get; set; }
public ulong? GameVoiceChannel { get; set; } public ulong? GameVoiceChannel { get; set; }
public bool VerboseErrors { get; set; } = true; public bool VerboseErrors { get; set; } = true;
public StreamRoleSettings StreamRole { get; set; }
public XpSettings XpSettings { get; set; }
public List<FeedSub> FeedSubs { get; set; } = new();
public bool NotifyStreamOffline { get; set; } public bool NotifyStreamOffline { get; set; }
public bool DeleteStreamOnlineMessage { get; set; } public bool DeleteStreamOnlineMessage { get; set; }
public int WarnExpireHours { get; set; } public int WarnExpireHours { get; set; }
@ -88,4 +62,9 @@ public class GuildConfig : DbEntity
public bool DisableGlobalExpressions { get; set; } = false; public bool DisableGlobalExpressions { get; set; } = false;
public bool StickyRoles { get; set; } public bool StickyRoles { get; set; }
public string TimeZoneId { get; set; }
public string Locale { get; set; }
public List<Permissionv2> Permissions { get; set; }
} }

View file

@ -1,4 +1,4 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
@ -6,14 +6,14 @@ public class NCPixel
{ {
[Key] [Key]
public int Id { get; set; } public int Id { get; set; }
public required int Position { get; init; } public required int Position { get; init; }
public required long Price { get; init; } public required long Price { get; init; }
public required ulong OwnerId { get; init; } public required ulong OwnerId { get; init; }
public required uint Color { get; init; } public required uint Color { get; init; }
[MaxLength(256)] [MaxLength(256)]
public required string Text { get; init; } public required string Text { get; init; }
} }

View file

@ -1,4 +1,6 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics; using System.Diagnostics;
@ -7,7 +9,8 @@ namespace EllieBot.Db.Models;
[DebuggerDisplay("{PrimaryTarget}{SecondaryTarget} {SecondaryTargetName} {State} {PrimaryTargetId}")] [DebuggerDisplay("{PrimaryTarget}{SecondaryTarget} {SecondaryTargetName} {State} {PrimaryTargetId}")]
public class Permissionv2 : DbEntity, IIndexed public class Permissionv2 : DbEntity, IIndexed
{ {
public int? GuildConfigId { get; set; } public ulong GuildId { get; set; }
public int Index { get; set; } public int Index { get; set; }
public PrimaryPermissionType PrimaryTarget { get; set; } public PrimaryPermissionType PrimaryTarget { get; set; }
@ -49,4 +52,12 @@ public enum SecondaryPermissionType
Module, Module,
Command, Command,
AllModules AllModules
} }
public sealed class Permissionv2EntityConfiguration : IEntityTypeConfiguration<Permissionv2>
{
public void Configure(EntityTypeBuilder<Permissionv2> builder)
{
builder.HasIndex(x => x.GuildId);
}
}

View file

@ -12,7 +12,7 @@ public sealed class SarGroup
public ulong? RoleReq { get; set; } public ulong? RoleReq { get; set; }
public ICollection<Sar> Roles { get; set; } = []; public ICollection<Sar> Roles { get; set; } = [];
public bool IsExclusive { get; set; } public bool IsExclusive { get; set; }
[MaxLength(100)] [MaxLength(100)]
public string? Name { get; set; } public string? Name { get; set; }
} }

View file

@ -1,16 +1,21 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public enum ShopEntryType public enum ShopEntryType
{ {
Role, Role,
List, List,
Command Command
} }
public class ShopEntry : DbEntity, IIndexed public class ShopEntry : DbEntity, IIndexed
{ {
public ulong GuildId { get; set; }
public int Index { get; set; } public int Index { get; set; }
public int Price { get; set; } public int Price { get; set; }
public string Name { get; set; } public string Name { get; set; }
@ -25,7 +30,7 @@ public class ShopEntry : DbEntity, IIndexed
//list //list
public HashSet<ShopEntryItem> Items { get; set; } = new(); public HashSet<ShopEntryItem> Items { get; set; } = new();
public ulong? RoleRequirement { get; set; } public ulong? RoleRequirement { get; set; }
// command // command
public string Command { get; set; } public string Command { get; set; }
} }
@ -43,4 +48,22 @@ public class ShopEntryItem : DbEntity
public override int GetHashCode() public override int GetHashCode()
=> Text.GetHashCode(StringComparison.InvariantCulture); => Text.GetHashCode(StringComparison.InvariantCulture);
}
public class ShopEntryEntityConfiguration : IEntityTypeConfiguration<ShopEntry>
{
public void Configure(EntityTypeBuilder<ShopEntry> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.Index
})
.IsUnique();
builder
.HasMany(x => x.Items)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
}
} }

View file

@ -1,10 +1,16 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class StreamRoleSettings : DbEntity public class StreamRoleSettings : DbEntity
{ {
public int GuildConfigId { get; set; } public int GuildConfigId { get; set; }
public GuildConfig GuildConfig { get; set; }
public ulong GuildId { get; set; }
/// <summary> /// <summary>
/// Whether the feature is enabled in the guild. /// Whether the feature is enabled in the guild.
@ -42,7 +48,7 @@ public class StreamRoleBlacklistedUser : DbEntity
{ {
public int StreamRoleSettingsId { get; set; } public int StreamRoleSettingsId { get; set; }
public StreamRoleSettings StreamRoleSettings { get; set; } public StreamRoleSettings StreamRoleSettings { get; set; }
public ulong UserId { get; set; } public ulong UserId { get; set; }
public string Username { get; set; } public string Username { get; set; }
@ -62,7 +68,7 @@ public class StreamRoleWhitelistedUser : DbEntity
{ {
public int StreamRoleSettingsId { get; set; } public int StreamRoleSettingsId { get; set; }
public StreamRoleSettings StreamRoleSettings { get; set; } public StreamRoleSettings StreamRoleSettings { get; set; }
public ulong UserId { get; set; } public ulong UserId { get; set; }
public string Username { get; set; } public string Username { get; set; }
@ -71,4 +77,25 @@ public class StreamRoleWhitelistedUser : DbEntity
public override int GetHashCode() public override int GetHashCode()
=> UserId.GetHashCode(); => UserId.GetHashCode();
}
public class StreamRoleSettingsEntityConfiguration : IEntityTypeConfiguration<StreamRoleSettings>
{
public void Configure(EntityTypeBuilder<StreamRoleSettings> builder)
{
builder.HasIndex(x => x.GuildId)
.IsUnique();
builder
.HasMany(x => x.Whitelist)
.WithOne(x => x.StreamRoleSettings)
.HasForeignKey(x => x.StreamRoleSettingsId)
.OnDelete(DeleteBehavior.Cascade);
builder
.HasMany(x => x.Blacklist)
.WithOne(x => x.StreamRoleSettings)
.HasForeignKey(x => x.StreamRoleSettingsId)
.OnDelete(DeleteBehavior.Cascade);
}
} }

View file

@ -1,8 +1,26 @@
#nullable disable using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class VcRoleInfo : DbEntity public class VcRoleInfo : DbEntity
{ {
public ulong GuildId { get; set; }
public ulong VoiceChannelId { get; set; } public ulong VoiceChannelId { get; set; }
public ulong RoleId { get; set; } public ulong RoleId { get; set; }
}
public class VcRoleInfoEntityConfiguration : IEntityTypeConfiguration<VcRoleInfo>
{
public void Configure(EntityTypeBuilder<VcRoleInfo> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.VoiceChannelId
})
.IsUnique();
}
} }

View file

@ -1,7 +1,7 @@
#nullable disable #nullable disable
using EllieBot.Db.Models; using EllieBot.Db.Models;
namespace EllieBot.Services.Database.Models; namespace Ellieices.Database.Models;
public class WaifuInfo : DbEntity public class WaifuInfo : DbEntity
{ {

View file

@ -1,12 +1,30 @@
namespace EllieBot.Db.Models; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models;
public class AntiAltSetting public class AntiAltSetting
{ {
public int GuildConfigId { get; set; } [Key]
public int Id { get; set; } public int Id { get; set; }
public int GuildConfigId { get; set; }
public ulong GuildId { get; set; }
public TimeSpan MinAge { get; set; } public TimeSpan MinAge { get; set; }
public PunishmentAction Action { get; set; } public PunishmentAction Action { get; set; }
public int ActionDurationMinutes { get; set; } public int ActionDurationMinutes { get; set; }
public ulong? RoleId { get; set; } public ulong? RoleId { get; set; }
}
public class AntiAltSettingEntityConfiguration : IEntityTypeConfiguration<AntiAltSetting>
{
public void Configure(EntityTypeBuilder<AntiAltSetting> builder)
{
builder.HasIndex(x => x.GuildId)
.IsUnique();
}
} }

View file

@ -1,11 +1,13 @@
#nullable disable #nullable disable
namespace EllieBot.Db.Models; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models;
public class AntiRaidSetting : DbEntity public class AntiRaidSetting : DbEntity
{ {
public int GuildConfigId { get; set; } public ulong GuildId { get; set; }
public int UserThreshold { get; set; } public int UserThreshold { get; set; }
public int Seconds { get; set; } public int Seconds { get; set; }
public PunishmentAction Action { get; set; } public PunishmentAction Action { get; set; }
@ -15,4 +17,13 @@ public class AntiRaidSetting : DbEntity
/// Mute, Chatmute, Voicemute, etc... /// Mute, Chatmute, Voicemute, etc...
/// </summary> /// </summary>
public int PunishDuration { get; set; } public int PunishDuration { get; set; }
}
public class AntiRaidSettingEntityConfiguration : IEntityTypeConfiguration<AntiRaidSetting>
{
public void Configure(EntityTypeBuilder<AntiRaidSetting> builder)
{
builder.HasIndex(x => x.GuildId)
.IsUnique();
}
} }

View file

@ -3,10 +3,4 @@
public class AntiSpamIgnore : DbEntity public class AntiSpamIgnore : DbEntity
{ {
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
public override int GetHashCode()
=> ChannelId.GetHashCode();
public override bool Equals(object? obj)
=> obj is AntiSpamIgnore inst && inst.ChannelId == ChannelId;
} }

View file

@ -1,13 +1,34 @@
namespace EllieBot.Db.Models; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models;
#nullable disable #nullable disable
public class AntiSpamSetting : DbEntity public class AntiSpamSetting : DbEntity
{ {
public int GuildConfigId { get; set; } public int GuildConfigId { get; set; }
public ulong GuildId { get; set; }
public PunishmentAction Action { get; set; } public PunishmentAction Action { get; set; }
public int MessageThreshold { get; set; } = 3; public int MessageThreshold { get; set; } = 3;
public int MuteTime { get; set; } public int MuteTime { get; set; }
public ulong? RoleId { get; set; } public ulong? RoleId { get; set; }
public HashSet<AntiSpamIgnore> IgnoredChannels { get; set; } = new(); public List<AntiSpamIgnore> IgnoredChannels { get; set; } = new();
}
// setup model
public class AntiSpamEntityConfiguration : IEntityTypeConfiguration<AntiSpamSetting>
{
public void Configure(EntityTypeBuilder<AntiSpamSetting> builder)
{
builder.HasIndex(x => x.GuildId)
.IsUnique();
builder.HasMany(x => x.IgnoredChannels)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
}
} }

View file

@ -1,4 +1,4 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
@ -24,4 +24,4 @@ public sealed class ButtonRole
public string Label { get; set; } = string.Empty; public string Label { get; set; } = string.Empty;
public bool Exclusive { get; set; } public bool Exclusive { get; set; }
} }

View file

@ -1,16 +1,21 @@
#nullable disable using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class FilterChannelId : DbEntity public class FilterChannelId
{ {
[Key]
public int Id { get; set; }
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
public bool Equals(FilterChannelId other) protected bool Equals(FilterChannelId other)
=> ChannelId == other.ChannelId; => ChannelId == other.ChannelId;
public override bool Equals(object obj) public override bool Equals(object? obj)
=> obj is FilterChannelId fci && Equals(fci); => obj is FilterChannelId fci && fci.Equals(this);
public override int GetHashCode() public override int GetHashCode()
=> ChannelId.GetHashCode(); => ChannelId.GetHashCode();
} }

View file

@ -1,12 +1,16 @@
#nullable disable using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class FilterLinksChannelId : DbEntity public class FilterLinksChannelId : DbEntity
{ {
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
public override bool Equals(object obj) protected bool Equals(FilterLinksChannelId other)
=> obj is FilterLinksChannelId f && f.ChannelId == ChannelId; => ChannelId == other.ChannelId;
public override bool Equals(object? obj)
=> obj is FilterLinksChannelId other && Equals(other);
public override int GetHashCode() public override int GetHashCode()
=> ChannelId.GetHashCode(); => ChannelId.GetHashCode();

View file

@ -1,16 +1,17 @@
#nullable disable #nullable disable
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class FilterWordsChannelId : DbEntity public class FilterWordsChannelId : DbEntity
{ {
public int? GuildConfigId { get; set; }
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
public bool Equals(FilterWordsChannelId other) protected bool Equals(FilterWordsChannelId other)
=> ChannelId == other.ChannelId; => ChannelId == other.ChannelId;
public override bool Equals(object obj) public override bool Equals(object obj)
=> obj is FilterWordsChannelId fci && Equals(fci); => obj is FilterWordsChannelId other && Equals(other);
public override int GetHashCode() public override int GetHashCode()
=> ChannelId.GetHashCode(); => ChannelId.GetHashCode();

View file

@ -1,7 +1,10 @@
#nullable disable
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class FilteredWord : DbEntity public class FilteredWord : DbEntity
{ {
public string Word { get; set; } public string Word { get; set; } = string.Empty;
public override bool Equals(object? obj) => obj is FilteredWord fw && fw.Word == Word;
public override int GetHashCode() => Word.GetHashCode();
} }

View file

@ -1,13 +1,24 @@
#nullable disable using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class MutedUserId : DbEntity public class MutedUserId : DbEntity
{ {
public ulong GuildId { get; set; }
public ulong UserId { get; set; } public ulong UserId { get; set; }
}
public override int GetHashCode() public class MutedUserIdEntityConfiguration : IEntityTypeConfiguration<MutedUserId>
=> UserId.GetHashCode(); {
public void Configure(EntityTypeBuilder<MutedUserId> builder)
public override bool Equals(object obj) {
=> obj is MutedUserId mui ? mui.UserId == UserId : false; builder.HasIndex(x => new
{
x.GuildId,
x.UserId
})
.IsUnique();
}
} }

View file

@ -7,6 +7,6 @@ public class TempRole
public bool Remove { get; set; } public bool Remove { get; set; }
public ulong RoleId { get; set; } public ulong RoleId { get; set; }
public ulong UserId { get; set; } public ulong UserId { get; set; }
public DateTime ExpiresAt { get; set; } public DateTime ExpiresAt { get; set; }
} }

View file

@ -1,20 +1,24 @@
#nullable disable using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class SlowmodeIgnoredRole : DbEntity public class SlowmodeIgnoredRole : DbEntity
{ {
public ulong GuildId { get; set; }
public ulong RoleId { get; set; } public ulong RoleId { get; set; }
}
// override object.Equals public class SlowmodeIgnoredRoleEntityConfiguration : IEntityTypeConfiguration<SlowmodeIgnoredRole>
public override bool Equals(object obj) {
public void Configure(EntityTypeBuilder<SlowmodeIgnoredRole> builder)
{ {
if (obj is null || GetType() != obj.GetType()) builder.HasIndex(x => new
return false; {
x.GuildId,
return ((SlowmodeIgnoredRole)obj).RoleId == RoleId; x.RoleId
})
.IsUnique();
} }
// override object.GetHashCode
public override int GetHashCode()
=> RoleId.GetHashCode();
} }

View file

@ -1,20 +1,24 @@
#nullable disable using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class SlowmodeIgnoredUser : DbEntity public class SlowmodeIgnoredUser : DbEntity
{ {
public ulong GuildId { get; set; }
public ulong UserId { get; set; } public ulong UserId { get; set; }
}
// override object.Equals public class SlowmodeIgnoredUserEntityConfiguration : IEntityTypeConfiguration<SlowmodeIgnoredUser>
public override bool Equals(object obj) {
public void Configure(EntityTypeBuilder<SlowmodeIgnoredUser> builder)
{ {
if (obj is null || GetType() != obj.GetType()) builder.HasIndex(x => new
return false; {
x.GuildId,
return ((SlowmodeIgnoredUser)obj).UserId == UserId; x.UserId
})
.IsUnique();
} }
// override object.GetHashCode
public override int GetHashCode()
=> UserId.GetHashCode();
} }

View file

@ -1,14 +1,26 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class UnbanTimer : DbEntity public class UnbanTimer : DbEntity
{ {
public ulong GuildId { get; set; }
public ulong UserId { get; set; } public ulong UserId { get; set; }
public DateTime UnbanAt { get; set; } public DateTime UnbanAt { get; set; }
}
public override int GetHashCode() public class UnbanTimerEntityConfiguration : IEntityTypeConfiguration<UnbanTimer>
=> UserId.GetHashCode(); {
public void Configure(EntityTypeBuilder<UnbanTimer> builder)
public override bool Equals(object obj) {
=> obj is UnbanTimer ut ? ut.UserId == UserId : false; builder.HasIndex(x => new
{
x.GuildId,
x.UserId
})
.IsUnique();
}
} }

View file

@ -1,14 +1,25 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class UnmuteTimer : DbEntity public class UnmuteTimer : DbEntity
{ {
public ulong GuildId { get; set; }
public ulong UserId { get; set; } public ulong UserId { get; set; }
public DateTime UnmuteAt { get; set; } public DateTime UnmuteAt { get; set; }
}
public override int GetHashCode() public class UnmuteTimerEntityConfiguration : IEntityTypeConfiguration<UnmuteTimer>
=> UserId.GetHashCode(); {
public void Configure(EntityTypeBuilder<UnmuteTimer> builder)
public override bool Equals(object obj) {
=> obj is UnmuteTimer ut ? ut.UserId == UserId : false; builder.HasIndex(x => new
{
x.GuildId,
x.UserId
}).IsUnique();
}
} }

View file

@ -1,15 +1,27 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
// todo UN* remove unroletimer in favor of temprole
public class UnroleTimer : DbEntity public class UnroleTimer : DbEntity
{ {
public ulong GuildId { get; set; }
public ulong UserId { get; set; } public ulong UserId { get; set; }
public ulong RoleId { get; set; } public ulong RoleId { get; set; }
public DateTime UnbanAt { get; set; } public DateTime UnbanAt { get; set; }
}
public override int GetHashCode() public class UnroleTimerEntityConfiguration : IEntityTypeConfiguration<UnroleTimer>
=> UserId.GetHashCode() ^ RoleId.GetHashCode(); {
public void Configure(EntityTypeBuilder<UnroleTimer> builder)
public override bool Equals(object obj) {
=> obj is UnroleTimer ut ? ut.UserId == UserId && ut.RoleId == RoleId : false; builder.HasIndex(x => new
{
x.GuildId,
x.UserId
})
.IsUnique();
}
} }

View file

@ -0,0 +1,13 @@
namespace EllieBot.Db.Models;
public class ExcludedItem : DbEntity
{
public ulong ItemId { get; set; }
public ExcludedItemType ItemType { get; set; }
public override int GetHashCode()
=> ItemId.GetHashCode() ^ ItemType.GetHashCode();
public override bool Equals(object? obj)
=> obj is ExcludedItem ei && ei.ItemId == ItemId && ei.ItemType == ItemType;
}

View file

@ -0,0 +1,3 @@
namespace EllieBot.Db.Models;
public enum ExcludedItemType { Channel, Role }

View file

@ -0,0 +1,8 @@
namespace EllieBot.Db.Models;
public class XpCurrencyReward : DbEntity
{
public int XpSettingsId { get; set; }
public int Level { get; set; }
public int Amount { get; set; }
}

View file

@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models;
public class XpCurrencyRewardEntityConfiguration : IEntityTypeConfiguration<XpCurrencyReward>
{
public void Configure(EntityTypeBuilder<XpCurrencyReward> builder)
{
builder.HasIndex(x => new
{
x.Level,
x.XpSettingsId
})
.IsUnique();
}
}

View file

@ -1,4 +1,4 @@
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public enum XpNotificationLocation public enum XpNotificationLocation
{ {

View file

@ -0,0 +1,13 @@
namespace EllieBot.Db.Models;
public class XpRoleReward : DbEntity
{
public int XpSettingsId { get; set; }
public int Level { get; set; }
public ulong RoleId { get; set; }
/// <summary>
/// Whether the role should be removed (true) or added (false)
/// </summary>
public bool Remove { get; set; }
}

View file

@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models;
public class XpRoleRewardEntityConfiguration : IEntityTypeConfiguration<XpRoleReward>
{
public void Configure(EntityTypeBuilder<XpRoleReward> builder)
{
builder.HasIndex(x => new
{
x.XpSettingsId,
x.Level
})
.IsUnique();
}
}

View file

@ -1,64 +1,15 @@
#nullable disable #nullable disable
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models; namespace EllieBot.Db.Models;
public class XpSettings : DbEntity public class XpSettings : DbEntity
{ {
public int GuildConfigId { get; set; } public ulong GuildId { get; set; }
public GuildConfig GuildConfig { get; set; } public bool ServerExcluded { get; set; }
public HashSet<ExcludedItem> ExclusionList { get; set; } = new();
public HashSet<XpRoleReward> RoleRewards { get; set; } = new(); public HashSet<XpRoleReward> RoleRewards { get; set; } = new();
public HashSet<XpCurrencyReward> CurrencyRewards { get; set; } = new(); public HashSet<XpCurrencyReward> CurrencyRewards { get; set; } = new();
public HashSet<ExcludedItem> ExclusionList { get; set; } = new();
public bool ServerExcluded { get; set; }
}
public enum ExcludedItemType { Channel, Role }
public class XpRoleReward : DbEntity
{
public int XpSettingsId { get; set; }
public XpSettings XpSettings { get; set; }
public int Level { get; set; }
public ulong RoleId { get; set; }
/// <summary>
/// Whether the role should be removed (true) or added (false)
/// </summary>
public bool Remove { get; set; }
public override int GetHashCode()
=> Level.GetHashCode() ^ XpSettingsId.GetHashCode();
public override bool Equals(object obj)
=> obj is XpRoleReward xrr && xrr.Level == Level && xrr.XpSettingsId == XpSettingsId;
}
public class XpCurrencyReward : DbEntity
{
public int XpSettingsId { get; set; }
public XpSettings XpSettings { get; set; }
public int Level { get; set; }
public int Amount { get; set; }
public override int GetHashCode()
=> Level.GetHashCode() ^ XpSettingsId.GetHashCode();
public override bool Equals(object obj)
=> obj is XpCurrencyReward xrr && xrr.Level == Level && xrr.XpSettingsId == XpSettingsId;
}
public class ExcludedItem : DbEntity
{
public XpSettings XpSettings { get; set; }
public ulong ItemId { get; set; }
public ExcludedItemType ItemType { get; set; }
public override int GetHashCode()
=> ItemId.GetHashCode() ^ ItemType.GetHashCode();
public override bool Equals(object obj)
=> obj is ExcludedItem ei && ei.ItemId == ItemId && ei.ItemType == ItemType;
} }

View file

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models;
public class XpSettingsEntityConfiguration : IEntityTypeConfiguration<XpSettings>
{
public void Configure(EntityTypeBuilder<XpSettings> builder)
{
builder.HasIndex(x => x.GuildId)
.IsUnique();
builder.HasMany(x => x.CurrencyRewards)
.WithOne();
builder.HasMany(x => x.RoleRewards)
.WithOne();
builder.HasMany(x => x.ExclusionList)
.WithOne();
}
}

View file

@ -1,133 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
namespace EllieBot.Migrations;
public static class MigrationQueries
{
public static void MergeAwardedXp(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("""
UPDATE UserXpStats
SET Xp = AwardedXp + Xp,
AwardedXp = 0
WHERE AwardedXp > 0;
""");
}
public static void MigrateSar(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.IsNpgsql())
return;
migrationBuilder.Sql("""
INSERT INTO GroupName (Number, GuildConfigId)
SELECT DISTINCT "Group", GC.Id
FROM SelfAssignableRoles as SAR
INNER JOIN GuildConfigs as GC
ON SAR.GuildId = GC.GuildId
WHERE SAR.GuildId not in (SELECT GuildConfigs.GuildId from GroupName LEFT JOIN GuildConfigs ON GroupName.GuildConfigId = GuildConfigs.Id);
INSERT INTO SarGroup (Id, GroupNumber, Name, IsExclusive, GuildId)
SELECT GN.Id, GN.Number, GN.Name, GC.ExclusiveSelfAssignedRoles, GC.GuildId
FROM GroupName as GN
INNER JOIN GuildConfigs as GC ON GN.GuildConfigId = GC.Id;
INSERT INTO Sar (GuildId, RoleId, SarGroupId, LevelReq)
SELECT SAR.GuildId, SAR.RoleId, (SELECT Id FROM SarGroup WHERE SG.Number = SarGroup.GroupNumber AND SG.GuildId = SarGroup.GuildId), MIN(SAR.LevelRequirement)
FROM SelfAssignableRoles as SAR
INNER JOIN (SELECT GuildId, gn.Number FROM GroupName as gn
INNER JOIN GuildConfigs as gc ON gn.GuildConfigId =gc.Id
) as SG
ON SG.GuildId = SAR.GuildId
WHERE SG.Number IN (SELECT GroupNumber FROM SarGroup WHERE Sar.GuildId = SarGroup.GuildId)
GROUP BY SAR.GuildId, SAR.RoleId;
INSERT INTO SarAutoDelete (GuildId, IsEnabled)
SELECT GuildId, AutoDeleteSelfAssignedRoleMessages FROM GuildConfigs WHERE AutoDeleteSelfAssignedRoleMessages = TRUE;
""");
}
public static void UpdateUsernames(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("UPDATE DiscordUser SET Username = '??' || Username WHERE Discriminator = '????';");
}
public static void MigrateRero(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.IsSqlite())
{
migrationBuilder.Sql(
@"insert or ignore into reactionroles(guildid, channelid, messageid, emote, roleid, 'group', levelreq, dateadded)
select guildid, channelid, messageid, emotename, roleid, exclusive, 0, reactionrolemessage.dateadded
from reactionrole
left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid
left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;");
}
else if (migrationBuilder.IsNpgsql())
{
migrationBuilder.Sql(
@"insert into reactionroles(guildid, channelid, messageid, emote, roleid, ""group"", levelreq, dateadded)
select guildid, channelid, messageid, emotename, roleid, exclusive::int, 0, reactionrolemessage.dateadded
from reactionrole
left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid
left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id
ON CONFLICT DO NOTHING;");
}
else
{
throw new NotSupportedException("This database provider doesn't have an implementation for MigrateRero");
}
}
public static void GuildConfigCleanup(MigrationBuilder builder)
{
builder.Sql($"""
DELETE FROM "DelMsgOnCmdChannel" WHERE "GuildConfigId" is NULL;
DELETE FROM "WarningPunishment" WHERE "GuildConfigId" NOT IN (SELECT "Id" from "GuildConfigs");
DELETE FROM "StreamRoleBlacklistedUser" WHERE "StreamRoleSettingsId" is NULL;
DELETE FROM "Permissions" WHERE "GuildConfigId" NOT IN (SELECT "Id" from "GuildConfigs");
""");
}
public static void GreetSettingsCopy(MigrationBuilder builder)
{
builder.Sql("""
INSERT INTO GreetSettings (GuildId, GreetType, MessageText, IsEnabled, ChannelId, AutoDeleteTimer)
SELECT GuildId, 0, ChannelGreetMessageText, SendChannelGreetMessage, GreetMessageChannelId, AutoDeleteGreetMessagesTimer
FROM GuildConfigs
WHERE SendChannelGreetMessage = TRUE;
INSERT INTO GreetSettings (GuildId, GreetType, MessageText, IsEnabled, ChannelId, AutoDeleteTimer)
SELECT GuildId, 1, DmGreetMessageText, SendDmGreetMessage, GreetMessageChannelId, 0
FROM GuildConfigs
WHERE SendDmGreetMessage = TRUE;
INSERT INTO GreetSettings (GuildId, GreetType, MessageText, IsEnabled, ChannelId, AutoDeleteTimer)
SELECT GuildId, 2, ChannelByeMessageText, SendChannelByeMessage, ByeMessageChannelId, AutoDeleteByeMessagesTimer
FROM GuildConfigs
WHERE SendChannelByeMessage = TRUE;
INSERT INTO GreetSettings (GuildId, GreetType, MessageText, IsEnabled, ChannelId, AutoDeleteTimer)
SELECT GuildId, 3, BoostMessage, SendBoostMessage, BoostMessageChannelId, BoostMessageDeleteAfter
FROM GuildConfigs
WHERE SendBoostMessage = TRUE;
""");
}
public static void AddGuildIdsToWarningPunishment(MigrationBuilder builder)
{
builder.Sql("""
DELETE FROM WarningPunishment WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE WarningPunishment
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE Id = GuildConfigId);
DELETE FROM WarningPunishment as wp
WHERE (wp.Count, wp.GuildConfigId) in (
SELECT wp2.Count, wp2.GuildConfigId FROM WarningPunishment as wp2
GROUP BY wp2.Count, wp2.GuildConfigId
HAVING COUNT(id) > 1
);
""");
}
}

View file

@ -0,0 +1,333 @@
START TRANSACTION;
ALTER TABLE antialtsetting DROP CONSTRAINT fk_antialtsetting_guildconfigs_guildconfigid;
ALTER TABLE antiraidsetting DROP CONSTRAINT fk_antiraidsetting_guildconfigs_guildconfigid;
ALTER TABLE antispamsetting DROP CONSTRAINT fk_antispamsetting_guildconfigs_guildconfigid;
ALTER TABLE commandalias DROP CONSTRAINT fk_commandalias_guildconfigs_guildconfigid;
ALTER TABLE commandcooldown DROP CONSTRAINT fk_commandcooldown_guildconfigs_guildconfigid;
ALTER TABLE delmsgoncmdchannel DROP CONSTRAINT fk_delmsgoncmdchannel_guildconfigs_guildconfigid;
ALTER TABLE excludeditem DROP CONSTRAINT fk_excludeditem_xpsettings_xpsettingsid;
ALTER TABLE feedsub DROP CONSTRAINT fk_feedsub_guildconfigs_guildconfigid;
ALTER TABLE filterchannelid DROP CONSTRAINT fk_filterchannelid_guildconfigs_guildconfigid;
ALTER TABLE filteredword DROP CONSTRAINT fk_filteredword_guildconfigs_guildconfigid;
ALTER TABLE filterlinkschannelid DROP CONSTRAINT fk_filterlinkschannelid_guildconfigs_guildconfigid;
ALTER TABLE filterwordschannelid DROP CONSTRAINT fk_filterwordschannelid_guildconfigs_guildconfigid;
ALTER TABLE followedstream DROP CONSTRAINT fk_followedstream_guildconfigs_guildconfigid;
ALTER TABLE gcchannelid DROP CONSTRAINT fk_gcchannelid_guildconfigs_guildconfigid;
ALTER TABLE muteduserid DROP CONSTRAINT fk_muteduserid_guildconfigs_guildconfigid;
ALTER TABLE permissions DROP CONSTRAINT fk_permissions_guildconfigs_guildconfigid;
ALTER TABLE shopentry DROP CONSTRAINT fk_shopentry_guildconfigs_guildconfigid;
ALTER TABLE slowmodeignoredrole DROP CONSTRAINT fk_slowmodeignoredrole_guildconfigs_guildconfigid;
ALTER TABLE slowmodeignoreduser DROP CONSTRAINT fk_slowmodeignoreduser_guildconfigs_guildconfigid;
ALTER TABLE streamrolesettings DROP CONSTRAINT fk_streamrolesettings_guildconfigs_guildconfigid;
ALTER TABLE unbantimer DROP CONSTRAINT fk_unbantimer_guildconfigs_guildconfigid;
ALTER TABLE unmutetimer DROP CONSTRAINT fk_unmutetimer_guildconfigs_guildconfigid;
ALTER TABLE unroletimer DROP CONSTRAINT fk_unroletimer_guildconfigs_guildconfigid;
ALTER TABLE vcroleinfo DROP CONSTRAINT fk_vcroleinfo_guildconfigs_guildconfigid;
ALTER TABLE xpsettings DROP CONSTRAINT fk_xpsettings_guildconfigs_guildconfigid;
DROP INDEX ix_xpsettings_guildconfigid;
DROP INDEX ix_vcroleinfo_guildconfigid;
DROP INDEX ix_unroletimer_guildconfigid;
DROP INDEX ix_unmutetimer_guildconfigid;
DROP INDEX ix_unbantimer_guildconfigid;
DROP INDEX ix_streamrolesettings_guildconfigid;
DROP INDEX ix_slowmodeignoreduser_guildconfigid;
DROP INDEX ix_slowmodeignoredrole_guildconfigid;
DROP INDEX ix_shopentry_guildconfigid;
DROP INDEX ix_muteduserid_guildconfigid;
DROP INDEX ix_gcchannelid_guildconfigid;
DROP INDEX ix_followedstream_guildconfigid;
ALTER TABLE feedsub DROP CONSTRAINT ak_feedsub_guildconfigid_url;
DROP INDEX ix_delmsgoncmdchannel_guildconfigid;
DROP INDEX ix_commandcooldown_guildconfigid;
DROP INDEX ix_commandalias_guildconfigid;
DROP INDEX ix_antispamsetting_guildconfigid;
DROP INDEX ix_antiraidsetting_guildconfigid;
DROP INDEX ix_antialtsetting_guildconfigid;
ALTER TABLE filterwordschannelid RENAME COLUMN guildconfigid TO guildfilterconfigid;
ALTER INDEX ix_filterwordschannelid_guildconfigid RENAME TO ix_filterwordschannelid_guildfilterconfigid;
ALTER TABLE filterlinkschannelid RENAME COLUMN guildconfigid TO guildfilterconfigid;
ALTER INDEX ix_filterlinkschannelid_guildconfigid RENAME TO ix_filterlinkschannelid_guildfilterconfigid;
ALTER TABLE filteredword RENAME COLUMN guildconfigid TO guildfilterconfigid;
ALTER INDEX ix_filteredword_guildconfigid RENAME TO ix_filteredword_guildfilterconfigid;
ALTER TABLE filterchannelid RENAME COLUMN guildconfigid TO guildfilterconfigid;
ALTER INDEX ix_filterchannelid_guildconfigid RENAME TO ix_filterchannelid_guildfilterconfigid;
ALTER TABLE xpsettings ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE vcroleinfo ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE unroletimer ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE unmutetimer ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE unbantimer ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE streamrolesettings ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE slowmodeignoreduser ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE slowmodeignoredrole ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE shopentry ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE permissions ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE muteduserid ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE gcchannelid ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE feedsub ALTER COLUMN url DROP NOT NULL;
ALTER TABLE feedsub ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE delmsgoncmdchannel ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE commandcooldown ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE commandalias ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE antispamsetting ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE antiraidsetting ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
ALTER TABLE antialtsetting ADD guildid numeric(20,0) NOT NULL DEFAULT 0.0;
CREATE TABLE guildfilterconfig (
id integer GENERATED BY DEFAULT AS IDENTITY,
guildid numeric(20,0) NOT NULL,
filterinvites boolean NOT NULL,
filterlinks boolean NOT NULL,
filterwords boolean NOT NULL,
CONSTRAINT pk_guildfilterconfig PRIMARY KEY (id)
);
insert into guildfilterconfig (Id, GuildId, FilterLinks, FilterInvites, FilterWords)
select Id, GuildId, FilterLinks, FilterInvites, FilterWords
from guildconfigs;
DELETE FROM XPSettings WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE XpSettings
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = XpSettings.GuildConfigId);
DELETE FROM StreamRoleSettings WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE StreamRoleSettings
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = StreamRoleSettings.GuildConfigId);
DELETE FROM FeedSub WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE FeedSub
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = FeedSub.GuildConfigId);
DELETE FROM DelMsgOnCmdChannel WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE DelMsgOnCmdChannel
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = DelMsgOnCmdChannel.GuildConfigId);
DELETE FROM AntiSpamSetting WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE AntiSpamSetting
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = AntiSpamSetting.GuildConfigId);
DELETE FROM AntiRaidSetting WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE AntiRaidSetting
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = AntiRaidSetting.GuildConfigId);
DELETE FROM AntiAltSetting WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE AntiAltSetting
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = AntiAltSetting.GuildConfigId);
DELETE FROM VcRoleInfo WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE VcRoleInfo
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = VcRoleInfo.GuildConfigId);
DELETE FROM UnroleTimer WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE UnroleTimer
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = UnroleTimer.GuildConfigId);
DELETE FROM UnmuteTimer WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE UnmuteTimer
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = UnmuteTimer.GuildConfigId);
DELETE FROM UnbanTimer WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE UnbanTimer
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = UnbanTimer.GuildConfigId);
DELETE FROM SlowmodeIgnoredUser WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE SlowmodeIgnoredUser
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = SlowmodeIgnoredUser.GuildConfigId);
DELETE FROM SlowmodeIgnoredRole WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE SlowmodeIgnoredRole
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = SlowmodeIgnoredRole.GuildConfigId);
DELETE FROM ShopEntry WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE ShopEntry
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = ShopEntry.GuildConfigId);
DELETE FROM Permissions WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE Permissions
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = Permissions.GuildConfigId);
DELETE FROM MutedUserId WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE MutedUserId
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = MutedUserId.GuildConfigId);
DELETE FROM GcChannelId WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE GcChannelId
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = GcChannelId.GuildConfigId);
DELETE FROM CommandCooldown WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE CommandCooldown
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = CommandCooldown.GuildConfigId);
DELETE FROM CommandAlias WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE CommandAlias
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = CommandAlias.GuildConfigId);
ALTER TABLE vcroleinfo DROP COLUMN guildconfigid;
ALTER TABLE unroletimer DROP COLUMN guildconfigid;
ALTER TABLE unmutetimer DROP COLUMN guildconfigid;
ALTER TABLE unbantimer DROP COLUMN guildconfigid;
ALTER TABLE slowmodeignoreduser DROP COLUMN guildconfigid;
ALTER TABLE slowmodeignoredrole DROP COLUMN guildconfigid;
ALTER TABLE shopentry DROP COLUMN guildconfigid;
ALTER TABLE muteduserid DROP COLUMN guildconfigid;
ALTER TABLE guildconfigs DROP COLUMN autodeleteselfassignedrolemessages;
ALTER TABLE guildconfigs DROP COLUMN exclusiveselfassignedroles;
ALTER TABLE guildconfigs DROP COLUMN filterinvites;
ALTER TABLE guildconfigs DROP COLUMN filterlinks;
ALTER TABLE guildconfigs DROP COLUMN filterwords;
ALTER TABLE gcchannelid DROP COLUMN guildconfigid;
ALTER TABLE followedstream DROP COLUMN dateadded;
ALTER TABLE followedstream DROP COLUMN guildconfigid;
ALTER TABLE filterchannelid DROP COLUMN dateadded;
ALTER TABLE commandcooldown DROP COLUMN guildconfigid;
ALTER TABLE commandalias DROP COLUMN guildconfigid;
CREATE UNIQUE INDEX ix_xpsettings_guildid ON xpsettings (guildid);
CREATE UNIQUE INDEX ix_xpcurrencyreward_level_xpsettingsid ON xpcurrencyreward (level, xpsettingsid);
CREATE UNIQUE INDEX ix_vcroleinfo_guildid_voicechannelid ON vcroleinfo (guildid, voicechannelid);
CREATE UNIQUE INDEX ix_unroletimer_guildid_userid ON unroletimer (guildid, userid);
CREATE UNIQUE INDEX ix_unmutetimer_guildid_userid ON unmutetimer (guildid, userid);
CREATE UNIQUE INDEX ix_unbantimer_guildid_userid ON unbantimer (guildid, userid);
CREATE UNIQUE INDEX ix_streamrolesettings_guildid ON streamrolesettings (guildid);
CREATE UNIQUE INDEX ix_slowmodeignoreduser_guildid_userid ON slowmodeignoreduser (guildid, userid);
CREATE UNIQUE INDEX ix_slowmodeignoredrole_guildid_roleid ON slowmodeignoredrole (guildid, roleid);
CREATE UNIQUE INDEX ix_shopentry_guildid_index ON shopentry (guildid, index);
CREATE INDEX ix_permissions_guildid ON permissions (guildid);
CREATE UNIQUE INDEX ix_muteduserid_guildid_userid ON muteduserid (guildid, userid);
CREATE UNIQUE INDEX ix_gcchannelid_guildid_channelid ON gcchannelid (guildid, channelid);
CREATE INDEX ix_followedstream_guildid_username_type ON followedstream (guildid, username, type);
CREATE UNIQUE INDEX ix_feedsub_guildid_url ON feedsub (guildid, url);
CREATE UNIQUE INDEX ix_delmsgoncmdchannel_guildid_channelid ON delmsgoncmdchannel (guildid, channelid);
CREATE UNIQUE INDEX ix_commandcooldown_guildid_commandname ON commandcooldown (guildid, commandname);
CREATE INDEX ix_commandalias_guildid ON commandalias (guildid);
CREATE UNIQUE INDEX ix_antispamsetting_guildid ON antispamsetting (guildid);
CREATE UNIQUE INDEX ix_antiraidsetting_guildid ON antiraidsetting (guildid);
CREATE UNIQUE INDEX ix_antialtsetting_guildid ON antialtsetting (guildid);
CREATE INDEX ix_guildfilterconfig_guildid ON guildfilterconfig (guildid);
ALTER TABLE excludeditem ADD CONSTRAINT fk_excludeditem_xpsettings_xpsettingsid FOREIGN KEY (xpsettingsid) REFERENCES xpsettings (id);
ALTER TABLE filterchannelid ADD CONSTRAINT fk_filterchannelid_guildfilterconfig_guildfilterconfigid FOREIGN KEY (guildfilterconfigid) REFERENCES guildfilterconfig (id);
ALTER TABLE filteredword ADD CONSTRAINT fk_filteredword_guildfilterconfig_guildfilterconfigid FOREIGN KEY (guildfilterconfigid) REFERENCES guildfilterconfig (id);
ALTER TABLE filterlinkschannelid ADD CONSTRAINT fk_filterlinkschannelid_guildfilterconfig_guildfilterconfigid FOREIGN KEY (guildfilterconfigid) REFERENCES guildfilterconfig (id);
ALTER TABLE filterwordschannelid ADD CONSTRAINT fk_filterwordschannelid_guildfilterconfig_guildfilterconfigid FOREIGN KEY (guildfilterconfigid) REFERENCES guildfilterconfig (id);
ALTER TABLE permissions ADD CONSTRAINT fk_permissions_guildconfigs_guildconfigid FOREIGN KEY (guildconfigid) REFERENCES guildconfigs (id);
INSERT INTO "__EFMigrationsHistory" (migrationid, productversion)
VALUES ('20250126062816_cleanup', '8.0.8');
COMMIT;

View file

@ -0,0 +1,762 @@
BEGIN TRANSACTION;
DROP INDEX "IX_XpSettings_GuildConfigId";
DROP INDEX "IX_VcRoleInfo_GuildConfigId";
DROP INDEX "IX_UnroleTimer_GuildConfigId";
DROP INDEX "IX_UnmuteTimer_GuildConfigId";
DROP INDEX "IX_UnbanTimer_GuildConfigId";
DROP INDEX "IX_StreamRoleSettings_GuildConfigId";
DROP INDEX "IX_SlowmodeIgnoredUser_GuildConfigId";
DROP INDEX "IX_SlowmodeIgnoredRole_GuildConfigId";
DROP INDEX "IX_ShopEntry_GuildConfigId";
DROP INDEX "IX_MutedUserId_GuildConfigId";
DROP INDEX "IX_GCChannelId_GuildConfigId";
DROP INDEX "IX_FollowedStream_GuildConfigId";
DROP INDEX "IX_DelMsgOnCmdChannel_GuildConfigId";
DROP INDEX "IX_CommandCooldown_GuildConfigId";
DROP INDEX "IX_CommandAlias_GuildConfigId";
DROP INDEX "IX_AntiSpamSetting_GuildConfigId";
DROP INDEX "IX_AntiRaidSetting_GuildConfigId";
DROP INDEX "IX_AntiAltSetting_GuildConfigId";
ALTER TABLE "FilterWordsChannelId" RENAME COLUMN "GuildConfigId" TO "GuildFilterConfigId";
DROP INDEX "IX_FilterWordsChannelId_GuildConfigId";
CREATE INDEX "IX_FilterWordsChannelId_GuildFilterConfigId" ON "FilterWordsChannelId" ("GuildFilterConfigId");
ALTER TABLE "FilterLinksChannelId" RENAME COLUMN "GuildConfigId" TO "GuildFilterConfigId";
DROP INDEX "IX_FilterLinksChannelId_GuildConfigId";
CREATE INDEX "IX_FilterLinksChannelId_GuildFilterConfigId" ON "FilterLinksChannelId" ("GuildFilterConfigId");
ALTER TABLE "FilteredWord" RENAME COLUMN "GuildConfigId" TO "GuildFilterConfigId";
DROP INDEX "IX_FilteredWord_GuildConfigId";
CREATE INDEX "IX_FilteredWord_GuildFilterConfigId" ON "FilteredWord" ("GuildFilterConfigId");
ALTER TABLE "FilterChannelId" RENAME COLUMN "GuildConfigId" TO "GuildFilterConfigId";
DROP INDEX "IX_FilterChannelId_GuildConfigId";
CREATE INDEX "IX_FilterChannelId_GuildFilterConfigId" ON "FilterChannelId" ("GuildFilterConfigId");
ALTER TABLE "XpSettings" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "VcRoleInfo" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "UnroleTimer" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "UnmuteTimer" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "UnbanTimer" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "StreamRoleSettings" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "SlowmodeIgnoredUser" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "SlowmodeIgnoredRole" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "ShopEntry" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "Permissions" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "MutedUserId" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "GCChannelId" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "FeedSub" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "DelMsgOnCmdChannel" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "CommandCooldown" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "CommandAlias" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "AntiSpamSetting" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "AntiRaidSetting" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "AntiAltSetting" ADD "GuildId" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "GuildFilterConfig" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_GuildFilterConfig" PRIMARY KEY AUTOINCREMENT,
"GuildId" INTEGER NOT NULL,
"FilterInvites" INTEGER NOT NULL,
"FilterLinks" INTEGER NOT NULL,
"FilterWords" INTEGER NOT NULL
);
insert into guildfilterconfig (Id, GuildId, FilterLinks, FilterInvites, FilterWords)
select Id, GuildId, FilterLinks, FilterInvites, FilterWords
from guildconfigs;
DELETE FROM XPSettings WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE XpSettings
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = XpSettings.GuildConfigId);
DELETE FROM StreamRoleSettings WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE StreamRoleSettings
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = StreamRoleSettings.GuildConfigId);
DELETE FROM FeedSub WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE FeedSub
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = FeedSub.GuildConfigId);
DELETE FROM DelMsgOnCmdChannel WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE DelMsgOnCmdChannel
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = DelMsgOnCmdChannel.GuildConfigId);
DELETE FROM AntiSpamSetting WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE AntiSpamSetting
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = AntiSpamSetting.GuildConfigId);
DELETE FROM AntiRaidSetting WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE AntiRaidSetting
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = AntiRaidSetting.GuildConfigId);
DELETE FROM AntiAltSetting WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE AntiAltSetting
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = AntiAltSetting.GuildConfigId);
DELETE FROM VcRoleInfo WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE VcRoleInfo
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = VcRoleInfo.GuildConfigId);
DELETE FROM UnroleTimer WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE UnroleTimer
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = UnroleTimer.GuildConfigId);
DELETE FROM UnmuteTimer WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE UnmuteTimer
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = UnmuteTimer.GuildConfigId);
DELETE FROM UnbanTimer WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE UnbanTimer
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = UnbanTimer.GuildConfigId);
DELETE FROM SlowmodeIgnoredUser WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE SlowmodeIgnoredUser
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = SlowmodeIgnoredUser.GuildConfigId);
DELETE FROM SlowmodeIgnoredRole WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE SlowmodeIgnoredRole
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = SlowmodeIgnoredRole.GuildConfigId);
DELETE FROM ShopEntry WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE ShopEntry
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = ShopEntry.GuildConfigId);
DELETE FROM Permissions WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE Permissions
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = Permissions.GuildConfigId);
DELETE FROM MutedUserId WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE MutedUserId
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = MutedUserId.GuildConfigId);
DELETE FROM GcChannelId WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE GcChannelId
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = GcChannelId.GuildConfigId);
DELETE FROM CommandCooldown WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE CommandCooldown
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = CommandCooldown.GuildConfigId);
DELETE FROM CommandAlias WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE CommandAlias
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE GuildConfigs.Id = CommandAlias.GuildConfigId);
CREATE UNIQUE INDEX "IX_XpSettings_GuildId" ON "XpSettings" ("GuildId");
CREATE UNIQUE INDEX "IX_XpCurrencyReward_Level_XpSettingsId" ON "XpCurrencyReward" ("Level", "XpSettingsId");
CREATE UNIQUE INDEX "IX_VcRoleInfo_GuildId_VoiceChannelId" ON "VcRoleInfo" ("GuildId", "VoiceChannelId");
CREATE UNIQUE INDEX "IX_UnroleTimer_GuildId_UserId" ON "UnroleTimer" ("GuildId", "UserId");
CREATE UNIQUE INDEX "IX_UnmuteTimer_GuildId_UserId" ON "UnmuteTimer" ("GuildId", "UserId");
CREATE UNIQUE INDEX "IX_UnbanTimer_GuildId_UserId" ON "UnbanTimer" ("GuildId", "UserId");
CREATE UNIQUE INDEX "IX_StreamRoleSettings_GuildId" ON "StreamRoleSettings" ("GuildId");
CREATE UNIQUE INDEX "IX_SlowmodeIgnoredUser_GuildId_UserId" ON "SlowmodeIgnoredUser" ("GuildId", "UserId");
CREATE UNIQUE INDEX "IX_SlowmodeIgnoredRole_GuildId_RoleId" ON "SlowmodeIgnoredRole" ("GuildId", "RoleId");
CREATE UNIQUE INDEX "IX_ShopEntry_GuildId_Index" ON "ShopEntry" ("GuildId", "Index");
CREATE INDEX "IX_Permissions_GuildId" ON "Permissions" ("GuildId");
CREATE UNIQUE INDEX "IX_MutedUserId_GuildId_UserId" ON "MutedUserId" ("GuildId", "UserId");
CREATE UNIQUE INDEX "IX_GCChannelId_GuildId_ChannelId" ON "GCChannelId" ("GuildId", "ChannelId");
CREATE INDEX "IX_FollowedStream_GuildId_Username_Type" ON "FollowedStream" ("GuildId", "Username", "Type");
CREATE UNIQUE INDEX "IX_FeedSub_GuildId_Url" ON "FeedSub" ("GuildId", "Url");
CREATE UNIQUE INDEX "IX_DelMsgOnCmdChannel_GuildId_ChannelId" ON "DelMsgOnCmdChannel" ("GuildId", "ChannelId");
CREATE UNIQUE INDEX "IX_CommandCooldown_GuildId_CommandName" ON "CommandCooldown" ("GuildId", "CommandName");
CREATE INDEX "IX_CommandAlias_GuildId" ON "CommandAlias" ("GuildId");
CREATE UNIQUE INDEX "IX_AntiSpamSetting_GuildId" ON "AntiSpamSetting" ("GuildId");
CREATE UNIQUE INDEX "IX_AntiRaidSetting_GuildId" ON "AntiRaidSetting" ("GuildId");
CREATE UNIQUE INDEX "IX_AntiAltSetting_GuildId" ON "AntiAltSetting" ("GuildId");
CREATE INDEX "IX_GuildFilterConfig_GuildId" ON "GuildFilterConfig" ("GuildId");
CREATE TABLE "ef_temp_AntiAltSetting" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_AntiAltSetting" PRIMARY KEY AUTOINCREMENT,
"Action" INTEGER NOT NULL,
"ActionDurationMinutes" INTEGER NOT NULL,
"GuildConfigId" INTEGER NOT NULL,
"GuildId" INTEGER NOT NULL,
"MinAge" TEXT NOT NULL,
"RoleId" INTEGER NULL
);
INSERT INTO "ef_temp_AntiAltSetting" ("Id", "Action", "ActionDurationMinutes", "GuildConfigId", "GuildId", "MinAge", "RoleId")
SELECT "Id", "Action", "ActionDurationMinutes", "GuildConfigId", "GuildId", "MinAge", "RoleId"
FROM "AntiAltSetting";
CREATE TABLE "ef_temp_AntiRaidSetting" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_AntiRaidSetting" PRIMARY KEY AUTOINCREMENT,
"Action" INTEGER NOT NULL,
"DateAdded" TEXT NULL,
"GuildConfigId" INTEGER NOT NULL,
"GuildId" INTEGER NOT NULL,
"PunishDuration" INTEGER NOT NULL,
"Seconds" INTEGER NOT NULL,
"UserThreshold" INTEGER NOT NULL
);
INSERT INTO "ef_temp_AntiRaidSetting" ("Id", "Action", "DateAdded", "GuildConfigId", "GuildId", "PunishDuration", "Seconds", "UserThreshold")
SELECT "Id", "Action", "DateAdded", "GuildConfigId", "GuildId", "PunishDuration", "Seconds", "UserThreshold"
FROM "AntiRaidSetting";
CREATE TABLE "ef_temp_AntiSpamSetting" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_AntiSpamSetting" PRIMARY KEY AUTOINCREMENT,
"Action" INTEGER NOT NULL,
"DateAdded" TEXT NULL,
"GuildConfigId" INTEGER NOT NULL,
"GuildId" INTEGER NOT NULL,
"MessageThreshold" INTEGER NOT NULL,
"MuteTime" INTEGER NOT NULL,
"RoleId" INTEGER NULL
);
INSERT INTO "ef_temp_AntiSpamSetting" ("Id", "Action", "DateAdded", "GuildConfigId", "GuildId", "MessageThreshold", "MuteTime", "RoleId")
SELECT "Id", "Action", "DateAdded", "GuildConfigId", "GuildId", "MessageThreshold", "MuteTime", "RoleId"
FROM "AntiSpamSetting";
CREATE TABLE "ef_temp_CommandAlias" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_CommandAlias" PRIMARY KEY AUTOINCREMENT,
"DateAdded" TEXT NULL,
"GuildId" INTEGER NOT NULL,
"Mapping" TEXT NULL,
"Trigger" TEXT NULL
);
INSERT INTO "ef_temp_CommandAlias" ("Id", "DateAdded", "GuildId", "Mapping", "Trigger")
SELECT "Id", "DateAdded", "GuildId", "Mapping", "Trigger"
FROM "CommandAlias";
CREATE TABLE "ef_temp_CommandCooldown" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_CommandCooldown" PRIMARY KEY AUTOINCREMENT,
"CommandName" TEXT NULL,
"DateAdded" TEXT NULL,
"GuildId" INTEGER NOT NULL,
"Seconds" INTEGER NOT NULL
);
INSERT INTO "ef_temp_CommandCooldown" ("Id", "CommandName", "DateAdded", "GuildId", "Seconds")
SELECT "Id", "CommandName", "DateAdded", "GuildId", "Seconds"
FROM "CommandCooldown";
CREATE TABLE "ef_temp_DelMsgOnCmdChannel" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_DelMsgOnCmdChannel" PRIMARY KEY AUTOINCREMENT,
"ChannelId" INTEGER NOT NULL,
"DateAdded" TEXT NULL,
"GuildConfigId" INTEGER NOT NULL,
"GuildId" INTEGER NOT NULL,
"State" INTEGER NOT NULL
);
INSERT INTO "ef_temp_DelMsgOnCmdChannel" ("Id", "ChannelId", "DateAdded", "GuildConfigId", "GuildId", "State")
SELECT "Id", "ChannelId", "DateAdded", "GuildConfigId", "GuildId", "State"
FROM "DelMsgOnCmdChannel";
CREATE TABLE "ef_temp_ExcludedItem" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_ExcludedItem" PRIMARY KEY AUTOINCREMENT,
"DateAdded" TEXT NULL,
"ItemId" INTEGER NOT NULL,
"ItemType" INTEGER NOT NULL,
"XpSettingsId" INTEGER NULL,
CONSTRAINT "FK_ExcludedItem_XpSettings_XpSettingsId" FOREIGN KEY ("XpSettingsId") REFERENCES "XpSettings" ("Id")
);
INSERT INTO "ef_temp_ExcludedItem" ("Id", "DateAdded", "ItemId", "ItemType", "XpSettingsId")
SELECT "Id", "DateAdded", "ItemId", "ItemType", "XpSettingsId"
FROM "ExcludedItem";
CREATE TABLE "ef_temp_FeedSub" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_FeedSub" PRIMARY KEY AUTOINCREMENT,
"ChannelId" INTEGER NOT NULL,
"DateAdded" TEXT NULL,
"GuildConfigId" INTEGER NOT NULL,
"GuildId" INTEGER NOT NULL,
"Message" TEXT NULL,
"Url" TEXT NULL
);
INSERT INTO "ef_temp_FeedSub" ("Id", "ChannelId", "DateAdded", "GuildConfigId", "GuildId", "Message", "Url")
SELECT "Id", "ChannelId", "DateAdded", "GuildConfigId", "GuildId", "Message", "Url"
FROM "FeedSub";
CREATE TABLE "ef_temp_FilterChannelId" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_FilterChannelId" PRIMARY KEY AUTOINCREMENT,
"ChannelId" INTEGER NOT NULL,
"GuildFilterConfigId" INTEGER NULL,
CONSTRAINT "FK_FilterChannelId_GuildFilterConfig_GuildFilterConfigId" FOREIGN KEY ("GuildFilterConfigId") REFERENCES "GuildFilterConfig" ("Id")
);
INSERT INTO "ef_temp_FilterChannelId" ("Id", "ChannelId", "GuildFilterConfigId")
SELECT "Id", "ChannelId", "GuildFilterConfigId"
FROM "FilterChannelId";
CREATE TABLE "ef_temp_FilteredWord" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_FilteredWord" PRIMARY KEY AUTOINCREMENT,
"DateAdded" TEXT NULL,
"GuildFilterConfigId" INTEGER NULL,
"Word" TEXT NULL,
CONSTRAINT "FK_FilteredWord_GuildFilterConfig_GuildFilterConfigId" FOREIGN KEY ("GuildFilterConfigId") REFERENCES "GuildFilterConfig" ("Id")
);
INSERT INTO "ef_temp_FilteredWord" ("Id", "DateAdded", "GuildFilterConfigId", "Word")
SELECT "Id", "DateAdded", "GuildFilterConfigId", "Word"
FROM "FilteredWord";
CREATE TABLE "ef_temp_FilterLinksChannelId" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_FilterLinksChannelId" PRIMARY KEY AUTOINCREMENT,
"ChannelId" INTEGER NOT NULL,
"DateAdded" TEXT NULL,
"GuildFilterConfigId" INTEGER NULL,
CONSTRAINT "FK_FilterLinksChannelId_GuildFilterConfig_GuildFilterConfigId" FOREIGN KEY ("GuildFilterConfigId") REFERENCES "GuildFilterConfig" ("Id")
);
INSERT INTO "ef_temp_FilterLinksChannelId" ("Id", "ChannelId", "DateAdded", "GuildFilterConfigId")
SELECT "Id", "ChannelId", "DateAdded", "GuildFilterConfigId"
FROM "FilterLinksChannelId";
CREATE TABLE "ef_temp_FilterWordsChannelId" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_FilterWordsChannelId" PRIMARY KEY AUTOINCREMENT,
"ChannelId" INTEGER NOT NULL,
"DateAdded" TEXT NULL,
"GuildFilterConfigId" INTEGER NULL,
CONSTRAINT "FK_FilterWordsChannelId_GuildFilterConfig_GuildFilterConfigId" FOREIGN KEY ("GuildFilterConfigId") REFERENCES "GuildFilterConfig" ("Id")
);
INSERT INTO "ef_temp_FilterWordsChannelId" ("Id", "ChannelId", "DateAdded", "GuildFilterConfigId")
SELECT "Id", "ChannelId", "DateAdded", "GuildFilterConfigId"
FROM "FilterWordsChannelId";
CREATE TABLE "ef_temp_FollowedStream" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_FollowedStream" PRIMARY KEY AUTOINCREMENT,
"ChannelId" INTEGER NOT NULL,
"GuildId" INTEGER NOT NULL,
"Message" TEXT NULL,
"Type" INTEGER NOT NULL,
"Username" TEXT NULL
);
INSERT INTO "ef_temp_FollowedStream" ("Id", "ChannelId", "GuildId", "Message", "Type", "Username")
SELECT "Id", "ChannelId", "GuildId", "Message", "Type", "Username"
FROM "FollowedStream";
CREATE TABLE "ef_temp_GCChannelId" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_GCChannelId" PRIMARY KEY AUTOINCREMENT,
"ChannelId" INTEGER NOT NULL,
"DateAdded" TEXT NULL,
"GuildId" INTEGER NOT NULL
);
INSERT INTO "ef_temp_GCChannelId" ("Id", "ChannelId", "DateAdded", "GuildId")
SELECT "Id", "ChannelId", "DateAdded", "GuildId"
FROM "GCChannelId";
CREATE TABLE "ef_temp_MutedUserId" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_MutedUserId" PRIMARY KEY AUTOINCREMENT,
"DateAdded" TEXT NULL,
"GuildId" INTEGER NOT NULL,
"UserId" INTEGER NOT NULL
);
INSERT INTO "ef_temp_MutedUserId" ("Id", "DateAdded", "GuildId", "UserId")
SELECT "Id", "DateAdded", "GuildId", "UserId"
FROM "MutedUserId";
CREATE TABLE "ef_temp_Permissions" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_Permissions" PRIMARY KEY AUTOINCREMENT,
"DateAdded" TEXT NULL,
"GuildConfigId" INTEGER NULL,
"GuildId" INTEGER NOT NULL,
"Index" INTEGER NOT NULL,
"IsCustomCommand" INTEGER NOT NULL,
"PrimaryTarget" INTEGER NOT NULL,
"PrimaryTargetId" INTEGER NOT NULL,
"SecondaryTarget" INTEGER NOT NULL,
"SecondaryTargetName" TEXT NULL,
"State" INTEGER NOT NULL,
CONSTRAINT "FK_Permissions_GuildConfigs_GuildConfigId" FOREIGN KEY ("GuildConfigId") REFERENCES "GuildConfigs" ("Id")
);
INSERT INTO "ef_temp_Permissions" ("Id", "DateAdded", "GuildConfigId", "GuildId", "Index", "IsCustomCommand", "PrimaryTarget", "PrimaryTargetId", "SecondaryTarget", "SecondaryTargetName", "State")
SELECT "Id", "DateAdded", "GuildConfigId", "GuildId", "Index", "IsCustomCommand", "PrimaryTarget", "PrimaryTargetId", "SecondaryTarget", "SecondaryTargetName", "State"
FROM "Permissions";
CREATE TABLE "ef_temp_ShopEntry" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_ShopEntry" PRIMARY KEY AUTOINCREMENT,
"AuthorId" INTEGER NOT NULL,
"Command" TEXT NULL,
"DateAdded" TEXT NULL,
"GuildId" INTEGER NOT NULL,
"Index" INTEGER NOT NULL,
"Name" TEXT NULL,
"Price" INTEGER NOT NULL,
"RoleId" INTEGER NOT NULL,
"RoleName" TEXT NULL,
"RoleRequirement" INTEGER NULL,
"Type" INTEGER NOT NULL
);
INSERT INTO "ef_temp_ShopEntry" ("Id", "AuthorId", "Command", "DateAdded", "GuildId", "Index", "Name", "Price", "RoleId", "RoleName", "RoleRequirement", "Type")
SELECT "Id", "AuthorId", "Command", "DateAdded", "GuildId", "Index", "Name", "Price", "RoleId", "RoleName", "RoleRequirement", "Type"
FROM "ShopEntry";
CREATE TABLE "ef_temp_SlowmodeIgnoredRole" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_SlowmodeIgnoredRole" PRIMARY KEY AUTOINCREMENT,
"DateAdded" TEXT NULL,
"GuildId" INTEGER NOT NULL,
"RoleId" INTEGER NOT NULL
);
INSERT INTO "ef_temp_SlowmodeIgnoredRole" ("Id", "DateAdded", "GuildId", "RoleId")
SELECT "Id", "DateAdded", "GuildId", "RoleId"
FROM "SlowmodeIgnoredRole";
CREATE TABLE "ef_temp_SlowmodeIgnoredUser" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_SlowmodeIgnoredUser" PRIMARY KEY AUTOINCREMENT,
"DateAdded" TEXT NULL,
"GuildId" INTEGER NOT NULL,
"UserId" INTEGER NOT NULL
);
INSERT INTO "ef_temp_SlowmodeIgnoredUser" ("Id", "DateAdded", "GuildId", "UserId")
SELECT "Id", "DateAdded", "GuildId", "UserId"
FROM "SlowmodeIgnoredUser";
CREATE TABLE "ef_temp_StreamRoleSettings" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_StreamRoleSettings" PRIMARY KEY AUTOINCREMENT,
"AddRoleId" INTEGER NOT NULL,
"DateAdded" TEXT NULL,
"Enabled" INTEGER NOT NULL,
"FromRoleId" INTEGER NOT NULL,
"GuildConfigId" INTEGER NOT NULL,
"GuildId" INTEGER NOT NULL,
"Keyword" TEXT NULL
);
INSERT INTO "ef_temp_StreamRoleSettings" ("Id", "AddRoleId", "DateAdded", "Enabled", "FromRoleId", "GuildConfigId", "GuildId", "Keyword")
SELECT "Id", "AddRoleId", "DateAdded", "Enabled", "FromRoleId", "GuildConfigId", "GuildId", "Keyword"
FROM "StreamRoleSettings";
CREATE TABLE "ef_temp_UnbanTimer" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_UnbanTimer" PRIMARY KEY AUTOINCREMENT,
"DateAdded" TEXT NULL,
"GuildId" INTEGER NOT NULL,
"UnbanAt" TEXT NOT NULL,
"UserId" INTEGER NOT NULL
);
INSERT INTO "ef_temp_UnbanTimer" ("Id", "DateAdded", "GuildId", "UnbanAt", "UserId")
SELECT "Id", "DateAdded", "GuildId", "UnbanAt", "UserId"
FROM "UnbanTimer";
CREATE TABLE "ef_temp_UnmuteTimer" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_UnmuteTimer" PRIMARY KEY AUTOINCREMENT,
"DateAdded" TEXT NULL,
"GuildId" INTEGER NOT NULL,
"UnmuteAt" TEXT NOT NULL,
"UserId" INTEGER NOT NULL
);
INSERT INTO "ef_temp_UnmuteTimer" ("Id", "DateAdded", "GuildId", "UnmuteAt", "UserId")
SELECT "Id", "DateAdded", "GuildId", "UnmuteAt", "UserId"
FROM "UnmuteTimer";
CREATE TABLE "ef_temp_UnroleTimer" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_UnroleTimer" PRIMARY KEY AUTOINCREMENT,
"DateAdded" TEXT NULL,
"GuildId" INTEGER NOT NULL,
"RoleId" INTEGER NOT NULL,
"UnbanAt" TEXT NOT NULL,
"UserId" INTEGER NOT NULL
);
INSERT INTO "ef_temp_UnroleTimer" ("Id", "DateAdded", "GuildId", "RoleId", "UnbanAt", "UserId")
SELECT "Id", "DateAdded", "GuildId", "RoleId", "UnbanAt", "UserId"
FROM "UnroleTimer";
CREATE TABLE "ef_temp_VcRoleInfo" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_VcRoleInfo" PRIMARY KEY AUTOINCREMENT,
"DateAdded" TEXT NULL,
"GuildId" INTEGER NOT NULL,
"RoleId" INTEGER NOT NULL,
"VoiceChannelId" INTEGER NOT NULL
);
INSERT INTO "ef_temp_VcRoleInfo" ("Id", "DateAdded", "GuildId", "RoleId", "VoiceChannelId")
SELECT "Id", "DateAdded", "GuildId", "RoleId", "VoiceChannelId"
FROM "VcRoleInfo";
CREATE TABLE "ef_temp_XpSettings" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_XpSettings" PRIMARY KEY AUTOINCREMENT,
"DateAdded" TEXT NULL,
"GuildConfigId" INTEGER NOT NULL,
"GuildId" INTEGER NOT NULL,
"ServerExcluded" INTEGER NOT NULL
);
INSERT INTO "ef_temp_XpSettings" ("Id", "DateAdded", "GuildConfigId", "GuildId", "ServerExcluded")
SELECT "Id", "DateAdded", "GuildConfigId", "GuildId", "ServerExcluded"
FROM "XpSettings";
CREATE TABLE "ef_temp_GuildConfigs" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_GuildConfigs" PRIMARY KEY AUTOINCREMENT,
"AutoAssignRoleIds" TEXT NULL,
"CleverbotEnabled" INTEGER NOT NULL,
"DateAdded" TEXT NULL,
"DeleteMessageOnCommand" INTEGER NOT NULL,
"DeleteStreamOnlineMessage" INTEGER NOT NULL,
"DisableGlobalExpressions" INTEGER NOT NULL,
"GameVoiceChannel" INTEGER NULL,
"GuildId" INTEGER NOT NULL,
"Locale" TEXT NULL,
"MuteRoleName" TEXT NULL,
"NotifyStreamOffline" INTEGER NOT NULL,
"PermissionRole" TEXT NULL,
"Prefix" TEXT NULL,
"StickyRoles" INTEGER NOT NULL,
"TimeZoneId" TEXT NULL,
"VerboseErrors" INTEGER NOT NULL DEFAULT 1,
"VerbosePermissions" INTEGER NOT NULL,
"WarnExpireAction" INTEGER NOT NULL,
"WarnExpireHours" INTEGER NOT NULL,
"WarningsInitialized" INTEGER NOT NULL
);
INSERT INTO "ef_temp_GuildConfigs" ("Id", "AutoAssignRoleIds", "CleverbotEnabled", "DateAdded", "DeleteMessageOnCommand", "DeleteStreamOnlineMessage", "DisableGlobalExpressions", "GameVoiceChannel", "GuildId", "Locale", "MuteRoleName", "NotifyStreamOffline", "PermissionRole", "Prefix", "StickyRoles", "TimeZoneId", "VerboseErrors", "VerbosePermissions", "WarnExpireAction", "WarnExpireHours", "WarningsInitialized")
SELECT "Id", "AutoAssignRoleIds", "CleverbotEnabled", "DateAdded", "DeleteMessageOnCommand", "DeleteStreamOnlineMessage", "DisableGlobalExpressions", "GameVoiceChannel", "GuildId", "Locale", "MuteRoleName", "NotifyStreamOffline", "PermissionRole", "Prefix", "StickyRoles", "TimeZoneId", "VerboseErrors", "VerbosePermissions", "WarnExpireAction", "WarnExpireHours", "WarningsInitialized"
FROM "GuildConfigs";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "AntiAltSetting";
ALTER TABLE "ef_temp_AntiAltSetting" RENAME TO "AntiAltSetting";
DROP TABLE "AntiRaidSetting";
ALTER TABLE "ef_temp_AntiRaidSetting" RENAME TO "AntiRaidSetting";
DROP TABLE "AntiSpamSetting";
ALTER TABLE "ef_temp_AntiSpamSetting" RENAME TO "AntiSpamSetting";
DROP TABLE "CommandAlias";
ALTER TABLE "ef_temp_CommandAlias" RENAME TO "CommandAlias";
DROP TABLE "CommandCooldown";
ALTER TABLE "ef_temp_CommandCooldown" RENAME TO "CommandCooldown";
DROP TABLE "DelMsgOnCmdChannel";
ALTER TABLE "ef_temp_DelMsgOnCmdChannel" RENAME TO "DelMsgOnCmdChannel";
DROP TABLE "ExcludedItem";
ALTER TABLE "ef_temp_ExcludedItem" RENAME TO "ExcludedItem";
DROP TABLE "FeedSub";
ALTER TABLE "ef_temp_FeedSub" RENAME TO "FeedSub";
DROP TABLE "FilterChannelId";
ALTER TABLE "ef_temp_FilterChannelId" RENAME TO "FilterChannelId";
DROP TABLE "FilteredWord";
ALTER TABLE "ef_temp_FilteredWord" RENAME TO "FilteredWord";
DROP TABLE "FilterLinksChannelId";
ALTER TABLE "ef_temp_FilterLinksChannelId" RENAME TO "FilterLinksChannelId";
DROP TABLE "FilterWordsChannelId";
ALTER TABLE "ef_temp_FilterWordsChannelId" RENAME TO "FilterWordsChannelId";
DROP TABLE "FollowedStream";
ALTER TABLE "ef_temp_FollowedStream" RENAME TO "FollowedStream";
DROP TABLE "GCChannelId";
ALTER TABLE "ef_temp_GCChannelId" RENAME TO "GCChannelId";
DROP TABLE "MutedUserId";
ALTER TABLE "ef_temp_MutedUserId" RENAME TO "MutedUserId";
DROP TABLE "Permissions";
ALTER TABLE "ef_temp_Permissions" RENAME TO "Permissions";
DROP TABLE "ShopEntry";
ALTER TABLE "ef_temp_ShopEntry" RENAME TO "ShopEntry";
DROP TABLE "SlowmodeIgnoredRole";
ALTER TABLE "ef_temp_SlowmodeIgnoredRole" RENAME TO "SlowmodeIgnoredRole";
DROP TABLE "SlowmodeIgnoredUser";
ALTER TABLE "ef_temp_SlowmodeIgnoredUser" RENAME TO "SlowmodeIgnoredUser";
DROP TABLE "StreamRoleSettings";
ALTER TABLE "ef_temp_StreamRoleSettings" RENAME TO "StreamRoleSettings";
DROP TABLE "UnbanTimer";
ALTER TABLE "ef_temp_UnbanTimer" RENAME TO "UnbanTimer";
DROP TABLE "UnmuteTimer";
ALTER TABLE "ef_temp_UnmuteTimer" RENAME TO "UnmuteTimer";
DROP TABLE "UnroleTimer";
ALTER TABLE "ef_temp_UnroleTimer" RENAME TO "UnroleTimer";
DROP TABLE "VcRoleInfo";
ALTER TABLE "ef_temp_VcRoleInfo" RENAME TO "VcRoleInfo";
DROP TABLE "XpSettings";
ALTER TABLE "ef_temp_XpSettings" RENAME TO "XpSettings";
DROP TABLE "GuildConfigs";
ALTER TABLE "ef_temp_GuildConfigs" RENAME TO "GuildConfigs";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE UNIQUE INDEX "IX_AntiAltSetting_GuildId" ON "AntiAltSetting" ("GuildId");
CREATE UNIQUE INDEX "IX_AntiRaidSetting_GuildId" ON "AntiRaidSetting" ("GuildId");
CREATE UNIQUE INDEX "IX_AntiSpamSetting_GuildId" ON "AntiSpamSetting" ("GuildId");
CREATE INDEX "IX_CommandAlias_GuildId" ON "CommandAlias" ("GuildId");
CREATE UNIQUE INDEX "IX_CommandCooldown_GuildId_CommandName" ON "CommandCooldown" ("GuildId", "CommandName");
CREATE UNIQUE INDEX "IX_DelMsgOnCmdChannel_GuildId_ChannelId" ON "DelMsgOnCmdChannel" ("GuildId", "ChannelId");
CREATE INDEX "IX_ExcludedItem_XpSettingsId" ON "ExcludedItem" ("XpSettingsId");
CREATE UNIQUE INDEX "IX_FeedSub_GuildId_Url" ON "FeedSub" ("GuildId", "Url");
CREATE INDEX "IX_FilterChannelId_GuildFilterConfigId" ON "FilterChannelId" ("GuildFilterConfigId");
CREATE INDEX "IX_FilteredWord_GuildFilterConfigId" ON "FilteredWord" ("GuildFilterConfigId");
CREATE INDEX "IX_FilterLinksChannelId_GuildFilterConfigId" ON "FilterLinksChannelId" ("GuildFilterConfigId");
CREATE INDEX "IX_FilterWordsChannelId_GuildFilterConfigId" ON "FilterWordsChannelId" ("GuildFilterConfigId");
CREATE INDEX "IX_FollowedStream_GuildId_Username_Type" ON "FollowedStream" ("GuildId", "Username", "Type");
CREATE UNIQUE INDEX "IX_GCChannelId_GuildId_ChannelId" ON "GCChannelId" ("GuildId", "ChannelId");
CREATE UNIQUE INDEX "IX_MutedUserId_GuildId_UserId" ON "MutedUserId" ("GuildId", "UserId");
CREATE INDEX "IX_Permissions_GuildConfigId" ON "Permissions" ("GuildConfigId");
CREATE INDEX "IX_Permissions_GuildId" ON "Permissions" ("GuildId");
CREATE UNIQUE INDEX "IX_ShopEntry_GuildId_Index" ON "ShopEntry" ("GuildId", "Index");
CREATE UNIQUE INDEX "IX_SlowmodeIgnoredRole_GuildId_RoleId" ON "SlowmodeIgnoredRole" ("GuildId", "RoleId");
CREATE UNIQUE INDEX "IX_SlowmodeIgnoredUser_GuildId_UserId" ON "SlowmodeIgnoredUser" ("GuildId", "UserId");
CREATE UNIQUE INDEX "IX_StreamRoleSettings_GuildId" ON "StreamRoleSettings" ("GuildId");
CREATE UNIQUE INDEX "IX_UnbanTimer_GuildId_UserId" ON "UnbanTimer" ("GuildId", "UserId");
CREATE UNIQUE INDEX "IX_UnmuteTimer_GuildId_UserId" ON "UnmuteTimer" ("GuildId", "UserId");
CREATE UNIQUE INDEX "IX_UnroleTimer_GuildId_UserId" ON "UnroleTimer" ("GuildId", "UserId");
CREATE UNIQUE INDEX "IX_VcRoleInfo_GuildId_VoiceChannelId" ON "VcRoleInfo" ("GuildId", "VoiceChannelId");
CREATE UNIQUE INDEX "IX_XpSettings_GuildId" ON "XpSettings" ("GuildId");
CREATE UNIQUE INDEX "IX_GuildConfigs_GuildId" ON "GuildConfigs" ("GuildId");
CREATE INDEX "IX_GuildConfigs_WarnExpireHours" ON "GuildConfigs" ("WarnExpireHours");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20250126062816_cleanup', '8.0.8');
COMMIT;

View file

@ -11,7 +11,7 @@ using EllieBot.Db;
namespace EllieBot.Migrations.Sqlite namespace EllieBot.Migrations.Sqlite
{ {
[DbContext(typeof(SqliteContext))] [DbContext(typeof(SqliteContext))]
[Migration("20250124023317_init")] [Migration("20250127062834_init")]
partial class init partial class init
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -35,6 +35,9 @@ namespace EllieBot.Migrations.Sqlite
b.Property<int>("GuildConfigId") b.Property<int>("GuildConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<TimeSpan>("MinAge") b.Property<TimeSpan>("MinAge")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
@ -43,7 +46,7 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("AntiAltSetting"); b.ToTable("AntiAltSetting");
@ -64,6 +67,9 @@ namespace EllieBot.Migrations.Sqlite
b.Property<int>("GuildConfigId") b.Property<int>("GuildConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<int>("PunishDuration") b.Property<int>("PunishDuration")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -75,7 +81,7 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("AntiRaidSetting"); b.ToTable("AntiRaidSetting");
@ -118,6 +124,9 @@ namespace EllieBot.Migrations.Sqlite
b.Property<int>("GuildConfigId") b.Property<int>("GuildConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<int>("MessageThreshold") b.Property<int>("MessageThreshold")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -129,7 +138,7 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("AntiSpamSetting"); b.ToTable("AntiSpamSetting");
@ -461,7 +470,7 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("Mapping") b.Property<string>("Mapping")
@ -472,7 +481,7 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId");
b.ToTable("CommandAlias"); b.ToTable("CommandAlias");
}); });
@ -489,7 +498,7 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int>("Seconds") b.Property<int>("Seconds")
@ -497,7 +506,8 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId", "CommandName")
.IsUnique();
b.ToTable("CommandCooldown"); b.ToTable("CommandCooldown");
}); });
@ -555,12 +565,16 @@ namespace EllieBot.Migrations.Sqlite
b.Property<int>("GuildConfigId") b.Property<int>("GuildConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<bool>("State") b.Property<bool>("State")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId", "ChannelId")
.IsUnique();
b.ToTable("DelMsgOnCmdChannel"); b.ToTable("DelMsgOnCmdChannel");
}); });
@ -689,16 +703,19 @@ namespace EllieBot.Migrations.Sqlite
b.Property<int>("GuildConfigId") b.Property<int>("GuildConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<string>("Message") b.Property<string>("Message")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("Url") b.Property<string>("Url")
.IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.HasKey("Id"); b.HasKey("Id");
b.HasAlternateKey("GuildConfigId", "Url"); b.HasIndex("GuildId", "Url")
.IsUnique();
b.ToTable("FeedSub"); b.ToTable("FeedSub");
}); });
@ -712,15 +729,12 @@ namespace EllieBot.Migrations.Sqlite
b.Property<ulong>("ChannelId") b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<DateTime?>("DateAdded") b.Property<int?>("GuildFilterConfigId")
.HasColumnType("TEXT");
b.Property<int?>("GuildConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildFilterConfigId");
b.ToTable("FilterChannelId"); b.ToTable("FilterChannelId");
}); });
@ -737,12 +751,12 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<int?>("GuildFilterConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildFilterConfigId");
b.ToTable("FilterLinksChannelId"); b.ToTable("FilterLinksChannelId");
}); });
@ -759,12 +773,12 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<int?>("GuildFilterConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildFilterConfigId");
b.ToTable("FilterWordsChannelId"); b.ToTable("FilterWordsChannelId");
}); });
@ -778,7 +792,7 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<int?>("GuildFilterConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("Word") b.Property<string>("Word")
@ -786,7 +800,7 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildFilterConfigId");
b.ToTable("FilteredWord"); b.ToTable("FilteredWord");
}); });
@ -823,12 +837,6 @@ namespace EllieBot.Migrations.Sqlite
b.Property<ulong>("ChannelId") b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");
b.Property<int?>("GuildConfigId")
.HasColumnType("INTEGER");
b.Property<ulong>("GuildId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -843,7 +851,7 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId", "Username", "Type");
b.ToTable("FollowedStream"); b.ToTable("FollowedStream");
}); });
@ -860,12 +868,13 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId", "ChannelId")
.IsUnique();
b.ToTable("GCChannelId"); b.ToTable("GCChannelId");
}); });
@ -983,9 +992,6 @@ namespace EllieBot.Migrations.Sqlite
b.Property<string>("AutoAssignRoleIds") b.Property<string>("AutoAssignRoleIds")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<bool>("AutoDeleteSelfAssignedRoleMessages")
.HasColumnType("INTEGER");
b.Property<bool>("CleverbotEnabled") b.Property<bool>("CleverbotEnabled")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -1001,18 +1007,6 @@ namespace EllieBot.Migrations.Sqlite
b.Property<bool>("DisableGlobalExpressions") b.Property<bool>("DisableGlobalExpressions")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<bool>("ExclusiveSelfAssignedRoles")
.HasColumnType("INTEGER");
b.Property<bool>("FilterInvites")
.HasColumnType("INTEGER");
b.Property<bool>("FilterLinks")
.HasColumnType("INTEGER");
b.Property<bool>("FilterWords")
.HasColumnType("INTEGER");
b.Property<ulong?>("GameVoiceChannel") b.Property<ulong?>("GameVoiceChannel")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -1067,6 +1061,31 @@ namespace EllieBot.Migrations.Sqlite
b.ToTable("GuildConfigs"); b.ToTable("GuildConfigs");
}); });
modelBuilder.Entity("EllieBot.Db.Models.GuildFilterConfig", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<bool>("FilterInvites")
.HasColumnType("INTEGER");
b.Property<bool>("FilterLinks")
.HasColumnType("INTEGER");
b.Property<bool>("FilterWords")
.HasColumnType("INTEGER");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GuildId");
b.ToTable("GuildFilterConfig");
});
modelBuilder.Entity("EllieBot.Db.Models.HoneypotChannel", b => modelBuilder.Entity("EllieBot.Db.Models.HoneypotChannel", b =>
{ {
b.Property<ulong>("GuildId") b.Property<ulong>("GuildId")
@ -1276,7 +1295,7 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("UserId") b.Property<ulong>("UserId")
@ -1284,7 +1303,8 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId", "UserId")
.IsUnique();
b.ToTable("MutedUserId"); b.ToTable("MutedUserId");
}); });
@ -1424,6 +1444,9 @@ namespace EllieBot.Migrations.Sqlite
b.Property<int?>("GuildConfigId") b.Property<int?>("GuildConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<int>("Index") b.Property<int>("Index")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -1449,6 +1472,8 @@ namespace EllieBot.Migrations.Sqlite
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.HasIndex("GuildId");
b.ToTable("Permissions"); b.ToTable("Permissions");
}); });
@ -1812,7 +1837,7 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int>("Index") b.Property<int>("Index")
@ -1838,7 +1863,8 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId", "Index")
.IsUnique();
b.ToTable("ShopEntry"); b.ToTable("ShopEntry");
}); });
@ -1874,7 +1900,7 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("RoleId") b.Property<ulong>("RoleId")
@ -1882,7 +1908,8 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId", "RoleId")
.IsUnique();
b.ToTable("SlowmodeIgnoredRole"); b.ToTable("SlowmodeIgnoredRole");
}); });
@ -1896,7 +1923,7 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("UserId") b.Property<ulong>("UserId")
@ -1904,7 +1931,8 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId", "UserId")
.IsUnique();
b.ToTable("SlowmodeIgnoredUser"); b.ToTable("SlowmodeIgnoredUser");
}); });
@ -2007,12 +2035,15 @@ namespace EllieBot.Migrations.Sqlite
b.Property<int>("GuildConfigId") b.Property<int>("GuildConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<string>("Keyword") b.Property<string>("Keyword")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("StreamRoleSettings"); b.ToTable("StreamRoleSettings");
@ -2112,7 +2143,7 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<DateTime>("UnbanAt") b.Property<DateTime>("UnbanAt")
@ -2123,7 +2154,8 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId", "UserId")
.IsUnique();
b.ToTable("UnbanTimer"); b.ToTable("UnbanTimer");
}); });
@ -2137,7 +2169,7 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<DateTime>("UnmuteAt") b.Property<DateTime>("UnmuteAt")
@ -2148,7 +2180,8 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId", "UserId")
.IsUnique();
b.ToTable("UnmuteTimer"); b.ToTable("UnmuteTimer");
}); });
@ -2162,7 +2195,7 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("RoleId") b.Property<ulong>("RoleId")
@ -2176,7 +2209,8 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId", "UserId")
.IsUnique();
b.ToTable("UnroleTimer"); b.ToTable("UnroleTimer");
}); });
@ -2222,7 +2256,7 @@ namespace EllieBot.Migrations.Sqlite
b.Property<DateTime?>("DateAdded") b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int?>("GuildConfigId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("RoleId") b.Property<ulong>("RoleId")
@ -2233,7 +2267,8 @@ namespace EllieBot.Migrations.Sqlite
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId"); b.HasIndex("GuildId", "VoiceChannelId")
.IsUnique();
b.ToTable("VcRoleInfo"); b.ToTable("VcRoleInfo");
}); });
@ -2426,6 +2461,9 @@ namespace EllieBot.Migrations.Sqlite
b.HasIndex("XpSettingsId"); b.HasIndex("XpSettingsId");
b.HasIndex("Level", "XpSettingsId")
.IsUnique();
b.ToTable("XpCurrencyReward"); b.ToTable("XpCurrencyReward");
}); });
@ -2470,12 +2508,15 @@ namespace EllieBot.Migrations.Sqlite
b.Property<int>("GuildConfigId") b.Property<int>("GuildConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<bool>("ServerExcluded") b.Property<bool>("ServerExcluded")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("GuildConfigId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("XpSettings"); b.ToTable("XpSettings");
@ -2646,24 +2687,6 @@ namespace EllieBot.Migrations.Sqlite
b.ToTable("UserBetStats"); b.ToTable("UserBetStats");
}); });
modelBuilder.Entity("EllieBot.Db.Models.AntiAltSetting", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithOne("AntiAltSetting")
.HasForeignKey("EllieBot.Db.Models.AntiAltSetting", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("EllieBot.Db.Models.AntiRaidSetting", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithOne("AntiRaidSetting")
.HasForeignKey("EllieBot.Db.Models.AntiRaidSetting", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("EllieBot.Db.Models.AntiSpamIgnore", b => modelBuilder.Entity("EllieBot.Db.Models.AntiSpamIgnore", b =>
{ {
b.HasOne("EllieBot.Db.Models.AntiSpamSetting", null) b.HasOne("EllieBot.Db.Models.AntiSpamSetting", null)
@ -2672,15 +2695,6 @@ namespace EllieBot.Migrations.Sqlite
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity("EllieBot.Db.Models.AntiSpamSetting", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithOne("AntiSpamSetting")
.HasForeignKey("EllieBot.Db.Models.AntiSpamSetting", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("EllieBot.Db.Models.AutoTranslateUser", b => modelBuilder.Entity("EllieBot.Db.Models.AutoTranslateUser", b =>
{ {
b.HasOne("EllieBot.Db.Models.AutoTranslateChannel", "Channel") b.HasOne("EllieBot.Db.Models.AutoTranslateChannel", "Channel")
@ -2740,31 +2754,6 @@ namespace EllieBot.Migrations.Sqlite
b.Navigation("Owner"); b.Navigation("Owner");
}); });
modelBuilder.Entity("EllieBot.Db.Models.CommandAlias", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("CommandAliases")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.CommandCooldown", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("CommandCooldowns")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.DelMsgOnCmdChannel", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("DelMsgOnCmdChannels")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("EllieBot.Db.Models.DiscordUser", b => modelBuilder.Entity("EllieBot.Db.Models.DiscordUser", b =>
{ {
b.HasOne("EllieBot.Db.Models.ClubInfo", "Club") b.HasOne("EllieBot.Db.Models.ClubInfo", "Club")
@ -2777,73 +2766,37 @@ namespace EllieBot.Migrations.Sqlite
modelBuilder.Entity("EllieBot.Db.Models.ExcludedItem", b => modelBuilder.Entity("EllieBot.Db.Models.ExcludedItem", b =>
{ {
b.HasOne("EllieBot.Db.Models.XpSettings", "XpSettings") b.HasOne("EllieBot.Db.Models.XpSettings", null)
.WithMany("ExclusionList") .WithMany("ExclusionList")
.HasForeignKey("XpSettingsId") .HasForeignKey("XpSettingsId");
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("XpSettings");
});
modelBuilder.Entity("EllieBot.Db.Models.FeedSub", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", "GuildConfig")
.WithMany("FeedSubs")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("GuildConfig");
}); });
modelBuilder.Entity("EllieBot.Db.Models.FilterChannelId", b => modelBuilder.Entity("EllieBot.Db.Models.FilterChannelId", b =>
{ {
b.HasOne("EllieBot.Db.Models.GuildConfig", null) b.HasOne("EllieBot.Db.Models.GuildFilterConfig", null)
.WithMany("FilterInvitesChannelIds") .WithMany("FilterInvitesChannelIds")
.HasForeignKey("GuildConfigId") .HasForeignKey("GuildFilterConfigId");
.OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity("EllieBot.Db.Models.FilterLinksChannelId", b => modelBuilder.Entity("EllieBot.Db.Models.FilterLinksChannelId", b =>
{ {
b.HasOne("EllieBot.Db.Models.GuildConfig", null) b.HasOne("EllieBot.Db.Models.GuildFilterConfig", null)
.WithMany("FilterLinksChannelIds") .WithMany("FilterLinksChannelIds")
.HasForeignKey("GuildConfigId") .HasForeignKey("GuildFilterConfigId");
.OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity("EllieBot.Db.Models.FilterWordsChannelId", b => modelBuilder.Entity("EllieBot.Db.Models.FilterWordsChannelId", b =>
{ {
b.HasOne("EllieBot.Db.Models.GuildConfig", null) b.HasOne("EllieBot.Db.Models.GuildFilterConfig", null)
.WithMany("FilterWordsChannelIds") .WithMany("FilterWordsChannelIds")
.HasForeignKey("GuildConfigId") .HasForeignKey("GuildFilterConfigId");
.OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity("EllieBot.Db.Models.FilteredWord", b => modelBuilder.Entity("EllieBot.Db.Models.FilteredWord", b =>
{ {
b.HasOne("EllieBot.Db.Models.GuildConfig", null) b.HasOne("EllieBot.Db.Models.GuildFilterConfig", null)
.WithMany("FilteredWords") .WithMany("FilteredWords")
.HasForeignKey("GuildConfigId") .HasForeignKey("GuildFilterConfigId");
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.FollowedStream", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("FollowedStreams")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.GCChannelId", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", "GuildConfig")
.WithMany("GenerateCurrencyChannelIds")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("GuildConfig");
}); });
modelBuilder.Entity("EllieBot.Db.Models.GiveawayUser", b => modelBuilder.Entity("EllieBot.Db.Models.GiveawayUser", b =>
@ -2866,20 +2819,11 @@ namespace EllieBot.Migrations.Sqlite
b.Navigation("LogSetting"); b.Navigation("LogSetting");
}); });
modelBuilder.Entity("EllieBot.Db.Models.MutedUserId", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("MutedUsers")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.Permissionv2", b => modelBuilder.Entity("EllieBot.Db.Models.Permissionv2", b =>
{ {
b.HasOne("EllieBot.Db.Models.GuildConfig", null) b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("Permissions") .WithMany("Permissions")
.HasForeignKey("GuildConfigId") .HasForeignKey("GuildConfigId");
.OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity("EllieBot.Db.Models.PlaylistSong", b => modelBuilder.Entity("EllieBot.Db.Models.PlaylistSong", b =>
@ -2899,14 +2843,6 @@ namespace EllieBot.Migrations.Sqlite
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("EllieBot.Db.Models.ShopEntry", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("ShopEntries")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.ShopEntryItem", b => modelBuilder.Entity("EllieBot.Db.Models.ShopEntryItem", b =>
{ {
b.HasOne("EllieBot.Db.Models.ShopEntry", null) b.HasOne("EllieBot.Db.Models.ShopEntry", null)
@ -2915,22 +2851,6 @@ namespace EllieBot.Migrations.Sqlite
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity("EllieBot.Db.Models.SlowmodeIgnoredRole", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("SlowmodeIgnoredRoles")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.SlowmodeIgnoredUser", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("SlowmodeIgnoredUsers")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.StreamRoleBlacklistedUser", b => modelBuilder.Entity("EllieBot.Db.Models.StreamRoleBlacklistedUser", b =>
{ {
b.HasOne("EllieBot.Db.Models.StreamRoleSettings", "StreamRoleSettings") b.HasOne("EllieBot.Db.Models.StreamRoleSettings", "StreamRoleSettings")
@ -2942,17 +2862,6 @@ namespace EllieBot.Migrations.Sqlite
b.Navigation("StreamRoleSettings"); b.Navigation("StreamRoleSettings");
}); });
modelBuilder.Entity("EllieBot.Db.Models.StreamRoleSettings", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", "GuildConfig")
.WithOne("StreamRole")
.HasForeignKey("EllieBot.Db.Models.StreamRoleSettings", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("GuildConfig");
});
modelBuilder.Entity("EllieBot.Db.Models.StreamRoleWhitelistedUser", b => modelBuilder.Entity("EllieBot.Db.Models.StreamRoleWhitelistedUser", b =>
{ {
b.HasOne("EllieBot.Db.Models.StreamRoleSettings", "StreamRoleSettings") b.HasOne("EllieBot.Db.Models.StreamRoleSettings", "StreamRoleSettings")
@ -2972,38 +2881,6 @@ namespace EllieBot.Migrations.Sqlite
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
}); });
modelBuilder.Entity("EllieBot.Db.Models.UnbanTimer", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("UnbanTimer")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.UnmuteTimer", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("UnmuteTimers")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.UnroleTimer", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("UnroleTimer")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.VcRoleInfo", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", null)
.WithMany("VcRoleInfos")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EllieBot.Db.Models.WaifuInfo", b => modelBuilder.Entity("EllieBot.Db.Models.WaifuInfo", b =>
{ {
b.HasOne("EllieBot.Db.Models.DiscordUser", "Affinity") b.HasOne("EllieBot.Db.Models.DiscordUser", "Affinity")
@ -3061,35 +2938,20 @@ namespace EllieBot.Migrations.Sqlite
modelBuilder.Entity("EllieBot.Db.Models.XpCurrencyReward", b => modelBuilder.Entity("EllieBot.Db.Models.XpCurrencyReward", b =>
{ {
b.HasOne("EllieBot.Db.Models.XpSettings", "XpSettings") b.HasOne("EllieBot.Db.Models.XpSettings", null)
.WithMany("CurrencyRewards") .WithMany("CurrencyRewards")
.HasForeignKey("XpSettingsId") .HasForeignKey("XpSettingsId")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.Navigation("XpSettings");
}); });
modelBuilder.Entity("EllieBot.Db.Models.XpRoleReward", b => modelBuilder.Entity("EllieBot.Db.Models.XpRoleReward", b =>
{ {
b.HasOne("EllieBot.Db.Models.XpSettings", "XpSettings") b.HasOne("EllieBot.Db.Models.XpSettings", null)
.WithMany("RoleRewards") .WithMany("RoleRewards")
.HasForeignKey("XpSettingsId") .HasForeignKey("XpSettingsId")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.Navigation("XpSettings");
});
modelBuilder.Entity("EllieBot.Db.Models.XpSettings", b =>
{
b.HasOne("EllieBot.Db.Models.GuildConfig", "GuildConfig")
.WithOne("XpSettings")
.HasForeignKey("EllieBot.Db.Models.XpSettings", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("GuildConfig");
}); });
modelBuilder.Entity("EllieBot.Db.Models.AntiSpamSetting", b => modelBuilder.Entity("EllieBot.Db.Models.AntiSpamSetting", b =>
@ -3123,20 +2985,11 @@ namespace EllieBot.Migrations.Sqlite
modelBuilder.Entity("EllieBot.Db.Models.GuildConfig", b => modelBuilder.Entity("EllieBot.Db.Models.GuildConfig", b =>
{ {
b.Navigation("AntiAltSetting"); b.Navigation("Permissions");
});
b.Navigation("AntiRaidSetting");
b.Navigation("AntiSpamSetting");
b.Navigation("CommandAliases");
b.Navigation("CommandCooldowns");
b.Navigation("DelMsgOnCmdChannels");
b.Navigation("FeedSubs");
modelBuilder.Entity("EllieBot.Db.Models.GuildFilterConfig", b =>
{
b.Navigation("FilterInvitesChannelIds"); b.Navigation("FilterInvitesChannelIds");
b.Navigation("FilterLinksChannelIds"); b.Navigation("FilterLinksChannelIds");
@ -3144,32 +2997,6 @@ namespace EllieBot.Migrations.Sqlite
b.Navigation("FilterWordsChannelIds"); b.Navigation("FilterWordsChannelIds");
b.Navigation("FilteredWords"); b.Navigation("FilteredWords");
b.Navigation("FollowedStreams");
b.Navigation("GenerateCurrencyChannelIds");
b.Navigation("MutedUsers");
b.Navigation("Permissions");
b.Navigation("ShopEntries");
b.Navigation("SlowmodeIgnoredRoles");
b.Navigation("SlowmodeIgnoredUsers");
b.Navigation("StreamRole");
b.Navigation("UnbanTimer");
b.Navigation("UnmuteTimers");
b.Navigation("UnroleTimer");
b.Navigation("VcRoleInfos");
b.Navigation("XpSettings");
}); });
modelBuilder.Entity("EllieBot.Db.Models.LogSetting", b => modelBuilder.Entity("EllieBot.Db.Models.LogSetting", b =>

File diff suppressed because it is too large Load diff

View file

@ -94,7 +94,7 @@ public partial class Administration : EllieModule<AdministrationService>
public async Task Delmsgoncmd(List _) public async Task Delmsgoncmd(List _)
{ {
var guild = (SocketGuild)ctx.Guild; var guild = (SocketGuild)ctx.Guild;
var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id); var (enabled, channels) = await _service.GetDelMsgOnCmdData(ctx.Guild.Id);
var embed = CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
@ -124,14 +124,13 @@ public partial class Administration : EllieModule<AdministrationService>
[Priority(1)] [Priority(1)]
public async Task Delmsgoncmd(Server _ = Server.Server) public async Task Delmsgoncmd(Server _ = Server.Server)
{ {
if (_service.ToggleDeleteMessageOnCommand(ctx.Guild.Id)) var enabled = await _service.ToggleDeleteMessageOnCommand(ctx.Guild.Id);
if (enabled)
{ {
_service.DeleteMessagesOnCommand.Add(ctx.Guild.Id);
await Response().Confirm(strs.delmsg_on).SendAsync(); await Response().Confirm(strs.delmsg_on).SendAsync();
} }
else else
{ {
_service.DeleteMessagesOnCommand.TryRemove(ctx.Guild.Id);
await Response().Confirm(strs.delmsg_off).SendAsync(); await Response().Confirm(strs.delmsg_off).SendAsync();
} }
} }

View file

@ -1,4 +1,6 @@
#nullable disable #nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using EllieBot.Modules.Administration._common.results; using EllieBot.Modules.Administration._common.results;
@ -7,95 +9,130 @@ namespace EllieBot.Modules.Administration;
public class AdministrationService : IEService public class AdministrationService : IEService
{ {
public ConcurrentHashSet<ulong> DeleteMessagesOnCommand { get; } private ConcurrentHashSet<ulong> deleteMessagesOnCommand;
public ConcurrentDictionary<ulong, bool> DeleteMessagesOnCommandChannels { get; } private ConcurrentDictionary<ulong, bool> delMsgOnCmdChannels;
private readonly DbService _db; private readonly DbService _db;
private readonly IReplacementService _repSvc; private readonly IReplacementService _repSvc;
private readonly ILogCommandService _logService; private readonly ILogCommandService _logService;
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
private readonly ShardData _shardData;
private readonly CommandHandler _cmdHandler;
public AdministrationService( public AdministrationService(
IBot bot,
CommandHandler cmdHandler,
DbService db, DbService db,
IReplacementService repSvc, IReplacementService repSvc,
ILogCommandService logService, ILogCommandService logService,
IHttpClientFactory factory) IHttpClientFactory factory,
ShardData shardData,
CommandHandler cmdHandler)
{ {
_db = db; _db = db;
_shardData = shardData;
_repSvc = repSvc; _repSvc = repSvc;
_logService = logService; _logService = logService;
_httpFactory = factory; _httpFactory = factory;
_cmdHandler = cmdHandler;
DeleteMessagesOnCommand = new(bot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
DeleteMessagesOnCommandChannels = new(bot.AllGuildConfigs.SelectMany(x => x.DelMsgOnCmdChannels)
.ToDictionary(x => x.ChannelId, x => x.State)
.ToConcurrent());
cmdHandler.CommandExecuted += DelMsgOnCmd_Handler;
} }
public (bool DelMsgOnCmd, IEnumerable<DelMsgOnCmdChannel> channels) GetDelMsgOnCmdData(ulong guildId) public async Task ReadyAsync()
{
await using var uow = _db.GetDbContext();
deleteMessagesOnCommand = new(await uow.GetTable<GuildConfig>()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId) && x.DeleteMessageOnCommand)
.Select(x => x.GuildId)
.ToListAsyncLinqToDB());
delMsgOnCmdChannels = (await uow.GetTable<DelMsgOnCmdChannel>()
.Where(x => deleteMessagesOnCommand.Contains(x.GuildId))
.ToDictionaryAsyncLinqToDB(x => x.ChannelId, x => x.State))
.ToConcurrent();
_cmdHandler.CommandExecuted += DelMsgOnCmd_Handler;
}
public async Task<(bool DelMsgOnCmd, IEnumerable<DelMsgOnCmdChannel> channels)> GetDelMsgOnCmdData(ulong guildId)
{ {
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set.Include(x => x.DelMsgOnCmdChannels));
return (conf.DeleteMessageOnCommand, conf.DelMsgOnCmdChannels); var conf = await uow.GetTable<GuildConfig>()
.Where(x => x.GuildId == guildId)
.Select(x => x.DeleteMessageOnCommand)
.FirstOrDefaultAsyncLinqToDB();
var channels = await uow.GetTable<DelMsgOnCmdChannel>()
.Where(x => x.GuildId == guildId)
.ToListAsyncLinqToDB();
return (conf, channels);
} }
private Task DelMsgOnCmd_Handler(IUserMessage msg, CommandInfo cmd) private Task DelMsgOnCmd_Handler(IUserMessage msg, CommandInfo cmd)
{ {
if (msg.Channel is not ITextChannel channel) if (msg.Channel is not ITextChannel channel)
return Task.CompletedTask; return Task.CompletedTask;
_ = Task.Run(async () => _ = Task.Run(async () =>
{ {
//wat ?! //wat ?!
if (DeleteMessagesOnCommandChannels.TryGetValue(channel.Id, out var state)) if (delMsgOnCmdChannels.TryGetValue(channel.Id, out var state))
{ {
if (state && cmd.Name != "prune" && cmd.Name != "pick") if (state && cmd.Name != "prune" && cmd.Name != "pick")
{ {
_logService.AddDeleteIgnore(msg.Id); _logService.AddDeleteIgnore(msg.Id);
try { await msg.DeleteAsync(); } try
{ await msg.DeleteAsync(); }
catch { } catch { }
} }
//if state is false, that means do not do it //if state is false, that means do not do it
} }
else if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune" && cmd.Name != "pick") else if (deleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune" && cmd.Name != "pick")
{ {
_logService.AddDeleteIgnore(msg.Id); _logService.AddDeleteIgnore(msg.Id);
try { await msg.DeleteAsync(); } try
{ await msg.DeleteAsync(); }
catch { } catch { }
} }
}); });
return Task.CompletedTask; return Task.CompletedTask;
} }
public bool ToggleDeleteMessageOnCommand(ulong guildId) public async Task<bool> ToggleDeleteMessageOnCommand(ulong guildId)
{ {
bool enabled;
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set);
enabled = conf.DeleteMessageOnCommand = !conf.DeleteMessageOnCommand;
uow.SaveChanges(); var conf = await uow.GetTable<GuildConfig>()
return enabled; .Where(x => x.GuildId == guildId)
.UpdateWithOutputAsync(x => new()
{
DeleteMessageOnCommand = !x.DeleteMessageOnCommand
});
if (conf.Length == 0)
return false;
var val = conf[0].Inserted.DeleteMessageOnCommand;
if (val)
deleteMessagesOnCommand.Add(guildId);
else
deleteMessagesOnCommand.TryRemove(guildId);
return val;
} }
public async Task SetDelMsgOnCmdState(ulong guildId, ulong chId, Administration.State newState) public async Task SetDelMsgOnCmdState(ulong guildId, ulong chId, Administration.State newState)
{ {
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var conf = uow.GuildConfigsForId(guildId, set => set.Include(x => x.DelMsgOnCmdChannels)); var old = await uow.GetTable<DelMsgOnCmdChannel>()
.Where(x => x.GuildId == guildId && x.ChannelId == chId)
.FirstOrDefaultAsyncLinqToDB();
var old = conf.DelMsgOnCmdChannels.FirstOrDefault(x => x.ChannelId == chId);
if (newState == Administration.State.Inherit) if (newState == Administration.State.Inherit)
{ {
if (old is not null) if (old is not null)
{ {
conf.DelMsgOnCmdChannels.Remove(old);
uow.Remove(old); uow.Remove(old);
} }
} }
@ -103,15 +140,17 @@ public class AdministrationService : IEService
{ {
if (old is null) if (old is null)
{ {
old = new() old = new DelMsgOnCmdChannel
{ {
ChannelId = chId GuildId = guildId,
ChannelId = chId,
State = newState == Administration.State.Enable
}; };
conf.DelMsgOnCmdChannels.Add(old); uow.Add(old);
} }
old.State = newState == Administration.State.Enable; old.State = newState == Administration.State.Enable;
DeleteMessagesOnCommandChannels[chId] = newState == Administration.State.Enable; delMsgOnCmdChannels[chId] = newState == Administration.State.Enable;
} }
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();
@ -121,9 +160,13 @@ public class AdministrationService : IEService
{ {
} }
else if (newState == Administration.State.Enable) else if (newState == Administration.State.Enable)
DeleteMessagesOnCommandChannels[chId] = true; {
delMsgOnCmdChannels[chId] = true;
}
else else
DeleteMessagesOnCommandChannels.TryRemove(chId, out _); {
delMsgOnCmdChannels.TryRemove(chId, out _);
}
} }
public async Task DeafenUsers(bool value, params IGuildUser[] users) public async Task DeafenUsers(bool value, params IGuildUser[] users)
@ -164,20 +207,22 @@ public class AdministrationService : IEService
public async Task<SetServerBannerResult> SetServerBannerAsync(IGuild guild, string img) public async Task<SetServerBannerResult> SetServerBannerAsync(IGuild guild, string img)
{ {
if (!IsValidUri(img)) return SetServerBannerResult.InvalidURL; if (!IsValidUri(img))
return SetServerBannerResult.InvalidURL;
var uri = new Uri(img); var uri = new Uri(img);
using var http = _httpFactory.CreateClient(); using var http = _httpFactory.CreateClient();
using var sr = await http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead); using var sr = await http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
if (!sr.IsImage()) return SetServerBannerResult.InvalidFileType; if (!sr.IsImage())
return SetServerBannerResult.InvalidFileType;
if (sr.GetContentLength() > 8.Megabytes()) if (sr.GetContentLength() > 8.Megabytes())
{ {
return SetServerBannerResult.Toolarge; return SetServerBannerResult.Toolarge;
} }
await using var imageStream = await sr.Content.ReadAsStreamAsync(); await using var imageStream = await sr.Content.ReadAsStreamAsync();
await guild.ModifyAsync(x => x.Banner = new Image(imageStream)); await guild.ModifyAsync(x => x.Banner = new Image(imageStream));
@ -186,20 +231,22 @@ public class AdministrationService : IEService
public async Task<SetServerIconResult> SetServerIconAsync(IGuild guild, string img) public async Task<SetServerIconResult> SetServerIconAsync(IGuild guild, string img)
{ {
if (!IsValidUri(img)) return SetServerIconResult.InvalidURL; if (!IsValidUri(img))
return SetServerIconResult.InvalidURL;
var uri = new Uri(img); var uri = new Uri(img);
using var http = _httpFactory.CreateClient(); using var http = _httpFactory.CreateClient();
using var sr = await http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead); using var sr = await http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
if (!sr.IsImage()) return SetServerIconResult.InvalidFileType; if (!sr.IsImage())
return SetServerIconResult.InvalidFileType;
await using var imageStream = await sr.Content.ReadAsStreamAsync(); await using var imageStream = await sr.Content.ReadAsStreamAsync();
await guild.ModifyAsync(x => x.Icon = new Image(imageStream)); await guild.ModifyAsync(x => x.Icon = new Image(imageStream));
return SetServerIconResult.Success; return SetServerIconResult.Success;
} }
private bool IsValidUri(string img) => !string.IsNullOrWhiteSpace(img) && Uri.IsWellFormedUriString(img, UriKind.Absolute); private bool IsValidUri(string img) => !string.IsNullOrWhiteSpace(img) && Uri.IsWellFormedUriString(img, UriKind.Absolute);
} }

View file

@ -3,6 +3,7 @@ using EllieBot.Db.Models;
using System.Net; using System.Net;
using System.Threading.Channels; using System.Threading.Channels;
using LinqToDB; using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace EllieBot.Modules.Administration.Services; namespace EllieBot.Modules.Administration.Services;
@ -11,9 +12,10 @@ public sealed class AutoAssignRoleService : IEService
{ {
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly DbService _db; private readonly DbService _db;
private readonly ShardData _shardData;
//guildid/roleid //guildid/roleid
private readonly ConcurrentDictionary<ulong, IReadOnlyList<ulong>> _autoAssignableRoles; private ConcurrentDictionary<ulong, IReadOnlyList<ulong>> _autoAssignableRoles;
private readonly Channel<SocketGuildUser> _assignQueue = Channel.CreateBounded<SocketGuildUser>( private readonly Channel<SocketGuildUser> _assignQueue = Channel.CreateBounded<SocketGuildUser>(
new BoundedChannelOptions(100) new BoundedChannelOptions(100)
@ -23,63 +25,78 @@ public sealed class AutoAssignRoleService : IEService
SingleWriter = false SingleWriter = false
}); });
public AutoAssignRoleService(DiscordSocketClient client, IBot bot, DbService db) public AutoAssignRoleService(
DiscordSocketClient client,
IBot bot,
DbService db,
ShardData shardData)
{ {
_client = client; _client = client;
_shardData = shardData;
_db = db; _db = db;
}
_autoAssignableRoles = bot.AllGuildConfigs.Where(x => !string.IsNullOrWhiteSpace(x.AutoAssignRoleIds)) public async Task OnReadyAsync()
.ToDictionary<GuildConfig, ulong, IReadOnlyList<ulong>>(k => k.GuildId, {
v => v.GetAutoAssignableRoles()) await using (var uow = _db.GetDbContext())
.ToConcurrent();
_ = Task.Run(async () =>
{ {
while (true) _autoAssignableRoles = await uow.GetTable<GuildConfig>()
.Where(x => Queries.GuildOnShard(x.GuildId,
_shardData.TotalShards,
_shardData.ShardId))
.Where(x => x.AutoAssignRoleIds != null)
.ToListAsyncLinqToDB()
.Fmap(x => x
.ToDictionary<GuildConfig, ulong, IReadOnlyList<ulong>>(
k => k.GuildId,
v => v.GetAutoAssignableRoles())
.ToConcurrent());
}
_client.UserJoined += OnClientOnUserJoined;
_client.RoleDeleted += OnClientRoleDeleted;
while (true)
{
var user = await _assignQueue.Reader.ReadAsync();
if (!_autoAssignableRoles.TryGetValue(user.Guild.Id, out var savedRoleIds))
continue;
try
{ {
var user = await _assignQueue.Reader.ReadAsync(); var roleIds = savedRoleIds.Select(roleId => user.Guild.GetRole(roleId))
if (!_autoAssignableRoles.TryGetValue(user.Guild.Id, out var savedRoleIds)) .Where(x => x is not null)
continue; .ToList();
try if (roleIds.Any())
{ {
var roleIds = savedRoleIds.Select(roleId => user.Guild.GetRole(roleId)) await user.AddRolesAsync(roleIds);
.Where(x => x is not null) await Task.Delay(250);
.ToList();
if (roleIds.Any())
{
await user.AddRolesAsync(roleIds);
await Task.Delay(250);
}
else
{
Log.Warning(
"Disabled 'Auto assign role' feature on {GuildName} [{GuildId}] server the roles dont exist",
user.Guild.Name,
user.Guild.Id);
await DisableAarAsync(user.Guild.Id);
}
} }
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden) else
{ {
Log.Warning( Log.Warning(
"Disabled 'Auto assign role' feature on {GuildName} [{GuildId}] server because I don't have role management permissions", "Disabled 'Auto assign role' feature on {GuildName} [{GuildId}] server the roles dont exist",
user.Guild.Name, user.Guild.Name,
user.Guild.Id); user.Guild.Id);
await DisableAarAsync(user.Guild.Id); await DisableAarAsync(user.Guild.Id);
} }
catch (Exception ex)
{
Log.Warning(ex, "Error in aar. Probably one of the roles doesn't exist");
}
} }
}); catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden)
{
Log.Warning(
"Disabled 'Auto assign role' feature on {GuildName} [{GuildId}] server because I don't have role management permissions",
user.Guild.Name,
user.Guild.Id);
_client.UserJoined += OnClientOnUserJoined; await DisableAarAsync(user.Guild.Id);
_client.RoleDeleted += OnClientRoleDeleted; }
catch (Exception ex)
{
Log.Warning(ex, "Error in aar. Probably one of the roles doesn't exist");
}
}
} }
private async Task OnClientRoleDeleted(SocketRole role) private async Task OnClientRoleDeleted(SocketRole role)
@ -117,7 +134,8 @@ public sealed class AutoAssignRoleService : IEService
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
await uow.Set<GuildConfig>().AsNoTracking() await uow.Set<GuildConfig>()
.AsNoTracking()
.Where(x => x.GuildId == guildId) .Where(x => x.GuildId == guildId)
.UpdateAsync(_ => new() .UpdateAsync(_ => new()
{ {

View file

@ -42,7 +42,7 @@ public class AutoPublishService : IExecNoCommand, IReadyExecutor, IEService
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var items = await ctx.GetTable<AutoPublishChannel>() var items = await ctx.GetTable<AutoPublishChannel>()
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, creds.TotalShards, _client.ShardId)) .Where(x => Queries.GuildOnShard(x.GuildId, creds.TotalShards, _client.ShardId))
.ToListAsyncLinqToDB(); .ToListAsyncLinqToDB();
_enabled = items _enabled = items

View file

@ -25,10 +25,11 @@ public partial class Administration
var id = _service.ToggleGameVoiceChannel(ctx.Guild.Id, vch.Id); var id = _service.ToggleGameVoiceChannel(ctx.Guild.Id, vch.Id);
if (id is null) if (id is null)
{
await Response().Confirm(strs.gvc_disabled).SendAsync(); await Response().Confirm(strs.gvc_disabled).SendAsync();
}
else else
{ {
_service.GameVoiceChannels.Add(vch.Id);
await Response().Confirm(strs.gvc_enabled(Format.Bold(vch.Name))).SendAsync(); await Response().Confirm(strs.gvc_enabled(Format.Bold(vch.Name))).SendAsync();
} }
} }

View file

@ -1,9 +1,12 @@
#nullable disable #nullable disable
using LinqToDB.EntityFrameworkCore;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Administration.Services; namespace EllieBot.Modules.Administration.Services;
public class GameVoiceChannelService : IEService public class GameVoiceChannelService : IEService
{ {
public ConcurrentHashSet<ulong> GameVoiceChannels { get; } private ConcurrentHashSet<ulong> _gameVoiceChannels = new();
private readonly DbService _db; private readonly DbService _db;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
@ -12,10 +15,16 @@ public class GameVoiceChannelService : IEService
{ {
_db = db; _db = db;
_client = client; _client = client;
}
GameVoiceChannels = new(bot.AllGuildConfigs public async Task InitializeAsync()
.Where(gc => gc.GameVoiceChannel is not null) {
.Select(gc => gc.GameVoiceChannel!.Value)); await using var uow = _db.GetDbContext();
_gameVoiceChannels = new(await uow.Set<GuildConfig>()
.Where(gc => gc.GameVoiceChannel != null)
.Select(gc => gc.GameVoiceChannel)
.ToListAsyncLinqToDB()
.Fmap(u => new ConcurrentHashSet<ulong>(u.Select(x => x.Value))));
_client.UserVoiceStateUpdated += OnUserVoiceStateUpdated; _client.UserVoiceStateUpdated += OnUserVoiceStateUpdated;
_client.PresenceUpdated += OnPresenceUpdate; _client.PresenceUpdated += OnPresenceUpdate;
@ -32,14 +41,14 @@ public class GameVoiceChannelService : IEService
// if the user is in the voice channel and that voice channel is gvc // if the user is in the voice channel and that voice channel is gvc
if (newUser.VoiceChannel is not { } vc if (newUser.VoiceChannel is not { } vc
|| !GameVoiceChannels.Contains(vc.Id)) || !_gameVoiceChannels.Contains(vc.Id))
return; return;
//if the activity has changed, and is a playi1ng activity //if the activity has changed, and is a playi1ng activity
foreach (var activity in after.Activities) foreach (var activity in after.Activities)
{ {
if (activity is { Type: ActivityType.Playing }) if (activity is { Type: ActivityType.Playing })
//trigger gvc //trigger gvc
{ {
if (await TriggerGvc(newUser, activity.Name)) if (await TriggerGvc(newUser, activity.Name))
return; return;
@ -58,18 +67,19 @@ public class GameVoiceChannelService : IEService
{ {
ulong? id; ulong? id;
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set); var gc = uow.GuildConfigsForId(guildId);
if (gc.GameVoiceChannel == vchId) if (gc.GameVoiceChannel == vchId)
{ {
GameVoiceChannels.TryRemove(vchId); _gameVoiceChannels.TryRemove(vchId);
id = gc.GameVoiceChannel = null; id = gc.GameVoiceChannel = null;
} }
else else
{ {
if (gc.GameVoiceChannel is not null) if (gc.GameVoiceChannel is not null)
GameVoiceChannels.TryRemove(gc.GameVoiceChannel.Value); _gameVoiceChannels.TryRemove(gc.GameVoiceChannel.Value);
GameVoiceChannels.Add(vchId); _gameVoiceChannels.Add(vchId);
id = gc.GameVoiceChannel = vchId; id = gc.GameVoiceChannel = vchId;
} }
@ -89,7 +99,7 @@ public class GameVoiceChannelService : IEService
if (newState.VoiceChannel is null) if (newState.VoiceChannel is null)
return; return;
if (!GameVoiceChannels.Contains(newState.VoiceChannel.Id)) if (!_gameVoiceChannels.Contains(newState.VoiceChannel.Id))
return; return;
foreach (var game in gUser.Activities.Select(x => x.Name)) foreach (var game in gUser.Activities.Select(x => x.Name))

View file

@ -1,5 +1,7 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore; using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models; using EllieBot.Db.Models;
namespace EllieBot.Modules.Administration.Services; namespace EllieBot.Modules.Administration.Services;
@ -11,7 +13,7 @@ public enum MuteType
All All
} }
public class MuteService : IEService public class MuteService : IEService, IReadyExecutor
{ {
public enum TimerType { Mute, Ban, AddRole } public enum TimerType { Mute, Ban, AddRole }
@ -23,90 +25,23 @@ public class MuteService : IEService
public event Action<IGuildUser, IUser, MuteType, string> UserMuted = delegate { }; public event Action<IGuildUser, IUser, MuteType, string> UserMuted = delegate { };
public event Action<IGuildUser, IUser, MuteType, string> UserUnmuted = delegate { }; public event Action<IGuildUser, IUser, MuteType, string> UserUnmuted = delegate { };
public ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } private ConcurrentDictionary<ulong, string> _guildMuteRoles = new();
public ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } private ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _mutedUsers = new();
public ConcurrentDictionary<ulong, ConcurrentDictionary<(ulong, TimerType), Timer>> UnTimers { get; } = new(); public ConcurrentDictionary<ulong, ConcurrentDictionary<(ulong, TimerType), Timer>> UnTimers { get; } = new();
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly DbService _db; private readonly DbService _db;
private readonly IMessageSenderService _sender; private readonly IMessageSenderService _sender;
private readonly ShardData _shardData;
public MuteService(DiscordSocketClient client, DbService db, IMessageSenderService sender) public MuteService(DiscordSocketClient client, DbService db, IMessageSenderService sender, ShardData shardData)
{ {
_client = client; _client = client;
_db = db; _db = db;
_sender = sender; _sender = sender;
_shardData = shardData;
using (var uow = db.GetDbContext())
{
var guildIds = client.Guilds.Select(x => x.Id).ToList();
var configs = uow.Set<GuildConfig>()
.AsNoTracking()
.AsSplitQuery()
.Include(x => x.MutedUsers)
.Include(x => x.UnbanTimer)
.Include(x => x.UnmuteTimers)
.Include(x => x.UnroleTimer)
.Where(x => guildIds.Contains(x.GuildId))
.ToList();
GuildMuteRoles = configs.Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
.ToDictionary(c => c.GuildId, c => c.MuteRoleName)
.ToConcurrent();
MutedUsers = new(configs.ToDictionary(k => k.GuildId,
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))));
var max = TimeSpan.FromDays(49);
foreach (var conf in configs)
{
foreach (var x in conf.UnmuteTimers)
{
TimeSpan after;
if (x.UnmuteAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
after = TimeSpan.FromMinutes(2);
else
{
var unmute = x.UnmuteAt - DateTime.UtcNow;
after = unmute > max ? max : unmute;
}
StartUn_Timer(conf.GuildId, x.UserId, after, TimerType.Mute);
}
foreach (var x in conf.UnbanTimer)
{
TimeSpan after;
if (x.UnbanAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
after = TimeSpan.FromMinutes(2);
else
{
var unban = x.UnbanAt - DateTime.UtcNow;
after = unban > max ? max : unban;
}
StartUn_Timer(conf.GuildId, x.UserId, after, TimerType.Ban);
}
foreach (var x in conf.UnroleTimer)
{
TimeSpan after;
if (x.UnbanAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
after = TimeSpan.FromMinutes(2);
else
{
var unban = x.UnbanAt - DateTime.UtcNow;
after = unban > max ? max : unban;
}
StartUn_Timer(conf.GuildId, x.UserId, after, TimerType.AddRole, x.RoleId);
}
}
_client.UserJoined += Client_UserJoined;
}
UserMuted += OnUserMuted; UserMuted += OnUserMuted;
UserUnmuted += OnUserUnmuted; UserUnmuted += OnUserUnmuted;
@ -123,10 +58,10 @@ public class MuteService : IEService
_ = Task.Run(() => _sender.Response(user) _ = Task.Run(() => _sender.Response(user)
.Embed(_sender.CreateEmbed(user?.GuildId) .Embed(_sender.CreateEmbed(user?.GuildId)
.WithDescription($"You've been muted in {user.Guild} server") .WithDescription($"You've been muted in {user.Guild} server")
.AddField("Mute Type", type.ToString()) .AddField("Mute Type", type.ToString())
.AddField("Moderator", mod.ToString()) .AddField("Moderator", mod.ToString())
.AddField("Reason", reason)) .AddField("Reason", reason))
.SendAsync()); .SendAsync());
} }
@ -141,10 +76,10 @@ public class MuteService : IEService
_ = Task.Run(() => _sender.Response(user) _ = Task.Run(() => _sender.Response(user)
.Embed(_sender.CreateEmbed(user.GuildId) .Embed(_sender.CreateEmbed(user.GuildId)
.WithDescription($"You've been unmuted in {user.Guild} server") .WithDescription($"You've been unmuted in {user.Guild} server")
.AddField("Unmute Type", type.ToString()) .AddField("Unmute Type", type.ToString())
.AddField("Moderator", mod.ToString()) .AddField("Moderator", mod.ToString())
.AddField("Reason", reason)) .AddField("Reason", reason))
.SendAsync()); .SendAsync());
} }
@ -152,7 +87,7 @@ public class MuteService : IEService
{ {
try try
{ {
MutedUsers.TryGetValue(usr.Guild.Id, out var muted); _mutedUsers.TryGetValue(usr.Guild.Id, out var muted);
if (muted is null || !muted.Contains(usr.Id)) if (muted is null || !muted.Contains(usr.Id))
return Task.CompletedTask; return Task.CompletedTask;
@ -169,9 +104,11 @@ public class MuteService : IEService
public async Task SetMuteRoleAsync(ulong guildId, string name) public async Task SetMuteRoleAsync(ulong guildId, string name)
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var config = uow.GuildConfigsForId(guildId, set => set); var config = uow.GetTable<GuildConfig>()
.Where(x => x.GuildId == guildId)
.FirstOrDefault();
config.MuteRoleName = name; config.MuteRoleName = name;
GuildMuteRoles.AddOrUpdate(guildId, name, (_, _) => name); _guildMuteRoles.AddOrUpdate(guildId, name, (_, _) => name);
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();
} }
@ -183,7 +120,8 @@ public class MuteService : IEService
{ {
if (type == MuteType.All) if (type == MuteType.All)
{ {
try { await usr.ModifyAsync(x => x.Mute = true); } try
{ await usr.ModifyAsync(x => x.Mute = true); }
catch { } catch { }
var muteRole = await GetMuteRole(usr.Guild); var muteRole = await GetMuteRole(usr.Guild);
@ -192,16 +130,23 @@ public class MuteService : IEService
StopTimer(usr.GuildId, usr.Id, TimerType.Mute); StopTimer(usr.GuildId, usr.Id, TimerType.Mute);
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var config = uow.GuildConfigsForId(usr.Guild.Id, await uow.GetTable<MutedUserId>()
set => set.Include(gc => gc.MutedUsers).Include(gc => gc.UnmuteTimers)); .InsertOrUpdateAsync(() => new()
config.MutedUsers.Add(new() {
{ GuildId = usr.GuildId,
UserId = usr.Id UserId = usr.Id
}); },
if (MutedUsers.TryGetValue(usr.Guild.Id, out var muted)) (_) => new()
muted.Add(usr.Id); {
},
() => new()
{
GuildId = usr.GuildId,
UserId = usr.Id
});
config.UnmuteTimers.RemoveWhere(x => x.UserId == usr.Id); if (_mutedUsers.TryGetValue(usr.Guild.Id, out var muted))
muted.Add(usr.Id);
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();
} }
@ -237,29 +182,26 @@ public class MuteService : IEService
StopTimer(guildId, usrId, TimerType.Mute); StopTimer(guildId, usrId, TimerType.Mute);
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var config = uow.GuildConfigsForId(guildId, await uow.GetTable<MutedUserId>()
set => set.Include(gc => gc.MutedUsers).Include(gc => gc.UnmuteTimers)); .Where(x => x.GuildId == guildId && x.UserId == usrId)
var match = new MutedUserId .DeleteAsync();
{
UserId = usrId await uow.GetTable<UnmuteTimer>()
}; .Where(x => x.GuildId == guildId && x.UserId == usrId)
var toRemove = config.MutedUsers.FirstOrDefault(x => x.Equals(match)); .DeleteAsync();
if (toRemove is not null)
uow.Remove(toRemove); if (_mutedUsers.TryGetValue(guildId, out var muted))
if (MutedUsers.TryGetValue(guildId, out var muted))
muted.TryRemove(usrId); muted.TryRemove(usrId);
config.UnmuteTimers.RemoveWhere(x => x.UserId == usrId);
await uow.SaveChangesAsync();
} }
if (usr is not null) if (usr is not null)
{ {
try { await usr.ModifyAsync(x => x.Mute = false); } try
{ await usr.ModifyAsync(x => x.Mute = false); }
catch { } catch { }
try { await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild)); } try
{ await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild)); }
catch catch
{ {
/*ignore*/ /*ignore*/
@ -272,12 +214,9 @@ public class MuteService : IEService
{ {
if (usr is null) if (usr is null)
return; return;
try
{ await usr.ModifyAsync(x => x.Mute = false);
await usr.ModifyAsync(x => x.Mute = false); UserUnmuted(usr, mod, MuteType.Voice, reason);
UserUnmuted(usr, mod, MuteType.Voice, reason);
}
catch { }
} }
else if (type == MuteType.Chat) else if (type == MuteType.Chat)
{ {
@ -292,15 +231,16 @@ public class MuteService : IEService
{ {
ArgumentNullException.ThrowIfNull(guild); ArgumentNullException.ThrowIfNull(guild);
const string defaultMuteRoleName = "ellie-mute"; const string defaultMuteRoleName = "nadeko-mute";
var muteRoleName = GuildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName); var muteRoleName = _guildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName);
var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName); var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName);
if (muteRole is null) if (muteRole is null)
//if it doesn't exist, create it //if it doesn't exist, create it
{ {
try { muteRole = await guild.CreateRoleAsync(muteRoleName, isMentionable: false); } try
{ muteRole = await guild.CreateRoleAsync(muteRoleName, isMentionable: false); }
catch catch
{ {
//if creations fails, maybe the name is not correct, find default one, if doesn't work, create default one //if creations fails, maybe the name is not correct, find default one, if doesn't work, create default one
@ -340,13 +280,14 @@ public class MuteService : IEService
await MuteUser(user, mod, muteType, reason); // mute the user. This will also remove any previous unmute timers await MuteUser(user, mod, muteType, reason); // mute the user. This will also remove any previous unmute timers
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var config = uow.GuildConfigsForId(user.GuildId, set => set.Include(x => x.UnmuteTimers)); var unmuteAt = DateTime.UtcNow + after;
config.UnmuteTimers.Add(new() await uow.GetTable<UnmuteTimer>()
{ .InsertAsync(() => new()
UserId = user.Id, {
UnmuteAt = DateTime.UtcNow + after GuildId = user.GuildId,
}); // add teh unmute timer to the database UserId = user.Id,
uow.SaveChanges(); UnmuteAt = unmuteAt
});
} }
StartUn_Timer(user.GuildId, user.Id, after, TimerType.Mute); // start the timer StartUn_Timer(user.GuildId, user.Id, after, TimerType.Mute); // start the timer
@ -362,39 +303,20 @@ public class MuteService : IEService
await guild.AddBanAsync(userId, pruneDays, reason); await guild.AddBanAsync(userId, pruneDays, reason);
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var config = uow.GuildConfigsForId(guild.Id, set => set.Include(x => x.UnbanTimer)); var unbanAt = DateTime.UtcNow + after;
config.UnbanTimer.Add(new() await uow.GetTable<UnbanTimer>()
{ .InsertAsync(() => new()
UserId = userId, {
UnbanAt = DateTime.UtcNow + after GuildId = guild.Id,
}); // add teh unmute timer to the database UserId = userId,
await uow.SaveChangesAsync(); UnbanAt = unbanAt
});
} }
StartUn_Timer(guild.Id, userId, after, TimerType.Ban); // start the timer StartUn_Timer(guild.Id, userId, after, TimerType.Ban); // start the timer
} }
public async Task TimedRole( // todo unrole timers -> temprole
IGuildUser user,
TimeSpan after,
string reason,
IRole role)
{
await user.AddRoleAsync(role);
await using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(user.GuildId, set => set.Include(x => x.UnroleTimer));
config.UnroleTimer.Add(new()
{
UserId = user.Id,
UnbanAt = DateTime.UtcNow + after,
RoleId = role.Id
}); // add teh unmute timer to the database
uow.SaveChanges();
}
StartUn_Timer(user.GuildId, user.Id, after, TimerType.AddRole, role.Id); // start the timer
}
public void StartUn_Timer( public void StartUn_Timer(
ulong guildId, ulong guildId,
@ -408,56 +330,56 @@ public class MuteService : IEService
//unmute timer to be added //unmute timer to be added
var toAdd = new Timer(async _ => var toAdd = new Timer(async _ =>
{
if (type == TimerType.Ban)
{ {
if (type == TimerType.Ban) try
{ {
try await RemoveTimerFromDb(guildId, userId, type);
{ StopTimer(guildId, userId, type);
RemoveTimerFromDb(guildId, userId, type); var guild = _client.GetGuild(guildId); // load the guild
StopTimer(guildId, userId, type); if (guild is not null)
var guild = _client.GetGuild(guildId); // load the guild await guild.RemoveBanAsync(userId);
if (guild is not null)
await guild.RemoveBanAsync(userId);
}
catch (Exception ex)
{
Log.Warning(ex, "Couldn't unban user {UserId} in guild {GuildId}", userId, guildId);
}
} }
else if (type == TimerType.AddRole) catch (Exception ex)
{ {
try Log.Warning(ex, "Couldn't unban user {UserId} in guild {GuildId}", userId, guildId);
{ }
if (roleId is null) }
return; else if (type == TimerType.AddRole)
{
try
{
if (roleId is null)
return;
RemoveTimerFromDb(guildId, userId, type); await RemoveTimerFromDb(guildId, userId, type);
StopTimer(guildId, userId, type); StopTimer(guildId, userId, type);
var guild = _client.GetGuild(guildId); var guild = _client.GetGuild(guildId);
var user = guild?.GetUser(userId); var user = guild?.GetUser(userId);
var role = guild?.GetRole(roleId.Value); var role = guild?.GetRole(roleId.Value);
if (guild is not null && user is not null && user.Roles.Contains(role)) if (guild is not null && user is not null && user.Roles.Contains(role))
await user.RemoveRoleAsync(role); await user.RemoveRoleAsync(role);
}
catch (Exception ex)
{
Log.Warning(ex, "Couldn't remove role from user {UserId} in guild {GuildId}", userId, guildId);
}
} }
else catch (Exception ex)
{ {
try Log.Warning(ex, "Couldn't remove role from user {UserId} in guild {GuildId}", userId, guildId);
{
// unmute the user, this will also remove the timer from the db
await UnmuteUser(guildId, userId, _client.CurrentUser, reason: "Timed mute expired");
}
catch (Exception ex)
{
RemoveTimerFromDb(guildId, userId, type); // if unmute errored, just remove unmute from db
Log.Warning(ex, "Couldn't unmute user {UserId} in guild {GuildId}", userId, guildId);
}
} }
}, }
else
{
try
{
// unmute the user, this will also remove the timer from the db
await UnmuteUser(guildId, userId, _client.CurrentUser, reason: "Timed mute expired");
}
catch (Exception ex)
{
await RemoveTimerFromDb(guildId, userId, type); // if unmute errored, just remove unmute from db
Log.Warning(ex, "Couldn't unmute user {UserId} in guild {GuildId}", userId, guildId);
}
}
},
null, null,
after, after,
Timeout.InfiniteTimeSpan); Timeout.InfiniteTimeSpan);
@ -481,23 +403,97 @@ public class MuteService : IEService
removed.Change(Timeout.Infinite, Timeout.Infinite); removed.Change(Timeout.Infinite, Timeout.Infinite);
} }
private void RemoveTimerFromDb(ulong guildId, ulong userId, TimerType type) private async Task RemoveTimerFromDb(ulong guildId, ulong userId, TimerType type)
{ {
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
object toDelete; await using var ctx = _db.GetDbContext();
if (type == TimerType.Mute) }
// todo update to new way of tracking expiries
public async Task OnReadyAsync()
{
await using var uow = _db.GetDbContext();
var configs = await uow.Set<GuildConfig>()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.ToListAsyncLinqToDB();
_guildMuteRoles = configs.Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
.ToDictionary(c => c.GuildId, c => c.MuteRoleName)
.ToConcurrent();
_mutedUsers = await uow.GetTable<MutedUserId>()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.ToListAsyncLinqToDB()
.Fmap(x => x.GroupBy(x => x.GuildId)
.ToDictionary(g => g.Key, g => new ConcurrentHashSet<ulong>(g.Select(x => x.UserId)))
.ToConcurrent());
var max = TimeSpan.FromDays(49);
var unmuteTimers = await uow.GetTable<UnmuteTimer>()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.ToListAsyncLinqToDB();
var unbanTimers = await uow.GetTable<UnbanTimer>()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.ToListAsyncLinqToDB();
var unroleTimers = await uow.GetTable<UnroleTimer>()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.ToListAsyncLinqToDB();
foreach (var conf in configs)
{ {
var config = uow.GuildConfigsForId(guildId, set => set.Include(x => x.UnmuteTimers)); foreach (var x in unmuteTimers)
toDelete = config.UnmuteTimers.FirstOrDefault(x => x.UserId == userId); {
} TimeSpan after;
else if (x.UnmuteAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
{ {
var config = uow.GuildConfigsForId(guildId, set => set.Include(x => x.UnbanTimer)); after = TimeSpan.FromMinutes(2);
toDelete = config.UnbanTimer.FirstOrDefault(x => x.UserId == userId); }
else
{
var unmute = x.UnmuteAt - DateTime.UtcNow;
after = unmute > max ? max : unmute;
}
StartUn_Timer(conf.GuildId, x.UserId, after, TimerType.Mute);
}
foreach (var x in unbanTimers)
{
TimeSpan after;
if (x.UnbanAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
{
after = TimeSpan.FromMinutes(2);
}
else
{
var unban = x.UnbanAt - DateTime.UtcNow;
after = unban > max ? max : unban;
}
StartUn_Timer(conf.GuildId, x.UserId, after, TimerType.Ban);
}
foreach (var x in unroleTimers)
{
TimeSpan after;
if (x.UnbanAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
{
after = TimeSpan.FromMinutes(2);
}
else
{
var unban = x.UnbanAt - DateTime.UtcNow;
after = unban > max ? max : unban;
}
StartUn_Timer(conf.GuildId, x.UserId, after, TimerType.AddRole, x.RoleId);
}
} }
if (toDelete is not null) _client.UserJoined += Client_UserJoined;
uow.Remove(toDelete);
uow.SaveChanges();
} }
} }

View file

@ -36,7 +36,7 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
_events = (await uow.GetTable<Notify>() _events = (await uow.GetTable<Notify>()
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, .Where(x => Queries.GuildOnShard(x.GuildId,
_creds.TotalShards, _creds.TotalShards,
_client.ShardId)) _client.ShardId))
.ToListAsyncLinqToDB()) .ToListAsyncLinqToDB())
@ -224,4 +224,4 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
.Where(x => x.GuildId == guildId && x.Type == nType) .Where(x => x.GuildId == guildId && x.Type == nType)
.FirstOrDefaultAsyncLinqToDB(); .FirstOrDefaultAsyncLinqToDB();
} }
} }

View file

@ -15,7 +15,7 @@ public partial class Administration
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public async Task AntiAlt() public async Task AntiAlt()
{ {
if (await _service.TryStopAntiAlt(ctx.Guild.Id)) if (await _service.TryStopAntiAltAsync(ctx.Guild.Id))
{ {
await Response().Confirm(strs.prot_disable("Anti-Alt")).SendAsync(); await Response().Confirm(strs.prot_disable("Anti-Alt")).SendAsync();
return; return;
@ -71,11 +71,15 @@ public partial class Administration
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public Task AntiRaid() public async Task AntiRaid()
{ {
if (_service.TryStopAntiRaid(ctx.Guild.Id)) if (await _service.TryStopAntiRaidAsync(ctx.Guild.Id))
return Response().Confirm(strs.prot_disable("Anti-Raid")).SendAsync(); {
return Response().Pending(strs.protection_not_running("Anti-Raid")).SendAsync(); await Response().Confirm(strs.prot_disable("Anti-Raid")).SendAsync();
return;
}
await Response().Pending(strs.protection_not_running("Anti-Raid")).SendAsync();
} }
[Cmd] [Cmd]
@ -147,11 +151,15 @@ public partial class Administration
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public Task AntiSpam() public async Task AntiSpam()
{ {
if (_service.TryStopAntiSpam(ctx.Guild.Id)) if (await _service.TryStopAntiSpamAsync(ctx.Guild.Id))
return Response().Confirm(strs.prot_disable("Anti-Spam")).SendAsync(); {
return Response().Pending(strs.protection_not_running("Anti-Spam")).SendAsync(); await Response().Confirm(strs.prot_disable("Anti-Spam")).SendAsync();
return;
}
await Response().Pending(strs.protection_not_running("Anti-Spam")).SendAsync();
} }
[Cmd] [Cmd]

View file

@ -1,11 +1,15 @@
#nullable disable #nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using EllieBot.Migrations;
using System.Threading.Channels; using System.Threading.Channels;
namespace EllieBot.Modules.Administration.Services; namespace EllieBot.Modules.Administration.Services;
public class ProtectionService : IEService public class ProtectionService : IReadyExecutor, IEService
{ {
public event Func<PunishmentAction, ProtectionType, IGuildUser[], Task> OnAntiProtectionTriggered = delegate public event Func<PunishmentAction, ProtectionType, IGuildUser[], Task> OnAntiProtectionTriggered = delegate
{ {
@ -23,7 +27,7 @@ public class ProtectionService : IEService
private readonly DbService _db; private readonly DbService _db;
private readonly UserPunishService _punishService; private readonly UserPunishService _punishService;
private readonly INotifySubscriber _notifySub; private readonly INotifySubscriber _notifySub;
private readonly ShardData _shardData;
private readonly Channel<PunishQueueItem> _punishUserQueue = private readonly Channel<PunishQueueItem> _punishUserQueue =
Channel.CreateUnbounded<PunishQueueItem>(new() Channel.CreateUnbounded<PunishQueueItem>(new()
{ {
@ -33,41 +37,23 @@ public class ProtectionService : IEService
public ProtectionService( public ProtectionService(
DiscordSocketClient client, DiscordSocketClient client,
IBot bot,
MuteService mute, MuteService mute,
DbService db, DbService db,
UserPunishService punishService, UserPunishService punishService,
INotifySubscriber notifySub) INotifySubscriber notifySub,
ShardData shardData)
{ {
_client = client; _client = client;
_mute = mute; _mute = mute;
_db = db; _db = db;
_punishService = punishService; _punishService = punishService;
_notifySub = notifySub; _notifySub = notifySub;
_shardData = shardData;
var ids = client.GetGuildIds();
using (var uow = db.GetDbContext())
{
var configs = uow.Set<GuildConfig>()
.AsQueryable()
.Include(x => x.AntiRaidSetting)
.Include(x => x.AntiSpamSetting)
.ThenInclude(x => x.IgnoredChannels)
.Include(x => x.AntiAltSetting)
.Where(x => ids.Contains(x.GuildId))
.ToList();
foreach (var gc in configs)
Initialize(gc);
}
_client.MessageReceived += HandleAntiSpam; _client.MessageReceived += HandleAntiSpam;
_client.UserJoined += HandleUserJoined; _client.UserJoined += HandleUserJoined;
bot.JoinedGuild += _bot_JoinedGuild;
_client.LeftGuild += _client_LeftGuild; _client.LeftGuild += _client_LeftGuild;
_ = Task.Run(RunQueue);
} }
private async Task RunQueue() private async Task RunQueue()
@ -103,53 +89,13 @@ public class ProtectionService : IEService
{ {
_ = Task.Run(async () => _ = Task.Run(async () =>
{ {
TryStopAntiRaid(guild.Id); await TryStopAntiRaidAsync(guild.Id);
TryStopAntiSpam(guild.Id); await TryStopAntiSpamAsync(guild.Id);
await TryStopAntiAlt(guild.Id); await TryStopAntiAltAsync(guild.Id);
}); });
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task _bot_JoinedGuild(GuildConfig gc)
{
using var uow = _db.GetDbContext();
var gcWithData = uow.GuildConfigsForId(gc.GuildId,
set => set.Include(x => x.AntiRaidSetting)
.Include(x => x.AntiAltSetting)
.Include(x => x.AntiSpamSetting)
.ThenInclude(x => x.IgnoredChannels));
Initialize(gcWithData);
return Task.CompletedTask;
}
private void Initialize(GuildConfig gc)
{
var raid = gc.AntiRaidSetting;
var spam = gc.AntiSpamSetting;
if (raid is not null)
{
var raidStats = new AntiRaidStats
{
AntiRaidSettings = raid
};
_antiRaidGuilds[gc.GuildId] = raidStats;
}
if (spam is not null)
{
_antiSpamGuilds[gc.GuildId] = new()
{
AntiSpamSettings = spam
};
}
var alt = gc.AntiAltSetting;
if (alt is not null)
_antiAltGuilds[gc.GuildId] = new(alt);
}
private Task HandleUserJoined(SocketGuildUser user) private Task HandleUserJoined(SocketGuildUser user)
{ {
if (user.IsBot) if (user.IsBot)
@ -201,7 +147,8 @@ public class ProtectionService : IEService
await PunishUsers(settings.Action, ProtectionType.Raiding, settings.PunishDuration, null, users); await PunishUsers(settings.Action, ProtectionType.Raiding, settings.PunishDuration, null, users);
await _notifySub.NotifyAsync( await _notifySub.NotifyAsync(
new ProtectionNotifyModel(user.Guild.Id, ProtectionType.Raiding, users[0].Id)); new ProtectionNotifyModel(user.Guild.Id, ProtectionType.Raiding, users[0].Id)
);
} }
await Task.Delay(1000 * stats.AntiRaidSettings.Seconds); await Task.Delay(1000 * stats.AntiRaidSettings.Seconds);
@ -327,39 +274,36 @@ public class ProtectionService : IEService
_antiRaidGuilds.AddOrUpdate(guildId, stats, (_, _) => stats); _antiRaidGuilds.AddOrUpdate(guildId, stats, (_, _) => stats);
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.AntiRaidSetting));
gc.AntiRaidSetting = stats.AntiRaidSettings; // todo finish this
await uow.SaveChangesAsync();
return stats; return stats;
} }
public bool TryStopAntiRaid(ulong guildId) public async Task<bool> TryStopAntiRaidAsync(ulong guildId)
{ {
if (_antiRaidGuilds.TryRemove(guildId, out _)) if (_antiRaidGuilds.TryRemove(guildId, out _))
{ {
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.AntiRaidSetting)); await uow.GetTable<AntiRaidSetting>()
.Where(x => x.GuildId == guildId)
.DeleteAsync();
gc.AntiRaidSetting = null;
uow.SaveChanges();
return true; return true;
} }
return false; return false;
} }
public bool TryStopAntiSpam(ulong guildId) public async Task<bool> TryStopAntiSpamAsync(ulong guildId)
{ {
if (_antiSpamGuilds.TryRemove(guildId, out _)) if (_antiSpamGuilds.TryRemove(guildId, out _))
{ {
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, await uow.GetTable<AntiSpamSetting>()
set => set.Include(x => x.AntiSpamSetting).ThenInclude(x => x.IgnoredChannels)); .Where(x => x.GuildId == guildId)
.DeleteAsync();
gc.AntiSpamSetting = null;
uow.SaveChanges();
return true; return true;
} }
@ -399,19 +343,26 @@ public class ProtectionService : IEService
}); });
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.AntiSpamSetting)); await uow.GetTable<AntiSpamSetting>()
.InsertOrUpdateAsync(() => new()
{
GuildId = guildId,
Action = stats.AntiSpamSettings.Action,
MessageThreshold = stats.AntiSpamSettings.MessageThreshold,
MuteTime = stats.AntiSpamSettings.MuteTime,
RoleId = stats.AntiSpamSettings.RoleId
}, (old) => new()
{
GuildId = guildId,
Action = stats.AntiSpamSettings.Action,
MessageThreshold = stats.AntiSpamSettings.MessageThreshold,
MuteTime = stats.AntiSpamSettings.MuteTime,
RoleId = stats.AntiSpamSettings.RoleId
}, () => new()
{
GuildId = guildId
});
if (gc.AntiSpamSetting is not null)
{
gc.AntiSpamSetting.Action = stats.AntiSpamSettings.Action;
gc.AntiSpamSetting.MessageThreshold = stats.AntiSpamSettings.MessageThreshold;
gc.AntiSpamSetting.MuteTime = stats.AntiSpamSettings.MuteTime;
gc.AntiSpamSetting.RoleId = stats.AntiSpamSettings.RoleId;
}
else
gc.AntiSpamSetting = stats.AntiSpamSettings;
await uow.SaveChangesAsync();
return stats; return stats;
} }
@ -421,15 +372,16 @@ public class ProtectionService : IEService
{ {
ChannelId = channelId ChannelId = channelId
}; };
bool added;
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, var spam = await uow.GetTable<AntiSpamSetting>()
set => set.Include(x => x.AntiSpamSetting).ThenInclude(x => x.IgnoredChannels)); .Include(x => x.IgnoredChannels)
var spam = gc.AntiSpamSetting; .Where(x => x.GuildId == guildId)
.FirstOrDefaultAsyncEF();
if (spam is null) if (spam is null)
return null; return null;
if (spam.IgnoredChannels.Add(obj)) // if adding to db is successful var added = false;
if (!spam.IgnoredChannels.Any(x => x.ChannelId == channelId))
{ {
if (_antiSpamGuilds.TryGetValue(guildId, out var temp)) if (_antiSpamGuilds.TryGetValue(guildId, out var temp))
temp.AntiSpamSettings.IgnoredChannels.Add(obj); // add to local cache temp.AntiSpamSettings.IgnoredChannels.Add(obj); // add to local cache
@ -439,7 +391,9 @@ public class ProtectionService : IEService
else else
{ {
var toRemove = spam.IgnoredChannels.First(x => x.ChannelId == channelId); var toRemove = spam.IgnoredChannels.First(x => x.ChannelId == channelId);
uow.Set<AntiSpamIgnore>().Remove(toRemove); // remove from db
uow.Set<AntiSpamIgnore>().Remove(toRemove);
if (_antiSpamGuilds.TryGetValue(guildId, out var temp)) if (_antiSpamGuilds.TryGetValue(guildId, out var temp))
temp.AntiSpamSettings.IgnoredChannels.Remove(toRemove); // remove from local cache temp.AntiSpamSettings.IgnoredChannels.Remove(toRemove); // remove from local cache
@ -483,28 +437,83 @@ public class ProtectionService : IEService
ulong? roleId = null) ulong? roleId = null)
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.AntiAltSetting));
gc.AntiAltSetting = new() await uow.GetTable<AntiAltSetting>()
.InsertOrUpdateAsync(() => new()
{
GuildId = guildId,
Action = action,
ActionDurationMinutes = actionDurationMinutes,
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
RoleId = roleId
}, _ => new()
{
Action = action,
ActionDurationMinutes = actionDurationMinutes,
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
RoleId = roleId
}, () => new()
{
GuildId = guildId
});
_antiAltGuilds[guildId] = new(new()
{ {
GuildId = guildId,
Action = action, Action = action,
ActionDurationMinutes = actionDurationMinutes, ActionDurationMinutes = actionDurationMinutes,
MinAge = TimeSpan.FromMinutes(minAgeMinutes), MinAge = TimeSpan.FromMinutes(minAgeMinutes),
RoleId = roleId RoleId = roleId
}; });
await uow.SaveChangesAsync();
_antiAltGuilds[guildId] = new(gc.AntiAltSetting);
} }
public async Task<bool> TryStopAntiAlt(ulong guildId) public async Task<bool> TryStopAntiAltAsync(ulong guildId)
{ {
if (!_antiAltGuilds.TryRemove(guildId, out _)) if (!_antiAltGuilds.TryRemove(guildId, out _))
return false; return false;
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.AntiAltSetting)); await uow.GetTable<AntiAltSetting>()
gc.AntiAltSetting = null; .Where(x => x.GuildId == guildId)
await uow.SaveChangesAsync(); .DeleteAsync();
return true; return true;
} }
public async Task OnReadyAsync()
{
await using var uow = _db.GetDbContext();
var configs = await uow.GetTable<AntiAltSetting>()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.ToListAsyncLinqToDB();
foreach (var config in configs)
_antiAltGuilds[config.GuildId] = new(config);
var raidConfigs = await uow.GetTable<AntiRaidSetting>()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.ToListAsyncLinqToDB();
foreach (var config in raidConfigs)
{
_antiRaidGuilds[config.GuildId] = new()
{
AntiRaidSettings = config,
};
}
var spamConfigs = await uow.GetTable<AntiSpamSetting>()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.ToListAsyncLinqToDB();
foreach (var config in spamConfigs)
{
_antiSpamGuilds[config.GuildId] = new()
{
AntiSpamSettings = config,
};
}
await RunQueue();
}
} }

View file

@ -33,7 +33,7 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var reros = await uow.GetTable<ReactionRoleV2>() var reros = await uow.GetTable<ReactionRoleV2>()
.Where( .Where(
x => Linq2DbExpressions.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId)) x => Queries.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId))
.ToListAsyncLinqToDB(); .ToListAsyncLinqToDB();
foreach (var group in reros.GroupBy(x => x.MessageId)) foreach (var group in reros.GroupBy(x => x.MessageId))

View file

@ -221,7 +221,7 @@ public partial class Administration
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
[BotPerm(GuildPerm.ManageRoles)] [BotPerm(GuildPerm.ManageRoles)]
public async Task TempRole(ParsedTimespan timespan, IUser user, [Leftover] IRole role) public async Task TempRole(ParsedTimespan timespan, IGuildUser user, [Leftover] IRole role)
{ {
if (!await CheckRoleHierarchy(role)) if (!await CheckRoleHierarchy(role))
{ {
@ -231,6 +231,7 @@ public partial class Administration
return; return;
} }
await user.AddRoleAsync(role);
await _tempRoleService.AddTempRoleAsync(ctx.Guild.Id, role.Id, user.Id, timespan.Time); await _tempRoleService.AddTempRoleAsync(ctx.Guild.Id, role.Id, user.Id, timespan.Time);

View file

@ -31,7 +31,7 @@ public sealed class StickyRolesService : IEService, IReadyExecutor
_stickyRoles = (await ctx _stickyRoles = (await ctx
.Set<GuildConfig>() .Set<GuildConfig>()
.ToLinqToDBTable() .ToLinqToDBTable()
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, .Where(x => Queries.GuildOnShard(x.GuildId,
_creds.TotalShards, _creds.TotalShards,
_client.ShardId)) _client.ShardId))
.Where(x => x.StickyRoles) .Where(x => x.StickyRoles)

View file

@ -72,7 +72,7 @@ public class TempRoleService : IReadyExecutor, IEService
_tcs = new(TaskCreationOptions.RunContinuationsAsynchronously); _tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
var latest = await _db.GetDbContext() var latest = await _db.GetDbContext()
.GetTable<TempRole>() .GetTable<TempRole>()
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, .Where(x => Queries.GuildOnShard(x.GuildId,
_creds.TotalShards, _creds.TotalShards,
_client.ShardId)) _client.ShardId))
.OrderBy(x => x.ExpiresAt) .OrderBy(x => x.ExpiresAt)
@ -93,7 +93,7 @@ public class TempRoleService : IReadyExecutor, IEService
var deleted = await _db.GetDbContext() var deleted = await _db.GetDbContext()
.GetTable<TempRole>() .GetTable<TempRole>()
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, .Where(x => Queries.GuildOnShard(x.GuildId,
_creds.TotalShards, _creds.TotalShards,
_client.ShardId) _client.ShardId)
&& x.ExpiresAt <= now) && x.ExpiresAt <= now)

View file

@ -216,7 +216,8 @@ public class SelfAssignedRolesService : IEService, IReadyExecutor
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var guilds = await uow.GetTable<SarAutoDelete>() var guilds = await uow.GetTable<SarAutoDelete>()
.Where(x => x.IsEnabled && Linq2DbExpressions.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId)) .Where(x => x.IsEnabled
&& Queries.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId))
.Select(x => x.GuildId) .Select(x => x.GuildId)
.ToListAsyncLinqToDB(); .ToListAsyncLinqToDB();
@ -309,7 +310,6 @@ public sealed class SarAssignerService : IEService, IReadyExecutor
{ {
await _channel.Writer.WriteAsync(item); await _channel.Writer.WriteAsync(item);
} }
} }
public sealed class SarAssignerDataItem public sealed class SarAssignerDataItem

View file

@ -1,4 +1,5 @@
#nullable disable #nullable disable
using LinqToDB.EntityFrameworkCore;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using EllieBot.Common.ModuleBehaviors; using EllieBot.Common.ModuleBehaviors;
@ -6,40 +7,30 @@ namespace EllieBot.Modules.Administration.Services;
public sealed class GuildTimezoneService : ITimezoneService, IReadyExecutor, IEService public sealed class GuildTimezoneService : ITimezoneService, IReadyExecutor, IEService
{ {
private readonly ConcurrentDictionary<ulong, TimeZoneInfo> _timezones; private ConcurrentDictionary<ulong, TimeZoneInfo> _timezones;
private readonly DbService _db; private readonly DbService _db;
private readonly IReplacementPatternStore _repStore; private readonly IReplacementPatternStore _repStore;
private readonly ShardData _shardData;
private readonly DiscordSocketClient _client;
public GuildTimezoneService(IBot bot, DbService db, IReplacementPatternStore repStore) public GuildTimezoneService(
DbService db,
IReplacementPatternStore repStore,
ShardData shardData,
DiscordSocketClient client)
{ {
_timezones = bot.AllGuildConfigs.Select(GetTimzezoneTuple)
.Where(x => x.Timezone is not null)
.ToDictionary(x => x.GuildId, x => x.Timezone)
.ToConcurrent();
_db = db; _db = db;
_repStore = repStore; _repStore = repStore;
_shardData = shardData;
bot.JoinedGuild += Bot_JoinedGuild; _client = client;
} }
private Task Bot_JoinedGuild(GuildConfig arg) private static (ulong GuildId, TimeZoneInfo Timezone) GetTimezoneTuple(GuildConfig x)
{
var (guildId, tz) = GetTimzezoneTuple(arg);
if (tz is not null)
_timezones.TryAdd(guildId, tz);
return Task.CompletedTask;
}
private static (ulong GuildId, TimeZoneInfo Timezone) GetTimzezoneTuple(GuildConfig x)
{ {
TimeZoneInfo tz; TimeZoneInfo tz;
try try
{ {
if (x.TimeZoneId is null) tz = x.TimeZoneId is null ? null : TimeZoneInfo.FindSystemTimeZoneById(x.TimeZoneId);
tz = null;
else
tz = TimeZoneInfo.FindSystemTimeZoneById(x.TimeZoneId);
} }
catch catch
{ {
@ -74,9 +65,18 @@ public sealed class GuildTimezoneService : ITimezoneService, IReadyExecutor, IES
public TimeZoneInfo GetTimeZoneOrUtc(ulong? guildId) public TimeZoneInfo GetTimeZoneOrUtc(ulong? guildId)
=> GetTimeZoneOrDefault(guildId) ?? TimeZoneInfo.Utc; => GetTimeZoneOrDefault(guildId) ?? TimeZoneInfo.Utc;
public Task OnReadyAsync() public async Task OnReadyAsync()
{ {
_repStore.Register("%server.time%", await using var uow = _db.GetDbContext();
_timezones = await uow.GetTable<GuildConfig>()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.ToListAsyncLinqToDB()
.Fmap(x => x
.Select(GetTimezoneTuple)
.ToDictionary(x => x.GuildId, x => x.Timezone)
.ToConcurrent());
await _repStore.Register("%server.time%",
(IGuild g) => (IGuild g) =>
{ {
var to = TimeZoneInfo.Local; var to = TimeZoneInfo.Local;
@ -88,7 +88,5 @@ public sealed class GuildTimezoneService : ITimezoneService, IReadyExecutor, IES
return TimeZoneInfo.ConvertTime(DateTime.UtcNow, TimeZoneInfo.Utc, to).ToString("HH:mm ") return TimeZoneInfo.ConvertTime(DateTime.UtcNow, TimeZoneInfo.Utc, to).ToString("HH:mm ")
+ to.StandardName.GetInitials(); + to.StandardName.GetInitials();
}); });
return Task.CompletedTask;
} }
} }

View file

@ -16,6 +16,7 @@ public class UserPunishService : IEService, IReadyExecutor
private readonly BotConfigService _bcs; private readonly BotConfigService _bcs;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IReplacementService _repSvc; private readonly IReplacementService _repSvc;
private readonly TempRoleService _tempRoleService;
public event Func<Warning, Task> OnUserWarned = static delegate { return Task.CompletedTask; }; public event Func<Warning, Task> OnUserWarned = static delegate { return Task.CompletedTask; };
@ -25,7 +26,8 @@ public class UserPunishService : IEService, IReadyExecutor
BlacklistService blacklistService, BlacklistService blacklistService,
BotConfigService bcs, BotConfigService bcs,
DiscordSocketClient client, DiscordSocketClient client,
IReplacementService repSvc) IReplacementService repSvc,
TempRoleService tempRoleService)
{ {
_mute = mute; _mute = mute;
_db = db; _db = db;
@ -33,6 +35,7 @@ public class UserPunishService : IEService, IReadyExecutor
_bcs = bcs; _bcs = bcs;
_client = client; _client = client;
_repSvc = repSvc; _repSvc = repSvc;
_tempRoleService = tempRoleService;
} }
public async Task OnReadyAsync() public async Task OnReadyAsync()
@ -188,10 +191,14 @@ public class UserPunishService : IEService, IReadyExecutor
var role = guild.GetRole(roleId.Value); var role = guild.GetRole(roleId.Value);
if (role is not null) if (role is not null)
{ {
if (minutes == 0) await user.AddRoleAsync(role);
await user.AddRoleAsync(role); if (minutes != 0)
else {
await _mute.TimedRole(user, TimeSpan.FromMinutes(minutes), reason, role); await _tempRoleService.AddTempRoleAsync(guild.Id,
role.Id,
user.Id,
TimeSpan.FromMinutes(minutes));
}
} }
else else
{ {
@ -233,7 +240,7 @@ public class UserPunishService : IEService, IReadyExecutor
case PunishmentAction.RemoveRoles: case PunishmentAction.RemoveRoles:
return botUser.GuildPermissions.ManageRoles; return botUser.GuildPermissions.ManageRoles;
case PunishmentAction.ChatMute: case PunishmentAction.ChatMute:
return botUser.GuildPermissions.ManageRoles; // adds ellie-mute role return botUser.GuildPermissions.ManageRoles; // adds nadeko-mute role
case PunishmentAction.VoiceMute: case PunishmentAction.VoiceMute:
return botUser.GuildPermissions.MuteMembers; return botUser.GuildPermissions.MuteMembers;
case PunishmentAction.AddRole: case PunishmentAction.AddRole:

View file

@ -1,109 +1,95 @@
#nullable disable #nullable disable
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models; using EllieBot.Db.Models;
namespace EllieBot.Modules.Administration.Services; namespace EllieBot.Modules.Administration.Services;
public class VcRoleService : IEService public class VcRoleService : IEService, IReadyExecutor
{ {
public ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>> VcRoles { get; } public ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>> VcRoles { get; }
public ConcurrentDictionary<ulong, System.Collections.Concurrent.ConcurrentQueue<(bool, IGuildUser, IRole)>> ToAssign { get; } public ConcurrentDictionary<ulong, System.Collections.Concurrent.ConcurrentQueue<(bool, IGuildUser, IRole)>> ToAssign { get; }
private readonly DbService _db; private readonly DbService _db;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IBot _bot;
private readonly ShardData _shardData;
public VcRoleService(DiscordSocketClient client, IBot bot, DbService db) public VcRoleService(DiscordSocketClient client, IBot bot, DbService db, ShardData shardData)
{ {
_db = db; _db = db;
_client = client; _client = client;
_bot = bot;
_shardData = shardData;
_client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated;
VcRoles = new(); VcRoles = new();
ToAssign = new(); ToAssign = new();
using (var uow = db.GetDbContext())
{
var guildIds = client.Guilds.Select(x => x.Id).ToList();
uow.Set<GuildConfig>()
.AsQueryable()
.Include(x => x.VcRoleInfos)
.Where(x => guildIds.Contains(x.GuildId))
.AsEnumerable()
.Select(InitializeVcRole)
.WhenAll();
}
Task.Run(async () =>
{
while (true)
{
Task Selector(System.Collections.Concurrent.ConcurrentQueue<(bool, IGuildUser, IRole)> queue)
{
return Task.Run(async () =>
{
while (queue.TryDequeue(out var item))
{
var (add, user, role) = item;
try
{
if (add)
{
if (!user.RoleIds.Contains(role.Id))
await user.AddRoleAsync(role);
}
else
{
if (user.RoleIds.Contains(role.Id))
await user.RemoveRoleAsync(role);
}
}
catch
{
}
await Task.Delay(250);
}
});
}
await ToAssign.Values.Select(Selector).Append(Task.Delay(1000)).WhenAll();
}
});
_client.LeftGuild += _client_LeftGuild;
bot.JoinedGuild += Bot_JoinedGuild;
} }
private Task Bot_JoinedGuild(GuildConfig arg) public async Task OnReadyAsync()
{ {
// includeall no longer loads vcrole IEnumerable<IGrouping<ulong, VcRoleInfo>> vcRoles;
// need to load new guildconfig with vc role included
using (var uow = _db.GetDbContext()) using (var uow = _db.GetDbContext())
{ {
var configWithVcRole = uow.GuildConfigsForId(arg.GuildId, set => set.Include(x => x.VcRoleInfos)); vcRoles = await uow.GetTable<VcRoleInfo>()
_ = InitializeVcRole(configWithVcRole); .AsQueryable()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.ToListAsync()
.Fmap(x => x.GroupBy(x => x.GuildId));
} }
return Task.CompletedTask; await vcRoles.Select(x => InitializeVcRole(x.Key, x.ToList())).WhenAll();
_client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated;
while (true)
{
Task Selector(System.Collections.Concurrent.ConcurrentQueue<(bool, IGuildUser, IRole)> queue)
{
return Task.Run(async () =>
{
while (queue.TryDequeue(out var item))
{
var (add, user, role) = item;
try
{
if (add)
{
if (!user.RoleIds.Contains(role.Id))
await user.AddRoleAsync(role);
}
else
{
if (user.RoleIds.Contains(role.Id))
await user.RemoveRoleAsync(role);
}
}
catch
{
}
await Task.Delay(250);
}
});
}
await ToAssign.Values.Select(Selector).Append(Task.Delay(1000)).WhenAll();
}
} }
private Task _client_LeftGuild(SocketGuild arg) private async Task InitializeVcRole(ulong guildId, IReadOnlyList<VcRoleInfo> confs)
{ {
VcRoles.TryRemove(arg.Id, out _); var g = _client.GetGuild(guildId);
ToAssign.TryRemove(arg.Id, out _);
return Task.CompletedTask;
}
private async Task InitializeVcRole(GuildConfig gconf)
{
var g = _client.GetGuild(gconf.GuildId);
if (g is null) if (g is null)
return; return;
var infos = new ConcurrentDictionary<ulong, IRole>(); var infos = new ConcurrentDictionary<ulong, IRole>();
var missingRoles = new List<VcRoleInfo>(); var missingRoles = new List<VcRoleInfo>();
VcRoles.AddOrUpdate(gconf.GuildId, infos, delegate { return infos; }); VcRoles.AddOrUpdate(guildId, infos, delegate
foreach (var ri in gconf.VcRoleInfos) { return infos; });
foreach (var ri in confs)
{ {
var role = g.GetRole(ri.RoleId); var role = g.GetRole(ri.RoleId);
if (role is null) if (role is null)
@ -120,7 +106,7 @@ public class VcRoleService : IEService
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
uow.RemoveRange(missingRoles); uow.RemoveRange(missingRoles);
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();
Log.Warning("Removed {MissingRoleCount} missing roles from {ServiceName}", Log.Warning("Removed {MissingRoleCount} missing roles from {ServiceName}",
missingRoles.Count, missingRoles.Count,
nameof(VcRoleService)); nameof(VcRoleService));
@ -135,11 +121,11 @@ public class VcRoleService : IEService
guildVcRoles.AddOrUpdate(vcId, role, (_, _) => role); guildVcRoles.AddOrUpdate(vcId, role, (_, _) => role);
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set.Include(x => x.VcRoleInfos)); var toDelete = uow.Set<VcRoleInfo>()
var toDelete = conf.VcRoleInfos.FirstOrDefault(x => x.VoiceChannelId == vcId); // remove old one .FirstOrDefault(x => x.VoiceChannelId == vcId); // remove old one
if (toDelete is not null) if (toDelete is not null)
uow.Remove(toDelete); uow.Remove(toDelete);
conf.VcRoleInfos.Add(new() uow.Set<VcRoleInfo>().Add(new()
{ {
VoiceChannelId = vcId, VoiceChannelId = vcId,
RoleId = role.Id RoleId = role.Id
@ -156,8 +142,7 @@ public class VcRoleService : IEService
return false; return false;
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set.Include(x => x.VcRoleInfos)); var toRemove = uow.Set<VcRoleInfo>().Where(x => x.VoiceChannelId == vcId).ToList();
var toRemove = conf.VcRoleInfos.Where(x => x.VoiceChannelId == vcId).ToList();
uow.RemoveRange(toRemove); uow.RemoveRange(toRemove);
uow.SaveChanges(); uow.SaveChanges();

View file

@ -77,6 +77,7 @@ public sealed class EllieExpressionsService : IExecOnMessage, IReadyExecutor
private bool ready; private bool ready;
private ConcurrentHashSet<ulong> _disabledGlobalExpressionGuilds; private ConcurrentHashSet<ulong> _disabledGlobalExpressionGuilds;
private readonly PermissionService _pc; private readonly PermissionService _pc;
private readonly ShardData _shardData;
public EllieExpressionsService( public EllieExpressionsService(
DbService db, DbService db,
@ -87,7 +88,8 @@ public sealed class EllieExpressionsService : IExecOnMessage, IReadyExecutor
IMessageSenderService sender, IMessageSenderService sender,
IReplacementService repSvc, IReplacementService repSvc,
IPermissionChecker permChecker, IPermissionChecker permChecker,
PermissionService pc) PermissionService pc,
ShardData shardData)
{ {
_db = db; _db = db;
_client = client; _client = client;
@ -98,6 +100,7 @@ public sealed class EllieExpressionsService : IExecOnMessage, IReadyExecutor
_repSvc = repSvc; _repSvc = repSvc;
_permChecker = permChecker; _permChecker = permChecker;
_pc = pc; _pc = pc;
_shardData = shardData;
_rng = new EllieRandom(); _rng = new EllieRandom();
_pubSub.Sub(_exprsReloadedKey, OnExprsShouldReload); _pubSub.Sub(_exprsReloadedKey, OnExprsShouldReload);
@ -105,26 +108,28 @@ public sealed class EllieExpressionsService : IExecOnMessage, IReadyExecutor
pubSub.Sub(_gexprDeletedkey, OnGexprDeleted); pubSub.Sub(_gexprDeletedkey, OnGexprDeleted);
pubSub.Sub(_gexprEditedKey, OnGexprEdited); pubSub.Sub(_gexprEditedKey, OnGexprEdited);
bot.JoinedGuild += OnJoinedGuild; _client.JoinedGuild += OnJoinedGuild;
_client.LeftGuild += OnLeftGuild;
} }
private async Task ReloadInternal(IReadOnlyList<ulong> allGuildIds) private async Task ReloadInternal()
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var guildItems = await uow.Set<EllieExpression>() var guildItems = await uow.GetTable<EllieExpression>()
.AsNoTracking() .AsNoTracking()
.Where(x => allGuildIds.Contains(x.GuildId.Value)) .Where(x => x.GuildId != null
.ToListAsync(); && Queries.GuildOnShard(x.GuildId.Value,
_shardData.TotalShards,
_shardData.ShardId))
.ToListAsyncLinqToDB();
newguildExpressions = guildItems.GroupBy(k => k.GuildId!.Value) newguildExpressions = guildItems.GroupBy(k => k.GuildId!.Value)
.ToDictionary(g => g.Key, .ToDictionary(g => g.Key,
g => g.Select(x => g => g.Select(x =>
{ {
x.Trigger = x.Trigger.Replace(MENTION_PH, x.Trigger = x.Trigger.Replace(MENTION_PH,
_client.CurrentUser.Mention); _client.CurrentUser.Mention);
return x; return x;
}) })
.ToArray()) .ToArray())
.ToConcurrent(); .ToConcurrent();
@ -635,10 +640,12 @@ public sealed class EllieExpressionsService : IExecOnMessage, IReadyExecutor
#region Event Handlers #region Event Handlers
public async Task OnReadyAsync() public async Task OnReadyAsync()
=> await OnExprsShouldReload(true); {
await OnExprsShouldReload(true);
}
private ValueTask OnExprsShouldReload(bool _) private ValueTask OnExprsShouldReload(bool _)
=> new(ReloadInternal(_bot.GetCurrentGuildIds())); => new(ReloadInternal());
private ValueTask OnGexprAdded(EllieExpression c) private ValueTask OnGexprAdded(EllieExpression c)
{ {
@ -692,21 +699,13 @@ public sealed class EllieExpressionsService : IExecOnMessage, IReadyExecutor
#region Client Event Handlers #region Client Event Handlers
private Task OnLeftGuild(SocketGuild arg) private Task OnJoinedGuild(SocketGuild g)
{ {
newguildExpressions.TryRemove(arg.Id, out _); newguildExpressions[g.Id] = [];
return Task.CompletedTask; return Task.CompletedTask;
} }
private async Task OnJoinedGuild(GuildConfig gc)
{
await using var uow = _db.GetDbContext();
var exprs = await uow.Set<EllieExpression>().AsNoTracking().Where(x => x.GuildId == gc.GuildId).ToArrayAsync();
newguildExpressions[gc.GuildId] = exprs;
}
#endregion #endregion
#region Basic Operations #region Basic Operations

View file

@ -77,7 +77,7 @@ public partial class Gambling
#endif #endif
public async Task GenCurrency() public async Task GenCurrency()
{ {
var enabled = _service.ToggleCurrencyGeneration(ctx.Guild.Id, ctx.Channel.Id); var enabled = await _service.ToggleCurrencyGeneration(ctx.Guild.Id, ctx.Channel.Id);
if (enabled) if (enabled)
await Response().Confirm(strs.curgen_enabled).SendAsync(); await Response().Confirm(strs.curgen_enabled).SendAsync();
else else
@ -88,14 +88,14 @@ public partial class Gambling
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)] [UserPerm(GuildPerm.ManageMessages)]
[OwnerOnly] [OwnerOnly]
public Task GenCurList(int page = 1) public async Task GenCurList(int page = 1)
{ {
if (--page < 0) if (--page < 0)
return Task.CompletedTask; return;
var enabledIn = _service.GetAllGeneratingChannels(); var enabledIn = await _service.GetAllGeneratingChannels();
return Response() await Response()
.Paginated() .Paginated()
.Items(enabledIn.ToList()) .Items(enabledIn.ToList())
.PageSize(9) .PageSize(9)

View file

@ -15,7 +15,7 @@ using Image = SixLabors.ImageSharp.Image;
namespace EllieBot.Modules.Gambling.Services; namespace EllieBot.Modules.Gambling.Services;
public class PlantPickService : IEService, IExecNoCommand public class PlantPickService : IEService, IExecNoCommand, IReadyExecutor
{ {
//channelId/last generation //channelId/last generation
public ConcurrentDictionary<ulong, long> LastGenerations { get; } = new(); public ConcurrentDictionary<ulong, long> LastGenerations { get; } = new();
@ -30,7 +30,7 @@ public class PlantPickService : IEService, IExecNoCommand
private readonly GamblingConfigService _gss; private readonly GamblingConfigService _gss;
private readonly GamblingService _gs; private readonly GamblingService _gs;
private readonly ConcurrentHashSet<ulong> _generationChannels; private ConcurrentHashSet<ulong> _generationChannels;
private readonly SemaphoreSlim _pickLock = new(1, 1); private readonly SemaphoreSlim _pickLock = new(1, 1);
public PlantPickService( public PlantPickService(
@ -57,13 +57,6 @@ public class PlantPickService : IEService, IExecNoCommand
using var uow = db.GetDbContext(); using var uow = db.GetDbContext();
var guildIds = client.Guilds.Select(x => x.Id).ToList(); var guildIds = client.Guilds.Select(x => x.Id).ToList();
var configs = uow.Set<GuildConfig>()
.AsQueryable()
.Include(x => x.GenerateCurrencyChannelIds)
.Where(x => guildIds.Contains(x.GuildId))
.ToList();
_generationChannels = new(configs.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
} }
public Task ExecOnNoCommandAsync(IGuild guild, IUserMessage msg) public Task ExecOnNoCommandAsync(IGuild guild, IUserMessage msg)
@ -72,41 +65,49 @@ public class PlantPickService : IEService, IExecNoCommand
private string GetText(ulong gid, LocStr str) private string GetText(ulong gid, LocStr str)
=> _strings.GetText(str, gid); => _strings.GetText(str, gid);
public bool ToggleCurrencyGeneration(ulong gid, ulong cid) public async Task<bool> ToggleCurrencyGeneration(ulong gid, ulong cid)
{ {
bool enabled; bool enabled;
using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var guildConfig = uow.GuildConfigsForId(gid, set => set.Include(gc => gc.GenerateCurrencyChannelIds));
var toAdd = new GCChannelId if (_generationChannels.Add(cid))
{ {
ChannelId = cid await uow.GetTable<GCChannelId>()
}; .InsertOrUpdateAsync(() => new()
if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd)) {
{ ChannelId = cid,
guildConfig.GenerateCurrencyChannelIds.Add(toAdd); GuildId = gid
}, (x) => new()
{
ChannelId = cid,
GuildId = gid
}, () => new()
{
ChannelId = cid,
GuildId = gid
});
_generationChannels.Add(cid); _generationChannels.Add(cid);
enabled = true; enabled = true;
} }
else else
{ {
var toDelete = guildConfig.GenerateCurrencyChannelIds.FirstOrDefault(x => x.Equals(toAdd)); await uow.GetTable<GCChannelId>()
if (toDelete is not null) .Where(x => x.ChannelId == cid && x.GuildId == gid)
uow.Remove(toDelete); .DeleteAsync();
_generationChannels.TryRemove(cid); _generationChannels.TryRemove(cid);
enabled = false; enabled = false;
} }
uow.SaveChanges();
return enabled; return enabled;
} }
public IEnumerable<GuildConfigExtensions.GeneratingChannel> GetAllGeneratingChannels() public async Task<IReadOnlyCollection<GCChannelId>> GetAllGeneratingChannels()
{ {
using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var chs = uow.Set<GuildConfig>().GetGeneratingChannels(); return await uow.GetTable<GCChannelId>()
return chs; .ToListAsyncLinqToDB();
} }
/// <summary> /// <summary>
@ -412,4 +413,16 @@ public class PlantPickService : IEService, IExecNoCommand
return (totalDeletedAmount + amount, deleted.Select(x => x.MessageId).ToArray()); return (totalDeletedAmount + amount, deleted.Select(x => x.MessageId).ToArray());
} }
public async Task OnReadyAsync()
{
await using var uow = _db.GetDbContext();
_generationChannels = (await uow.GetTable<GCChannelId>()
.Select(x => x.ChannelId)
.ToListAsyncLinqToDB())
.ToHashSet()
.ToConcurrentSet();
}
} }

View file

@ -4,6 +4,7 @@ using EllieBot.Modules.Gambling.Common;
using EllieBot.Modules.Gambling.Services; using EllieBot.Modules.Gambling.Services;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using EllieBot.Modules.Administration; using EllieBot.Modules.Administration;
using LinqToDB.EntityFrameworkCore;
namespace EllieBot.Modules.Gambling; namespace EllieBot.Modules.Gambling;
@ -30,25 +31,28 @@ public partial class Gambling
private readonly DbService _db; private readonly DbService _db;
private readonly ICurrencyService _cs; private readonly ICurrencyService _cs;
private readonly EllieRandom _rng;
public ShopCommands(DbService db, ICurrencyService cs, GamblingConfigService gamblingConf) public ShopCommands(DbService db, ICurrencyService cs, GamblingConfigService gamblingConf)
: base(gamblingConf) : base(gamblingConf)
{ {
_db = db; _db = db;
_cs = cs; _cs = cs;
_rng = new EllieRandom();
} }
private Task ShopInternalAsync(int page = 0) private async Task ShopInternalAsync(int page = 0)
{ {
if (page < 0) if (page < 0)
throw new ArgumentOutOfRangeException(nameof(page)); throw new ArgumentOutOfRangeException(nameof(page));
using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var entries = uow.GuildConfigsForId(ctx.Guild.Id, var entries = await uow.Set<ShopEntry>()
set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items)) .Where(x => x.GuildId == ctx.Guild.Id)
.ShopEntries.ToIndexed(); .Include(x => x.Items)
.ToListAsyncEF();
return Response() await Response()
.Paginated() .Paginated()
.Items(entries.ToList()) .Items(entries.ToList())
.PageSize(9) .PageSize(9)
@ -92,13 +96,15 @@ public partial class Gambling
ShopEntry entry; ShopEntry entry;
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var config = uow.GuildConfigsForId(ctx.Guild.Id, entry = await uow.Set<ShopEntry>()
set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items)); .Where(x => x.GuildId == ctx.Guild.Id)
var entries = new IndexedCollection<ShopEntry>(config.ShopEntries); .Include(x => x.Items)
entry = entries.ElementAtOrDefault(index); .OrderBy(x => x.Id)
uow.SaveChanges(); .Skip(index)
.FirstOrDefaultAsync();
} }
if (entry is null) if (entry is null)
{ {
await Response().Error(strs.shop_item_not_found).SendAsync(); await Response().Error(strs.shop_item_not_found).SendAsync();
@ -174,7 +180,7 @@ public partial class Gambling
return; return;
} }
var item = entry.Items.ToArray()[new EllieRandom().Next(0, entry.Items.Count)]; var item = entry.Items.ToArray()[_rng.Next(0, entry.Items.Count)];
if (await _cs.RemoveAsync(ctx.User.Id, entry.Price, new("shop", "buy", entry.Type.ToString()))) if (await _cs.RemoveAsync(ctx.User.Id, entry.Price, new("shop", "buy", entry.Type.ToString())))
{ {
@ -205,15 +211,15 @@ public partial class Gambling
await _cs.AddAsync(ctx.User.Id, entry.Price, new("shop", "error-refund", entry.Name)); await _cs.AddAsync(ctx.User.Id, entry.Price, new("shop", "error-refund", entry.Name));
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id, var entries = new IndexedCollection<ShopEntry>(await uow.Set<ShopEntry>()
set => set.Include(x => x.ShopEntries) .Where(x => x.GuildId == ctx.Guild.Id)
.ThenInclude(x => x.Items)) .Include(x => x.Items)
.ShopEntries); .ToListAsyncEF());
entry = entries.ElementAtOrDefault(index); entry = entries.ElementAtOrDefault(index);
if (entry is not null) if (entry is not null)
{ {
if (entry.Items.Add(item)) if (entry.Items.Add(item))
uow.SaveChanges(); await uow.SaveChangesAsync();
} }
} }
@ -224,7 +230,9 @@ public partial class Gambling
await Response().Confirm(strs.shop_item_purchase).SendAsync(); await Response().Confirm(strs.shop_item_purchase).SendAsync();
} }
else else
{
await Response().Error(strs.not_enough(CurrencySign)).SendAsync(); await Response().Error(strs.not_enough(CurrencySign)).SendAsync();
}
} }
else if (entry.Type == ShopEntryType.Command) else if (entry.Type == ShopEntryType.Command)
{ {
@ -253,7 +261,7 @@ public partial class Gambling
.Replace("%you.username%", buyer.Username) .Replace("%you.username%", buyer.Username)
.Replace("%you.name%", buyer.GlobalName ?? buyer.Username) .Replace("%you.name%", buyer.GlobalName ?? buyer.Username)
.Replace("%you.nick%", buyer.DisplayName); .Replace("%you.nick%", buyer.DisplayName);
var eb = CreateEmbed() var eb = CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithTitle("Executing shop command") .WithTitle("Executing shop command")
@ -328,15 +336,14 @@ public partial class Gambling
}; };
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id, var entries = new IndexedCollection<ShopEntry>(await uow.Set<ShopEntry>()
set => set.Include(x => x.ShopEntries) .Where(x => x.GuildId == ctx.Guild.Id)
.ThenInclude(x => x.Items)) .Include(x => x.Items)
.ShopEntries) .ToListAsyncEF())
{ {
entry entry
}; };
uow.GuildConfigsForId(ctx.Guild.Id, set => set).ShopEntries = entries; await uow.SaveChangesAsync();
uow.SaveChanges();
} }
await Response().Embed(EntryToEmbed(entry).WithTitle(GetText(strs.shop_item_add))).SendAsync(); await Response().Embed(EntryToEmbed(entry).WithTitle(GetText(strs.shop_item_add))).SendAsync();
@ -360,15 +367,16 @@ public partial class Gambling
}; };
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id, var entries = await uow.Set<ShopEntry>()
set => set.Include(x => x.ShopEntries) .Where(x => x.GuildId == ctx.Guild.Id)
.ThenInclude(x => x.Items)) .ToListAsyncEF();
.ShopEntries)
{ var indexed = new IndexedCollection<ShopEntry>(entries);
entry indexed.Add(entry);
};
uow.GuildConfigsForId(ctx.Guild.Id, set => set).ShopEntries = entries; uow.Add(entry);
uow.SaveChanges(); await uow.SaveChangesAsync();
} }
await Response().Embed(EntryToEmbed(entry).WithTitle(GetText(strs.shop_item_add))).SendAsync(); await Response().Embed(EntryToEmbed(entry).WithTitle(GetText(strs.shop_item_add))).SendAsync();
@ -391,11 +399,13 @@ public partial class Gambling
var added = false; var added = false;
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id, var entries = await uow.Set<ShopEntry>()
set => set.Include(x => x.ShopEntries) .Where(x => x.GuildId == ctx.Guild.Id)
.ThenInclude(x => x.Items)) .Include(x => x.Items)
.ShopEntries); .ToListAsyncEF();
entry = entries.ElementAtOrDefault(index);
var indexed = new IndexedCollection<ShopEntry>(entries);
entry = indexed.ElementAtOrDefault(index);
if (entry is not null && (rightType = entry.Type == ShopEntryType.List)) if (entry is not null && (rightType = entry.Type == ShopEntryType.List))
{ {
if (entry.Items.Add(item)) if (entry.Items.Add(item))
@ -427,10 +437,12 @@ public partial class Gambling
ShopEntry removed; ShopEntry removed;
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var config = uow.GuildConfigsForId(ctx.Guild.Id, var items = await uow.Set<ShopEntry>()
set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items)); .Where(x => x.GuildId == ctx.Guild.Id)
.Include(x => x.Items)
.ToListAsyncLinqToDB();
var entries = new IndexedCollection<ShopEntry>(config.ShopEntries); var entries = new IndexedCollection<ShopEntry>(items);
removed = entries.ElementAtOrDefault(index); removed = entries.ElementAtOrDefault(index);
if (removed is not null) if (removed is not null)
{ {

View file

@ -1,4 +1,6 @@
#nullable disable #nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EllieBot.Db.Models; using EllieBot.Db.Models;
@ -11,11 +13,14 @@ public class ShopService : IShopService, IEService
public ShopService(DbService db) public ShopService(DbService db)
=> _db = db; => _db = db;
private IndexedCollection<ShopEntry> GetEntriesInternal(DbContext uow, ulong guildId) private async Task<IndexedCollection<ShopEntry>> GetEntriesInternal(DbContext uow, ulong guildId)
=> uow.GuildConfigsForId(guildId, {
set => set.Include(x => x.ShopEntries) var items = await uow.GetTable<ShopEntry>()
.ThenInclude(x => x.Items)) .Where(x => x.GuildId == guildId)
.ShopEntries.ToIndexed(); .ToListAsyncLinqToDB();
return items.ToIndexed();
}
public async Task<bool> ChangeEntryPriceAsync(ulong guildId, int index, int newPrice) public async Task<bool> ChangeEntryPriceAsync(ulong guildId, int index, int newPrice)
{ {
@ -23,32 +28,33 @@ public class ShopService : IShopService, IEService
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(newPrice); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(newPrice);
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var entries = GetEntriesInternal(uow, guildId);
if (index >= entries.Count) var changed = await uow.GetTable<ShopEntry>()
return false; .Where(x => x.GuildId == guildId && x.Index == index)
.UpdateAsync(x => new ShopEntry()
{
Price = newPrice,
});
entries[index].Price = newPrice; return changed > 0;
await uow.SaveChangesAsync();
return true;
} }
public async Task<bool> ChangeEntryNameAsync(ulong guildId, int index, string newName) public async Task<bool> ChangeEntryNameAsync(ulong guildId, int index, string newName)
{ {
ArgumentOutOfRangeException.ThrowIfNegative(index); ArgumentOutOfRangeException.ThrowIfNegative(index);
if (string.IsNullOrWhiteSpace(newName)) if (string.IsNullOrWhiteSpace(newName))
throw new ArgumentNullException(nameof(newName)); throw new ArgumentNullException(nameof(newName));
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var entries = GetEntriesInternal(uow, guildId);
if (index >= entries.Count) var changed = await uow.GetTable<ShopEntry>()
return false; .Where(x => x.GuildId == guildId && x.Index == index)
.UpdateAsync(x => new ShopEntry()
entries[index].Name = newName.TrimTo(100); {
await uow.SaveChangesAsync(); Name = newName,
return true; });
return changed > 0;
} }
public async Task<bool> SwapEntriesAsync(ulong guildId, int index1, int index2) public async Task<bool> SwapEntriesAsync(ulong guildId, int index1, int index2)
@ -57,7 +63,7 @@ public class ShopService : IShopService, IEService
ArgumentOutOfRangeException.ThrowIfNegative(index2); ArgumentOutOfRangeException.ThrowIfNegative(index2);
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var entries = GetEntriesInternal(uow, guildId); var entries = await GetEntriesInternal(uow, guildId);
if (index1 >= entries.Count || index2 >= entries.Count || index1 == index2) if (index1 >= entries.Count || index2 >= entries.Count || index1 == index2)
return false; return false;
@ -65,7 +71,14 @@ public class ShopService : IShopService, IEService
entries[index1].Index = index2; entries[index1].Index = index2;
entries[index2].Index = index1; entries[index2].Index = index1;
await uow.SaveChangesAsync(); // todo fix swap
await uow.GetTable<ShopEntry>()
.Where(x => x.GuildId == guildId)
.UpdateAsync(x => new ShopEntry()
{
Index = x.Index == index1 ? index2 : x.Index == index2 ? index1 : x.Index,
});
return true; return true;
} }
@ -77,49 +90,38 @@ public class ShopService : IShopService, IEService
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var entries = GetEntriesInternal(uow, guildId); var entries = GetEntriesInternal(uow, guildId);
if (fromIndex >= entries.Count || toIndex >= entries.Count || fromIndex == toIndex) // todo move
return false;
var entry = entries[fromIndex];
entries.RemoveAt(fromIndex);
entries.Insert(toIndex, entry);
await uow.SaveChangesAsync();
return true; return true;
} }
public async Task<bool> SetItemRoleRequirementAsync(ulong guildId, int index, ulong? roleId) public async Task<bool> SetItemRoleRequirementAsync(ulong guildId, int index, ulong? roleId)
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var entries = GetEntriesInternal(uow, guildId);
if (index >= entries.Count) var changes = await uow.GetTable<ShopEntry>()
return false; .Where(x => x.GuildId == guildId && x.Index == index)
.UpdateAsync(x => new ShopEntry()
var entry = entries[index]; {
RoleRequirement = roleId,
entry.RoleRequirement = roleId; });
return changes > 0;
await uow.SaveChangesAsync();
return true;
} }
public async Task<ShopEntry> AddShopCommandAsync(ulong guildId, ulong userId, int price, string command) public async Task<ShopEntry> AddShopCommandAsync(
ulong guildId,
ulong userId,
int price,
string command)
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var entry = await uow.GetTable<ShopEntry>()
var entries = GetEntriesInternal(uow, guildId); .InsertWithOutputAsync(() => new()
var entry = new ShopEntry() {
{ AuthorId = userId,
AuthorId = userId, Command = command,
Command = command, Type = ShopEntryType.Command,
Type = ShopEntryType.Command, Price = price,
Price = price, });
};
entries.Add(entry);
uow.GuildConfigsForId(guildId, set => set).ShopEntries = entries;
await uow.SaveChangesAsync();
return entry; return entry;
} }

View file

@ -1,6 +1,7 @@
#nullable disable #nullable disable
using LinqToDB; using LinqToDB;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors; using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using EllieBot.Modules.Games.Common; using EllieBot.Modules.Games.Common;
@ -10,9 +11,9 @@ using EllieBot.Modules.Permissions;
namespace EllieBot.Modules.Games.Services; namespace EllieBot.Modules.Games.Services;
public class ChatterBotService : IExecOnMessage public class ChatterBotService : IExecOnMessage, IReadyExecutor
{ {
private ConcurrentDictionary<ulong, Lazy<IChatterBotSession>> ChatterBotGuilds { get; } private ConcurrentDictionary<ulong, Lazy<IChatterBotSession>> _chatterBotGuilds;
public int Priority public int Priority
=> 1; => 1;
@ -29,7 +30,6 @@ public class ChatterBotService : IExecOnMessage
public ChatterBotService( public ChatterBotService(
DiscordSocketClient client, DiscordSocketClient client,
IPermissionChecker perms, IPermissionChecker perms,
IBot bot,
IPatronageService ps, IPatronageService ps,
IHttpClientFactory factory, IHttpClientFactory factory,
IBotCreds creds, IBotCreds creds,
@ -46,11 +46,6 @@ public class ChatterBotService : IExecOnMessage
_perms = perms; _perms = perms;
_gcs = gcs; _gcs = gcs;
_ps = ps; _ps = ps;
ChatterBotGuilds = new(bot.AllGuildConfigs
.Where(gc => gc.CleverbotEnabled)
.ToDictionary(gc => gc.GuildId,
_ => new Lazy<IChatterBotSession>(() => CreateSession(), true)));
} }
public IChatterBotSession CreateSession() public IChatterBotSession CreateSession()
@ -86,25 +81,25 @@ public class ChatterBotService : IExecOnMessage
public IChatterBotSession GetOrCreateSession(ulong guildId) public IChatterBotSession GetOrCreateSession(ulong guildId)
{ {
if (ChatterBotGuilds.TryGetValue(guildId, out var lazyChatBot)) if (_chatterBotGuilds.TryGetValue(guildId, out var lazyChatBot))
return lazyChatBot.Value; return lazyChatBot.Value;
lazyChatBot = new(() => CreateSession(), true); lazyChatBot = new(() => CreateSession(), true);
ChatterBotGuilds.TryAdd(guildId, lazyChatBot); _chatterBotGuilds.TryAdd(guildId, lazyChatBot);
return lazyChatBot.Value; return lazyChatBot.Value;
} }
public string PrepareMessage(IUserMessage msg) public string PrepareMessage(IUserMessage msg)
{ {
var ellieId = _client.CurrentUser.Id; var nadekoId = _client.CurrentUser.Id;
var normalMention = $"<@{ellieId}> "; var normalMention = $"<@{nadekoId}> ";
var nickMention = $"<@!{ellieId}> "; var nickMention = $"<@!{nadekoId}> ";
string message; string message;
if (msg.Content.StartsWith(normalMention, StringComparison.InvariantCulture)) if (msg.Content.StartsWith(normalMention, StringComparison.InvariantCulture))
message = msg.Content[normalMention.Length..].Trim(); message = msg.Content[normalMention.Length..].Trim();
else if (msg.Content.StartsWith(nickMention, StringComparison.InvariantCulture)) else if (msg.Content.StartsWith(nickMention, StringComparison.InvariantCulture))
message = msg.Content[nickMention.Length..].Trim(); message = msg.Content[nickMention.Length..].Trim();
else if (msg.ReferencedMessage?.Author.Id == ellieId) else if (msg.ReferencedMessage?.Author.Id == nadekoId)
message = msg.Content; message = msg.Content;
else else
return null; return null;
@ -121,7 +116,7 @@ public class ChatterBotService : IExecOnMessage
if (channel is null) if (channel is null)
return false; return false;
if (!ChatterBotGuilds.TryGetValue(channel.Guild.Id, out var lazyChatBot)) if (!_chatterBotGuilds.TryGetValue(channel.Guild.Id, out var lazyChatBot))
return false; return false;
var chatBot = lazyChatBot.Value; var chatBot = lazyChatBot.Value;
@ -205,7 +200,7 @@ public class ChatterBotService : IExecOnMessage
public async Task<bool> ToggleChatterBotAsync(ulong guildId) public async Task<bool> ToggleChatterBotAsync(ulong guildId)
{ {
if (ChatterBotGuilds.TryRemove(guildId, out _)) if (_chatterBotGuilds.TryRemove(guildId, out _))
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
await uow.Set<GuildConfig>() await uow.Set<GuildConfig>()
@ -219,7 +214,7 @@ public class ChatterBotService : IExecOnMessage
return false; return false;
} }
ChatterBotGuilds.TryAdd(guildId, new(() => CreateSession(), true)); _chatterBotGuilds.TryAdd(guildId, new(() => CreateSession(), true));
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
@ -236,4 +231,16 @@ public class ChatterBotService : IExecOnMessage
return true; return true;
} }
public async Task OnReadyAsync()
{
await using var uow = _db.GetDbContext();
_chatterBotGuilds = uow.GuildConfigs
.AsNoTracking()
.Where(gc => gc.CleverbotEnabled)
.AsEnumerable()
.ToDictionary(gc => gc.GuildId,
_ => new Lazy<IChatterBotSession>(() => CreateSession(), true))
.ToConcurrent();
}
} }

View file

@ -1,28 +1,28 @@
using Microsoft.EntityFrameworkCore; using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors; using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Permissions.Services; namespace EllieBot.Modules.Permissions.Services;
public sealed class CmdCdService : IExecPreCommand, IReadyExecutor, IEService public sealed class CmdCdService : IExecPreCommand, IReadyExecutor, IEService
{ {
private readonly DbService _db; private readonly DbService _db;
private readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, int>> _settings = new(); private ConcurrentDictionary<ulong, ConcurrentDictionary<string, int>> _settings = new();
private readonly ConcurrentDictionary<(ulong, string), ConcurrentDictionary<ulong, DateTime>> _activeCooldowns = private readonly ConcurrentDictionary<(ulong, string), ConcurrentDictionary<ulong, DateTime>> _activeCooldowns =
new(); new();
public int Priority => 0; public int Priority
=> 0;
public CmdCdService(IBot bot, DbService db) private readonly ShardData _shardData;
public CmdCdService(DbService db, ShardData shardData)
{ {
_db = db; _db = db;
_settings = bot _shardData = shardData;
.AllGuildConfigs
.ToDictionary(x => x.GuildId, x => x.CommandCooldowns
.DistinctBy(x => x.CommandName.ToLowerInvariant())
.ToDictionary(c => c.CommandName, c => c.Seconds)
.ToConcurrent())
.ToConcurrent();
} }
public Task<bool> ExecPreCommandAsync(ICommandContext context, string moduleName, CommandInfo command) public Task<bool> ExecPreCommandAsync(ICommandContext context, string moduleName, CommandInfo command)
@ -32,7 +32,7 @@ public sealed class CmdCdService : IExecPreCommand, IReadyExecutor, IEService
{ {
if (guild is null) if (guild is null)
return Task.FromResult(false); return Task.FromResult(false);
if (!_settings.TryGetValue(guild.Id, out var cooldownSettings)) if (!_settings.TryGetValue(guild.Id, out var cooldownSettings))
return Task.FromResult(false); return Task.FromResult(false);
@ -66,8 +66,23 @@ public sealed class CmdCdService : IExecPreCommand, IReadyExecutor, IEService
public async Task OnReadyAsync() public async Task OnReadyAsync()
{ {
using var timer = new PeriodicTimer(TimeSpan.FromHours(1)); await using (var uow = _db.GetDbContext())
{
_settings = await uow.GetTable<CommandCooldown>()
.Where(
x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.ToListAsyncLinqToDB()
.Fmap(cmdcds => cmdcds
.GroupBy(x => x.GuildId)
.ToDictionary(x => x.Key,
x => x
.DistinctBy(x => x.CommandName.ToLower())
.ToDictionary(y => y.CommandName, y => y.Seconds)
.ToConcurrent())
.ToConcurrent());
}
using var timer = new PeriodicTimer(TimeSpan.FromHours(1));
while (await timer.WaitForNextTickAsync()) while (await timer.WaitForNextTickAsync())
{ {
// once per hour delete expired entries // once per hour delete expired entries
@ -81,7 +96,7 @@ public sealed class CmdCdService : IExecPreCommand, IReadyExecutor, IEService
_activeCooldowns.Remove((guildId, commandName), out _); _activeCooldowns.Remove((guildId, commandName), out _);
continue; continue;
} }
Cleanup(dict, cdSeconds); Cleanup(dict, cdSeconds);
} }
} }
@ -96,20 +111,20 @@ public sealed class CmdCdService : IExecPreCommand, IReadyExecutor, IEService
} }
} }
public void ClearCooldowns(ulong guildId, string cmdName) public async Task ClearCooldowns(ulong guildId, string cmdName)
{ {
if (_settings.TryGetValue(guildId, out var dict)) if (_settings.TryGetValue(guildId, out var dict))
dict.TryRemove(cmdName, out _); dict.TryRemove(cmdName, out _);
_activeCooldowns.TryRemove((guildId, cmdName), out _); _activeCooldowns.TryRemove((guildId, cmdName), out _);
using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var gc = ctx.GuildConfigsForId(guildId, x => x.Include(x => x.CommandCooldowns)); await ctx.GetTable<CommandCooldown>()
gc.CommandCooldowns.RemoveWhere(x => x.CommandName == cmdName); .Where(x => x.GuildId == guildId && x.CommandName == cmdName)
ctx.SaveChanges(); .DeleteAsync();
} }
public void AddCooldown(ulong guildId, string name, int secs) public async Task AddCooldown(ulong guildId, string name, int secs)
{ {
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(secs); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(secs);
@ -119,16 +134,24 @@ public sealed class CmdCdService : IExecPreCommand, IReadyExecutor, IEService
// force cleanup // force cleanup
if (_activeCooldowns.TryGetValue((guildId, name), out var dict)) if (_activeCooldowns.TryGetValue((guildId, name), out var dict))
Cleanup(dict, secs); Cleanup(dict, secs);
using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var gc = ctx.GuildConfigsForId(guildId, x => x.Include(x => x.CommandCooldowns)); await ctx.GetTable<CommandCooldown>()
gc.CommandCooldowns.RemoveWhere(x => x.CommandName == name); .InsertOrUpdateAsync(() => new()
gc.CommandCooldowns.Add(new() {
{ GuildId = guildId,
Seconds = secs, CommandName = name,
CommandName = name Seconds = secs
}); },
ctx.SaveChanges(); (old) => new()
{
Seconds = secs
},
() => new()
{
GuildId = guildId,
CommandName = name,
});
} }
public IReadOnlyCollection<(string CommandName, int Seconds)> GetCommandCooldowns(ulong guildId) public IReadOnlyCollection<(string CommandName, int Seconds)> GetCommandCooldowns(ulong guildId)

View file

@ -22,7 +22,6 @@ public partial class Permissions
private async Task CmdCooldownInternal(string cmdName, int secs) private async Task CmdCooldownInternal(string cmdName, int secs)
{ {
var channel = (ITextChannel)ctx.Channel;
if (secs is < 0 or > 3600) if (secs is < 0 or > 3600)
{ {
await Response().Error(strs.invalid_second_param_between(0, 3600)).SendAsync(); await Response().Error(strs.invalid_second_param_between(0, 3600)).SendAsync();
@ -30,34 +29,17 @@ public partial class Permissions
} }
var name = cmdName.ToLowerInvariant(); var name = cmdName.ToLowerInvariant();
await using (var uow = _db.GetDbContext()) await _service.AddCooldown(ctx.Guild.Id, name, secs);
{
var config = uow.GuildConfigsForId(channel.Guild.Id, set => set.Include(gc => gc.CommandCooldowns));
var toDelete = config.CommandCooldowns.FirstOrDefault(cc => cc.CommandName == name);
if (toDelete is not null)
uow.Set<CommandCooldown>().Remove(toDelete);
if (secs != 0)
{
var cc = new CommandCooldown
{
CommandName = name,
Seconds = secs
};
config.CommandCooldowns.Add(cc);
_service.AddCooldown(channel.Guild.Id, name, secs);
}
await uow.SaveChangesAsync();
}
if (secs == 0) if (secs == 0)
{ {
_service.ClearCooldowns(ctx.Guild.Id, cmdName); await _service.ClearCooldowns(ctx.Guild.Id, cmdName);
await Response().Confirm(strs.cmdcd_cleared(Format.Bold(name))).SendAsync(); await Response().Confirm(strs.cmdcd_cleared(Format.Bold(name))).SendAsync();
} }
else else
{
await Response().Confirm(strs.cmdcd_add(Format.Bold(name), Format.Bold(secs.ToString()))).SendAsync(); await Response().Confirm(strs.cmdcd_add(Format.Bold(name), Format.Bold(secs.ToString()))).SendAsync();
}
} }
[Cmd] [Cmd]

View file

@ -20,7 +20,7 @@ public partial class Permissions
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public async Task FwClear() public async Task FwClear()
{ {
_service.ClearFilteredWords(ctx.Guild.Id); await _service.ClearFilteredWords(ctx.Guild.Id);
await Response().Confirm(strs.fw_cleared).SendAsync(); await Response().Confirm(strs.fw_cleared).SendAsync();
} }
@ -71,22 +71,14 @@ public partial class Permissions
{ {
var channel = (ITextChannel)ctx.Channel; var channel = (ITextChannel)ctx.Channel;
bool enabled; var enabled = await _service.ToggleServerInviteFilteringAsync(channel.Guild.Id);
await using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(channel.Guild.Id, set => set);
enabled = config.FilterInvites = !config.FilterInvites;
await uow.SaveChangesAsync();
}
if (enabled) if (enabled)
{ {
_service.InviteFilteringServers.Add(channel.Guild.Id);
await Response().Confirm(strs.invite_filter_server_on).SendAsync(); await Response().Confirm(strs.invite_filter_server_on).SendAsync();
} }
else else
{ {
_service.InviteFilteringServers.TryRemove(channel.Guild.Id);
await Response().Confirm(strs.invite_filter_server_off).SendAsync(); await Response().Confirm(strs.invite_filter_server_off).SendAsync();
} }
} }
@ -97,32 +89,14 @@ public partial class Permissions
{ {
var channel = (ITextChannel)ctx.Channel; var channel = (ITextChannel)ctx.Channel;
FilterChannelId removed; var enabled = await _service.ToggleChannelInviteFilteringAsync(channel.Guild.Id, channel.Id);
await using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(channel.Guild.Id,
set => set.Include(gc => gc.FilterInvitesChannelIds));
var match = new FilterChannelId
{
ChannelId = channel.Id
};
removed = config.FilterInvitesChannelIds.FirstOrDefault(fc => fc.Equals(match));
if (removed is null) if (enabled)
config.FilterInvitesChannelIds.Add(match);
else
uow.Remove(removed);
await uow.SaveChangesAsync();
}
if (removed is null)
{ {
_service.InviteFilteringChannels.Add(channel.Id);
await Response().Confirm(strs.invite_filter_channel_on).SendAsync(); await Response().Confirm(strs.invite_filter_channel_on).SendAsync();
} }
else else
{ {
_service.InviteFilteringChannels.TryRemove(channel.Id);
await Response().Confirm(strs.invite_filter_channel_off).SendAsync(); await Response().Confirm(strs.invite_filter_channel_off).SendAsync();
} }
} }
@ -133,22 +107,14 @@ public partial class Permissions
{ {
var channel = (ITextChannel)ctx.Channel; var channel = (ITextChannel)ctx.Channel;
bool enabled; var enabled = await _service.ToggleServerLinkFilteringAsync(channel.Guild.Id);
await using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(channel.Guild.Id, set => set);
enabled = config.FilterLinks = !config.FilterLinks;
await uow.SaveChangesAsync();
}
if (enabled) if (enabled)
{ {
_service.LinkFilteringServers.Add(channel.Guild.Id);
await Response().Confirm(strs.link_filter_server_on).SendAsync(); await Response().Confirm(strs.link_filter_server_on).SendAsync();
} }
else else
{ {
_service.LinkFilteringServers.TryRemove(channel.Guild.Id);
await Response().Confirm(strs.link_filter_server_off).SendAsync(); await Response().Confirm(strs.link_filter_server_off).SendAsync();
} }
} }
@ -159,32 +125,14 @@ public partial class Permissions
{ {
var channel = (ITextChannel)ctx.Channel; var channel = (ITextChannel)ctx.Channel;
FilterLinksChannelId removed; var enabled = await _service.ToggleChannelLinkFilteringAsync(channel.Guild.Id, channel.Id);
await using (var uow = _db.GetDbContext())
{
var config =
uow.GuildConfigsForId(channel.Guild.Id, set => set.Include(gc => gc.FilterLinksChannelIds));
var match = new FilterLinksChannelId
{
ChannelId = channel.Id
};
removed = config.FilterLinksChannelIds.FirstOrDefault(fc => fc.Equals(match));
if (removed is null) if (enabled)
config.FilterLinksChannelIds.Add(match);
else
uow.Remove(removed);
await uow.SaveChangesAsync();
}
if (removed is null)
{ {
_service.LinkFilteringChannels.Add(channel.Id);
await Response().Confirm(strs.link_filter_channel_on).SendAsync(); await Response().Confirm(strs.link_filter_channel_on).SendAsync();
} }
else else
{ {
_service.LinkFilteringChannels.TryRemove(channel.Id);
await Response().Confirm(strs.link_filter_channel_off).SendAsync(); await Response().Confirm(strs.link_filter_channel_off).SendAsync();
} }
} }
@ -195,22 +143,14 @@ public partial class Permissions
{ {
var channel = (ITextChannel)ctx.Channel; var channel = (ITextChannel)ctx.Channel;
bool enabled; var enabled = await _service.ToggleServerWordFilteringAsync(channel.Guild.Id);
await using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(channel.Guild.Id, set => set);
enabled = config.FilterWords = !config.FilterWords;
await uow.SaveChangesAsync();
}
if (enabled) if (enabled)
{ {
_service.WordFilteringServers.Add(channel.Guild.Id);
await Response().Confirm(strs.word_filter_server_on).SendAsync(); await Response().Confirm(strs.word_filter_server_on).SendAsync();
} }
else else
{ {
_service.WordFilteringServers.TryRemove(channel.Guild.Id);
await Response().Confirm(strs.word_filter_server_off).SendAsync(); await Response().Confirm(strs.word_filter_server_off).SendAsync();
} }
} }
@ -221,32 +161,14 @@ public partial class Permissions
{ {
var channel = (ITextChannel)ctx.Channel; var channel = (ITextChannel)ctx.Channel;
FilterWordsChannelId removed; var enabled = await _service.ToggleChannelWordFilteringAsync(channel.Guild.Id, channel.Id);
await using (var uow = _db.GetDbContext())
{
var config =
uow.GuildConfigsForId(channel.Guild.Id, set => set.Include(gc => gc.FilterWordsChannelIds));
var match = new FilterWordsChannelId if (enabled)
{
ChannelId = channel.Id
};
removed = config.FilterWordsChannelIds.FirstOrDefault(fc => fc.Equals(match));
if (removed is null)
config.FilterWordsChannelIds.Add(match);
else
uow.Remove(removed);
await uow.SaveChangesAsync();
}
if (removed is null)
{ {
_service.WordFilteringChannels.Add(channel.Id);
await Response().Confirm(strs.word_filter_channel_on).SendAsync(); await Response().Confirm(strs.word_filter_channel_on).SendAsync();
} }
else else
{ {
_service.WordFilteringChannels.TryRemove(channel.Id);
await Response().Confirm(strs.word_filter_channel_off).SendAsync(); await Response().Confirm(strs.word_filter_channel_off).SendAsync();
} }
} }
@ -255,44 +177,17 @@ public partial class Permissions
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task FilterWord([Leftover] string word) public async Task FilterWord([Leftover] string word)
{ {
var channel = (ITextChannel)ctx.Channel;
word = word?.Trim().ToLowerInvariant();
if (string.IsNullOrWhiteSpace(word)) if (string.IsNullOrWhiteSpace(word))
return; return;
FilteredWord removed; var enabled = await _service.ToggleFilteredWordAsync(ctx.Guild.Id, word);
await using (var uow = _db.GetDbContext())
if (enabled)
{ {
var config = uow.GuildConfigsForId(channel.Guild.Id, set => set.Include(gc => gc.FilteredWords));
removed = config.FilteredWords.FirstOrDefault(fw => fw.Word.Trim().ToLowerInvariant() == word);
if (removed is null)
{
config.FilteredWords.Add(new()
{
Word = word
});
}
else
uow.Remove(removed);
await uow.SaveChangesAsync();
}
var filteredWords =
_service.ServerFilteredWords.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<string>());
if (removed is null)
{
filteredWords.Add(word);
await Response().Confirm(strs.filter_word_add(Format.Code(word))).SendAsync(); await Response().Confirm(strs.filter_word_add(Format.Code(word))).SendAsync();
} }
else else
{ {
filteredWords.TryRemove(word);
await Response().Confirm(strs.filter_word_remove(Format.Code(word))).SendAsync(); await Response().Confirm(strs.filter_word_remove(Format.Code(word))).SendAsync();
} }
} }
@ -305,11 +200,7 @@ public partial class Permissions
if (page < 0) if (page < 0)
return; return;
var channel = (ITextChannel)ctx.Channel; var fws = _service.FilteredWordsForServer(ctx.Guild.Id);
_service.ServerFilteredWords.TryGetValue(channel.Guild.Id, out var fwHash);
var fws = fwHash.ToArray();
await Response() await Response()
.Paginated() .Paginated()

View file

@ -1,11 +1,13 @@
#nullable disable #nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors; using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models; using EllieBot.Db.Models;
namespace EllieBot.Modules.Permissions.Services; namespace EllieBot.Modules.Permissions.Services;
public sealed class FilterService : IExecOnMessage public sealed class FilterService : IExecOnMessage, IReadyExecutor
{ {
public ConcurrentHashSet<ulong> InviteFilteringChannels { get; } public ConcurrentHashSet<ulong> InviteFilteringChannels { get; }
public ConcurrentHashSet<ulong> InviteFilteringServers { get; } public ConcurrentHashSet<ulong> InviteFilteringServers { get; }
@ -23,40 +25,20 @@ public sealed class FilterService : IExecOnMessage
=> int.MaxValue - 1; => int.MaxValue - 1;
private readonly DbService _db; private readonly DbService _db;
private readonly ShardData _shardData;
public FilterService(DiscordSocketClient client, DbService db) public FilterService(DiscordSocketClient client, DbService db, ShardData shardData)
{ {
_db = db; _db = db;
_shardData = shardData;
using (var uow = db.GetDbContext()) using (var uow = db.GetDbContext())
{ {
var ids = client.GetGuildIds(); var ids = client.GetGuildIds();
var configs = uow.Set<GuildConfig>() var configs = uow.Set<GuildConfig>()
.AsQueryable() .AsQueryable()
.Include(x => x.FilteredWords) .Where(gc => ids.Contains(gc.GuildId))
.Include(x => x.FilterLinksChannelIds) .ToList();
.Include(x => x.FilterWordsChannelIds)
.Include(x => x.FilterInvitesChannelIds)
.Where(gc => ids.Contains(gc.GuildId))
.ToList();
InviteFilteringServers = new(configs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
InviteFilteringChannels =
new(configs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
LinkFilteringServers = new(configs.Where(gc => gc.FilterLinks).Select(gc => gc.GuildId));
LinkFilteringChannels =
new(configs.SelectMany(gc => gc.FilterLinksChannelIds.Select(fci => fci.ChannelId)));
var dict = configs.ToDictionary(gc => gc.GuildId,
gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word).Distinct()));
ServerFilteredWords = new(dict);
var serverFiltering = configs.Where(gc => gc.FilterWords);
WordFilteringServers = new(serverFiltering.Select(gc => gc.GuildId));
WordFilteringChannels =
new(configs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId)));
} }
client.MessageUpdated += (oldData, newMsg, channel) => client.MessageUpdated += (oldData, newMsg, channel) =>
@ -74,6 +56,43 @@ public sealed class FilterService : IExecOnMessage
}; };
} }
public async Task OnReadyAsync()
{
await using var uow = _db.GetDbContext();
var confs = await uow.GetTable<GuildFilterConfig>()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.LoadWith(x => x.FilterInvitesChannelIds)
.LoadWith(x => x.FilterWordsChannelIds)
.LoadWith(x => x.FilterLinksChannelIds)
.LoadWith(x => x.FilteredWords)
.ToListAsyncLinqToDB();
foreach (var conf in confs)
{
foreach (var c in conf.FilterInvitesChannelIds)
InviteFilteringChannels.Add(c.ChannelId);
foreach (var c in conf.FilterWordsChannelIds)
WordFilteringChannels.Add(c.ChannelId);
foreach (var c in conf.FilterLinksChannelIds)
LinkFilteringChannels.Add(c.ChannelId);
if (conf.FilterInvites)
InviteFilteringServers.Add(conf.GuildId);
if (conf.FilterWords)
WordFilteringServers.Add(conf.GuildId);
if (conf.FilterLinks)
LinkFilteringServers.Add(conf.GuildId);
foreach (var word in conf.FilteredWords)
ServerFilteredWords.GetOrAdd(conf.GuildId, new ConcurrentHashSet<string>()).Add(word.Word);
}
}
public ConcurrentHashSet<string> FilteredWordsForChannel(ulong channelId, ulong guildId) public ConcurrentHashSet<string> FilteredWordsForChannel(ulong channelId, ulong guildId)
{ {
var words = new ConcurrentHashSet<string>(); var words = new ConcurrentHashSet<string>();
@ -82,23 +101,24 @@ public sealed class FilterService : IExecOnMessage
return words; return words;
} }
public void ClearFilteredWords(ulong guildId) public async Task ClearFilteredWords(ulong guildId)
{ {
using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, var fc = uow.FilterConfigForId(guildId,
set => set.Include(x => x.FilteredWords).Include(x => x.FilterWordsChannelIds)); set => set.Include(x => x.FilteredWords)
.Include(x => x.FilterWordsChannelIds));
WordFilteringServers.TryRemove(guildId); WordFilteringServers.TryRemove(guildId);
ServerFilteredWords.TryRemove(guildId, out _); ServerFilteredWords.TryRemove(guildId, out _);
foreach (var c in gc.FilterWordsChannelIds) foreach (var c in fc.FilterWordsChannelIds)
WordFilteringChannels.TryRemove(c.ChannelId); WordFilteringChannels.TryRemove(c.ChannelId);
gc.FilterWords = false; fc.FilterWords = false;
gc.FilteredWords.Clear(); fc.FilteredWords.Clear();
gc.FilterWordsChannelIds.Clear(); fc.FilterWordsChannelIds.Clear();
uow.SaveChanges(); await uow.SaveChangesAsync();
} }
public ConcurrentHashSet<string> FilteredWordsForServer(ulong guildId) public ConcurrentHashSet<string> FilteredWordsForServer(ulong guildId)
@ -170,7 +190,7 @@ public sealed class FilterService : IExecOnMessage
// if user has manage messages perm, don't filter // if user has manage messages perm, don't filter
if (usrMsg.Channel is ITextChannel ch && usrMsg.Author is IGuildUser gu && gu.GetPermissions(ch).ManageMessages) if (usrMsg.Channel is ITextChannel ch && usrMsg.Author is IGuildUser gu && gu.GetPermissions(ch).ManageMessages)
return false; return false;
if ((InviteFilteringChannels.Contains(usrMsg.Channel.Id) || InviteFilteringServers.Contains(guild.Id)) if ((InviteFilteringChannels.Contains(usrMsg.Channel.Id) || InviteFilteringServers.Contains(guild.Id))
&& usrMsg.Content.IsDiscordInvite()) && usrMsg.Content.IsDiscordInvite())
{ {
@ -206,7 +226,7 @@ public sealed class FilterService : IExecOnMessage
// if user has manage messages perm, don't filter // if user has manage messages perm, don't filter
if (usrMsg.Channel is ITextChannel ch && usrMsg.Author is IGuildUser gu && gu.GetPermissions(ch).ManageMessages) if (usrMsg.Channel is ITextChannel ch && usrMsg.Author is IGuildUser gu && gu.GetPermissions(ch).ManageMessages)
return false; return false;
if ((LinkFilteringChannels.Contains(usrMsg.Channel.Id) || LinkFilteringServers.Contains(guild.Id)) if ((LinkFilteringChannels.Contains(usrMsg.Channel.Id) || LinkFilteringServers.Contains(guild.Id))
&& usrMsg.Content.TryGetUrlPath(out _)) && usrMsg.Content.TryGetUrlPath(out _))
{ {
@ -233,17 +253,162 @@ public sealed class FilterService : IExecOnMessage
public async Task<ServerFilterSettings> GetFilterSettings(ulong guildId) public async Task<ServerFilterSettings> GetFilterSettings(ulong guildId)
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId,
set => set var conf = await uow.GetTable<GuildFilterConfig>()
.Include(x => x.FilterInvitesChannelIds) .Where(fi => fi.GuildId == guildId)
.Include(x => x.FilterLinksChannelIds)); .LoadWith(x => x.FilterInvitesChannelIds)
.LoadWith(x => x.FilterLinksChannelIds)
.FirstOrDefaultAsyncLinqToDB();
return new() return new()
{ {
FilterInvitesChannels = gc.FilterInvitesChannelIds.Map(x => x.ChannelId), FilterInvitesChannels = conf?.FilterInvitesChannelIds.Select(x => x.ChannelId).ToArray() ?? [],
FilterLinksChannels = gc.FilterLinksChannelIds.Map(x => x.ChannelId), FilterLinksChannels = conf?.FilterLinksChannelIds.Select(x => x.ChannelId).ToArray() ?? [],
FilterInvitesEnabled = gc.FilterInvites, FilterInvitesEnabled = false,
FilterLinksEnabled = gc.FilterLinks, FilterLinksEnabled = false,
}; };
} }
public async Task<bool> ToggleServerLinkFilteringAsync(ulong guildId)
{
await using var uow = _db.GetDbContext();
var fc = uow.FilterConfigForId(guildId);
if (LinkFilteringServers.Add(guildId))
{
fc.FilterLinks = true;
}
else
{
fc.FilterLinks = false;
}
await uow.SaveChangesAsync();
return fc.FilterLinks;
}
public async Task<bool> ToggleChannelLinkFilteringAsync(ulong guildId, ulong channelId)
{
await using var uow = _db.GetDbContext();
var fc = uow.FilterConfigForId(guildId, set => set.Include(x => x.FilterLinksChannelIds));
if (LinkFilteringChannels.Add(channelId))
{
fc.FilterLinksChannelIds.Add(new FilterLinksChannelId
{
ChannelId = channelId
});
await uow.SaveChangesAsync();
return true;
}
LinkFilteringChannels.TryRemove(channelId);
fc.FilterLinksChannelIds.RemoveWhere(x => x.ChannelId == channelId);
await uow.SaveChangesAsync();
return false;
}
public async Task<bool> ToggleServerInviteFilteringAsync(ulong guildId)
{
await using var uow = _db.GetDbContext();
var fc = uow.FilterConfigForId(guildId);
if (InviteFilteringServers.Add(guildId))
{
fc.FilterInvites = true;
await uow.SaveChangesAsync();
return true;
}
fc.FilterInvites = false;
await uow.SaveChangesAsync();
return false;
}
public async Task<bool> ToggleChannelInviteFilteringAsync(ulong guildId, ulong channelId)
{
await using var uow = _db.GetDbContext();
var fc = uow.FilterConfigForId(guildId, set => set.Include(x => x.FilterInvitesChannelIds));
if (InviteFilteringChannels.Add(channelId))
{
fc.FilterInvitesChannelIds.Add(new FilterChannelId()
{
ChannelId = channelId
});
await uow.SaveChangesAsync();
return true;
}
InviteFilteringChannels.TryRemove(channelId);
fc.FilterInvitesChannelIds.RemoveWhere(x => x.ChannelId == channelId);
await uow.SaveChangesAsync();
return false;
}
public async Task<bool> ToggleServerWordFilteringAsync(ulong guildId)
{
await using var uow = _db.GetDbContext();
var fc = uow.FilterConfigForId(guildId);
if (WordFilteringServers.Add(guildId))
{
fc.FilterWords = true;
await uow.SaveChangesAsync();
return true;
}
fc.FilterWords = false;
await uow.SaveChangesAsync();
return false;
}
public async Task<bool> ToggleChannelWordFilteringAsync(ulong guildId, ulong channelId)
{
await using var uow = _db.GetDbContext();
var fc = uow.FilterConfigForId(guildId, set => set.Include(x => x.FilterWordsChannelIds));
if (WordFilteringChannels.Add(channelId))
{
fc.FilterWordsChannelIds.Add(new FilterWordsChannelId()
{
ChannelId = channelId
});
await uow.SaveChangesAsync();
return true;
}
WordFilteringChannels.TryRemove(channelId);
fc.FilterWordsChannelIds.RemoveWhere(x => x.ChannelId == channelId);
await uow.SaveChangesAsync();
return false;
}
public async Task<bool> ToggleFilteredWordAsync(ulong guildId, string word)
{
word = word?.Trim().ToLowerInvariant();
await using var uow = _db.GetDbContext();
var fc = uow.FilterConfigForId(guildId, set => set.Include(x => x.FilteredWords));
var sfw = ServerFilteredWords.GetOrAdd(guildId, []);
if (sfw.Add(word))
{
fc.FilteredWords.Add(new FilteredWord()
{
Word = word
});
await uow.SaveChangesAsync();
}
sfw.TryRemove(word);
fc.FilteredWords.RemoveWhere(x => x.Word == word);
await uow.SaveChangesAsync();
return true;
}
} }

View file

@ -4,46 +4,52 @@ using CodeHollow.FeedReader.Feeds;
using LinqToDB; using LinqToDB;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using System.Collections.Concurrent;
namespace EllieBot.Modules.Searches.Services; namespace EllieBot.Modules.Searches.Services;
public class FeedsService : IEService public class FeedsService : IEService, IReadyExecutor
{ {
private readonly DbService _db; private readonly DbService _db;
private readonly ConcurrentDictionary<string, List<FeedSub>> _subs; private NonBlocking.ConcurrentDictionary<string, List<FeedSub>> _subs;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IMessageSenderService _sender; private readonly IMessageSenderService _sender;
private readonly ShardData _shardData;
private readonly ConcurrentDictionary<string, DateTime> _lastPosts = new(); private readonly NonBlocking.ConcurrentDictionary<string, DateTime> _lastPosts = new();
private readonly Dictionary<string, uint> _errorCounters = new(); private readonly Dictionary<string, uint> _errorCounters = new();
public FeedsService( public FeedsService(
IBot bot,
DbService db, DbService db,
DiscordSocketClient client, DiscordSocketClient client,
IMessageSenderService sender) IMessageSenderService sender,
ShardData shardData)
{ {
_db = db; _db = db;
using (var uow = db.GetDbContext())
{
var guildConfigIds = bot.AllGuildConfigs.Select(x => x.Id).ToList();
_subs = uow.Set<GuildConfig>()
.AsQueryable()
.Where(x => guildConfigIds.Contains(x.Id))
.Include(x => x.FeedSubs)
.ToList()
.SelectMany(x => x.FeedSubs)
.GroupBy(x => x.Url.ToLower())
.ToDictionary(x => x.Key, x => x.ToList())
.ToConcurrent();
}
_client = client; _client = client;
_sender = sender; _sender = sender;
_shardData = shardData;
}
_ = Task.Run(TrackFeeds); public async Task OnReadyAsync()
{
await using (var uow = _db.GetDbContext())
{
var subs = await uow.Set<FeedSub>()
.AsQueryable()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
.ToListAsyncLinqToDB();
_subs = subs
.GroupBy(x => x.Url.ToLower())
.ToDictionary(x => x.Key, x => x.ToList())
.ToConcurrent();
}
await TrackFeeds();
} }
private void ClearErrors(string url) private void ClearErrors(string url)
@ -116,7 +122,7 @@ public class FeedsService : IEService
if (items.Count > 2) if (items.Count > 2)
break; break;
} }
if (items.Count == 0) if (items.Count == 0)
continue; continue;
@ -189,7 +195,7 @@ public class FeedsService : IEService
foreach (var val in kvp.Value) foreach (var val in kvp.Value)
{ {
var ch = _client.GetGuild(val.GuildConfig.GuildId).GetTextChannel(val.ChannelId); var ch = _client.GetGuild(val.GuildId).GetTextChannel(val.ChannelId);
if (ch is null) if (ch is null)
continue; continue;
@ -228,8 +234,10 @@ public class FeedsService : IEService
public List<FeedSub> GetFeeds(ulong guildId) public List<FeedSub> GetFeeds(ulong guildId)
{ {
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
return uow.GuildConfigsForId(guildId, set => set.Include(x => x.FeedSubs))
.FeedSubs.OrderBy(x => x.Id) return uow.GetTable<FeedSub>()
.Where(x => x.GuildId == guildId)
.OrderBy(x => x.Id)
.ToList(); .ToList();
} }
@ -249,26 +257,21 @@ public class FeedsService : IEService
}; };
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.FeedSubs)); var feeds = uow.GetTable<FeedSub>()
.Where(x => x.GuildId == guildId)
.ToArray();
if (gc.FeedSubs.Any(x => x.Url.ToLower() == fs.Url.ToLower())) if (feeds.Any(x => x.Url.ToLower() == fs.Url.ToLower()))
return FeedAddResult.Duplicate; return FeedAddResult.Duplicate;
if (gc.FeedSubs.Count >= 10) if (feeds.Length >= 10)
return FeedAddResult.LimitReached; return FeedAddResult.LimitReached;
gc.FeedSubs.Add(fs); uow.Add(fs);
uow.SaveChanges(); uow.SaveChanges();
//adding all, in case bot wasn't on this guild when it started
foreach (var feed in gc.FeedSubs) _subs.AddOrUpdate(fs.Url.ToLower(),
{ [fs],
_subs.AddOrUpdate(feed.Url.ToLower(), (_, old) => old.Append(fs).ToList());
[feed],
(_, old) =>
{
old.Add(feed);
return old;
});
}
return FeedAddResult.Success; return FeedAddResult.Success;
} }
@ -279,19 +282,20 @@ public class FeedsService : IEService
return false; return false;
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var items = uow.GuildConfigsForId(guildId, set => set.Include(x => x.FeedSubs)) var items = uow.Set<FeedSub>()
.FeedSubs.OrderBy(x => x.Id) .Where(x => x.GuildId == guildId)
.ToList(); .OrderBy(x => x.Id)
.ToList();
if (items.Count <= index) if (items.Count <= index)
return false; return false;
var toRemove = items[index]; var toRemove = items[index];
_subs.AddOrUpdate(toRemove.Url.ToLower(), _subs.AddOrUpdate(toRemove.Url.ToLower(),
[], [],
(_, old) => (_, old) =>
{ {
old.Remove(toRemove); return old.Where(x => x.Id != toRemove.Id).ToList();
return old;
}); });
uow.Remove(toRemove); uow.Remove(toRemove);
uow.SaveChanges(); uow.SaveChanges();

View file

@ -1,6 +1,4 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Db.Models;
using EllieBot.Modules.Searches.Services; using EllieBot.Modules.Searches.Services;
namespace EllieBot.Modules.Searches; namespace EllieBot.Modules.Searches;
@ -100,7 +98,7 @@ public partial class Searches
[UserPerm(GuildPerm.ManageMessages)] [UserPerm(GuildPerm.ManageMessages)]
public async Task StreamOffline() public async Task StreamOffline()
{ {
var newValue = _service.ToggleStreamOffline(ctx.Guild.Id); var newValue = await _service.ToggleStreamOffline(ctx.Guild.Id);
if (newValue) if (newValue)
await Response().Confirm(strs.stream_off_enabled).SendAsync(); await Response().Confirm(strs.stream_off_enabled).SendAsync();
else else
@ -112,7 +110,7 @@ public partial class Searches
[UserPerm(GuildPerm.ManageMessages)] [UserPerm(GuildPerm.ManageMessages)]
public async Task StreamOnlineDelete() public async Task StreamOnlineDelete()
{ {
var newValue = _service.ToggleStreamOnlineDelete(ctx.Guild.Id); var newValue = await _service.ToggleStreamOnlineDelete(ctx.Guild.Id);
if (newValue) if (newValue)
await Response().Confirm(strs.stream_online_delete_enabled).SendAsync(); await Response().Confirm(strs.stream_online_delete_enabled).SendAsync();
else else
@ -151,7 +149,7 @@ public partial class Searches
var canMentionEveryone = (ctx.User as IGuildUser)?.GuildPermissions.MentionEveryone ?? true; var canMentionEveryone = (ctx.User as IGuildUser)?.GuildPermissions.MentionEveryone ?? true;
if (!canMentionEveryone) if (!canMentionEveryone)
message = message?.SanitizeAllMentions(); message = message?.SanitizeAllMentions();
var count = _service.SetStreamMessageForAll(ctx.Guild.Id, message); var count = _service.SetStreamMessageForAll(ctx.Guild.Id, message);
if (count == 0) if (count == 0)

View file

@ -1,5 +1,6 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore; using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors; using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using EllieBot.Modules.Searches.Common; using EllieBot.Modules.Searches.Common;
@ -17,16 +18,17 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
private readonly object _shardLock = new(); private readonly object _shardLock = new();
private readonly Dictionary<StreamDataKey, HashSet<ulong>> _trackCounter = new(); private Dictionary<StreamDataKey, HashSet<ulong>> _trackCounter = new();
private readonly Dictionary<StreamDataKey, Dictionary<ulong, HashSet<FollowedStream>>> _shardTrackedStreams; private Dictionary<StreamDataKey, Dictionary<ulong, HashSet<FollowedStream>>> _shardTrackedStreams;
private readonly ConcurrentHashSet<ulong> _offlineNotificationServers; private readonly ConcurrentHashSet<ulong> _offlineNotificationServers = [];
private readonly ConcurrentHashSet<ulong> _deleteOnOfflineServers; private readonly ConcurrentHashSet<ulong> _deleteOnOfflineServers = [];
private readonly IPubSub _pubSub; private readonly IPubSub _pubSub;
private readonly IMessageSenderService _sender; private readonly IMessageSenderService _sender;
private readonly SearchesConfigService _config; private readonly SearchesConfigService _config;
private readonly IReplacementService _repSvc; private readonly IReplacementService _repSvc;
private readonly ShardData _shardData;
public TypedKey<List<StreamData>> StreamsOnlineKey { get; } public TypedKey<List<StreamData>> StreamsOnlineKey { get; }
public TypedKey<List<StreamData>> StreamsOfflineKey { get; } public TypedKey<List<StreamData>> StreamsOfflineKey { get; }
@ -46,10 +48,10 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
IBotStrings strings, IBotStrings strings,
IBotCredsProvider creds, IBotCredsProvider creds,
IHttpClientFactory httpFactory, IHttpClientFactory httpFactory,
IBot bot,
IPubSub pubSub, IPubSub pubSub,
IMessageSenderService sender, IMessageSenderService sender,
SearchesConfigService config, SearchesConfigService config,
ShardData shardData,
IReplacementService repSvc) IReplacementService repSvc)
{ {
_db = db; _db = db;
@ -59,6 +61,7 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
_sender = sender; _sender = sender;
_config = config; _config = config;
_repSvc = repSvc; _repSvc = repSvc;
_shardData = shardData;
_streamTracker = new(httpFactory, creds); _streamTracker = new(httpFactory, creds);
@ -68,56 +71,6 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
_streamFollowKey = new("stream.follow"); _streamFollowKey = new("stream.follow");
_streamUnfollowKey = new("stream.unfollow"); _streamUnfollowKey = new("stream.unfollow");
using (var uow = db.GetDbContext())
{
var ids = client.GetGuildIds();
var guildConfigs = uow.Set<GuildConfig>()
.AsQueryable()
.Include(x => x.FollowedStreams)
.Where(x => ids.Contains(x.GuildId))
.ToList();
_offlineNotificationServers = new(guildConfigs
.Where(gc => gc.NotifyStreamOffline)
.Select(x => x.GuildId)
.ToList());
_deleteOnOfflineServers = new(guildConfigs
.Where(gc => gc.DeleteStreamOnlineMessage)
.Select(x => x.GuildId)
.ToList());
var followedStreams = guildConfigs.SelectMany(x => x.FollowedStreams).ToList();
_shardTrackedStreams = followedStreams.GroupBy(x => new
{
x.Type,
Name = x.Username.ToLower()
})
.ToList()
.ToDictionary(
x => new StreamDataKey(x.Key.Type, x.Key.Name.ToLower()),
x => x.GroupBy(y => y.GuildId)
.ToDictionary(y => y.Key,
y => y.AsEnumerable().ToHashSet()));
// shard 0 will keep track of when there are no more guilds which track a stream
if (client.ShardId == 0)
{
var allFollowedStreams = uow.Set<FollowedStream>().AsQueryable().ToList();
foreach (var fs in allFollowedStreams)
_streamTracker.AddLastData(fs.CreateKey(), null, false);
_trackCounter = allFollowedStreams.GroupBy(x => new
{
x.Type,
Name = x.Username.ToLower()
})
.ToDictionary(x => new StreamDataKey(x.Key.Type, x.Key.Name),
x => x.Select(fs => fs.GuildId).ToHashSet());
}
}
_pubSub.Sub(StreamsOfflineKey, HandleStreamsOffline); _pubSub.Sub(StreamsOfflineKey, HandleStreamsOffline);
_pubSub.Sub(StreamsOnlineKey, HandleStreamsOnline); _pubSub.Sub(StreamsOnlineKey, HandleStreamsOnline);
@ -134,15 +87,76 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
_pubSub.Sub(_streamUnfollowKey, HandleUnfollowStream); _pubSub.Sub(_streamUnfollowKey, HandleUnfollowStream);
} }
bot.JoinedGuild += ClientOnJoinedGuild; client.JoinedGuild += ClientOnJoinedGuild;
client.LeftGuild += ClientOnLeftGuild; client.LeftGuild += ClientOnLeftGuild;
} }
private async Task InitStateAsync()
{
await using var uow = _db.GetDbContext();
// todo check all guilds on shard for correct param order
var notifyOffline = await uow.GetTable<GuildConfig>()
.Where(gc => gc.NotifyStreamOffline)
.Select(x => x.GuildId)
.ToListAsyncLinqToDB();
foreach (var guildId in notifyOffline)
_offlineNotificationServers.Add(guildId);
var deleteOnOffline = await uow.GetTable<GuildConfig>()
.Where(gc => Queries.GuildOnShard(gc.GuildId,
_shardData.TotalShards,
_shardData.ShardId))
.Where(gc => gc.DeleteStreamOnlineMessage)
.Select(x => x.GuildId)
.ToListAsyncLinqToDB();
foreach (var guildId in deleteOnOffline)
_deleteOnOfflineServers.Add(guildId);
var followedStreams = await uow.GetTable<FollowedStream>()
.Where(x => Queries.GuildOnShard(x.GuildId,
_shardData.TotalShards,
_shardData.ShardId))
.ToListAsyncLinqToDB();
_shardTrackedStreams = followedStreams.GroupBy(x => new
{
x.Type,
Name = x.Username.ToLower()
})
.ToList()
.ToDictionary(
x => new StreamDataKey(x.Key.Type, x.Key.Name.ToLower()),
x => x.GroupBy(y => y.GuildId)
.ToDictionary(y => y.Key,
y => y.AsEnumerable().ToHashSet()));
// shard 0 will keep track of when there are no more guilds which track a stream
if (_client.ShardId == 0)
{
var allFollowedStreams = uow.Set<FollowedStream>().AsQueryable().ToList();
foreach (var fs in allFollowedStreams)
_streamTracker.AddLastData(fs.CreateKey(), null, false);
_trackCounter = allFollowedStreams.GroupBy(x => new
{
x.Type,
Name = x.Username.ToLower()
})
.ToDictionary(x => new StreamDataKey(x.Key.Type, x.Key.Name),
x => x.Select(fs => fs.GuildId).ToHashSet());
}
}
public async Task OnReadyAsync() public async Task OnReadyAsync()
{ {
if (_client.ShardId != 0) if (_client.ShardId != 0)
return; return;
await InitStateAsync();
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(30)); using var timer = new PeriodicTimer(TimeSpan.FromMinutes(30));
while (await timer.WaitForNextTickAsync()) while (await timer.WaitForNextTickAsync())
{ {
@ -198,7 +212,9 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
{ {
var key = info.Key; var key = info.Key;
if (_trackCounter.ContainsKey(key)) if (_trackCounter.ContainsKey(key))
{
_trackCounter[key].Add(info.GuildId); _trackCounter[key].Add(info.GuildId);
}
else else
{ {
_trackCounter[key] = [info.GuildId]; _trackCounter[key] = [info.GuildId];
@ -332,65 +348,67 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
private Task OnStreamsOffline(List<StreamData> data) private Task OnStreamsOffline(List<StreamData> data)
=> _pubSub.Pub(StreamsOfflineKey, data); => _pubSub.Pub(StreamsOfflineKey, data);
private Task ClientOnJoinedGuild(GuildConfig guildConfig) private async Task ClientOnJoinedGuild(SocketGuild guild)
{ {
using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var gc = uow.Set<GuildConfig>() var fs = await uow.Set<FollowedStream>()
.AsQueryable() .Where(x => x.GuildId == guild.Id)
.Include(x => x.FollowedStreams) .ToListAsyncLinqToDB();
.FirstOrDefault(x => x.GuildId == guildConfig.GuildId);
if (gc is null) var notifyOffline = await uow.GetTable<GuildConfig>()
return Task.CompletedTask; .Where(x => x.GuildId == guild.Id)
.Select(x => x.NotifyStreamOffline)
.FirstOrDefaultAsyncLinqToDB();
if (gc.NotifyStreamOffline) // todo hashset
_offlineNotificationServers.Add(gc.GuildId); if (notifyOffline)
_offlineNotificationServers.Add(guild.Id);
foreach (var followedStream in gc.FollowedStreams) foreach (var followedStream in fs)
{ {
var key = followedStream.CreateKey(); var key = followedStream.CreateKey();
var streams = GetLocalGuildStreams(key, gc.GuildId); var streams = GetLocalGuildStreams(key, guild.Id);
streams.Add(followedStream); streams.Add(followedStream);
PublishFollowStream(followedStream); PublishFollowStream(followedStream);
} }
} }
return Task.CompletedTask;
} }
private Task ClientOnLeftGuild(SocketGuild guild) private async Task ClientOnLeftGuild(SocketGuild guild)
{ {
using (var uow = _db.GetDbContext()) await using var uow = _db.GetDbContext();
var followedStreams = await uow.Set<FollowedStream>()
.Where(x => x.GuildId == guild.Id)
.ToListAsyncLinqToDB();
_offlineNotificationServers.TryRemove(guild.Id);
foreach (var followedStream in followedStreams)
{ {
var gc = uow.GuildConfigsForId(guild.Id, set => set.Include(x => x.FollowedStreams)); var streams = GetLocalGuildStreams(followedStream.CreateKey(), guild.Id);
streams.Remove(followedStream);
_offlineNotificationServers.TryRemove(gc.GuildId); await PublishUnfollowStream(followedStream);
foreach (var followedStream in gc.FollowedStreams)
{
var streams = GetLocalGuildStreams(followedStream.CreateKey(), guild.Id);
streams.Remove(followedStream);
PublishUnfollowStream(followedStream);
}
} }
return Task.CompletedTask;
} }
public async Task<int> ClearAllStreams(ulong guildId) public async Task<int> ClearAllStreams(ulong guildId)
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.FollowedStreams));
uow.RemoveRange(gc.FollowedStreams);
foreach (var s in gc.FollowedStreams) var followedStreams = await uow.Set<FollowedStream>()
.Where(x => x.GuildId == guildId)
.ToListAsyncEF();
uow.RemoveRange(followedStreams);
foreach (var s in followedStreams)
await PublishUnfollowStream(s); await PublishUnfollowStream(s);
uow.SaveChanges(); await uow.SaveChangesAsync();
return gc.FollowedStreams.Count; return followedStreams.Count;
} }
public async Task<FollowedStream> UnfollowStreamAsync(ulong guildId, int index) public async Task<FollowedStream> UnfollowStreamAsync(ulong guildId, int index)
@ -398,11 +416,11 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
FollowedStream fs; FollowedStream fs;
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var fss = uow.Set<FollowedStream>() var fss = await uow.Set<FollowedStream>()
.AsQueryable() .AsQueryable()
.Where(x => x.GuildId == guildId) .Where(x => x.GuildId == guildId)
.OrderBy(x => x.Id) .OrderBy(x => x.Id)
.ToList(); .ToListAsyncEF();
// out of range // out of range
if (fss.Count <= index) if (fss.Count <= index)
@ -454,7 +472,9 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
FollowedStream fs; FollowedStream fs;
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.FollowedStreams)); var followedStreams = await uow.Set<FollowedStream>()
.Where(x => x.GuildId == guildId)
.ToListAsyncEF();
// add it to the database // add it to the database
fs = new() fs = new()
@ -467,10 +487,10 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
var config = _config.Data; var config = _config.Data;
if (config.FollowedStreams.MaxCount is not -1 if (config.FollowedStreams.MaxCount is not -1
&& gc.FollowedStreams.Count >= config.FollowedStreams.MaxCount) && followedStreams.Count >= config.FollowedStreams.MaxCount)
return null; return null;
gc.FollowedStreams.Add(fs); uow.Add(fs);
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();
// add it to the local cache of tracked streams // add it to the local cache of tracked streams
@ -529,13 +549,27 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
private string GetText(ulong guildId, LocStr str) private string GetText(ulong guildId, LocStr str)
=> _strings.GetText(str, guildId); => _strings.GetText(str, guildId);
public bool ToggleStreamOffline(ulong guildId) public async Task<bool> ToggleStreamOffline(ulong guildId)
{ {
bool newValue; await using var uow = _db.GetDbContext();
using var uow = _db.GetDbContext(); await uow.GetTable<GuildConfig>()
var gc = uow.GuildConfigsForId(guildId, set => set); .InsertOrUpdateAsync(() => new()
newValue = gc.NotifyStreamOffline = !gc.NotifyStreamOffline; {
uow.SaveChanges(); GuildId = guildId,
},
(old) => new()
{
NotifyStreamOffline = !old.NotifyStreamOffline
},
() => new()
{
GuildId = guildId
});
var newValue = await uow.GetTable<GuildConfig>()
.Where(x => x.GuildId == guildId)
.Select(x => x.NotifyStreamOffline)
.FirstOrDefaultAsyncLinqToDB();
if (newValue) if (newValue)
_offlineNotificationServers.Add(guildId); _offlineNotificationServers.Add(guildId);
@ -545,12 +579,27 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
return newValue; return newValue;
} }
public bool ToggleStreamOnlineDelete(ulong guildId) public async Task<bool> ToggleStreamOnlineDelete(ulong guildId)
{ {
using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set); await uow.GetTable<GuildConfig>()
var newValue = gc.DeleteStreamOnlineMessage = !gc.DeleteStreamOnlineMessage; .InsertOrUpdateAsync(() => new()
uow.SaveChanges(); {
GuildId = guildId,
},
(old) => new()
{
DeleteStreamOnlineMessage = !old.DeleteStreamOnlineMessage
},
() => new()
{
GuildId = guildId
});
var newValue = await uow.GetTable<GuildConfig>()
.Where(x => x.GuildId == guildId)
.Select(x => x.DeleteStreamOnlineMessage)
.FirstOrDefaultAsyncLinqToDB();
if (newValue) if (newValue)
_deleteOnOfflineServers.Add(guildId); _deleteOnOfflineServers.Add(guildId);
@ -650,13 +699,13 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
public async Task<List<FollowedStream>> GetAllStreamsAsync(SocketGuild guild) public async Task<List<FollowedStream>> GetAllStreamsAsync(SocketGuild guild)
{ {
var allStreams = new List<FollowedStream>();
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var all = uow.GuildConfigsForId(guild.Id, set => set.Include(gc => gc.FollowedStreams)) var all = await uow.Set<FollowedStream>()
.FollowedStreams .Where(x => x.GuildId == guild.Id)
.OrderBy(x => x.Id) .OrderBy(x => x.Id)
.ToList(); .ToListAsyncEF();
var allStreams = new List<FollowedStream>();
for (var index = all.Count - 1; index >= 0; index--) for (var index = all.Count - 1; index >= 0; index--)
{ {
var fs = all[index]; var fs = all[index];

View file

@ -48,7 +48,7 @@ public sealed partial class FlagTranslateService : IReadyExecutor, IEService
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
_enabledChannels = (await uow.GetTable<FlagTranslateChannel>() _enabledChannels = (await uow.GetTable<FlagTranslateChannel>()
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, .Where(x => Queries.GuildOnShard(x.GuildId,
_creds.TotalShards, _creds.TotalShards,
_client.ShardId)) _client.ShardId))
.Select(x => new .Select(x => new

View file

@ -35,7 +35,9 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
List<AutoTranslateChannel> cs; List<AutoTranslateChannel> cs;
await using (var ctx = _db.GetDbContext()) await using (var ctx = _db.GetDbContext())
{ {
var guilds = _bot.AllGuildConfigs.Select(x => x.GuildId).ToList(); var guilds = await ctx.GetTable<GuildConfig>()
.Select(x => x.GuildId)
.ToListAsyncLinqToDB();
cs = await ctx.Set<AutoTranslateChannel>().Include(x => x.Users) cs = await ctx.Set<AutoTranslateChannel>().Include(x => x.Users)
.Where(x => guilds.Contains(x.GuildId)) .Where(x => guilds.Contains(x.GuildId))
.ToListAsyncEF(); .ToListAsyncEF();
@ -106,8 +108,8 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
{ {
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var old = await ctx.Set<AutoTranslateChannel>().ToLinqToDBTable() var old = await ctx.Set<AutoTranslateChannel>()
.FirstOrDefaultAsyncLinqToDB(x => x.ChannelId == channelId); .FirstOrDefaultAsyncEF(x => x.ChannelId == channelId);
if (old is null) if (old is null)
{ {

View file

@ -3,7 +3,7 @@ using EllieBot.Db.Models;
namespace EllieBot.Modules.Searches.Common; namespace EllieBot.Modules.Searches.Common;
public readonly struct StreamDataKey public readonly record struct StreamDataKey
{ {
public FollowedStream.FType Type { get; init; } public FollowedStream.FType Type { get; init; }
public string Name { get; init; } public string Name { get; init; }

View file

@ -24,7 +24,7 @@ public partial class Utility
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public async Task AliasesClear() public async Task AliasesClear()
{ {
var count = _service.ClearAliases(ctx.Guild.Id); var count = await _service.ClearAliases(ctx.Guild.Id);
await Response().Confirm(strs.aliases_cleared(count)).SendAsync(); await Response().Confirm(strs.aliases_cleared(count)).SendAsync();
} }
@ -40,64 +40,17 @@ public partial class Utility
if (string.IsNullOrWhiteSpace(mapping)) if (string.IsNullOrWhiteSpace(mapping))
{ {
if (!_service.AliasMaps.TryGetValue(ctx.Guild.Id, out var maps) || !maps.TryRemove(trigger, out _)) if (!await _service.RemoveAliasAsync(ctx.Guild.Id, trigger))
{ {
await Response().Error(strs.alias_remove_fail(Format.Code(trigger))).SendAsync(); await Response().Error(strs.alias_remove_fail(Format.Code(trigger))).SendAsync();
return; return;
} }
await using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(ctx.Guild.Id, set => set.Include(x => x.CommandAliases));
var tr = config.CommandAliases.FirstOrDefault(x => x.Trigger == trigger);
if (tr is not null)
uow.Set<CommandAlias>().Remove(tr);
uow.SaveChanges();
}
await Response().Confirm(strs.alias_removed(Format.Code(trigger))).SendAsync(); await Response().Confirm(strs.alias_removed(Format.Code(trigger))).SendAsync();
return; return;
} }
_service.AliasMaps.AddOrUpdate(ctx.Guild.Id, await _service.AddAliasAsync(ctx.Guild.Id, trigger, mapping);
_ =>
{
using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(ctx.Guild.Id, set => set.Include(x => x.CommandAliases));
config.CommandAliases.Add(new()
{
Mapping = mapping,
Trigger = trigger
});
uow.SaveChanges();
}
return new(new Dictionary<string, string>
{
{ trigger.Trim().ToLowerInvariant(), mapping.ToLowerInvariant() }
});
},
(_, map) =>
{
using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(ctx.Guild.Id, set => set.Include(x => x.CommandAliases));
var toAdd = new CommandAlias
{
Mapping = mapping,
Trigger = trigger
};
var toRemove = config.CommandAliases.Where(x => x.Trigger == trigger).ToArray();
if (toRemove.Any())
uow.RemoveRange(toRemove);
config.CommandAliases.Add(toAdd);
uow.SaveChanges();
}
map.AddOrUpdate(trigger, mapping, (_, _) => mapping);
return map;
});
await Response().Confirm(strs.alias_added(Format.Code(trigger), Format.Code(mapping))).SendAsync(); await Response().Confirm(strs.alias_added(Format.Code(trigger), Format.Code(mapping))).SendAsync();
} }
@ -112,13 +65,14 @@ public partial class Utility
if (page < 0) if (page < 0)
return; return;
if (!_service.AliasMaps.TryGetValue(ctx.Guild.Id, out var maps) || !maps.Any()) var aliases = await _service.GetAliasesAsync(ctx.Guild.Id);
if (aliases is null || aliases.Count == 0)
{ {
await Response().Error(strs.aliases_none).SendAsync(); await Response().Error(strs.aliases_none).SendAsync();
return; return;
} }
var arr = maps.ToArray(); var arr = aliases.Select(x => (Trigger: x.Key, Mapping: x.Value)).ToArray();
await Response() await Response()
.Paginated() .Paginated()
@ -130,7 +84,7 @@ public partial class Utility
return CreateEmbed() return CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.alias_list)) .WithTitle(GetText(strs.alias_list))
.WithDescription(string.Join("\n", items.Select(x => $"`{x.Key}` => `{x.Value}`"))); .WithDescription(string.Join("\n", items.Select(x => $"`{x.Trigger}` => `{x.Mapping}`")));
}) })
.SendAsync(); .SendAsync();
} }

View file

@ -1,54 +1,47 @@
#nullable disable using LinqToDB;
using Microsoft.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors; using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models; using EllieBot.Db.Models;
namespace EllieBot.Modules.Utility.Services; namespace EllieBot.Modules.Utility.Services;
public class AliasService : IInputTransformer, IEService public class AliasService : IInputTransformer, IReadyExecutor, IEService
{ {
public ConcurrentDictionary<ulong, ConcurrentDictionary<string, string>> AliasMaps { get; } = new(); private ConcurrentDictionary<ulong, ConcurrentDictionary<string, string>> _aliases = new();
private readonly DbService _db; private readonly DbService _db;
private readonly IMessageSenderService _sender; private readonly IMessageSenderService _sender;
private readonly ShardData _shardData;
public AliasService( public AliasService(
DiscordSocketClient client,
DbService db, DbService db,
IMessageSenderService sender) IMessageSenderService sender,
ShardData shardData)
{ {
_sender = sender; _sender = sender;
_shardData = shardData;
using var uow = db.GetDbContext(); using var uow = db.GetDbContext();
var guildIds = client.Guilds.Select(x => x.Id).ToList();
var configs = uow.Set<GuildConfig>()
.Include(gc => gc.CommandAliases)
.Where(x => guildIds.Contains(x.GuildId))
.ToList();
AliasMaps = new(configs.ToDictionary(x => x.GuildId,
x => new ConcurrentDictionary<string, string>(x.CommandAliases.DistinctBy(ca => ca.Trigger)
.ToDictionary(ca => ca.Trigger, ca => ca.Mapping),
StringComparer.OrdinalIgnoreCase)));
_db = db; _db = db;
} }
public int ClearAliases(ulong guildId) public async Task<int> ClearAliases(ulong guildId)
{ {
AliasMaps.TryRemove(guildId, out _); _aliases.TryRemove(guildId, out _);
int count; await using var uow = _db.GetDbContext();
using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.CommandAliases)); var deleted = await uow.GetTable<CommandAlias>()
count = gc.CommandAliases.Count; .Where(x => x.GuildId == guildId)
gc.CommandAliases.Clear(); .DeleteAsync();
uow.SaveChanges();
return count; return deleted;
} }
public async Task<string> TransformInput( public async Task<string?> TransformInput(
IGuild guild, IGuild? guild,
IMessageChannel channel, IMessageChannel channel,
IUser user, IUser user,
string input) string input)
@ -56,39 +49,47 @@ public class AliasService : IInputTransformer, IEService
if (guild is null || string.IsNullOrWhiteSpace(input)) if (guild is null || string.IsNullOrWhiteSpace(input))
return null; return null;
if (AliasMaps.TryGetValue(guild.Id, out var maps)) if (_aliases.TryGetValue(guild.Id, out var maps))
{ {
string newInput = null; string? newInput = null;
foreach (var (k, v) in maps)
if (maps.TryGetValue(input, out var alias))
{ {
if (string.Equals(input, k, StringComparison.OrdinalIgnoreCase)) newInput = alias;
}
else
{
foreach (var (k, v) in maps)
{ {
newInput = v; if (string.Equals(input, k, StringComparison.OrdinalIgnoreCase))
{
newInput = v;
}
else if (input.StartsWith(k + ' ', StringComparison.OrdinalIgnoreCase))
{
if (v.Contains("%target%"))
newInput = v.Replace("%target%", input[k.Length..]);
else
newInput = v + ' ' + input[k.Length..];
}
} }
else if (input.StartsWith(k + ' ', StringComparison.OrdinalIgnoreCase)) }
if (newInput is not null)
{
try
{ {
if (v.Contains("%target%")) var toDelete = await _sender.Response(channel)
newInput = v.Replace("%target%", input[k.Length..]); .Confirm($"{input} => {newInput}")
else .SendAsync();
newInput = v + ' ' + input[k.Length..]; toDelete.DeleteAfter(1.5f);
}
catch
{
// ignored
} }
if (newInput is not null) return newInput;
{
try
{
var toDelete = await _sender.Response(channel)
.Confirm($"{input} => {newInput}")
.SendAsync();
toDelete.DeleteAfter(1.5f);
}
catch
{
// ignored
}
return newInput;
}
} }
return null; return null;
@ -96,4 +97,71 @@ public class AliasService : IInputTransformer, IEService
return null; return null;
} }
public async Task OnReadyAsync()
{
await using var ctx = _db.GetDbContext();
var aliases = ctx.GetTable<CommandAlias>()
.Where(x => Queries.GuildOnShard(x.GuildId,
_shardData.TotalShards,
_shardData.ShardId))
.ToList();
_aliases = new();
foreach (var alias in aliases)
{
_aliases.GetOrAdd(alias.GuildId, _ => new(StringComparer.OrdinalIgnoreCase))
.TryAdd(alias.Trigger, alias.Mapping);
}
}
public async Task<bool> RemoveAliasAsync(ulong guildId, string trigger)
{
await using var ctx = _db.GetDbContext();
var deleted = await ctx.GetTable<CommandAlias>()
.Where(x => x.GuildId == guildId && x.Trigger == trigger)
.DeleteAsync();
if (_aliases.TryGetValue(guildId, out var aliases))
aliases.TryRemove(trigger, out _);
return deleted > 0;
}
public async Task AddAliasAsync(ulong guildId, string trigger, string mapping)
{
await using var ctx = _db.GetDbContext();
await ctx.GetTable<CommandAlias>()
.InsertOrUpdateAsync(() => new()
{
GuildId = guildId,
Trigger = trigger,
Mapping = mapping,
},
(old) => new()
{
Mapping = mapping
},
() => new()
{
GuildId = guildId,
Trigger = trigger,
});
var guildDict = _aliases.GetOrAdd(guildId, (_) => new());
guildDict[trigger] = mapping;
}
public async Task<IReadOnlyDictionary<string, string>?> GetAliasesAsync(ulong guildId)
{
await Task.Yield();
if (_aliases.TryGetValue(guildId, out var aliases))
return aliases;
return null;
}
} }

View file

@ -94,7 +94,7 @@ public sealed class GiveawayService : IEService, IReadyExecutor
var gas = await ctx var gas = await ctx
.GetTable<GiveawayModel>() .GetTable<GiveawayModel>()
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId)) .Where(x => Queries.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId))
.ToArrayAsync(); .ToArrayAsync();
lock (_giveawayCache) lock (_giveawayCache)

View file

@ -93,7 +93,7 @@ public class RemindService : IEService, IReadyExecutor, IRemindService
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var earliest = await uow.Set<Reminder>() var earliest = await uow.Set<Reminder>()
.ToLinqToDBTable() .ToLinqToDBTable()
.Where(x => Linq2DbExpressions.GuildOnShard(x.ServerId, .Where(x => Queries.GuildOnShard(x.ServerId,
_creds.TotalShards, _creds.TotalShards,
_client.ShardId)) _client.ShardId))
.OrderBy(x => x.When) .OrderBy(x => x.When)
@ -116,7 +116,7 @@ public class RemindService : IEService, IReadyExecutor, IRemindService
var reminders = await uow.Set<Reminder>() var reminders = await uow.Set<Reminder>()
.ToLinqToDBTable() .ToLinqToDBTable()
.Where(x => Linq2DbExpressions.GuildOnShard(x.ServerId, .Where(x => Queries.GuildOnShard(x.ServerId,
_creds.TotalShards, _creds.TotalShards,
_client.ShardId)) _client.ShardId))
.Where(x => x.When <= now) .Where(x => x.When <= now)

View file

@ -3,6 +3,7 @@ using EllieBot.Modules.Utility.Common;
using EllieBot.Modules.Utility.Common.Exceptions; using EllieBot.Modules.Utility.Common.Exceptions;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using System.Net; using System.Net;
using LinqToDB.EntityFrameworkCore;
namespace EllieBot.Modules.Utility.Services; namespace EllieBot.Modules.Utility.Services;
@ -10,26 +11,19 @@ public class StreamRoleService : IReadyExecutor, IEService
{ {
private readonly DbService _db; private readonly DbService _db;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly ConcurrentDictionary<ulong, StreamRoleSettings> _guildSettings; private ConcurrentDictionary<ulong, StreamRoleSettings> _guildSettings = new();
private readonly QueueRunner _queueRunner; private QueueRunner _queueRunner;
public StreamRoleService(DiscordSocketClient client, DbService db, IBot bot) public StreamRoleService(DiscordSocketClient client, DbService db)
{ {
_db = db; _db = db;
_client = client; _client = client;
_guildSettings = bot.AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.StreamRole)
.Where(x => x.Value is { Enabled: true })
.ToConcurrent();
_client.PresenceUpdated += OnPresenceUpdate;
_queueRunner = new QueueRunner(); _queueRunner = new QueueRunner();
} }
private Task OnPresenceUpdate(SocketUser user, SocketPresence? oldPresence, SocketPresence? newPresence) private Task OnPresenceUpdate(SocketUser user, SocketPresence? oldPresence, SocketPresence? newPresence)
{ {
_ = Task.Run(async () => _ = Task.Run(async () =>
{ {
if (oldPresence?.Activities?.Count != newPresence?.Activities?.Count) if (oldPresence?.Activities?.Count != newPresence?.Activities?.Count)
@ -49,8 +43,21 @@ public class StreamRoleService : IReadyExecutor, IEService
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task OnReadyAsync() public async Task OnReadyAsync()
=> Task.WhenAll(_client.Guilds.Select(RescanUsers).WhenAll(), _queueRunner.RunAsync()); {
await using var uow = _db.GetDbContext();
_guildSettings = await uow.GetTable<StreamRoleSettings>()
.Where(x => x.Enabled)
.ToDictionaryAsyncLinqToDB(x => x.GuildId, x => x)
.Fmap(x => x.ToConcurrent());
_client.PresenceUpdated += OnPresenceUpdate;
await Task.WhenAll(_client.Guilds.Select(RescanUsers).WhenAll(), _queueRunner.RunAsync());
}
/// <summary> /// <summary>
/// Adds or removes a user from a blacklist or a whitelist in the specified guild. /// Adds or removes a user from a blacklist or a whitelist in the specified guild.
@ -73,7 +80,7 @@ public class StreamRoleService : IReadyExecutor, IEService
var success = false; var success = false;
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var streamRoleSettings = uow.GetStreamRoleSettings(guild.Id); var streamRoleSettings = await uow.GetOrCreateStreamRoleSettings(guild.Id);
if (listType == StreamRoleListType.Whitelist) if (listType == StreamRoleListType.Whitelist)
{ {
@ -93,7 +100,9 @@ public class StreamRoleService : IReadyExecutor, IEService
} }
} }
else else
{
success = streamRoleSettings.Whitelist.Add(userObj); success = streamRoleSettings.Whitelist.Add(userObj);
}
} }
else else
{ {
@ -110,7 +119,9 @@ public class StreamRoleService : IReadyExecutor, IEService
success = streamRoleSettings.Blacklist.Remove(toRemove); success = streamRoleSettings.Blacklist.Remove(toRemove);
} }
else else
{
success = streamRoleSettings.Blacklist.Add(userObj); success = streamRoleSettings.Blacklist.Add(userObj);
}
} }
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();
@ -134,7 +145,7 @@ public class StreamRoleService : IReadyExecutor, IEService
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var streamRoleSettings = uow.GetStreamRoleSettings(guild.Id); var streamRoleSettings = await uow.GetOrCreateStreamRoleSettings(guild.Id);
streamRoleSettings.Keyword = keyword; streamRoleSettings.Keyword = keyword;
UpdateCache(guild.Id, streamRoleSettings); UpdateCache(guild.Id, streamRoleSettings);
@ -150,15 +161,15 @@ public class StreamRoleService : IReadyExecutor, IEService
/// </summary> /// </summary>
/// <param name="guildId">Guild Id</param> /// <param name="guildId">Guild Id</param>
/// <returns>The keyword set</returns> /// <returns>The keyword set</returns>
public string GetKeyword(ulong guildId) public async Task<string> GetKeyword(ulong guildId)
{ {
if (_guildSettings.TryGetValue(guildId, out var outSetting)) if (_guildSettings.TryGetValue(guildId, out var outSetting))
return outSetting.Keyword; return outSetting.Keyword;
StreamRoleSettings setting; StreamRoleSettings setting;
using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
setting = uow.GetStreamRoleSettings(guildId); setting = await uow.GetOrCreateStreamRoleSettings(guildId);
} }
UpdateCache(guildId, setting); UpdateCache(guildId, setting);
@ -180,7 +191,7 @@ public class StreamRoleService : IReadyExecutor, IEService
StreamRoleSettings setting; StreamRoleSettings setting;
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var streamRoleSettings = uow.GetStreamRoleSettings(fromRole.Guild.Id); var streamRoleSettings = await uow.GetOrCreateStreamRoleSettings(fromRole.Guild.Id);
streamRoleSettings.Enabled = true; streamRoleSettings.Enabled = true;
streamRoleSettings.AddRoleId = addRole.Id; streamRoleSettings.AddRoleId = addRole.Id;
@ -207,7 +218,7 @@ public class StreamRoleService : IReadyExecutor, IEService
{ {
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var streamRoleSettings = uow.GetStreamRoleSettings(guild.Id); var streamRoleSettings = await uow.GetOrCreateStreamRoleSettings(guild.Id);
streamRoleSettings.Enabled = false; streamRoleSettings.Enabled = false;
streamRoleSettings.AddRoleId = 0; streamRoleSettings.AddRoleId = 0;
streamRoleSettings.FromRoleId = 0; streamRoleSettings.FromRoleId = 0;

View file

@ -699,7 +699,7 @@ public partial class Utility : EllieModule
[UserPerm(GuildPerm.ManageMessages)] [UserPerm(GuildPerm.ManageMessages)]
public async Task VerboseError(bool? newstate = null) public async Task VerboseError(bool? newstate = null)
{ {
var state = _veService.ToggleVerboseErrors(ctx.Guild.Id, newstate); var state = await _veService.ToggleVerboseErrors(ctx.Guild.Id, newstate);
if (state) if (state)
await Response().Confirm(strs.verbose_errors_enabled).SendAsync(); await Response().Confirm(strs.verbose_errors_enabled).SendAsync();

View file

@ -1,29 +1,31 @@
#nullable disable #nullable disable
using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Utility.Services; namespace EllieBot.Modules.Utility.Services;
public class VerboseErrorsService : IEService public class VerboseErrorsService : IReadyExecutor, IEService
{ {
private readonly ConcurrentHashSet<ulong> _guildsDisabled; private readonly ConcurrentHashSet<ulong> _guildsDisabled = [];
private readonly DbService _db; private readonly DbService _db;
private readonly CommandHandler _ch; private readonly CommandHandler _ch;
private readonly ICommandsUtilityService _hs; private readonly ICommandsUtilityService _hs;
private readonly IMessageSenderService _sender; private readonly IMessageSenderService _sender;
private readonly ShardData _shardData;
public VerboseErrorsService( public VerboseErrorsService(
IBot bot,
DbService db, DbService db,
CommandHandler ch, CommandHandler ch,
IMessageSenderService sender, IMessageSenderService sender,
ICommandsUtilityService hs) ICommandsUtilityService hs,
ShardData shardData)
{ {
_db = db; _db = db;
_ch = ch; _ch = ch;
_hs = hs; _hs = hs;
_sender = sender; _sender = sender;
_shardData = shardData;
_ch.CommandErrored += LogVerboseError;
_guildsDisabled = new(bot.AllGuildConfigs.Where(x => !x.VerboseErrors).Select(x => x.GuildId));
} }
private async Task LogVerboseError(CommandInfo cmd, ITextChannel channel, string reason) private async Task LogVerboseError(CommandInfo cmd, ITextChannel channel, string reason)
@ -48,23 +50,37 @@ public class VerboseErrorsService : IEService
} }
} }
public bool ToggleVerboseErrors(ulong guildId, bool? maybeEnabled = null) public async Task<bool> ToggleVerboseErrors(ulong guildId, bool? maybeEnabled = null)
{ {
using var uow = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set);
if (maybeEnabled is bool isEnabled) // set it var isEnabled = ctx.GetTable<GuildConfig>()
gc.VerboseErrors = isEnabled; .Where(x => x.GuildId == guildId)
else // toggle it .Select(x => x.VerboseErrors)
isEnabled = gc.VerboseErrors = !gc.VerboseErrors; .FirstOrDefault();
uow.SaveChanges();
if (isEnabled) // This doesn't need to be duplicated inside the using block if (isEnabled) // This doesn't need to be duplicated inside the using block
{
_guildsDisabled.TryRemove(guildId); _guildsDisabled.TryRemove(guildId);
}
else else
{
_guildsDisabled.Add(guildId); _guildsDisabled.Add(guildId);
}
return isEnabled; return isEnabled;
} }
public async Task OnReadyAsync()
{
await using var ctx = _db.GetDbContext();
var disabledOn = ctx.GetTable<GuildConfig>()
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId) && !x.VerboseErrors)
.Select(x => x.GuildId);
foreach (var guildId in disabledOn)
_guildsDisabled.Add(guildId);
_ch.CommandErrored += LogVerboseError;
}
} }

View file

@ -56,7 +56,7 @@ public partial class Xp : EllieModule<XpService>
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public async Task XpExclude(Server _) public async Task XpExclude(Server _)
{ {
var ex = _service.ToggleExcludeServer(ctx.Guild.Id); var ex = await _service.ToggleExcludeServerAsync(ctx.Guild.Id);
if (ex) if (ex)
await Response().Confirm(strs.excluded(Format.Bold(ctx.Guild.ToString()))).SendAsync(); await Response().Confirm(strs.excluded(Format.Bold(ctx.Guild.ToString()))).SendAsync();
@ -69,7 +69,7 @@ public partial class Xp : EllieModule<XpService>
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task XpExclude(Role _, [Leftover] IRole role) public async Task XpExclude(Role _, [Leftover] IRole role)
{ {
var ex = _service.ToggleExcludeRole(ctx.Guild.Id, role.Id); var ex = await _service.ToggleExcludeRoleAsync(ctx.Guild.Id, role.Id);
if (ex) if (ex)
await Response().Confirm(strs.excluded(Format.Bold(role.ToString()))).SendAsync(); await Response().Confirm(strs.excluded(Format.Bold(role.ToString()))).SendAsync();
@ -85,7 +85,7 @@ public partial class Xp : EllieModule<XpService>
if (channel is null) if (channel is null)
channel = ctx.Channel; channel = ctx.Channel;
var ex = _service.ToggleExcludeChannel(ctx.Guild.Id, channel.Id); var ex = await _service.ToggleExcludeChannelAsync(ctx.Guild.Id, channel.Id);
if (ex) if (ex)
await Response().Confirm(strs.excluded(Format.Bold(channel.ToString()))).SendAsync(); await Response().Confirm(strs.excluded(Format.Bold(channel.ToString()))).SendAsync();
@ -308,7 +308,7 @@ public partial class Xp : EllieModule<XpService>
if (amount == 0) if (amount == 0)
return; return;
_service.AddXp(userId, ctx.Guild.Id, amount); await _service.AddXpAsync(userId, ctx.Guild.Id, amount);
var usr = ((SocketGuild)ctx.Guild).GetUser(userId)?.ToString() ?? userId.ToString(); var usr = ((SocketGuild)ctx.Guild).GetUser(userId)?.ToString() ?? userId.ToString();
await Response().Confirm(strs.modified(Format.Bold(usr), Format.Bold(amount.ToString()))).SendAsync(); await Response().Confirm(strs.modified(Format.Bold(usr), Format.Bold(amount.ToString()))).SendAsync();
} }
@ -455,7 +455,7 @@ public partial class Xp : EllieModule<XpService>
if (!string.IsNullOrWhiteSpace(item.Desc)) if (!string.IsNullOrWhiteSpace(item.Desc))
eb.AddField(GetText(strs.desc), item.Desc); eb.AddField(GetText(strs.desc), item.Desc);
#if GLOBAL_ELLIE #if GLOBAL_NADEKO
if (key == "default") if (key == "default")
eb.WithDescription(GetText(strs.xpshop_website)); eb.WithDescription(GetText(strs.xpshop_website));
#endif #endif

Some files were not shown because too many files have changed in this diff Show more