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
- `.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`
- 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

View file

@ -1,12 +1,9 @@
#nullable disable
using DryIoc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using EllieBot.Common.Configs;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection;
using RunMode = Discord.Commands.RunMode;
@ -15,10 +12,7 @@ namespace EllieBot;
public sealed class Bot : IBot
{
public event Func<GuildConfig, Task> JoinedGuild = delegate { return Task.CompletedTask; };
public DiscordSocketClient Client { get; }
public IReadOnlyCollection<GuildConfig> AllGuildConfigs { get; private set; }
private IContainer Services { get; set; }
@ -102,7 +96,6 @@ public sealed class Bot : IBot
await using (var uow = _db.GetDbContext())
{
AllGuildConfigs = await uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList);
uow.EnsureUserCreated(bot.Id, bot.Username, bot.Discriminator, bot.AvatarId);
}
@ -114,10 +107,6 @@ public sealed class Bot : IBot
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<DbService, DbService>(_db);
svcs.AddSingleton<IBotCredsProvider>(_credsProvider);
@ -245,16 +234,6 @@ public sealed class Bot : IBot
private Task Client_JoinedGuild(SocketGuild arg)
{
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;
}

View file

@ -1,8 +1,8 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using EllieBot.Db.Models;
using EllieBot.Modules.Administration.Services;
// ReSharper disable UnusedAutoPropertyAccessor.Global
@ -11,6 +11,11 @@ namespace EllieBot.Db;
public abstract class EllieContext : DbContext
{
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<Quote> Quotes { get; set; }
@ -46,7 +51,6 @@ public abstract class EllieContext : DbContext
public DbSet<AutoTranslateChannel> AutoTranslateChannels { get; set; }
public DbSet<AutoTranslateUser> AutoTranslateUsers { get; set; }
public DbSet<Permissionv2> Permissions { get; set; }
public DbSet<BankUser> BankUsers { get; set; }
@ -76,7 +80,7 @@ public abstract class EllieContext : DbContext
{
// load all entities from current assembly
modelBuilder.ApplyConfigurationsFromAssembly(typeof(EllieContext).Assembly);
#region Notify
modelBuilder.Entity<Notify>(e =>
@ -170,10 +174,10 @@ public abstract class EllieContext : DbContext
modelBuilder.Entity<UserBetStats>(ubs =>
{
ubs.HasIndex(x => new
{
x.UserId,
x.Game
})
{
x.UserId,
x.Game
})
.IsUnique();
ubs.HasIndex(x => x.MaxWin)
@ -221,174 +225,8 @@ public abstract class EllieContext : DbContext
configEntity.Property(x => x.VerboseErrors)
.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
// 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.ChannelId);
@ -397,9 +235,7 @@ public abstract class EllieContext : DbContext
#endregion
#region WarningPunishments
var warnpunishmentEntity = modelBuilder.Entity<WarningPunishment>(b =>
modelBuilder.Entity<WarningPunishment>(b =>
{
b.HasAlternateKey(x => new
{
@ -408,8 +244,6 @@ public abstract class EllieContext : DbContext
});
});
#endregion
#region MusicPlaylists
var musicPlaylistEntity = modelBuilder.Entity<MusicPlaylist>();
@ -489,33 +323,6 @@ public abstract class EllieContext : DbContext
#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
var ci = modelBuilder.Entity<ClubInfo>();
@ -806,8 +613,15 @@ public abstract class EllieContext : DbContext
#if DEBUG
private static readonly ILoggerFactory _debugLoggerFactory = LoggerFactory.Create(x => x.AddConsole());
#endif
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseLoggerFactory(_debugLoggerFactory);
{
#if DEBUG
optionsBuilder.UseLoggerFactory(_debugLoggerFactory);
#endif
optionsBuilder.ConfigureWarnings(x => x.Log(RelationalEventId.PendingModelChangesWarning)
.Ignore());
}
}

View file

@ -2,7 +2,6 @@
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using EllieBot.Migrations;
namespace EllieBot.Db;
@ -28,14 +27,14 @@ public sealed class EllieDbService : DbService
public override async Task SetupAsync()
{
await using var context = CreateRawDbContext(DbType, ConnString);
await RunMigration(context);
// make sure sqlite db is in wal journal mode
if (context is SqliteContext)
{
await context.Database.ExecuteSqlRawAsync("PRAGMA journal_mode=WAL");
}
await RunMigration(context);
}
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)
where T : DbEntity
=> 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())
.Count()
+ 1;
public static Task<List<DiscordUser>> GetTopRichest(
this DbSet<DiscordUser> users,
ulong botId,

View file

@ -1,4 +1,5 @@
#nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Db.Models;
@ -29,102 +30,23 @@ public static class GuildConfigExtensions
/// <param name="ctx">Db Context</param>
/// <param name="guildId">Id of the guild to get stream role settings for.</param>
/// <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,
set => set.Include(y => y.StreamRole)
.Include(y => y.StreamRole.Whitelist)
.Include(y => y.StreamRole.Blacklist));
var srs = await ctx.GetTable<StreamRoleSettings>()
.Where(x => x.GuildId == guildId)
.FirstOrDefaultAsyncEF();
if (conf.StreamRole is null)
conf.StreamRole = new();
if (srs is not null)
return srs;
return conf.StreamRole;
}
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
srs = new()
{
var set = includes(ctx.Set<GuildConfig>());
config = set.FirstOrDefault(c => c.GuildId == guildId);
}
GuildId = guildId,
};
if (config is null)
{
ctx.Set<GuildConfig>()
.Add(config = new()
{
GuildId = guildId,
Permissions = Permissionv2.GetDefaultPermlist,
WarningsInitialized = true,
});
ctx.SaveChanges();
}
ctx.Set<StreamRoleSettings>().Add(srs);
if (!config.WarningsInitialized)
{
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);
return srs;
}
public static LogSetting LogSettingsFor(this DbContext ctx, ulong guildId)
@ -148,6 +70,8 @@ public static class GuildConfigExtensions
return logSetting;
}
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);
@ -182,45 +106,24 @@ public static class GuildConfigExtensions
return config;
}
public static IEnumerable<FollowedStream> GetFollowedStreams(this DbSet<GuildConfig> configs)
=> 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)
public static async Task<XpSettings> XpSettingsFor(this DbContext ctx, ulong guildId)
{
var gc = ctx.GuildConfigsForId(guildId,
set => set.Include(x => x.XpSettings)
.ThenInclude(x => x.RoleRewards)
.Include(x => x.XpSettings)
.ThenInclude(x => x.CurrencyRewards)
.Include(x => x.XpSettings)
.ThenInclude(x => x.ExclusionList));
var srs = await ctx.GetTable<XpSettings>()
.Where(x => x.GuildId == guildId)
.FirstOrDefaultAsyncLinqToDB();
if (gc.XpSettings is null)
gc.XpSettings = new();
if (srs is not null)
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 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)
=> await table.FirstOrDefaultAsyncLinqToDB(x => x.GuildId == guildId && x.UserId == 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);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,85 +1,59 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
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 bool Keep { get; set; }
public ulong GuildId { get; set; }
public string Prefix { get; set; }
public bool DeleteMessageOnCommand { get; set; }
public HashSet<DelMsgOnCmdChannel> DelMsgOnCmdChannels { get; set; } = new();
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 string PermissionRole { get; set; }
public HashSet<CommandCooldown> CommandCooldowns { get; set; } = new();
//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; }
// chatterbot
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
public HashSet<CommandAlias> CommandAliases { get; set; } = new();
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 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 DeleteStreamOnlineMessage { get; set; }
public int WarnExpireHours { get; set; }
@ -88,4 +62,9 @@ public class GuildConfig : DbEntity
public bool DisableGlobalExpressions { get; set; } = false;
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;
@ -6,14 +6,14 @@ public class NCPixel
{
[Key]
public int Id { get; set; }
public required int Position { get; init; }
public required long Price { get; init; }
public required ulong OwnerId { get; init; }
public required uint Color { get; init; }
[MaxLength(256)]
public required string Text { get; init; }
}

View file

@ -1,4 +1,6 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
@ -7,7 +9,8 @@ namespace EllieBot.Db.Models;
[DebuggerDisplay("{PrimaryTarget}{SecondaryTarget} {SecondaryTargetName} {State} {PrimaryTargetId}")]
public class Permissionv2 : DbEntity, IIndexed
{
public int? GuildConfigId { get; set; }
public ulong GuildId { get; set; }
public int Index { get; set; }
public PrimaryPermissionType PrimaryTarget { get; set; }
@ -49,4 +52,12 @@ public enum SecondaryPermissionType
Module,
Command,
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 ICollection<Sar> Roles { get; set; } = [];
public bool IsExclusive { get; set; }
[MaxLength(100)]
public string? Name { get; set; }
}

View file

@ -1,16 +1,21 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
public enum ShopEntryType
{
Role,
List,
Command
}
public class ShopEntry : DbEntity, IIndexed
{
public ulong GuildId { get; set; }
public int Index { get; set; }
public int Price { get; set; }
public string Name { get; set; }
@ -25,7 +30,7 @@ public class ShopEntry : DbEntity, IIndexed
//list
public HashSet<ShopEntryItem> Items { get; set; } = new();
public ulong? RoleRequirement { get; set; }
// command
public string Command { get; set; }
}
@ -43,4 +48,22 @@ public class ShopEntryItem : DbEntity
public override int GetHashCode()
=> 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
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models;
public class StreamRoleSettings : DbEntity
{
public int GuildConfigId { get; set; }
public GuildConfig GuildConfig { get; set; }
public ulong GuildId { get; set; }
/// <summary>
/// Whether the feature is enabled in the guild.
@ -42,7 +48,7 @@ public class StreamRoleBlacklistedUser : DbEntity
{
public int StreamRoleSettingsId { get; set; }
public StreamRoleSettings StreamRoleSettings { get; set; }
public ulong UserId { get; set; }
public string Username { get; set; }
@ -62,7 +68,7 @@ public class StreamRoleWhitelistedUser : DbEntity
{
public int StreamRoleSettingsId { get; set; }
public StreamRoleSettings StreamRoleSettings { get; set; }
public ulong UserId { get; set; }
public string Username { get; set; }
@ -71,4 +77,25 @@ public class StreamRoleWhitelistedUser : DbEntity
public override int 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;
public class VcRoleInfo : DbEntity
{
public ulong GuildId { get; set; }
public ulong VoiceChannelId { 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
using EllieBot.Db.Models;
namespace EllieBot.Services.Database.Models;
namespace Ellieices.Database.Models;
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 int GuildConfigId { get; set; }
[Key]
public int Id { get; set; }
public int GuildConfigId { get; set; }
public ulong GuildId { get; set; }
public TimeSpan MinAge { get; set; }
public PunishmentAction Action { get; set; }
public int ActionDurationMinutes { 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
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 int GuildConfigId { get; set; }
public ulong GuildId { get; set; }
public int UserThreshold { get; set; }
public int Seconds { get; set; }
public PunishmentAction Action { get; set; }
@ -15,4 +17,13 @@ public class AntiRaidSetting : DbEntity
/// Mute, Chatmute, Voicemute, etc...
/// </summary>
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 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
public class AntiSpamSetting : DbEntity
{
public int GuildConfigId { get; set; }
public ulong GuildId { get; set; }
public PunishmentAction Action { get; set; }
public int MessageThreshold { get; set; } = 3;
public int MuteTime { get; set; }
public ulong? RoleId { get; set; }
public HashSet<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;
@ -24,4 +24,4 @@ public sealed class ButtonRole
public string Label { get; set; } = string.Empty;
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;
public class FilterChannelId : DbEntity
public class FilterChannelId
{
[Key]
public int Id { get; set; }
public ulong ChannelId { get; set; }
public bool Equals(FilterChannelId other)
protected bool Equals(FilterChannelId other)
=> ChannelId == other.ChannelId;
public override bool Equals(object obj)
=> obj is FilterChannelId fci && Equals(fci);
public override bool Equals(object? obj)
=> obj is FilterChannelId fci && fci.Equals(this);
public override int GetHashCode()
=> ChannelId.GetHashCode();
}
}

View file

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

View file

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

View file

@ -1,7 +1,10 @@
#nullable disable
namespace EllieBot.Db.Models;
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;
public class MutedUserId : DbEntity
{
public ulong GuildId { get; set; }
public ulong UserId { get; set; }
}
public override int GetHashCode()
=> UserId.GetHashCode();
public override bool Equals(object obj)
=> obj is MutedUserId mui ? mui.UserId == UserId : false;
public class MutedUserIdEntityConfiguration : IEntityTypeConfiguration<MutedUserId>
{
public void Configure(EntityTypeBuilder<MutedUserId> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.UserId
})
.IsUnique();
}
}

View file

@ -7,6 +7,6 @@ public class TempRole
public bool Remove { get; set; }
public ulong RoleId { get; set; }
public ulong UserId { 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;
public class SlowmodeIgnoredRole : DbEntity
{
public ulong GuildId { get; set; }
public ulong RoleId { get; set; }
}
// override object.Equals
public override bool Equals(object obj)
public class SlowmodeIgnoredRoleEntityConfiguration : IEntityTypeConfiguration<SlowmodeIgnoredRole>
{
public void Configure(EntityTypeBuilder<SlowmodeIgnoredRole> builder)
{
if (obj is null || GetType() != obj.GetType())
return false;
return ((SlowmodeIgnoredRole)obj).RoleId == RoleId;
builder.HasIndex(x => new
{
x.GuildId,
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;
public class SlowmodeIgnoredUser : DbEntity
{
public ulong GuildId { get; set; }
public ulong UserId { get; set; }
}
// override object.Equals
public override bool Equals(object obj)
public class SlowmodeIgnoredUserEntityConfiguration : IEntityTypeConfiguration<SlowmodeIgnoredUser>
{
public void Configure(EntityTypeBuilder<SlowmodeIgnoredUser> builder)
{
if (obj is null || GetType() != obj.GetType())
return false;
return ((SlowmodeIgnoredUser)obj).UserId == UserId;
builder.HasIndex(x => new
{
x.GuildId,
x.UserId
})
.IsUnique();
}
// override object.GetHashCode
public override int GetHashCode()
=> UserId.GetHashCode();
}

View file

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

View file

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

View file

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

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

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 _)
{
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()
.WithOkColor()
@ -124,14 +124,13 @@ public partial class Administration : EllieModule<AdministrationService>
[Priority(1)]
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();
}
else
{
_service.DeleteMessagesOnCommand.TryRemove(ctx.Guild.Id);
await Response().Confirm(strs.delmsg_off).SendAsync();
}
}

View file

@ -1,4 +1,6 @@
#nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Db.Models;
using EllieBot.Modules.Administration._common.results;
@ -7,95 +9,130 @@ namespace EllieBot.Modules.Administration;
public class AdministrationService : IEService
{
public ConcurrentHashSet<ulong> DeleteMessagesOnCommand { get; }
public ConcurrentDictionary<ulong, bool> DeleteMessagesOnCommandChannels { get; }
private ConcurrentHashSet<ulong> deleteMessagesOnCommand;
private ConcurrentDictionary<ulong, bool> delMsgOnCmdChannels;
private readonly DbService _db;
private readonly IReplacementService _repSvc;
private readonly ILogCommandService _logService;
private readonly IHttpClientFactory _httpFactory;
private readonly ShardData _shardData;
private readonly CommandHandler _cmdHandler;
public AdministrationService(
IBot bot,
CommandHandler cmdHandler,
DbService db,
IReplacementService repSvc,
ILogCommandService logService,
IHttpClientFactory factory)
IHttpClientFactory factory,
ShardData shardData,
CommandHandler cmdHandler)
{
_db = db;
_shardData = shardData;
_repSvc = repSvc;
_logService = logService;
_httpFactory = factory;
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;
_cmdHandler = cmdHandler;
}
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();
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)
{
if (msg.Channel is not ITextChannel channel)
return Task.CompletedTask;
_ = Task.Run(async () =>
{
//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")
{
_logService.AddDeleteIgnore(msg.Id);
try { await msg.DeleteAsync(); }
try
{ await msg.DeleteAsync(); }
catch { }
}
//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);
try { await msg.DeleteAsync(); }
try
{ await msg.DeleteAsync(); }
catch { }
}
});
return Task.CompletedTask;
}
public bool ToggleDeleteMessageOnCommand(ulong guildId)
public async Task<bool> ToggleDeleteMessageOnCommand(ulong guildId)
{
bool enabled;
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set);
enabled = conf.DeleteMessageOnCommand = !conf.DeleteMessageOnCommand;
uow.SaveChanges();
return enabled;
var conf = await uow.GetTable<GuildConfig>()
.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)
{
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 (old is not null)
{
conf.DelMsgOnCmdChannels.Remove(old);
uow.Remove(old);
}
}
@ -103,15 +140,17 @@ public class AdministrationService : IEService
{
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;
DeleteMessagesOnCommandChannels[chId] = newState == Administration.State.Enable;
delMsgOnCmdChannels[chId] = newState == Administration.State.Enable;
}
await uow.SaveChangesAsync();
@ -121,9 +160,13 @@ public class AdministrationService : IEService
{
}
else if (newState == Administration.State.Enable)
DeleteMessagesOnCommandChannels[chId] = true;
{
delMsgOnCmdChannels[chId] = true;
}
else
DeleteMessagesOnCommandChannels.TryRemove(chId, out _);
{
delMsgOnCmdChannels.TryRemove(chId, out _);
}
}
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)
{
if (!IsValidUri(img)) return SetServerBannerResult.InvalidURL;
if (!IsValidUri(img))
return SetServerBannerResult.InvalidURL;
var uri = new Uri(img);
using var http = _httpFactory.CreateClient();
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())
{
return SetServerBannerResult.Toolarge;
}
await using var imageStream = await sr.Content.ReadAsStreamAsync();
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)
{
if (!IsValidUri(img)) return SetServerIconResult.InvalidURL;
if (!IsValidUri(img))
return SetServerIconResult.InvalidURL;
var uri = new Uri(img);
using var http = _httpFactory.CreateClient();
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 guild.ModifyAsync(x => x.Icon = new Image(imageStream));
return SetServerIconResult.Success;
}
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.Threading.Channels;
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace EllieBot.Modules.Administration.Services;
@ -11,9 +12,10 @@ public sealed class AutoAssignRoleService : IEService
{
private readonly DiscordSocketClient _client;
private readonly DbService _db;
private readonly ShardData _shardData;
//guildid/roleid
private readonly ConcurrentDictionary<ulong, IReadOnlyList<ulong>> _autoAssignableRoles;
private ConcurrentDictionary<ulong, IReadOnlyList<ulong>> _autoAssignableRoles;
private readonly Channel<SocketGuildUser> _assignQueue = Channel.CreateBounded<SocketGuildUser>(
new BoundedChannelOptions(100)
@ -23,63 +25,78 @@ public sealed class AutoAssignRoleService : IEService
SingleWriter = false
});
public AutoAssignRoleService(DiscordSocketClient client, IBot bot, DbService db)
public AutoAssignRoleService(
DiscordSocketClient client,
IBot bot,
DbService db,
ShardData shardData)
{
_client = client;
_shardData = shardData;
_db = db;
}
_autoAssignableRoles = bot.AllGuildConfigs.Where(x => !string.IsNullOrWhiteSpace(x.AutoAssignRoleIds))
.ToDictionary<GuildConfig, ulong, IReadOnlyList<ulong>>(k => k.GuildId,
v => v.GetAutoAssignableRoles())
.ToConcurrent();
_ = Task.Run(async () =>
public async Task OnReadyAsync()
{
await using (var uow = _db.GetDbContext())
{
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();
if (!_autoAssignableRoles.TryGetValue(user.Guild.Id, out var savedRoleIds))
continue;
var roleIds = savedRoleIds.Select(roleId => user.Guild.GetRole(roleId))
.Where(x => x is not null)
.ToList();
try
if (roleIds.Any())
{
var roleIds = savedRoleIds.Select(roleId => user.Guild.GetRole(roleId))
.Where(x => x is not null)
.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);
}
await user.AddRolesAsync(roleIds);
await Task.Delay(250);
}
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden)
else
{
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.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;
_client.RoleDeleted += OnClientRoleDeleted;
await DisableAarAsync(user.Guild.Id);
}
catch (Exception ex)
{
Log.Warning(ex, "Error in aar. Probably one of the roles doesn't exist");
}
}
}
private async Task OnClientRoleDeleted(SocketRole role)
@ -117,7 +134,8 @@ public sealed class AutoAssignRoleService : IEService
{
await using var uow = _db.GetDbContext();
await uow.Set<GuildConfig>().AsNoTracking()
await uow.Set<GuildConfig>()
.AsNoTracking()
.Where(x => x.GuildId == guildId)
.UpdateAsync(_ => new()
{

View file

@ -42,7 +42,7 @@ public class AutoPublishService : IExecNoCommand, IReadyExecutor, IEService
await using var ctx = _db.GetDbContext();
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();
_enabled = items

View file

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

View file

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

View file

@ -1,5 +1,7 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Administration.Services;
@ -11,7 +13,7 @@ public enum MuteType
All
}
public class MuteService : IEService
public class MuteService : IEService, IReadyExecutor
{
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> UserUnmuted = delegate { };
public ConcurrentDictionary<ulong, string> GuildMuteRoles { get; }
public ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; }
private ConcurrentDictionary<ulong, string> _guildMuteRoles = new();
private ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _mutedUsers = new();
public ConcurrentDictionary<ulong, ConcurrentDictionary<(ulong, TimerType), Timer>> UnTimers { get; } = new();
private readonly DiscordSocketClient _client;
private readonly DbService _db;
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;
_db = db;
_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;
UserUnmuted += OnUserUnmuted;
@ -123,10 +58,10 @@ public class MuteService : IEService
_ = Task.Run(() => _sender.Response(user)
.Embed(_sender.CreateEmbed(user?.GuildId)
.WithDescription($"You've been muted in {user.Guild} server")
.AddField("Mute Type", type.ToString())
.AddField("Moderator", mod.ToString())
.AddField("Reason", reason))
.WithDescription($"You've been muted in {user.Guild} server")
.AddField("Mute Type", type.ToString())
.AddField("Moderator", mod.ToString())
.AddField("Reason", reason))
.SendAsync());
}
@ -141,10 +76,10 @@ public class MuteService : IEService
_ = Task.Run(() => _sender.Response(user)
.Embed(_sender.CreateEmbed(user.GuildId)
.WithDescription($"You've been unmuted in {user.Guild} server")
.AddField("Unmute Type", type.ToString())
.AddField("Moderator", mod.ToString())
.AddField("Reason", reason))
.WithDescription($"You've been unmuted in {user.Guild} server")
.AddField("Unmute Type", type.ToString())
.AddField("Moderator", mod.ToString())
.AddField("Reason", reason))
.SendAsync());
}
@ -152,7 +87,7 @@ public class MuteService : IEService
{
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))
return Task.CompletedTask;
@ -169,9 +104,11 @@ public class MuteService : IEService
public async Task SetMuteRoleAsync(ulong guildId, string name)
{
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;
GuildMuteRoles.AddOrUpdate(guildId, name, (_, _) => name);
_guildMuteRoles.AddOrUpdate(guildId, name, (_, _) => name);
await uow.SaveChangesAsync();
}
@ -183,7 +120,8 @@ public class MuteService : IEService
{
if (type == MuteType.All)
{
try { await usr.ModifyAsync(x => x.Mute = true); }
try
{ await usr.ModifyAsync(x => x.Mute = true); }
catch { }
var muteRole = await GetMuteRole(usr.Guild);
@ -192,16 +130,23 @@ public class MuteService : IEService
StopTimer(usr.GuildId, usr.Id, TimerType.Mute);
await using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(usr.Guild.Id,
set => set.Include(gc => gc.MutedUsers).Include(gc => gc.UnmuteTimers));
config.MutedUsers.Add(new()
{
UserId = usr.Id
});
if (MutedUsers.TryGetValue(usr.Guild.Id, out var muted))
muted.Add(usr.Id);
await uow.GetTable<MutedUserId>()
.InsertOrUpdateAsync(() => new()
{
GuildId = usr.GuildId,
UserId = usr.Id
},
(_) => new()
{
},
() => 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();
}
@ -237,29 +182,26 @@ public class MuteService : IEService
StopTimer(guildId, usrId, TimerType.Mute);
await using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(guildId,
set => set.Include(gc => gc.MutedUsers).Include(gc => gc.UnmuteTimers));
var match = new MutedUserId
{
UserId = usrId
};
var toRemove = config.MutedUsers.FirstOrDefault(x => x.Equals(match));
if (toRemove is not null)
uow.Remove(toRemove);
if (MutedUsers.TryGetValue(guildId, out var muted))
await uow.GetTable<MutedUserId>()
.Where(x => x.GuildId == guildId && x.UserId == usrId)
.DeleteAsync();
await uow.GetTable<UnmuteTimer>()
.Where(x => x.GuildId == guildId && x.UserId == usrId)
.DeleteAsync();
if (_mutedUsers.TryGetValue(guildId, out var muted))
muted.TryRemove(usrId);
config.UnmuteTimers.RemoveWhere(x => x.UserId == usrId);
await uow.SaveChangesAsync();
}
if (usr is not null)
{
try { await usr.ModifyAsync(x => x.Mute = false); }
try
{ await usr.ModifyAsync(x => x.Mute = false); }
catch { }
try { await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild)); }
try
{ await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild)); }
catch
{
/*ignore*/
@ -272,12 +214,9 @@ public class MuteService : IEService
{
if (usr is null)
return;
try
{
await usr.ModifyAsync(x => x.Mute = false);
UserUnmuted(usr, mod, MuteType.Voice, reason);
}
catch { }
await usr.ModifyAsync(x => x.Mute = false);
UserUnmuted(usr, mod, MuteType.Voice, reason);
}
else if (type == MuteType.Chat)
{
@ -292,15 +231,16 @@ public class MuteService : IEService
{
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);
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
{
//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 using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(user.GuildId, set => set.Include(x => x.UnmuteTimers));
config.UnmuteTimers.Add(new()
{
UserId = user.Id,
UnmuteAt = DateTime.UtcNow + after
}); // add teh unmute timer to the database
uow.SaveChanges();
var unmuteAt = DateTime.UtcNow + after;
await uow.GetTable<UnmuteTimer>()
.InsertAsync(() => new()
{
GuildId = user.GuildId,
UserId = user.Id,
UnmuteAt = unmuteAt
});
}
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 using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(guild.Id, set => set.Include(x => x.UnbanTimer));
config.UnbanTimer.Add(new()
{
UserId = userId,
UnbanAt = DateTime.UtcNow + after
}); // add teh unmute timer to the database
await uow.SaveChangesAsync();
var unbanAt = DateTime.UtcNow + after;
await uow.GetTable<UnbanTimer>()
.InsertAsync(() => new()
{
GuildId = guild.Id,
UserId = userId,
UnbanAt = unbanAt
});
}
StartUn_Timer(guild.Id, userId, after, TimerType.Ban); // start the timer
}
public async Task TimedRole(
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
}
// todo unrole timers -> temprole
public void StartUn_Timer(
ulong guildId,
@ -408,56 +330,56 @@ public class MuteService : IEService
//unmute timer to be added
var toAdd = new Timer(async _ =>
{
if (type == TimerType.Ban)
{
if (type == TimerType.Ban)
try
{
try
{
RemoveTimerFromDb(guildId, userId, type);
StopTimer(guildId, userId, type);
var guild = _client.GetGuild(guildId); // load the guild
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);
}
await RemoveTimerFromDb(guildId, userId, type);
StopTimer(guildId, userId, type);
var guild = _client.GetGuild(guildId); // load the guild
if (guild is not null)
await guild.RemoveBanAsync(userId);
}
else if (type == TimerType.AddRole)
catch (Exception ex)
{
try
{
if (roleId is null)
return;
Log.Warning(ex, "Couldn't unban user {UserId} in guild {GuildId}", userId, guildId);
}
}
else if (type == TimerType.AddRole)
{
try
{
if (roleId is null)
return;
RemoveTimerFromDb(guildId, userId, type);
StopTimer(guildId, userId, type);
var guild = _client.GetGuild(guildId);
var user = guild?.GetUser(userId);
var role = guild?.GetRole(roleId.Value);
if (guild is not null && user is not null && user.Roles.Contains(role))
await user.RemoveRoleAsync(role);
}
catch (Exception ex)
{
Log.Warning(ex, "Couldn't remove role from user {UserId} in guild {GuildId}", userId, guildId);
}
await RemoveTimerFromDb(guildId, userId, type);
StopTimer(guildId, userId, type);
var guild = _client.GetGuild(guildId);
var user = guild?.GetUser(userId);
var role = guild?.GetRole(roleId.Value);
if (guild is not null && user is not null && user.Roles.Contains(role))
await user.RemoveRoleAsync(role);
}
else
catch (Exception ex)
{
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)
{
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);
}
Log.Warning(ex, "Couldn't remove role from 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,
after,
Timeout.InfiniteTimeSpan);
@ -481,23 +403,97 @@ public class MuteService : IEService
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();
object toDelete;
if (type == TimerType.Mute)
await using var ctx = _db.GetDbContext();
}
// 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));
toDelete = config.UnmuteTimers.FirstOrDefault(x => x.UserId == userId);
}
else
{
var config = uow.GuildConfigsForId(guildId, set => set.Include(x => x.UnbanTimer));
toDelete = config.UnbanTimer.FirstOrDefault(x => x.UserId == userId);
foreach (var x in 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 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)
uow.Remove(toDelete);
uow.SaveChanges();
_client.UserJoined += Client_UserJoined;
}
}

View file

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

View file

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

View file

@ -1,11 +1,15 @@
#nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
using EllieBot.Migrations;
using System.Threading.Channels;
namespace EllieBot.Modules.Administration.Services;
public class ProtectionService : IEService
public class ProtectionService : IReadyExecutor, IEService
{
public event Func<PunishmentAction, ProtectionType, IGuildUser[], Task> OnAntiProtectionTriggered = delegate
{
@ -23,7 +27,7 @@ public class ProtectionService : IEService
private readonly DbService _db;
private readonly UserPunishService _punishService;
private readonly INotifySubscriber _notifySub;
private readonly ShardData _shardData;
private readonly Channel<PunishQueueItem> _punishUserQueue =
Channel.CreateUnbounded<PunishQueueItem>(new()
{
@ -33,41 +37,23 @@ public class ProtectionService : IEService
public ProtectionService(
DiscordSocketClient client,
IBot bot,
MuteService mute,
DbService db,
UserPunishService punishService,
INotifySubscriber notifySub)
INotifySubscriber notifySub,
ShardData shardData)
{
_client = client;
_mute = mute;
_db = db;
_punishService = punishService;
_notifySub = notifySub;
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);
}
_shardData = shardData;
_client.MessageReceived += HandleAntiSpam;
_client.UserJoined += HandleUserJoined;
bot.JoinedGuild += _bot_JoinedGuild;
_client.LeftGuild += _client_LeftGuild;
_ = Task.Run(RunQueue);
}
private async Task RunQueue()
@ -103,53 +89,13 @@ public class ProtectionService : IEService
{
_ = Task.Run(async () =>
{
TryStopAntiRaid(guild.Id);
TryStopAntiSpam(guild.Id);
await TryStopAntiAlt(guild.Id);
await TryStopAntiRaidAsync(guild.Id);
await TryStopAntiSpamAsync(guild.Id);
await TryStopAntiAltAsync(guild.Id);
});
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)
{
if (user.IsBot)
@ -201,7 +147,8 @@ public class ProtectionService : IEService
await PunishUsers(settings.Action, ProtectionType.Raiding, settings.PunishDuration, null, users);
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);
@ -327,39 +274,36 @@ public class ProtectionService : IEService
_antiRaidGuilds.AddOrUpdate(guildId, stats, (_, _) => stats);
await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.AntiRaidSetting));
gc.AntiRaidSetting = stats.AntiRaidSettings;
await uow.SaveChangesAsync();
// todo finish this
return stats;
}
public bool TryStopAntiRaid(ulong guildId)
public async Task<bool> TryStopAntiRaidAsync(ulong guildId)
{
if (_antiRaidGuilds.TryRemove(guildId, out _))
{
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 false;
}
public bool TryStopAntiSpam(ulong guildId)
public async Task<bool> TryStopAntiSpamAsync(ulong guildId)
{
if (_antiSpamGuilds.TryRemove(guildId, out _))
{
using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId,
set => set.Include(x => x.AntiSpamSetting).ThenInclude(x => x.IgnoredChannels));
await uow.GetTable<AntiSpamSetting>()
.Where(x => x.GuildId == guildId)
.DeleteAsync();
gc.AntiSpamSetting = null;
uow.SaveChanges();
return true;
}
@ -399,19 +343,26 @@ public class ProtectionService : IEService
});
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;
}
@ -421,15 +372,16 @@ public class ProtectionService : IEService
{
ChannelId = channelId
};
bool added;
await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId,
set => set.Include(x => x.AntiSpamSetting).ThenInclude(x => x.IgnoredChannels));
var spam = gc.AntiSpamSetting;
var spam = await uow.GetTable<AntiSpamSetting>()
.Include(x => x.IgnoredChannels)
.Where(x => x.GuildId == guildId)
.FirstOrDefaultAsyncEF();
if (spam is 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))
temp.AntiSpamSettings.IgnoredChannels.Add(obj); // add to local cache
@ -439,7 +391,9 @@ public class ProtectionService : IEService
else
{
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))
temp.AntiSpamSettings.IgnoredChannels.Remove(toRemove); // remove from local cache
@ -483,28 +437,83 @@ public class ProtectionService : IEService
ulong? roleId = null)
{
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,
ActionDurationMinutes = actionDurationMinutes,
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
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 _))
return false;
await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.AntiAltSetting));
gc.AntiAltSetting = null;
await uow.SaveChangesAsync();
await uow.GetTable<AntiAltSetting>()
.Where(x => x.GuildId == guildId)
.DeleteAsync();
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();
var reros = await uow.GetTable<ReactionRoleV2>()
.Where(
x => Linq2DbExpressions.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId))
x => Queries.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId))
.ToListAsyncLinqToDB();
foreach (var group in reros.GroupBy(x => x.MessageId))

View file

@ -221,7 +221,7 @@ public partial class Administration
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[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))
{
@ -231,6 +231,7 @@ public partial class Administration
return;
}
await user.AddRoleAsync(role);
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
.Set<GuildConfig>()
.ToLinqToDBTable()
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId,
.Where(x => Queries.GuildOnShard(x.GuildId,
_creds.TotalShards,
_client.ShardId))
.Where(x => x.StickyRoles)

View file

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

View file

@ -216,7 +216,8 @@ public class SelfAssignedRolesService : IEService, IReadyExecutor
{
await using var uow = _db.GetDbContext();
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)
.ToListAsyncLinqToDB();
@ -309,7 +310,6 @@ public sealed class SarAssignerService : IEService, IReadyExecutor
{
await _channel.Writer.WriteAsync(item);
}
}
public sealed class SarAssignerDataItem

View file

@ -1,4 +1,5 @@
#nullable disable
using LinqToDB.EntityFrameworkCore;
using EllieBot.Db.Models;
using EllieBot.Common.ModuleBehaviors;
@ -6,40 +7,30 @@ namespace EllieBot.Modules.Administration.Services;
public sealed class GuildTimezoneService : ITimezoneService, IReadyExecutor, IEService
{
private readonly ConcurrentDictionary<ulong, TimeZoneInfo> _timezones;
private ConcurrentDictionary<ulong, TimeZoneInfo> _timezones;
private readonly DbService _db;
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;
_repStore = repStore;
bot.JoinedGuild += Bot_JoinedGuild;
_shardData = shardData;
_client = client;
}
private Task Bot_JoinedGuild(GuildConfig arg)
{
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)
private static (ulong GuildId, TimeZoneInfo Timezone) GetTimezoneTuple(GuildConfig x)
{
TimeZoneInfo tz;
try
{
if (x.TimeZoneId is null)
tz = null;
else
tz = TimeZoneInfo.FindSystemTimeZoneById(x.TimeZoneId);
tz = x.TimeZoneId is null ? null : TimeZoneInfo.FindSystemTimeZoneById(x.TimeZoneId);
}
catch
{
@ -74,9 +65,18 @@ public sealed class GuildTimezoneService : ITimezoneService, IReadyExecutor, IES
public TimeZoneInfo GetTimeZoneOrUtc(ulong? guildId)
=> 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) =>
{
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 ")
+ to.StandardName.GetInitials();
});
return Task.CompletedTask;
}
}

View file

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

View file

@ -1,109 +1,95 @@
#nullable disable
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
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, System.Collections.Concurrent.ConcurrentQueue<(bool, IGuildUser, IRole)>> ToAssign { get; }
private readonly DbService _db;
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;
_client = client;
_bot = bot;
_shardData = shardData;
_client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated;
VcRoles = 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
// need to load new guildconfig with vc role included
IEnumerable<IGrouping<ulong, VcRoleInfo>> vcRoles;
using (var uow = _db.GetDbContext())
{
var configWithVcRole = uow.GuildConfigsForId(arg.GuildId, set => set.Include(x => x.VcRoleInfos));
_ = InitializeVcRole(configWithVcRole);
vcRoles = await uow.GetTable<VcRoleInfo>()
.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 _);
ToAssign.TryRemove(arg.Id, out _);
return Task.CompletedTask;
}
private async Task InitializeVcRole(GuildConfig gconf)
{
var g = _client.GetGuild(gconf.GuildId);
var g = _client.GetGuild(guildId);
if (g is null)
return;
var infos = new ConcurrentDictionary<ulong, IRole>();
var missingRoles = new List<VcRoleInfo>();
VcRoles.AddOrUpdate(gconf.GuildId, infos, delegate { return infos; });
foreach (var ri in gconf.VcRoleInfos)
VcRoles.AddOrUpdate(guildId, infos, delegate
{ return infos; });
foreach (var ri in confs)
{
var role = g.GetRole(ri.RoleId);
if (role is null)
@ -120,7 +106,7 @@ public class VcRoleService : IEService
await using var uow = _db.GetDbContext();
uow.RemoveRange(missingRoles);
await uow.SaveChangesAsync();
Log.Warning("Removed {MissingRoleCount} missing roles from {ServiceName}",
missingRoles.Count,
nameof(VcRoleService));
@ -135,11 +121,11 @@ public class VcRoleService : IEService
guildVcRoles.AddOrUpdate(vcId, role, (_, _) => role);
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set.Include(x => x.VcRoleInfos));
var toDelete = conf.VcRoleInfos.FirstOrDefault(x => x.VoiceChannelId == vcId); // remove old one
var toDelete = uow.Set<VcRoleInfo>()
.FirstOrDefault(x => x.VoiceChannelId == vcId); // remove old one
if (toDelete is not null)
uow.Remove(toDelete);
conf.VcRoleInfos.Add(new()
uow.Set<VcRoleInfo>().Add(new()
{
VoiceChannelId = vcId,
RoleId = role.Id
@ -156,8 +142,7 @@ public class VcRoleService : IEService
return false;
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set.Include(x => x.VcRoleInfos));
var toRemove = conf.VcRoleInfos.Where(x => x.VoiceChannelId == vcId).ToList();
var toRemove = uow.Set<VcRoleInfo>().Where(x => x.VoiceChannelId == vcId).ToList();
uow.RemoveRange(toRemove);
uow.SaveChanges();

View file

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

View file

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

View file

@ -15,7 +15,7 @@ using Image = SixLabors.ImageSharp.Image;
namespace EllieBot.Modules.Gambling.Services;
public class PlantPickService : IEService, IExecNoCommand
public class PlantPickService : IEService, IExecNoCommand, IReadyExecutor
{
//channelId/last generation
public ConcurrentDictionary<ulong, long> LastGenerations { get; } = new();
@ -30,7 +30,7 @@ public class PlantPickService : IEService, IExecNoCommand
private readonly GamblingConfigService _gss;
private readonly GamblingService _gs;
private readonly ConcurrentHashSet<ulong> _generationChannels;
private ConcurrentHashSet<ulong> _generationChannels;
private readonly SemaphoreSlim _pickLock = new(1, 1);
public PlantPickService(
@ -57,13 +57,6 @@ public class PlantPickService : IEService, IExecNoCommand
using var uow = db.GetDbContext();
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)
@ -72,41 +65,49 @@ public class PlantPickService : IEService, IExecNoCommand
private string GetText(ulong gid, LocStr str)
=> _strings.GetText(str, gid);
public bool ToggleCurrencyGeneration(ulong gid, ulong cid)
public async Task<bool> ToggleCurrencyGeneration(ulong gid, ulong cid)
{
bool enabled;
using var uow = _db.GetDbContext();
var guildConfig = uow.GuildConfigsForId(gid, set => set.Include(gc => gc.GenerateCurrencyChannelIds));
await using var uow = _db.GetDbContext();
var toAdd = new GCChannelId
if (_generationChannels.Add(cid))
{
ChannelId = cid
};
if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd))
{
guildConfig.GenerateCurrencyChannelIds.Add(toAdd);
await uow.GetTable<GCChannelId>()
.InsertOrUpdateAsync(() => new()
{
ChannelId = cid,
GuildId = gid
}, (x) => new()
{
ChannelId = cid,
GuildId = gid
}, () => new()
{
ChannelId = cid,
GuildId = gid
});
_generationChannels.Add(cid);
enabled = true;
}
else
{
var toDelete = guildConfig.GenerateCurrencyChannelIds.FirstOrDefault(x => x.Equals(toAdd));
if (toDelete is not null)
uow.Remove(toDelete);
await uow.GetTable<GCChannelId>()
.Where(x => x.ChannelId == cid && x.GuildId == gid)
.DeleteAsync();
_generationChannels.TryRemove(cid);
enabled = false;
}
uow.SaveChanges();
return enabled;
}
public IEnumerable<GuildConfigExtensions.GeneratingChannel> GetAllGeneratingChannels()
public async Task<IReadOnlyCollection<GCChannelId>> GetAllGeneratingChannels()
{
using var uow = _db.GetDbContext();
var chs = uow.Set<GuildConfig>().GetGeneratingChannels();
return chs;
await using var uow = _db.GetDbContext();
return await uow.GetTable<GCChannelId>()
.ToListAsyncLinqToDB();
}
/// <summary>
@ -412,4 +413,16 @@ public class PlantPickService : IEService, IExecNoCommand
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.Db.Models;
using EllieBot.Modules.Administration;
using LinqToDB.EntityFrameworkCore;
namespace EllieBot.Modules.Gambling;
@ -30,25 +31,28 @@ public partial class Gambling
private readonly DbService _db;
private readonly ICurrencyService _cs;
private readonly EllieRandom _rng;
public ShopCommands(DbService db, ICurrencyService cs, GamblingConfigService gamblingConf)
: base(gamblingConf)
{
_db = db;
_cs = cs;
_rng = new EllieRandom();
}
private Task ShopInternalAsync(int page = 0)
private async Task ShopInternalAsync(int page = 0)
{
if (page < 0)
throw new ArgumentOutOfRangeException(nameof(page));
using var uow = _db.GetDbContext();
var entries = uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items))
.ShopEntries.ToIndexed();
await using var uow = _db.GetDbContext();
var entries = await uow.Set<ShopEntry>()
.Where(x => x.GuildId == ctx.Guild.Id)
.Include(x => x.Items)
.ToListAsyncEF();
return Response()
await Response()
.Paginated()
.Items(entries.ToList())
.PageSize(9)
@ -92,13 +96,15 @@ public partial class Gambling
ShopEntry entry;
await using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items));
var entries = new IndexedCollection<ShopEntry>(config.ShopEntries);
entry = entries.ElementAtOrDefault(index);
uow.SaveChanges();
entry = await uow.Set<ShopEntry>()
.Where(x => x.GuildId == ctx.Guild.Id)
.Include(x => x.Items)
.OrderBy(x => x.Id)
.Skip(index)
.FirstOrDefaultAsync();
}
if (entry is null)
{
await Response().Error(strs.shop_item_not_found).SendAsync();
@ -174,7 +180,7 @@ public partial class Gambling
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())))
{
@ -205,15 +211,15 @@ public partial class Gambling
await _cs.AddAsync(ctx.User.Id, entry.Price, new("shop", "error-refund", entry.Name));
await using (var uow = _db.GetDbContext())
{
var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries)
.ThenInclude(x => x.Items))
.ShopEntries);
var entries = new IndexedCollection<ShopEntry>(await uow.Set<ShopEntry>()
.Where(x => x.GuildId == ctx.Guild.Id)
.Include(x => x.Items)
.ToListAsyncEF());
entry = entries.ElementAtOrDefault(index);
if (entry is not null)
{
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();
}
else
{
await Response().Error(strs.not_enough(CurrencySign)).SendAsync();
}
}
else if (entry.Type == ShopEntryType.Command)
{
@ -253,7 +261,7 @@ public partial class Gambling
.Replace("%you.username%", buyer.Username)
.Replace("%you.name%", buyer.GlobalName ?? buyer.Username)
.Replace("%you.nick%", buyer.DisplayName);
var eb = CreateEmbed()
.WithPendingColor()
.WithTitle("Executing shop command")
@ -328,15 +336,14 @@ public partial class Gambling
};
await using (var uow = _db.GetDbContext())
{
var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries)
.ThenInclude(x => x.Items))
.ShopEntries)
var entries = new IndexedCollection<ShopEntry>(await uow.Set<ShopEntry>()
.Where(x => x.GuildId == ctx.Guild.Id)
.Include(x => x.Items)
.ToListAsyncEF())
{
entry
};
uow.GuildConfigsForId(ctx.Guild.Id, set => set).ShopEntries = entries;
uow.SaveChanges();
await uow.SaveChangesAsync();
}
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())
{
var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries)
.ThenInclude(x => x.Items))
.ShopEntries)
{
entry
};
uow.GuildConfigsForId(ctx.Guild.Id, set => set).ShopEntries = entries;
uow.SaveChanges();
var entries = await uow.Set<ShopEntry>()
.Where(x => x.GuildId == ctx.Guild.Id)
.ToListAsyncEF();
var indexed = new IndexedCollection<ShopEntry>(entries);
indexed.Add(entry);
uow.Add(entry);
await uow.SaveChangesAsync();
}
await Response().Embed(EntryToEmbed(entry).WithTitle(GetText(strs.shop_item_add))).SendAsync();
@ -391,11 +399,13 @@ public partial class Gambling
var added = false;
await using (var uow = _db.GetDbContext())
{
var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries)
.ThenInclude(x => x.Items))
.ShopEntries);
entry = entries.ElementAtOrDefault(index);
var entries = await uow.Set<ShopEntry>()
.Where(x => x.GuildId == ctx.Guild.Id)
.Include(x => x.Items)
.ToListAsyncEF();
var indexed = new IndexedCollection<ShopEntry>(entries);
entry = indexed.ElementAtOrDefault(index);
if (entry is not null && (rightType = entry.Type == ShopEntryType.List))
{
if (entry.Items.Add(item))
@ -427,10 +437,12 @@ public partial class Gambling
ShopEntry removed;
await using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items));
var items = await uow.Set<ShopEntry>()
.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);
if (removed is not null)
{

View file

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

View file

@ -1,6 +1,7 @@
#nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
using EllieBot.Modules.Games.Common;
@ -10,9 +11,9 @@ using EllieBot.Modules.Permissions;
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
=> 1;
@ -29,7 +30,6 @@ public class ChatterBotService : IExecOnMessage
public ChatterBotService(
DiscordSocketClient client,
IPermissionChecker perms,
IBot bot,
IPatronageService ps,
IHttpClientFactory factory,
IBotCreds creds,
@ -46,11 +46,6 @@ public class ChatterBotService : IExecOnMessage
_perms = perms;
_gcs = gcs;
_ps = ps;
ChatterBotGuilds = new(bot.AllGuildConfigs
.Where(gc => gc.CleverbotEnabled)
.ToDictionary(gc => gc.GuildId,
_ => new Lazy<IChatterBotSession>(() => CreateSession(), true)));
}
public IChatterBotSession CreateSession()
@ -86,25 +81,25 @@ public class ChatterBotService : IExecOnMessage
public IChatterBotSession GetOrCreateSession(ulong guildId)
{
if (ChatterBotGuilds.TryGetValue(guildId, out var lazyChatBot))
if (_chatterBotGuilds.TryGetValue(guildId, out var lazyChatBot))
return lazyChatBot.Value;
lazyChatBot = new(() => CreateSession(), true);
ChatterBotGuilds.TryAdd(guildId, lazyChatBot);
_chatterBotGuilds.TryAdd(guildId, lazyChatBot);
return lazyChatBot.Value;
}
public string PrepareMessage(IUserMessage msg)
{
var ellieId = _client.CurrentUser.Id;
var normalMention = $"<@{ellieId}> ";
var nickMention = $"<@!{ellieId}> ";
var nadekoId = _client.CurrentUser.Id;
var normalMention = $"<@{nadekoId}> ";
var nickMention = $"<@!{nadekoId}> ";
string message;
if (msg.Content.StartsWith(normalMention, StringComparison.InvariantCulture))
message = msg.Content[normalMention.Length..].Trim();
else if (msg.Content.StartsWith(nickMention, StringComparison.InvariantCulture))
message = msg.Content[nickMention.Length..].Trim();
else if (msg.ReferencedMessage?.Author.Id == ellieId)
else if (msg.ReferencedMessage?.Author.Id == nadekoId)
message = msg.Content;
else
return null;
@ -121,7 +116,7 @@ public class ChatterBotService : IExecOnMessage
if (channel is null)
return false;
if (!ChatterBotGuilds.TryGetValue(channel.Guild.Id, out var lazyChatBot))
if (!_chatterBotGuilds.TryGetValue(channel.Guild.Id, out var lazyChatBot))
return false;
var chatBot = lazyChatBot.Value;
@ -205,7 +200,7 @@ public class ChatterBotService : IExecOnMessage
public async Task<bool> ToggleChatterBotAsync(ulong guildId)
{
if (ChatterBotGuilds.TryRemove(guildId, out _))
if (_chatterBotGuilds.TryRemove(guildId, out _))
{
await using var uow = _db.GetDbContext();
await uow.Set<GuildConfig>()
@ -219,7 +214,7 @@ public class ChatterBotService : IExecOnMessage
return false;
}
ChatterBotGuilds.TryAdd(guildId, new(() => CreateSession(), true));
_chatterBotGuilds.TryAdd(guildId, new(() => CreateSession(), true));
await using (var uow = _db.GetDbContext())
{
@ -236,4 +231,16 @@ public class ChatterBotService : IExecOnMessage
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.Db.Models;
namespace EllieBot.Modules.Permissions.Services;
public sealed class CmdCdService : IExecPreCommand, IReadyExecutor, IEService
{
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 =
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;
_settings = bot
.AllGuildConfigs
.ToDictionary(x => x.GuildId, x => x.CommandCooldowns
.DistinctBy(x => x.CommandName.ToLowerInvariant())
.ToDictionary(c => c.CommandName, c => c.Seconds)
.ToConcurrent())
.ToConcurrent();
_shardData = shardData;
}
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)
return Task.FromResult(false);
if (!_settings.TryGetValue(guild.Id, out var cooldownSettings))
return Task.FromResult(false);
@ -66,8 +66,23 @@ public sealed class CmdCdService : IExecPreCommand, IReadyExecutor, IEService
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())
{
// once per hour delete expired entries
@ -81,7 +96,7 @@ public sealed class CmdCdService : IExecPreCommand, IReadyExecutor, IEService
_activeCooldowns.Remove((guildId, commandName), out _);
continue;
}
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))
dict.TryRemove(cmdName, out _);
_activeCooldowns.TryRemove((guildId, cmdName), out _);
using var ctx = _db.GetDbContext();
var gc = ctx.GuildConfigsForId(guildId, x => x.Include(x => x.CommandCooldowns));
gc.CommandCooldowns.RemoveWhere(x => x.CommandName == cmdName);
ctx.SaveChanges();
await using var ctx = _db.GetDbContext();
await ctx.GetTable<CommandCooldown>()
.Where(x => x.GuildId == guildId && x.CommandName == cmdName)
.DeleteAsync();
}
public void AddCooldown(ulong guildId, string name, int secs)
public async Task AddCooldown(ulong guildId, string name, int secs)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(secs);
@ -119,16 +134,24 @@ public sealed class CmdCdService : IExecPreCommand, IReadyExecutor, IEService
// force cleanup
if (_activeCooldowns.TryGetValue((guildId, name), out var dict))
Cleanup(dict, secs);
using var ctx = _db.GetDbContext();
var gc = ctx.GuildConfigsForId(guildId, x => x.Include(x => x.CommandCooldowns));
gc.CommandCooldowns.RemoveWhere(x => x.CommandName == name);
gc.CommandCooldowns.Add(new()
{
Seconds = secs,
CommandName = name
});
ctx.SaveChanges();
await using var ctx = _db.GetDbContext();
await ctx.GetTable<CommandCooldown>()
.InsertOrUpdateAsync(() => new()
{
GuildId = guildId,
CommandName = name,
Seconds = secs
},
(old) => new()
{
Seconds = secs
},
() => new()
{
GuildId = guildId,
CommandName = name,
});
}
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)
{
var channel = (ITextChannel)ctx.Channel;
if (secs is < 0 or > 3600)
{
await Response().Error(strs.invalid_second_param_between(0, 3600)).SendAsync();
@ -30,34 +29,17 @@ public partial class Permissions
}
var name = cmdName.ToLowerInvariant();
await using (var uow = _db.GetDbContext())
{
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();
}
await _service.AddCooldown(ctx.Guild.Id, name, secs);
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();
}
else
{
await Response().Confirm(strs.cmdcd_add(Format.Bold(name), Format.Bold(secs.ToString()))).SendAsync();
}
}
[Cmd]

View file

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

View file

@ -1,11 +1,13 @@
#nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Permissions.Services;
public sealed class FilterService : IExecOnMessage
public sealed class FilterService : IExecOnMessage, IReadyExecutor
{
public ConcurrentHashSet<ulong> InviteFilteringChannels { get; }
public ConcurrentHashSet<ulong> InviteFilteringServers { get; }
@ -23,40 +25,20 @@ public sealed class FilterService : IExecOnMessage
=> int.MaxValue - 1;
private readonly DbService _db;
private readonly ShardData _shardData;
public FilterService(DiscordSocketClient client, DbService db)
public FilterService(DiscordSocketClient client, DbService db, ShardData shardData)
{
_db = db;
_shardData = shardData;
using (var uow = db.GetDbContext())
{
var ids = client.GetGuildIds();
var configs = uow.Set<GuildConfig>()
.AsQueryable()
.Include(x => x.FilteredWords)
.Include(x => x.FilterLinksChannelIds)
.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)));
.AsQueryable()
.Where(gc => ids.Contains(gc.GuildId))
.ToList();
}
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)
{
var words = new ConcurrentHashSet<string>();
@ -82,23 +101,24 @@ public sealed class FilterService : IExecOnMessage
return words;
}
public void ClearFilteredWords(ulong guildId)
public async Task ClearFilteredWords(ulong guildId)
{
using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId,
set => set.Include(x => x.FilteredWords).Include(x => x.FilterWordsChannelIds));
await using var uow = _db.GetDbContext();
var fc = uow.FilterConfigForId(guildId,
set => set.Include(x => x.FilteredWords)
.Include(x => x.FilterWordsChannelIds));
WordFilteringServers.TryRemove(guildId);
ServerFilteredWords.TryRemove(guildId, out _);
foreach (var c in gc.FilterWordsChannelIds)
foreach (var c in fc.FilterWordsChannelIds)
WordFilteringChannels.TryRemove(c.ChannelId);
gc.FilterWords = false;
gc.FilteredWords.Clear();
gc.FilterWordsChannelIds.Clear();
fc.FilterWords = false;
fc.FilteredWords.Clear();
fc.FilterWordsChannelIds.Clear();
uow.SaveChanges();
await uow.SaveChangesAsync();
}
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 (usrMsg.Channel is ITextChannel ch && usrMsg.Author is IGuildUser gu && gu.GetPermissions(ch).ManageMessages)
return false;
if ((InviteFilteringChannels.Contains(usrMsg.Channel.Id) || InviteFilteringServers.Contains(guild.Id))
&& usrMsg.Content.IsDiscordInvite())
{
@ -206,7 +226,7 @@ public sealed class FilterService : IExecOnMessage
// if user has manage messages perm, don't filter
if (usrMsg.Channel is ITextChannel ch && usrMsg.Author is IGuildUser gu && gu.GetPermissions(ch).ManageMessages)
return false;
if ((LinkFilteringChannels.Contains(usrMsg.Channel.Id) || LinkFilteringServers.Contains(guild.Id))
&& usrMsg.Content.TryGetUrlPath(out _))
{
@ -233,17 +253,162 @@ public sealed class FilterService : IExecOnMessage
public async Task<ServerFilterSettings> GetFilterSettings(ulong guildId)
{
await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId,
set => set
.Include(x => x.FilterInvitesChannelIds)
.Include(x => x.FilterLinksChannelIds));
var conf = await uow.GetTable<GuildFilterConfig>()
.Where(fi => fi.GuildId == guildId)
.LoadWith(x => x.FilterInvitesChannelIds)
.LoadWith(x => x.FilterLinksChannelIds)
.FirstOrDefaultAsyncLinqToDB();
return new()
{
FilterInvitesChannels = gc.FilterInvitesChannelIds.Map(x => x.ChannelId),
FilterLinksChannels = gc.FilterLinksChannelIds.Map(x => x.ChannelId),
FilterInvitesEnabled = gc.FilterInvites,
FilterLinksEnabled = gc.FilterLinks,
FilterInvitesChannels = conf?.FilterInvitesChannelIds.Select(x => x.ChannelId).ToArray() ?? [],
FilterLinksChannels = conf?.FilterLinksChannelIds.Select(x => x.ChannelId).ToArray() ?? [],
FilterInvitesEnabled = false,
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.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
using System.Collections.Concurrent;
namespace EllieBot.Modules.Searches.Services;
public class FeedsService : IEService
public class FeedsService : IEService, IReadyExecutor
{
private readonly DbService _db;
private readonly ConcurrentDictionary<string, List<FeedSub>> _subs;
private NonBlocking.ConcurrentDictionary<string, List<FeedSub>> _subs;
private readonly DiscordSocketClient _client;
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();
public FeedsService(
IBot bot,
DbService db,
DiscordSocketClient client,
IMessageSenderService sender)
IMessageSenderService sender,
ShardData shardData)
{
_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;
_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)
@ -116,7 +122,7 @@ public class FeedsService : IEService
if (items.Count > 2)
break;
}
if (items.Count == 0)
continue;
@ -189,7 +195,7 @@ public class FeedsService : IEService
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)
continue;
@ -228,8 +234,10 @@ public class FeedsService : IEService
public List<FeedSub> GetFeeds(ulong guildId)
{
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();
}
@ -249,26 +257,21 @@ public class FeedsService : IEService
};
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;
if (gc.FeedSubs.Count >= 10)
if (feeds.Length >= 10)
return FeedAddResult.LimitReached;
gc.FeedSubs.Add(fs);
uow.Add(fs);
uow.SaveChanges();
//adding all, in case bot wasn't on this guild when it started
foreach (var feed in gc.FeedSubs)
{
_subs.AddOrUpdate(feed.Url.ToLower(),
[feed],
(_, old) =>
{
old.Add(feed);
return old;
});
}
_subs.AddOrUpdate(fs.Url.ToLower(),
[fs],
(_, old) => old.Append(fs).ToList());
return FeedAddResult.Success;
}
@ -279,19 +282,20 @@ public class FeedsService : IEService
return false;
using var uow = _db.GetDbContext();
var items = uow.GuildConfigsForId(guildId, set => set.Include(x => x.FeedSubs))
.FeedSubs.OrderBy(x => x.Id)
.ToList();
var items = uow.Set<FeedSub>()
.Where(x => x.GuildId == guildId)
.OrderBy(x => x.Id)
.ToList();
if (items.Count <= index)
return false;
var toRemove = items[index];
_subs.AddOrUpdate(toRemove.Url.ToLower(),
[],
(_, old) =>
{
old.Remove(toRemove);
return old;
return old.Where(x => x.Id != toRemove.Id).ToList();
});
uow.Remove(toRemove);
uow.SaveChanges();

View file

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

View file

@ -1,5 +1,6 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
using EllieBot.Modules.Searches.Common;
@ -17,16 +18,17 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
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 readonly ConcurrentHashSet<ulong> _offlineNotificationServers;
private readonly ConcurrentHashSet<ulong> _deleteOnOfflineServers;
private Dictionary<StreamDataKey, Dictionary<ulong, HashSet<FollowedStream>>> _shardTrackedStreams;
private readonly ConcurrentHashSet<ulong> _offlineNotificationServers = [];
private readonly ConcurrentHashSet<ulong> _deleteOnOfflineServers = [];
private readonly IPubSub _pubSub;
private readonly IMessageSenderService _sender;
private readonly SearchesConfigService _config;
private readonly IReplacementService _repSvc;
private readonly ShardData _shardData;
public TypedKey<List<StreamData>> StreamsOnlineKey { get; }
public TypedKey<List<StreamData>> StreamsOfflineKey { get; }
@ -46,10 +48,10 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
IBotStrings strings,
IBotCredsProvider creds,
IHttpClientFactory httpFactory,
IBot bot,
IPubSub pubSub,
IMessageSenderService sender,
SearchesConfigService config,
ShardData shardData,
IReplacementService repSvc)
{
_db = db;
@ -59,6 +61,7 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
_sender = sender;
_config = config;
_repSvc = repSvc;
_shardData = shardData;
_streamTracker = new(httpFactory, creds);
@ -68,56 +71,6 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
_streamFollowKey = new("stream.follow");
_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(StreamsOnlineKey, HandleStreamsOnline);
@ -134,15 +87,76 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
_pubSub.Sub(_streamUnfollowKey, HandleUnfollowStream);
}
bot.JoinedGuild += ClientOnJoinedGuild;
client.JoinedGuild += ClientOnJoinedGuild;
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()
{
if (_client.ShardId != 0)
return;
await InitStateAsync();
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(30));
while (await timer.WaitForNextTickAsync())
{
@ -198,7 +212,9 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
{
var key = info.Key;
if (_trackCounter.ContainsKey(key))
{
_trackCounter[key].Add(info.GuildId);
}
else
{
_trackCounter[key] = [info.GuildId];
@ -332,65 +348,67 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
private Task OnStreamsOffline(List<StreamData> 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>()
.AsQueryable()
.Include(x => x.FollowedStreams)
.FirstOrDefault(x => x.GuildId == guildConfig.GuildId);
var fs = await uow.Set<FollowedStream>()
.Where(x => x.GuildId == guild.Id)
.ToListAsyncLinqToDB();
if (gc is null)
return Task.CompletedTask;
var notifyOffline = await uow.GetTable<GuildConfig>()
.Where(x => x.GuildId == guild.Id)
.Select(x => x.NotifyStreamOffline)
.FirstOrDefaultAsyncLinqToDB();
if (gc.NotifyStreamOffline)
_offlineNotificationServers.Add(gc.GuildId);
// todo hashset
if (notifyOffline)
_offlineNotificationServers.Add(guild.Id);
foreach (var followedStream in gc.FollowedStreams)
foreach (var followedStream in fs)
{
var key = followedStream.CreateKey();
var streams = GetLocalGuildStreams(key, gc.GuildId);
var streams = GetLocalGuildStreams(key, guild.Id);
streams.Add(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);
foreach (var followedStream in gc.FollowedStreams)
{
var streams = GetLocalGuildStreams(followedStream.CreateKey(), guild.Id);
streams.Remove(followedStream);
PublishUnfollowStream(followedStream);
}
await PublishUnfollowStream(followedStream);
}
return Task.CompletedTask;
}
public async Task<int> ClearAllStreams(ulong guildId)
{
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);
uow.SaveChanges();
await uow.SaveChangesAsync();
return gc.FollowedStreams.Count;
return followedStreams.Count;
}
public async Task<FollowedStream> UnfollowStreamAsync(ulong guildId, int index)
@ -398,11 +416,11 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
FollowedStream fs;
await using (var uow = _db.GetDbContext())
{
var fss = uow.Set<FollowedStream>()
.AsQueryable()
.Where(x => x.GuildId == guildId)
.OrderBy(x => x.Id)
.ToList();
var fss = await uow.Set<FollowedStream>()
.AsQueryable()
.Where(x => x.GuildId == guildId)
.OrderBy(x => x.Id)
.ToListAsyncEF();
// out of range
if (fss.Count <= index)
@ -454,7 +472,9 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
FollowedStream fs;
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
fs = new()
@ -467,10 +487,10 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
var config = _config.Data;
if (config.FollowedStreams.MaxCount is not -1
&& gc.FollowedStreams.Count >= config.FollowedStreams.MaxCount)
&& followedStreams.Count >= config.FollowedStreams.MaxCount)
return null;
gc.FollowedStreams.Add(fs);
uow.Add(fs);
await uow.SaveChangesAsync();
// 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)
=> _strings.GetText(str, guildId);
public bool ToggleStreamOffline(ulong guildId)
public async Task<bool> ToggleStreamOffline(ulong guildId)
{
bool newValue;
using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set);
newValue = gc.NotifyStreamOffline = !gc.NotifyStreamOffline;
uow.SaveChanges();
await using var uow = _db.GetDbContext();
await uow.GetTable<GuildConfig>()
.InsertOrUpdateAsync(() => new()
{
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)
_offlineNotificationServers.Add(guildId);
@ -545,12 +579,27 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
return newValue;
}
public bool ToggleStreamOnlineDelete(ulong guildId)
public async Task<bool> ToggleStreamOnlineDelete(ulong guildId)
{
using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set);
var newValue = gc.DeleteStreamOnlineMessage = !gc.DeleteStreamOnlineMessage;
uow.SaveChanges();
await using var uow = _db.GetDbContext();
await uow.GetTable<GuildConfig>()
.InsertOrUpdateAsync(() => new()
{
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)
_deleteOnOfflineServers.Add(guildId);
@ -650,13 +699,13 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
public async Task<List<FollowedStream>> GetAllStreamsAsync(SocketGuild guild)
{
var allStreams = new List<FollowedStream>();
await using var uow = _db.GetDbContext();
var all = uow.GuildConfigsForId(guild.Id, set => set.Include(gc => gc.FollowedStreams))
.FollowedStreams
.OrderBy(x => x.Id)
.ToList();
var all = await uow.Set<FollowedStream>()
.Where(x => x.GuildId == guild.Id)
.OrderBy(x => x.Id)
.ToListAsyncEF();
var allStreams = new List<FollowedStream>();
for (var index = all.Count - 1; index >= 0; index--)
{
var fs = all[index];

View file

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

View file

@ -35,7 +35,9 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
List<AutoTranslateChannel> cs;
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)
.Where(x => guilds.Contains(x.GuildId))
.ToListAsyncEF();
@ -106,8 +108,8 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
{
await using var ctx = _db.GetDbContext();
var old = await ctx.Set<AutoTranslateChannel>().ToLinqToDBTable()
.FirstOrDefaultAsyncLinqToDB(x => x.ChannelId == channelId);
var old = await ctx.Set<AutoTranslateChannel>()
.FirstOrDefaultAsyncEF(x => x.ChannelId == channelId);
if (old is null)
{

View file

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

View file

@ -24,7 +24,7 @@ public partial class Utility
[UserPerm(GuildPerm.Administrator)]
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();
}
@ -40,64 +40,17 @@ public partial class Utility
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();
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();
return;
}
_service.AliasMaps.AddOrUpdate(ctx.Guild.Id,
_ =>
{
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 _service.AddAliasAsync(ctx.Guild.Id, trigger, mapping);
await Response().Confirm(strs.alias_added(Format.Code(trigger), Format.Code(mapping))).SendAsync();
}
@ -112,13 +65,14 @@ public partial class Utility
if (page < 0)
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();
return;
}
var arr = maps.ToArray();
var arr = aliases.Select(x => (Trigger: x.Key, Mapping: x.Value)).ToArray();
await Response()
.Paginated()
@ -130,7 +84,7 @@ public partial class Utility
return CreateEmbed()
.WithOkColor()
.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();
}

View file

@ -1,54 +1,47 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
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 IMessageSenderService _sender;
private readonly ShardData _shardData;
public AliasService(
DiscordSocketClient client,
DbService db,
IMessageSenderService sender)
IMessageSenderService sender,
ShardData shardData)
{
_sender = sender;
_shardData = shardData;
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;
}
public int ClearAliases(ulong guildId)
public async Task<int> ClearAliases(ulong guildId)
{
AliasMaps.TryRemove(guildId, out _);
_aliases.TryRemove(guildId, out _);
int count;
using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.CommandAliases));
count = gc.CommandAliases.Count;
gc.CommandAliases.Clear();
uow.SaveChanges();
return count;
await using var uow = _db.GetDbContext();
var deleted = await uow.GetTable<CommandAlias>()
.Where(x => x.GuildId == guildId)
.DeleteAsync();
return deleted;
}
public async Task<string> TransformInput(
IGuild guild,
public async Task<string?> TransformInput(
IGuild? guild,
IMessageChannel channel,
IUser user,
string input)
@ -56,39 +49,47 @@ public class AliasService : IInputTransformer, IEService
if (guild is null || string.IsNullOrWhiteSpace(input))
return null;
if (AliasMaps.TryGetValue(guild.Id, out var maps))
if (_aliases.TryGetValue(guild.Id, out var maps))
{
string newInput = null;
foreach (var (k, v) in maps)
string? newInput = null;
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%"))
newInput = v.Replace("%target%", input[k.Length..]);
else
newInput = v + ' ' + input[k.Length..];
var toDelete = await _sender.Response(channel)
.Confirm($"{input} => {newInput}")
.SendAsync();
toDelete.DeleteAfter(1.5f);
}
catch
{
// ignored
}
if (newInput is not null)
{
try
{
var toDelete = await _sender.Response(channel)
.Confirm($"{input} => {newInput}")
.SendAsync();
toDelete.DeleteAfter(1.5f);
}
catch
{
// ignored
}
return newInput;
}
return newInput;
}
return null;
@ -96,4 +97,71 @@ public class AliasService : IInputTransformer, IEService
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
.GetTable<GiveawayModel>()
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId))
.Where(x => Queries.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId))
.ToArrayAsync();
lock (_giveawayCache)

View file

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

View file

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

View file

@ -699,7 +699,7 @@ public partial class Utility : EllieModule
[UserPerm(GuildPerm.ManageMessages)]
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)
await Response().Confirm(strs.verbose_errors_enabled).SendAsync();

View file

@ -1,29 +1,31 @@
#nullable disable
using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
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 CommandHandler _ch;
private readonly ICommandsUtilityService _hs;
private readonly IMessageSenderService _sender;
private readonly ShardData _shardData;
public VerboseErrorsService(
IBot bot,
DbService db,
CommandHandler ch,
IMessageSenderService sender,
ICommandsUtilityService hs)
ICommandsUtilityService hs,
ShardData shardData)
{
_db = db;
_ch = ch;
_hs = hs;
_sender = sender;
_ch.CommandErrored += LogVerboseError;
_guildsDisabled = new(bot.AllGuildConfigs.Where(x => !x.VerboseErrors).Select(x => x.GuildId));
_shardData = shardData;
}
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();
var gc = uow.GuildConfigsForId(guildId, set => set);
await using var ctx = _db.GetDbContext();
if (maybeEnabled is bool isEnabled) // set it
gc.VerboseErrors = isEnabled;
else // toggle it
isEnabled = gc.VerboseErrors = !gc.VerboseErrors;
uow.SaveChanges();
var isEnabled = ctx.GetTable<GuildConfig>()
.Where(x => x.GuildId == guildId)
.Select(x => x.VerboseErrors)
.FirstOrDefault();
if (isEnabled) // This doesn't need to be duplicated inside the using block
{
_guildsDisabled.TryRemove(guildId);
}
else
{
_guildsDisabled.Add(guildId);
}
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)]
public async Task XpExclude(Server _)
{
var ex = _service.ToggleExcludeServer(ctx.Guild.Id);
var ex = await _service.ToggleExcludeServerAsync(ctx.Guild.Id);
if (ex)
await Response().Confirm(strs.excluded(Format.Bold(ctx.Guild.ToString()))).SendAsync();
@ -69,7 +69,7 @@ public partial class Xp : EllieModule<XpService>
[RequireContext(ContextType.Guild)]
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)
await Response().Confirm(strs.excluded(Format.Bold(role.ToString()))).SendAsync();
@ -85,7 +85,7 @@ public partial class Xp : EllieModule<XpService>
if (channel is null)
channel = ctx.Channel;
var ex = _service.ToggleExcludeChannel(ctx.Guild.Id, channel.Id);
var ex = await _service.ToggleExcludeChannelAsync(ctx.Guild.Id, channel.Id);
if (ex)
await Response().Confirm(strs.excluded(Format.Bold(channel.ToString()))).SendAsync();
@ -308,7 +308,7 @@ public partial class Xp : EllieModule<XpService>
if (amount == 0)
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();
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))
eb.AddField(GetText(strs.desc), item.Desc);
#if GLOBAL_ELLIE
#if GLOBAL_NADEKO
if (key == "default")
eb.WithDescription(GetText(strs.xpshop_website));
#endif

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