mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	Restructured the project structure back to the way it was, there's no reasonable way to split the modules
This commit is contained in:
		
							
								
								
									
										34
									
								
								src/NadekoBot/Db/Extensions/ClubExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/NadekoBot/Db/Extensions/ClubExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class ClubExtensions
 | 
			
		||||
{
 | 
			
		||||
    private static IQueryable<ClubInfo> Include(this DbSet<ClubInfo> clubs)
 | 
			
		||||
        => clubs.Include(x => x.Owner)
 | 
			
		||||
                .Include(x => x.Applicants)
 | 
			
		||||
                .ThenInclude(x => x.User)
 | 
			
		||||
                .Include(x => x.Bans)
 | 
			
		||||
                .ThenInclude(x => x.User)
 | 
			
		||||
                .Include(x => x.Members)
 | 
			
		||||
                .AsQueryable();
 | 
			
		||||
 | 
			
		||||
    public static ClubInfo GetByOwner(this DbSet<ClubInfo> clubs, ulong userId)
 | 
			
		||||
        => Include(clubs).FirstOrDefault(c => c.Owner.UserId == userId);
 | 
			
		||||
 | 
			
		||||
    public static ClubInfo GetByOwnerOrAdmin(this DbSet<ClubInfo> clubs, ulong userId)
 | 
			
		||||
        => Include(clubs)
 | 
			
		||||
            .FirstOrDefault(c => c.Owner.UserId == userId || c.Members.Any(u => u.UserId == userId && u.IsClubAdmin));
 | 
			
		||||
 | 
			
		||||
    public static ClubInfo GetByMember(this DbSet<ClubInfo> clubs, ulong userId)
 | 
			
		||||
        => Include(clubs).FirstOrDefault(c => c.Members.Any(u => u.UserId == userId));
 | 
			
		||||
 | 
			
		||||
    public static ClubInfo GetByName(this DbSet<ClubInfo> clubs, string name)
 | 
			
		||||
        => Include(clubs)
 | 
			
		||||
            .FirstOrDefault(c => c.Name == name);
 | 
			
		||||
 | 
			
		||||
    public static List<ClubInfo> GetClubLeaderboardPage(this DbSet<ClubInfo> clubs, int page)
 | 
			
		||||
        => clubs.AsNoTracking().OrderByDescending(x => x.Xp).Skip(page * 9).Take(9).ToList();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								src/NadekoBot/Db/Extensions/CurrencyTransactionExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/NadekoBot/Db/Extensions/CurrencyTransactionExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class CurrencyTransactionExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static Task<List<CurrencyTransaction>> GetPageFor(
 | 
			
		||||
        this DbSet<CurrencyTransaction> set,
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        int page)
 | 
			
		||||
        => set.ToLinqToDBTable()
 | 
			
		||||
            .Where(x => x.UserId == userId)
 | 
			
		||||
            .OrderByDescending(x => x.DateAdded)
 | 
			
		||||
            .Skip(15 * page)
 | 
			
		||||
            .Take(15)
 | 
			
		||||
            .ToListAsyncLinqToDB();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/NadekoBot/Db/Extensions/DbExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/NadekoBot/Db/Extensions/DbExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class DbExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static T GetById<T>(this DbSet<T> set, int id)
 | 
			
		||||
        where T : DbEntity
 | 
			
		||||
        => set.FirstOrDefault(x => x.Id == id);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										125
									
								
								src/NadekoBot/Db/Extensions/DiscordUserExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/NadekoBot/Db/Extensions/DiscordUserExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,125 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using LinqToDB;
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class DiscordUserExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static Task<DiscordUser> GetByUserIdAsync(
 | 
			
		||||
        this IQueryable<DiscordUser> set,
 | 
			
		||||
        ulong userId)
 | 
			
		||||
        => set.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId);
 | 
			
		||||
 | 
			
		||||
    public static void EnsureUserCreated(
 | 
			
		||||
        this DbContext ctx,
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        string username,
 | 
			
		||||
        string discrim,
 | 
			
		||||
        string avatarId)
 | 
			
		||||
        => ctx.GetTable<DiscordUser>()
 | 
			
		||||
            .InsertOrUpdate(
 | 
			
		||||
                () => new()
 | 
			
		||||
                {
 | 
			
		||||
                    UserId = userId,
 | 
			
		||||
                    Username = username,
 | 
			
		||||
                    Discriminator = discrim,
 | 
			
		||||
                    AvatarId = avatarId,
 | 
			
		||||
                    TotalXp = 0,
 | 
			
		||||
                    CurrencyAmount = 0
 | 
			
		||||
                },
 | 
			
		||||
                old => new()
 | 
			
		||||
                {
 | 
			
		||||
                    Username = username,
 | 
			
		||||
                    Discriminator = discrim,
 | 
			
		||||
                    AvatarId = avatarId
 | 
			
		||||
                },
 | 
			
		||||
                () => new()
 | 
			
		||||
                {
 | 
			
		||||
                    UserId = userId
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
    public static Task EnsureUserCreatedAsync(
 | 
			
		||||
        this DbContext ctx,
 | 
			
		||||
        ulong userId)
 | 
			
		||||
        => ctx.GetTable<DiscordUser>()
 | 
			
		||||
            .InsertOrUpdateAsync(
 | 
			
		||||
                () => new()
 | 
			
		||||
                {
 | 
			
		||||
                    UserId = userId,
 | 
			
		||||
                    Username = "Unknown",
 | 
			
		||||
                    Discriminator = "????",
 | 
			
		||||
                    AvatarId = string.Empty,
 | 
			
		||||
                    TotalXp = 0,
 | 
			
		||||
                    CurrencyAmount = 0
 | 
			
		||||
                },
 | 
			
		||||
                old => new()
 | 
			
		||||
                {
 | 
			
		||||
                },
 | 
			
		||||
                () => new()
 | 
			
		||||
                {
 | 
			
		||||
                    UserId = userId
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
    //temp is only used in updatecurrencystate, so that i don't overwrite real usernames/discrims with Unknown
 | 
			
		||||
    public static DiscordUser GetOrCreateUser(
 | 
			
		||||
        this DbContext ctx,
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        string username,
 | 
			
		||||
        string discrim,
 | 
			
		||||
        string avatarId,
 | 
			
		||||
        Func<IQueryable<DiscordUser>, IQueryable<DiscordUser>> includes = null)
 | 
			
		||||
    {
 | 
			
		||||
        ctx.EnsureUserCreated(userId, username, discrim, avatarId);
 | 
			
		||||
 | 
			
		||||
        IQueryable<DiscordUser> queryable = ctx.Set<DiscordUser>();
 | 
			
		||||
        if (includes is not null)
 | 
			
		||||
            queryable = includes(queryable);
 | 
			
		||||
        return queryable.First(u => u.UserId == userId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static int GetUserGlobalRank(this DbSet<DiscordUser> users, ulong id)
 | 
			
		||||
        => users.AsQueryable()
 | 
			
		||||
               .Where(x => x.TotalXp
 | 
			
		||||
                           > users.AsQueryable().Where(y => y.UserId == id).Select(y => y.TotalXp).FirstOrDefault())
 | 
			
		||||
               .Count()
 | 
			
		||||
           + 1;
 | 
			
		||||
 | 
			
		||||
    public static DiscordUser[] GetUsersXpLeaderboardFor(this DbSet<DiscordUser> users, int page, int perPage)
 | 
			
		||||
        => users.AsQueryable().OrderByDescending(x => x.TotalXp).Skip(page * perPage).Take(perPage).AsEnumerable()
 | 
			
		||||
            .ToArray();
 | 
			
		||||
 | 
			
		||||
    public static Task<List<DiscordUser>> GetTopRichest(
 | 
			
		||||
        this DbSet<DiscordUser> users,
 | 
			
		||||
        ulong botId,
 | 
			
		||||
        int page = 0, int perPage = 9)
 | 
			
		||||
        => users.AsQueryable()
 | 
			
		||||
            .Where(c => c.CurrencyAmount > 0 && botId != c.UserId)
 | 
			
		||||
            .OrderByDescending(c => c.CurrencyAmount)
 | 
			
		||||
            .Skip(page * perPage)
 | 
			
		||||
            .Take(perPage)
 | 
			
		||||
            .ToListAsyncLinqToDB();
 | 
			
		||||
 | 
			
		||||
    public static async Task<long> GetUserCurrencyAsync(this DbSet<DiscordUser> users, ulong userId)
 | 
			
		||||
        => (await users.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId))?.CurrencyAmount ?? 0;
 | 
			
		||||
 | 
			
		||||
    public static void RemoveFromMany(this DbSet<DiscordUser> users, IEnumerable<ulong> ids)
 | 
			
		||||
    {
 | 
			
		||||
        var items = users.AsQueryable().Where(x => ids.Contains(x.UserId));
 | 
			
		||||
        foreach (var item in items)
 | 
			
		||||
            item.CurrencyAmount = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static decimal GetTotalCurrency(this DbSet<DiscordUser> users)
 | 
			
		||||
        => users.Sum((Func<DiscordUser, decimal>)(x => x.CurrencyAmount));
 | 
			
		||||
 | 
			
		||||
    public static decimal GetTopOnePercentCurrency(this DbSet<DiscordUser> users, ulong botId)
 | 
			
		||||
        => users.AsQueryable()
 | 
			
		||||
            .Where(x => x.UserId != botId)
 | 
			
		||||
            .OrderByDescending(x => x.CurrencyAmount)
 | 
			
		||||
            .Take(users.Count() / 100 == 0 ? 1 : users.Count() / 100)
 | 
			
		||||
            .Sum(x => x.CurrencyAmount);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										229
									
								
								src/NadekoBot/Db/Extensions/GuildConfigExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								src/NadekoBot/Db/Extensions/GuildConfigExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,229 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class GuildConfigExtensions
 | 
			
		||||
{
 | 
			
		||||
    private static List<WarningPunishment> DefaultWarnPunishments
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            new()
 | 
			
		||||
            {
 | 
			
		||||
                Count = 3,
 | 
			
		||||
                Punishment = PunishmentAction.Kick
 | 
			
		||||
            },
 | 
			
		||||
            new()
 | 
			
		||||
            {
 | 
			
		||||
                Count = 5,
 | 
			
		||||
                Punishment = PunishmentAction.Ban
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Gets full stream role settings for the guild with the specified id.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <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)
 | 
			
		||||
    {
 | 
			
		||||
        var conf = ctx.GuildConfigsForId(guildId,
 | 
			
		||||
            set => set.Include(y => y.StreamRole)
 | 
			
		||||
                .Include(y => y.StreamRole.Whitelist)
 | 
			
		||||
                .Include(y => y.StreamRole.Blacklist));
 | 
			
		||||
 | 
			
		||||
        if (conf.StreamRole is null)
 | 
			
		||||
            conf.StreamRole = new();
 | 
			
		||||
 | 
			
		||||
        return conf.StreamRole;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static IQueryable<GuildConfig> IncludeEverything(this DbSet<GuildConfig> configs)
 | 
			
		||||
        => configs.AsQueryable()
 | 
			
		||||
            .AsSplitQuery()
 | 
			
		||||
            .Include(gc => gc.CommandCooldowns)
 | 
			
		||||
            .Include(gc => gc.FollowedStreams)
 | 
			
		||||
            .Include(gc => gc.StreamRole)
 | 
			
		||||
            .Include(gc => gc.XpSettings)
 | 
			
		||||
            .ThenInclude(x => x.ExclusionList)
 | 
			
		||||
            .Include(gc => gc.DelMsgOnCmdChannels);
 | 
			
		||||
 | 
			
		||||
    public static IEnumerable<GuildConfig> GetAllGuildConfigs(
 | 
			
		||||
        this DbSet<GuildConfig> configs,
 | 
			
		||||
        IReadOnlyList<ulong> availableGuilds)
 | 
			
		||||
        => configs.IncludeEverything().AsNoTracking().Where(x => availableGuilds.Contains(x.GuildId)).ToList();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Gets and creates if it doesn't exist a config for a guild.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="ctx">Context</param>
 | 
			
		||||
    /// <param name="guildId">Id of the guide</param>
 | 
			
		||||
    /// <param name="includes">Use to manipulate the set however you want. Pass null to include everything</param>
 | 
			
		||||
    /// <returns>Config for the guild</returns>
 | 
			
		||||
    public static GuildConfig GuildConfigsForId(
 | 
			
		||||
        this DbContext ctx,
 | 
			
		||||
        ulong guildId,
 | 
			
		||||
        Func<DbSet<GuildConfig>, IQueryable<GuildConfig>> includes)
 | 
			
		||||
    {
 | 
			
		||||
        GuildConfig config;
 | 
			
		||||
 | 
			
		||||
        if (includes is null)
 | 
			
		||||
            config = ctx.Set<GuildConfig>().IncludeEverything().FirstOrDefault(c => c.GuildId == guildId);
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            var set = includes(ctx.Set<GuildConfig>());
 | 
			
		||||
            config = set.FirstOrDefault(c => c.GuildId == guildId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (config is null)
 | 
			
		||||
        {
 | 
			
		||||
            ctx.Set<GuildConfig>().Add(config = new()
 | 
			
		||||
            {
 | 
			
		||||
                GuildId = guildId,
 | 
			
		||||
                Permissions = Permissionv2.GetDefaultPermlist,
 | 
			
		||||
                WarningsInitialized = true,
 | 
			
		||||
                WarnPunishments = DefaultWarnPunishments
 | 
			
		||||
            });
 | 
			
		||||
            ctx.SaveChanges();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!config.WarningsInitialized)
 | 
			
		||||
        {
 | 
			
		||||
            config.WarningsInitialized = true;
 | 
			
		||||
            config.WarnPunishments = DefaultWarnPunishments;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return config;
 | 
			
		||||
 | 
			
		||||
        // ctx.GuildConfigs
 | 
			
		||||
        //    .ToLinqToDBTable()
 | 
			
		||||
        //    .InsertOrUpdate(() => new()
 | 
			
		||||
        //        {
 | 
			
		||||
        //            GuildId = guildId,
 | 
			
		||||
        //            Permissions = Permissionv2.GetDefaultPermlist,
 | 
			
		||||
        //            WarningsInitialized = true,
 | 
			
		||||
        //            WarnPunishments = DefaultWarnPunishments
 | 
			
		||||
        //        },
 | 
			
		||||
        //        _ => new(),
 | 
			
		||||
        //        () => new()
 | 
			
		||||
        //        {
 | 
			
		||||
        //            GuildId = guildId
 | 
			
		||||
        //        });
 | 
			
		||||
        //
 | 
			
		||||
        // if(includes is null)
 | 
			
		||||
        // return ctx.GuildConfigs
 | 
			
		||||
        //    .ToLinqToDBTable()
 | 
			
		||||
        //    .First(x => x.GuildId == guildId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static LogSetting LogSettingsFor(this DbContext ctx, ulong guildId)
 | 
			
		||||
    {
 | 
			
		||||
        var logSetting = ctx.Set<LogSetting>()
 | 
			
		||||
            .AsQueryable()
 | 
			
		||||
            .Include(x => x.LogIgnores)
 | 
			
		||||
            .Where(x => x.GuildId == guildId)
 | 
			
		||||
            .FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
        if (logSetting is null)
 | 
			
		||||
        {
 | 
			
		||||
            ctx.Set<LogSetting>()
 | 
			
		||||
                .Add(logSetting = new()
 | 
			
		||||
                {
 | 
			
		||||
                    GuildId = guildId
 | 
			
		||||
                });
 | 
			
		||||
            ctx.SaveChanges();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        return query.ToList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static GuildConfig GcWithPermissionsFor(this DbContext ctx, ulong guildId)
 | 
			
		||||
    {
 | 
			
		||||
        var config = ctx.Set<GuildConfig>().AsQueryable()
 | 
			
		||||
            .Where(gc => gc.GuildId == guildId)
 | 
			
		||||
            .Include(gc => gc.Permissions)
 | 
			
		||||
            .FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
        if (config is null) // if there is no guildconfig, create new one
 | 
			
		||||
        {
 | 
			
		||||
            ctx.Set<GuildConfig>().Add(config = new()
 | 
			
		||||
            {
 | 
			
		||||
                GuildId = guildId,
 | 
			
		||||
                Permissions = Permissionv2.GetDefaultPermlist
 | 
			
		||||
            });
 | 
			
		||||
            ctx.SaveChanges();
 | 
			
		||||
        }
 | 
			
		||||
        else if (config.Permissions is null || !config.Permissions.Any()) // if no perms, add default ones
 | 
			
		||||
        {
 | 
			
		||||
            config.Permissions = Permissionv2.GetDefaultPermlist;
 | 
			
		||||
            ctx.SaveChanges();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return config;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static IEnumerable<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 void SetCleverbotEnabled(this DbSet<GuildConfig> configs, ulong id, bool cleverbotEnabled)
 | 
			
		||||
    {
 | 
			
		||||
        var conf = configs.FirstOrDefault(gc => gc.GuildId == id);
 | 
			
		||||
 | 
			
		||||
        if (conf is null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        conf.CleverbotEnabled = cleverbotEnabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static XpSettings XpSettingsFor(this DbContext ctx, ulong guildId)
 | 
			
		||||
    {
 | 
			
		||||
        var gc = ctx.GuildConfigsForId(guildId,
 | 
			
		||||
            set => set.Include(x => x.XpSettings)
 | 
			
		||||
                .ThenInclude(x => x.RoleRewards)
 | 
			
		||||
                .Include(x => x.XpSettings)
 | 
			
		||||
                .ThenInclude(x => x.CurrencyRewards)
 | 
			
		||||
                .Include(x => x.XpSettings)
 | 
			
		||||
                .ThenInclude(x => x.ExclusionList));
 | 
			
		||||
 | 
			
		||||
        if (gc.XpSettings is null)
 | 
			
		||||
            gc.XpSettings = new();
 | 
			
		||||
 | 
			
		||||
        return gc.XpSettings;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static IEnumerable<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; }
 | 
			
		||||
        public ulong ChannelId { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/NadekoBot/Db/Extensions/NadekoExpressionExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/NadekoBot/Db/Extensions/NadekoExpressionExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using LinqToDB;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class NadekoExpressionExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static int ClearFromGuild(this DbSet<NadekoExpression> exprs, ulong guildId)
 | 
			
		||||
        => exprs.Delete(x => x.GuildId == guildId);
 | 
			
		||||
 | 
			
		||||
    public static IEnumerable<NadekoExpression> ForId(this DbSet<NadekoExpression> exprs, ulong id)
 | 
			
		||||
        => exprs.AsNoTracking().AsQueryable().Where(x => x.GuildId == id).ToList();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								src/NadekoBot/Db/Extensions/QuoteExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/NadekoBot/Db/Extensions/QuoteExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class QuoteExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static IEnumerable<Quote> GetForGuild(this DbSet<Quote> quotes, ulong guildId)
 | 
			
		||||
        => quotes.AsQueryable().Where(x => x.GuildId == guildId);
 | 
			
		||||
 | 
			
		||||
    public static IReadOnlyCollection<Quote> GetGroup(
 | 
			
		||||
        this DbSet<Quote> quotes,
 | 
			
		||||
        ulong guildId,
 | 
			
		||||
        int page,
 | 
			
		||||
        OrderType order)
 | 
			
		||||
    {
 | 
			
		||||
        var q = quotes.AsQueryable().Where(x => x.GuildId == guildId);
 | 
			
		||||
        if (order == OrderType.Keyword)
 | 
			
		||||
            q = q.OrderBy(x => x.Keyword);
 | 
			
		||||
        else
 | 
			
		||||
            q = q.OrderBy(x => x.Id);
 | 
			
		||||
 | 
			
		||||
        return q.Skip(15 * page).Take(15).ToArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static async Task<Quote> GetRandomQuoteByKeywordAsync(
 | 
			
		||||
        this DbSet<Quote> quotes,
 | 
			
		||||
        ulong guildId,
 | 
			
		||||
        string keyword)
 | 
			
		||||
    {
 | 
			
		||||
        return (await quotes.AsQueryable().Where(q => q.GuildId == guildId && q.Keyword == keyword).ToArrayAsync())
 | 
			
		||||
            .RandomOrDefault();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static async Task<Quote> SearchQuoteKeywordTextAsync(
 | 
			
		||||
        this DbSet<Quote> quotes,
 | 
			
		||||
        ulong guildId,
 | 
			
		||||
        string keyword,
 | 
			
		||||
        string text)
 | 
			
		||||
    {
 | 
			
		||||
        return (await quotes.AsQueryable()
 | 
			
		||||
                            .Where(q => q.GuildId == guildId
 | 
			
		||||
                                        && (keyword == null || q.Keyword == keyword)
 | 
			
		||||
                                        && (EF.Functions.Like(q.Text.ToUpper(), $"%{text.ToUpper()}%")
 | 
			
		||||
                                            || EF.Functions.Like(q.AuthorName, text)))
 | 
			
		||||
                            .ToArrayAsync())
 | 
			
		||||
               .RandomOrDefault();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void RemoveAllByKeyword(this DbSet<Quote> quotes, ulong guildId, string keyword)
 | 
			
		||||
        => quotes.RemoveRange(quotes.AsQueryable().Where(x => x.GuildId == guildId && x.Keyword.ToUpper() == keyword));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								src/NadekoBot/Db/Extensions/ReminderExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/NadekoBot/Db/Extensions/ReminderExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class ReminderExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static IEnumerable<Reminder> GetIncludedReminders(
 | 
			
		||||
        this DbSet<Reminder> reminders,
 | 
			
		||||
        IEnumerable<ulong> guildIds)
 | 
			
		||||
        => reminders.AsQueryable().Where(x => guildIds.Contains(x.ServerId) || x.ServerId == 0).ToList();
 | 
			
		||||
 | 
			
		||||
    public static IEnumerable<Reminder> RemindersFor(this DbSet<Reminder> reminders, ulong userId, int page)
 | 
			
		||||
        => reminders.AsQueryable().Where(x => x.UserId == userId).OrderBy(x => x.DateAdded).Skip(page * 10).Take(10);
 | 
			
		||||
 | 
			
		||||
    public static IEnumerable<Reminder> RemindersForServer(this DbSet<Reminder> reminders, ulong serverId, int page)
 | 
			
		||||
        => reminders.AsQueryable()
 | 
			
		||||
                    .Where(x => x.ServerId == serverId)
 | 
			
		||||
                    .OrderBy(x => x.DateAdded)
 | 
			
		||||
                    .Skip(page * 10)
 | 
			
		||||
                    .Take(10);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								src/NadekoBot/Db/Extensions/SelfAssignableRolesExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/NadekoBot/Db/Extensions/SelfAssignableRolesExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class SelfAssignableRolesExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static bool DeleteByGuildAndRoleId(this DbSet<SelfAssignedRole> roles, ulong guildId, ulong roleId)
 | 
			
		||||
    {
 | 
			
		||||
        var role = roles.FirstOrDefault(s => s.GuildId == guildId && s.RoleId == roleId);
 | 
			
		||||
 | 
			
		||||
        if (role is null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        roles.Remove(role);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static IReadOnlyCollection<SelfAssignedRole> GetFromGuild(this DbSet<SelfAssignedRole> roles, ulong guildId)
 | 
			
		||||
        => roles.AsQueryable().Where(s => s.GuildId == guildId).ToArray();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								src/NadekoBot/Db/Extensions/UserXpExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/NadekoBot/Db/Extensions/UserXpExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using LinqToDB;
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class UserXpExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static UserXpStats GetOrCreateUserXpStats(this DbContext ctx, ulong guildId, ulong userId)
 | 
			
		||||
    {
 | 
			
		||||
        var usr = ctx.Set<UserXpStats>().FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId);
 | 
			
		||||
 | 
			
		||||
        if (usr is null)
 | 
			
		||||
        {
 | 
			
		||||
            ctx.Add(usr = new()
 | 
			
		||||
            {
 | 
			
		||||
                Xp = 0,
 | 
			
		||||
                UserId = userId,
 | 
			
		||||
                NotifyOnLevelUp = XpNotificationLocation.None,
 | 
			
		||||
                GuildId = guildId
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return usr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static List<UserXpStats> GetUsersFor(this DbSet<UserXpStats> xps, ulong guildId, int page)
 | 
			
		||||
        => xps.AsQueryable()
 | 
			
		||||
              .AsNoTracking()
 | 
			
		||||
              .Where(x => x.GuildId == guildId)
 | 
			
		||||
              .OrderByDescending(x => x.Xp + x.AwardedXp)
 | 
			
		||||
              .Skip(page * 9)
 | 
			
		||||
              .Take(9)
 | 
			
		||||
              .ToList();
 | 
			
		||||
 | 
			
		||||
    public static List<UserXpStats> GetTopUserXps(this DbSet<UserXpStats> xps, ulong guildId, int count)
 | 
			
		||||
        => xps.AsQueryable()
 | 
			
		||||
              .AsNoTracking()
 | 
			
		||||
              .Where(x => x.GuildId == guildId)
 | 
			
		||||
              .OrderByDescending(x => x.Xp + x.AwardedXp)
 | 
			
		||||
              .Take(count)
 | 
			
		||||
              .ToList();
 | 
			
		||||
 | 
			
		||||
    public static int GetUserGuildRanking(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
 | 
			
		||||
        => xps.AsQueryable()
 | 
			
		||||
              .AsNoTracking()
 | 
			
		||||
              .Where(x => x.GuildId == guildId
 | 
			
		||||
                          && x.Xp + x.AwardedXp
 | 
			
		||||
                          > xps.AsQueryable()
 | 
			
		||||
                               .Where(y => y.UserId == userId && y.GuildId == guildId)
 | 
			
		||||
                               .Select(y => y.Xp + y.AwardedXp)
 | 
			
		||||
                               .FirstOrDefault())
 | 
			
		||||
              .Count()
 | 
			
		||||
           + 1;
 | 
			
		||||
 | 
			
		||||
    public static void ResetGuildUserXp(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
 | 
			
		||||
        => xps.Delete(x => x.UserId == userId && x.GuildId == guildId);
 | 
			
		||||
 | 
			
		||||
    public static void ResetGuildXp(this DbSet<UserXpStats> xps, ulong guildId)
 | 
			
		||||
        => xps.Delete(x => x.GuildId == guildId);
 | 
			
		||||
    
 | 
			
		||||
    public static async Task<LevelStats> GetLevelDataFor(this ITable<UserXpStats> userXp, ulong guildId, ulong userId)
 | 
			
		||||
            => await userXp
 | 
			
		||||
                     .Where(x => x.GuildId == guildId && x.UserId == userId)
 | 
			
		||||
                     .FirstOrDefaultAsyncLinqToDB() is UserXpStats uxs
 | 
			
		||||
                ? new(uxs.Xp + uxs.AwardedXp)
 | 
			
		||||
                : new(0);
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										60
									
								
								src/NadekoBot/Db/Extensions/WarningExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/NadekoBot/Db/Extensions/WarningExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class WarningExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static Warning[] ForId(this DbSet<Warning> warnings, ulong guildId, ulong userId)
 | 
			
		||||
    {
 | 
			
		||||
        var query = warnings.AsQueryable()
 | 
			
		||||
                            .Where(x => x.GuildId == guildId && x.UserId == userId)
 | 
			
		||||
                            .OrderByDescending(x => x.DateAdded);
 | 
			
		||||
 | 
			
		||||
        return query.ToArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static bool Forgive(
 | 
			
		||||
        this DbSet<Warning> warnings,
 | 
			
		||||
        ulong guildId,
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        string mod,
 | 
			
		||||
        int index)
 | 
			
		||||
    {
 | 
			
		||||
        if (index < 0)
 | 
			
		||||
            throw new ArgumentOutOfRangeException(nameof(index));
 | 
			
		||||
 | 
			
		||||
        var warn = warnings.AsQueryable()
 | 
			
		||||
                           .Where(x => x.GuildId == guildId && x.UserId == userId)
 | 
			
		||||
                           .OrderByDescending(x => x.DateAdded)
 | 
			
		||||
                           .Skip(index)
 | 
			
		||||
                           .FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
        if (warn is null || warn.Forgiven)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        warn.Forgiven = true;
 | 
			
		||||
        warn.ForgivenBy = mod;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static async Task ForgiveAll(
 | 
			
		||||
        this DbSet<Warning> warnings,
 | 
			
		||||
        ulong guildId,
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        string mod)
 | 
			
		||||
        => await warnings.AsQueryable()
 | 
			
		||||
                         .Where(x => x.GuildId == guildId && x.UserId == userId)
 | 
			
		||||
                         .ForEachAsync(x =>
 | 
			
		||||
                         {
 | 
			
		||||
                             if (x.Forgiven != true)
 | 
			
		||||
                             {
 | 
			
		||||
                                 x.Forgiven = true;
 | 
			
		||||
                                 x.ForgivenBy = mod;
 | 
			
		||||
                             }
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
    public static Warning[] GetForGuild(this DbSet<Warning> warnings, ulong id)
 | 
			
		||||
        => warnings.AsQueryable().Where(x => x.GuildId == id).ToArray();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								src/NadekoBot/Db/Helpers/ActivityType.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/NadekoBot/Db/Helpers/ActivityType.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
namespace Nadeko.Bot.Db;
 | 
			
		||||
 | 
			
		||||
public enum ActivityType
 | 
			
		||||
{
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								src/NadekoBot/Db/Helpers/GuildPerm.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/NadekoBot/Db/Helpers/GuildPerm.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
namespace Nadeko.Bot.Db;
 | 
			
		||||
 | 
			
		||||
[Flags]
 | 
			
		||||
public enum GuildPerm : ulong
 | 
			
		||||
{
 | 
			
		||||
    CreateInstantInvite = 1,
 | 
			
		||||
    KickMembers = 2,
 | 
			
		||||
    BanMembers = 4,
 | 
			
		||||
    Administrator = 8,
 | 
			
		||||
    ManageChannels = 16, // 0x0000000000000010
 | 
			
		||||
    ManageGuild = 32, // 0x0000000000000020
 | 
			
		||||
    ViewGuildInsights = 524288, // 0x0000000000080000
 | 
			
		||||
    AddReactions = 64, // 0x0000000000000040
 | 
			
		||||
    ViewAuditLog = 128, // 0x0000000000000080
 | 
			
		||||
    ViewChannel = 1024, // 0x0000000000000400
 | 
			
		||||
    SendMessages = 2048, // 0x0000000000000800
 | 
			
		||||
    SendTTSMessages = 4096, // 0x0000000000001000
 | 
			
		||||
    ManageMessages = 8192, // 0x0000000000002000
 | 
			
		||||
    EmbedLinks = 16384, // 0x0000000000004000
 | 
			
		||||
    AttachFiles = 32768, // 0x0000000000008000
 | 
			
		||||
    ReadMessageHistory = 65536, // 0x0000000000010000
 | 
			
		||||
    MentionEveryone = 131072, // 0x0000000000020000
 | 
			
		||||
    UseExternalEmojis = 262144, // 0x0000000000040000
 | 
			
		||||
    Connect = 1048576, // 0x0000000000100000
 | 
			
		||||
    Speak = 2097152, // 0x0000000000200000
 | 
			
		||||
    MuteMembers = 4194304, // 0x0000000000400000
 | 
			
		||||
    DeafenMembers = 8388608, // 0x0000000000800000
 | 
			
		||||
    MoveMembers = 16777216, // 0x0000000001000000
 | 
			
		||||
    UseVAD = 33554432, // 0x0000000002000000
 | 
			
		||||
    PrioritySpeaker = 256, // 0x0000000000000100
 | 
			
		||||
    Stream = 512, // 0x0000000000000200
 | 
			
		||||
    ChangeNickname = 67108864, // 0x0000000004000000
 | 
			
		||||
    ManageNicknames = 134217728, // 0x0000000008000000
 | 
			
		||||
    ManageRoles = 268435456, // 0x0000000010000000
 | 
			
		||||
    ManageWebhooks = 536870912, // 0x0000000020000000
 | 
			
		||||
    ManageEmojisAndStickers = 1073741824, // 0x0000000040000000
 | 
			
		||||
    UseApplicationCommands = 2147483648, // 0x0000000080000000
 | 
			
		||||
    RequestToSpeak = 4294967296, // 0x0000000100000000
 | 
			
		||||
    ManageEvents = 8589934592, // 0x0000000200000000
 | 
			
		||||
    ManageThreads = 17179869184, // 0x0000000400000000
 | 
			
		||||
    CreatePublicThreads = 34359738368, // 0x0000000800000000
 | 
			
		||||
    CreatePrivateThreads = 68719476736, // 0x0000001000000000
 | 
			
		||||
    UseExternalStickers = 137438953472, // 0x0000002000000000
 | 
			
		||||
    SendMessagesInThreads = 274877906944, // 0x0000004000000000
 | 
			
		||||
    StartEmbeddedActivities = 549755813888, // 0x0000008000000000
 | 
			
		||||
    ModerateMembers = 1099511627776, // 0x0000010000000000
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								src/NadekoBot/Db/LevelStats.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/NadekoBot/Db/LevelStats.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public readonly struct LevelStats
 | 
			
		||||
{
 | 
			
		||||
    public const int XP_REQUIRED_LVL_1 = 36;
 | 
			
		||||
    
 | 
			
		||||
    public long Level { get; }
 | 
			
		||||
    public long LevelXp { get; }
 | 
			
		||||
    public long RequiredXp { get; }
 | 
			
		||||
    public long TotalXp { get; }
 | 
			
		||||
 | 
			
		||||
    public LevelStats(long xp)
 | 
			
		||||
    {
 | 
			
		||||
        if (xp < 0)
 | 
			
		||||
            xp = 0;
 | 
			
		||||
 | 
			
		||||
        TotalXp = xp;
 | 
			
		||||
 | 
			
		||||
        const int baseXp = XP_REQUIRED_LVL_1;
 | 
			
		||||
 | 
			
		||||
        var required = baseXp;
 | 
			
		||||
        var totalXp = 0;
 | 
			
		||||
        var lvl = 1;
 | 
			
		||||
        while (true)
 | 
			
		||||
        {
 | 
			
		||||
            required = (int)(baseXp + (baseXp / 4.0 * (lvl - 1)));
 | 
			
		||||
 | 
			
		||||
            if (required + totalXp > xp)
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            totalXp += required;
 | 
			
		||||
            lvl++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Level = lvl - 1;
 | 
			
		||||
        LevelXp = xp - totalXp;
 | 
			
		||||
        RequiredXp = required;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/NadekoBot/Db/Models/AutoCommand.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/NadekoBot/Db/Models/AutoCommand.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class AutoCommand : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string CommandText { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public string ChannelName { get; set; }
 | 
			
		||||
    public ulong? GuildId { get; set; }
 | 
			
		||||
    public string GuildName { get; set; }
 | 
			
		||||
    public ulong? VoiceChannelId { get; set; }
 | 
			
		||||
    public string VoiceChannelName { get; set; }
 | 
			
		||||
    public int Interval { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/NadekoBot/Db/Models/AutoPublishChannel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/NadekoBot/Db/Models/AutoPublishChannel.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class AutoPublishChannel : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/NadekoBot/Db/Models/AutoTranslateChannel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/NadekoBot/Db/Models/AutoTranslateChannel.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class AutoTranslateChannel : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public bool AutoDelete { get; set; }
 | 
			
		||||
    public IList<AutoTranslateUser> Users { get; set; } = new List<AutoTranslateUser>();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/NadekoBot/Db/Models/AutoTranslateUser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/NadekoBot/Db/Models/AutoTranslateUser.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class AutoTranslateUser : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public int ChannelId { get; set; }
 | 
			
		||||
    public AutoTranslateChannel Channel { get; set; }
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public string Source { get; set; }
 | 
			
		||||
    public string Target { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/NadekoBot/Db/Models/BlacklistEntry.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/NadekoBot/Db/Models/BlacklistEntry.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class BlacklistEntry : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong ItemId { get; set; }
 | 
			
		||||
    public BlacklistType Type { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum BlacklistType
 | 
			
		||||
{
 | 
			
		||||
    Server,
 | 
			
		||||
    Channel,
 | 
			
		||||
    User
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/NadekoBot/Db/Models/CommandAlias.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/NadekoBot/Db/Models/CommandAlias.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class CommandAlias : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string Trigger { get; set; }
 | 
			
		||||
    public string Mapping { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/NadekoBot/Db/Models/CommandCooldown.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/NadekoBot/Db/Models/CommandCooldown.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class CommandCooldown : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public int Seconds { get; set; }
 | 
			
		||||
    public string CommandName { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/NadekoBot/Db/Models/CurrencyTransaction.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/NadekoBot/Db/Models/CurrencyTransaction.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class CurrencyTransaction : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public long Amount { get; set; }
 | 
			
		||||
    public string Note { get; set; }
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public string Type { get; set; }
 | 
			
		||||
    public string Extra { get; set; }
 | 
			
		||||
    public ulong? OtherId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/NadekoBot/Db/Models/DbEntity.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/NadekoBot/Db/Models/DbEntity.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class DbEntity
 | 
			
		||||
{
 | 
			
		||||
    [Key]
 | 
			
		||||
    public int Id { get; set; }
 | 
			
		||||
 | 
			
		||||
    public DateTime? DateAdded { get; set; } = DateTime.UtcNow;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/NadekoBot/Db/Models/DelMsgOnCmdChannel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/NadekoBot/Db/Models/DelMsgOnCmdChannel.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class DelMsgOnCmdChannel : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public bool State { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => ChannelId.GetHashCode();
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is DelMsgOnCmdChannel x && x.ChannelId == ChannelId;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/NadekoBot/Db/Models/DiscordPemOverride.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/NadekoBot/Db/Models/DiscordPemOverride.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Nadeko.Bot.Db;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class DiscordPermOverride : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public GuildPerm Perm { get; set; }
 | 
			
		||||
 | 
			
		||||
    public ulong? GuildId { get; set; }
 | 
			
		||||
    public string Command { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								src/NadekoBot/Db/Models/DiscordUser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/NadekoBot/Db/Models/DiscordUser.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// FUTURE remove LastLevelUp from here and UserXpStats
 | 
			
		||||
public class DiscordUser : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public string Username { get; set; }
 | 
			
		||||
    public string Discriminator { get; set; }
 | 
			
		||||
    public string AvatarId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public int? ClubId { get; set; }
 | 
			
		||||
    public ClubInfo Club { get; set; }
 | 
			
		||||
    public bool IsClubAdmin { get; set; }
 | 
			
		||||
 | 
			
		||||
    public long TotalXp { get; set; }
 | 
			
		||||
    public XpNotificationLocation NotifyOnLevelUp { get; set; }
 | 
			
		||||
 | 
			
		||||
    public long CurrencyAmount { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is DiscordUser du ? du.UserId == UserId : false;
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => UserId.GetHashCode();
 | 
			
		||||
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(Discriminator) || Discriminator == "0000")
 | 
			
		||||
            return Username;
 | 
			
		||||
        
 | 
			
		||||
        return Username + "#" + Discriminator;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								src/NadekoBot/Db/Models/Event.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/NadekoBot/Db/Models/Event.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class CurrencyEvent
 | 
			
		||||
{
 | 
			
		||||
    public enum Type
 | 
			
		||||
    {
 | 
			
		||||
        Reaction,
 | 
			
		||||
 | 
			
		||||
        GameStatus
 | 
			
		||||
        //NotRaid,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ulong ServerId { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public ulong MessageId { get; set; }
 | 
			
		||||
    public Type EventType { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Amount of currency that the user will be rewarded.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long Amount { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Maximum amount of currency that can be handed out.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long PotSize { get; set; }
 | 
			
		||||
 | 
			
		||||
    public List<AwardedUser> AwardedUsers { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Used as extra data storage for events which need it.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ulong ExtraId { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     May be used for some future event.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ulong ExtraId2 { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     May be used for some future event.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string ExtraString { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class AwardedUser
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/NadekoBot/Db/Models/FeedSub.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/NadekoBot/Db/Models/FeedSub.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class FeedSub : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public int GuildConfigId { get; set; }
 | 
			
		||||
    public GuildConfig GuildConfig { get; set; }
 | 
			
		||||
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public string Url { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    public string Message { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => Url.GetHashCode(StringComparison.InvariantCulture) ^ GuildConfigId.GetHashCode();
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is FeedSub s && s.Url.ToLower() == Url.ToLower() && s.GuildConfigId == GuildConfigId;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								src/NadekoBot/Db/Models/FollowedStream.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/NadekoBot/Db/Models/FollowedStream.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class FollowedStream : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public enum FType
 | 
			
		||||
    {
 | 
			
		||||
        Twitch = 0,
 | 
			
		||||
        Picarto = 3,
 | 
			
		||||
        Youtube = 4,
 | 
			
		||||
        Facebook = 5,
 | 
			
		||||
        Trovo = 6
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public string Username { get; set; }
 | 
			
		||||
    public FType Type { get; set; }
 | 
			
		||||
    public string Message { get; set; }
 | 
			
		||||
 | 
			
		||||
    protected bool Equals(FollowedStream other)
 | 
			
		||||
        => ChannelId == other.ChannelId
 | 
			
		||||
           && Username.Trim().ToUpperInvariant() == other.Username.Trim().ToUpperInvariant()
 | 
			
		||||
           && Type == other.Type;
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => HashCode.Combine(ChannelId, Username, (int)Type);
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is FollowedStream fs && Equals(fs);
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/NadekoBot/Db/Models/GCChannelId.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/NadekoBot/Db/Models/GCChannelId.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class GCChannelId : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public GuildConfig GuildConfig { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is GCChannelId gc && gc.ChannelId == ChannelId;
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => ChannelId.GetHashCode();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/NadekoBot/Db/Models/GamblingStats.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/NadekoBot/Db/Models/GamblingStats.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class GamblingStats : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string Feature { get; set; }
 | 
			
		||||
    public decimal Bet { get; set; }
 | 
			
		||||
    public decimal PaidOut { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/NadekoBot/Db/Models/GroupName.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/NadekoBot/Db/Models/GroupName.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class GroupName : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public int GuildConfigId { get; set; }
 | 
			
		||||
    public GuildConfig GuildConfig { get; set; }
 | 
			
		||||
 | 
			
		||||
    public int Number { get; set; }
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										109
									
								
								src/NadekoBot/Db/Models/GuildConfig.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/NadekoBot/Db/Models/GuildConfig.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class GuildConfig : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    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; }
 | 
			
		||||
 | 
			
		||||
    //greet stuff
 | 
			
		||||
    public int AutoDeleteGreetMessagesTimer { get; set; } = 30;
 | 
			
		||||
    public int AutoDeleteByeMessagesTimer { get; set; } = 30;
 | 
			
		||||
 | 
			
		||||
    public ulong GreetMessageChannelId { get; set; }
 | 
			
		||||
    public ulong ByeMessageChannelId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public bool SendDmGreetMessage { get; set; }
 | 
			
		||||
    public string DmGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
 | 
			
		||||
 | 
			
		||||
    public bool SendChannelGreetMessage { get; set; }
 | 
			
		||||
    public string ChannelGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
 | 
			
		||||
 | 
			
		||||
    public bool SendChannelByeMessage { get; set; }
 | 
			
		||||
    public string ChannelByeMessageText { get; set; } = "%user% has left!";
 | 
			
		||||
 | 
			
		||||
    //self assignable roles
 | 
			
		||||
    public bool ExclusiveSelfAssignedRoles { get; set; }
 | 
			
		||||
    public bool AutoDeleteSelfAssignedRoleMessages { get; set; }
 | 
			
		||||
 | 
			
		||||
    //stream notifications
 | 
			
		||||
    public HashSet<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 FilterLinks { get; set; }
 | 
			
		||||
    //public HashSet<FilterLinksChannelId> FilterLinksChannels { get; set; } = new HashSet<FilterLinksChannelId>();
 | 
			
		||||
 | 
			
		||||
    public bool FilterWords { get; set; }
 | 
			
		||||
    public HashSet<FilteredWord> FilteredWords { get; set; } = new();
 | 
			
		||||
    public HashSet<FilterWordsChannelId> FilterWordsChannelIds { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    public HashSet<MutedUserId> MutedUsers { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    public string MuteRoleName { get; set; }
 | 
			
		||||
    public bool CleverbotEnabled { get; set; }
 | 
			
		||||
 | 
			
		||||
    public AntiRaidSetting AntiRaidSetting { get; set; }
 | 
			
		||||
    public AntiSpamSetting AntiSpamSetting { get; set; }
 | 
			
		||||
    public AntiAltSetting AntiAltSetting { get; set; }
 | 
			
		||||
 | 
			
		||||
    public string Locale { get; set; }
 | 
			
		||||
    public string TimeZoneId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public HashSet<UnmuteTimer> UnmuteTimers { get; set; } = new();
 | 
			
		||||
    public HashSet<UnbanTimer> UnbanTimer { get; set; } = new();
 | 
			
		||||
    public HashSet<UnroleTimer> UnroleTimer { get; set; } = new();
 | 
			
		||||
    public HashSet<VcRoleInfo> VcRoleInfos { get; set; }
 | 
			
		||||
    public HashSet<CommandAlias> CommandAliases { get; set; } = new();
 | 
			
		||||
    public List<WarningPunishment> WarnPunishments { 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 List<GroupName> SelfAssignableRoleGroupNames { get; set; }
 | 
			
		||||
    public int WarnExpireHours { get; set; }
 | 
			
		||||
    public WarnExpireAction WarnExpireAction { get; set; } = WarnExpireAction.Clear;
 | 
			
		||||
 | 
			
		||||
    public bool DisableGlobalExpressions { get; set; } = false;
 | 
			
		||||
 | 
			
		||||
    #region Boost Message
 | 
			
		||||
 | 
			
		||||
    public bool SendBoostMessage { get; set; }
 | 
			
		||||
    public string BoostMessage { get; set; } = "%user% just boosted this server!";
 | 
			
		||||
    public ulong BoostMessageChannelId { get; set; }
 | 
			
		||||
    public int BoostMessageDeleteAfter { get; set; }
 | 
			
		||||
    public bool StickyRoles { get; set; }
 | 
			
		||||
 | 
			
		||||
    #endregion
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/NadekoBot/Db/Models/IgnoredLogItem.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/NadekoBot/Db/Models/IgnoredLogItem.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class IgnoredLogItem : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public int LogSettingId { get; set; }
 | 
			
		||||
    public LogSetting LogSetting { get; set; }
 | 
			
		||||
    public ulong LogItemId { get; set; }
 | 
			
		||||
    public IgnoredItemType ItemType { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum IgnoredItemType
 | 
			
		||||
{
 | 
			
		||||
    Channel,
 | 
			
		||||
    User
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/NadekoBot/Db/Models/IgnoredVoicePresenceChannel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/NadekoBot/Db/Models/IgnoredVoicePresenceChannel.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class IgnoredVoicePresenceChannel : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public LogSetting LogSetting { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/NadekoBot/Db/Models/ImageOnlyChannel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/NadekoBot/Db/Models/ImageOnlyChannel.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class ImageOnlyChannel : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public OnlyChannelType Type { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum OnlyChannelType
 | 
			
		||||
{
 | 
			
		||||
    Image,
 | 
			
		||||
    Link
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								src/NadekoBot/Db/Models/LogSetting.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/NadekoBot/Db/Models/LogSetting.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class LogSetting : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public List<IgnoredLogItem> LogIgnores { get; set; } = new();
 | 
			
		||||
    
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong? LogOtherId { get; set; }
 | 
			
		||||
    public ulong? MessageUpdatedId { get; set; }
 | 
			
		||||
    public ulong? MessageDeletedId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public ulong? UserJoinedId { get; set; }
 | 
			
		||||
    public ulong? UserLeftId { get; set; }
 | 
			
		||||
    public ulong? UserBannedId { get; set; }
 | 
			
		||||
    public ulong? UserUnbannedId { get; set; }
 | 
			
		||||
    public ulong? UserUpdatedId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public ulong? ChannelCreatedId { get; set; }
 | 
			
		||||
    public ulong? ChannelDestroyedId { get; set; }
 | 
			
		||||
    public ulong? ChannelUpdatedId { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    public ulong? ThreadDeletedId { get; set; }
 | 
			
		||||
    public ulong? ThreadCreatedId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public ulong? UserMutedId { get; set; }
 | 
			
		||||
 | 
			
		||||
    //userpresence
 | 
			
		||||
    public ulong? LogUserPresenceId { get; set; }
 | 
			
		||||
 | 
			
		||||
    //voicepresence
 | 
			
		||||
 | 
			
		||||
    public ulong? LogVoicePresenceId { get; set; }
 | 
			
		||||
    public ulong? LogVoicePresenceTTSId { get; set; }
 | 
			
		||||
    public ulong? LogWarnsId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								src/NadekoBot/Db/Models/Permission.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/NadekoBot/Db/Models/Permission.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using System.ComponentModel.DataAnnotations.Schema;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
[DebuggerDisplay("{PrimaryTarget}{SecondaryTarget} {SecondaryTargetName} {State} {PrimaryTargetId}")]
 | 
			
		||||
public class Permissionv2 : DbEntity, IIndexed
 | 
			
		||||
{
 | 
			
		||||
    public int? GuildConfigId { get; set; }
 | 
			
		||||
    public int Index { get; set; }
 | 
			
		||||
 | 
			
		||||
    public PrimaryPermissionType PrimaryTarget { get; set; }
 | 
			
		||||
    public ulong PrimaryTargetId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public SecondaryPermissionType SecondaryTarget { get; set; }
 | 
			
		||||
    public string SecondaryTargetName { get; set; }
 | 
			
		||||
 | 
			
		||||
    public bool IsCustomCommand { get; set; }
 | 
			
		||||
 | 
			
		||||
    public bool State { get; set; }
 | 
			
		||||
 | 
			
		||||
    [NotMapped]
 | 
			
		||||
    public static Permissionv2 AllowAllPerm
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            PrimaryTarget = PrimaryPermissionType.Server,
 | 
			
		||||
            PrimaryTargetId = 0,
 | 
			
		||||
            SecondaryTarget = SecondaryPermissionType.AllModules,
 | 
			
		||||
            SecondaryTargetName = "*",
 | 
			
		||||
            State = true,
 | 
			
		||||
            Index = 0
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public static List<Permissionv2> GetDefaultPermlist
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            AllowAllPerm
 | 
			
		||||
        };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum PrimaryPermissionType
 | 
			
		||||
{
 | 
			
		||||
    User,
 | 
			
		||||
    Channel,
 | 
			
		||||
    Role,
 | 
			
		||||
    Server
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum SecondaryPermissionType
 | 
			
		||||
{
 | 
			
		||||
    Module,
 | 
			
		||||
    Command,
 | 
			
		||||
    AllModules
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/NadekoBot/Db/Models/PlantedCurrency.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/NadekoBot/Db/Models/PlantedCurrency.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class PlantedCurrency : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public long Amount { get; set; }
 | 
			
		||||
    public string Password { get; set; }
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public ulong MessageId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								src/NadekoBot/Db/Models/PlaylistSong.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/NadekoBot/Db/Models/PlaylistSong.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class PlaylistSong : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string Provider { get; set; }
 | 
			
		||||
    public MusicType ProviderType { get; set; }
 | 
			
		||||
    public string Title { get; set; }
 | 
			
		||||
    public string Uri { get; set; }
 | 
			
		||||
    public string Query { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum MusicType
 | 
			
		||||
{
 | 
			
		||||
    Radio,
 | 
			
		||||
    YouTube,
 | 
			
		||||
    Local,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/NadekoBot/Db/Models/Reminder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/NadekoBot/Db/Models/Reminder.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class Reminder : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public DateTime When { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public ulong ServerId { get; set; }
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public string Message { get; set; }
 | 
			
		||||
    public bool IsPrivate { get; set; }
 | 
			
		||||
    public ReminderType Type { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum ReminderType
 | 
			
		||||
{
 | 
			
		||||
    User,
 | 
			
		||||
    Timely
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/NadekoBot/Db/Models/Repeater.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/NadekoBot/Db/Models/Repeater.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class Repeater
 | 
			
		||||
{
 | 
			
		||||
    public int Id { get; set; }
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public ulong? LastMessageId { get; set; }
 | 
			
		||||
    public string Message { get; set; }
 | 
			
		||||
    public TimeSpan Interval { get; set; }
 | 
			
		||||
    public TimeSpan? StartTimeOfDay { get; set; }
 | 
			
		||||
    public bool NoRedundant { get; set; }
 | 
			
		||||
    public DateTime DateAdded { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/NadekoBot/Db/Models/RotatingPlayingStatus.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/NadekoBot/Db/Models/RotatingPlayingStatus.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Nadeko.Bot.Db;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class RotatingPlayingStatus : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string Status { get; set; }
 | 
			
		||||
    public ActivityType Type { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								src/NadekoBot/Db/Models/ShopEntry.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/NadekoBot/Db/Models/ShopEntry.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public enum ShopEntryType
 | 
			
		||||
{
 | 
			
		||||
    Role,
 | 
			
		||||
 | 
			
		||||
    List,
 | 
			
		||||
    Command
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class ShopEntry : DbEntity, IIndexed
 | 
			
		||||
{
 | 
			
		||||
    public int Index { get; set; }
 | 
			
		||||
    public int Price { get; set; }
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
    public ulong AuthorId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public ShopEntryType Type { get; set; }
 | 
			
		||||
 | 
			
		||||
    //role
 | 
			
		||||
    public string RoleName { get; set; }
 | 
			
		||||
    public ulong RoleId { get; set; }
 | 
			
		||||
 | 
			
		||||
    //list
 | 
			
		||||
    public HashSet<ShopEntryItem> Items { get; set; } = new();
 | 
			
		||||
    public ulong? RoleRequirement { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    // command 
 | 
			
		||||
    public string Command { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class ShopEntryItem : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string Text { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
    {
 | 
			
		||||
        if (obj is null || GetType() != obj.GetType())
 | 
			
		||||
            return false;
 | 
			
		||||
        return ((ShopEntryItem)obj).Text == Text;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => Text.GetHashCode(StringComparison.InvariantCulture);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/NadekoBot/Db/Models/StreamOnlineMessage.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/NadekoBot/Db/Models/StreamOnlineMessage.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class StreamOnlineMessage : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public ulong MessageId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public FollowedStream.FType Type { get; set; }
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										68
									
								
								src/NadekoBot/Db/Models/StreamRoleSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/NadekoBot/Db/Models/StreamRoleSettings.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class StreamRoleSettings : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public int GuildConfigId { get; set; }
 | 
			
		||||
    public GuildConfig GuildConfig { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Whether the feature is enabled in the guild.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool Enabled { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Id of the role to give to the users in the role 'FromRole' when they start streaming
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ulong AddRoleId { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Id of the role whose users are eligible to get the 'AddRole'
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ulong FromRoleId { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     If set, feature will only apply to users who have this keyword in their streaming status.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Keyword { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     A collection of whitelisted users' IDs. Whitelisted users don't require 'keyword' in
 | 
			
		||||
    ///     order to get the stream role.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public HashSet<StreamRoleWhitelistedUser> Whitelist { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     A collection of blacklisted users' IDs. Blacklisted useres will never get the stream role.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public HashSet<StreamRoleBlacklistedUser> Blacklist { get; set; } = new();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class StreamRoleBlacklistedUser : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public string Username { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
    {
 | 
			
		||||
        if (obj is not StreamRoleBlacklistedUser x)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return x.UserId == UserId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => UserId.GetHashCode();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class StreamRoleWhitelistedUser : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public string Username { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is StreamRoleWhitelistedUser x ? x.UserId == UserId : false;
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => UserId.GetHashCode();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/NadekoBot/Db/Models/VcRoleInfo.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/NadekoBot/Db/Models/VcRoleInfo.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class VcRoleInfo : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong VoiceChannelId { get; set; }
 | 
			
		||||
    public ulong RoleId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/NadekoBot/Db/Models/anti/AntiAltSetting.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/NadekoBot/Db/Models/anti/AntiAltSetting.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class AntiAltSetting
 | 
			
		||||
{
 | 
			
		||||
    public int Id { get; set; }
 | 
			
		||||
    public int GuildConfigId { get; set; }
 | 
			
		||||
    public TimeSpan MinAge { get; set; }
 | 
			
		||||
    public PunishmentAction Action { get; set; }
 | 
			
		||||
    public int ActionDurationMinutes { get; set; }
 | 
			
		||||
    public ulong? RoleId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/NadekoBot/Db/Models/anti/AntiRaidSetting.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/NadekoBot/Db/Models/anti/AntiRaidSetting.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public class AntiRaidSetting : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public int GuildConfigId { get; set; }
 | 
			
		||||
    public GuildConfig GuildConfig { get; set; }
 | 
			
		||||
 | 
			
		||||
    public int UserThreshold { get; set; }
 | 
			
		||||
    public int Seconds { get; set; }
 | 
			
		||||
    public PunishmentAction Action { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Duration of the punishment, in minutes. This works only for supported Actions, like:
 | 
			
		||||
    ///     Mute, Chatmute, Voicemute, etc...
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int PunishDuration { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/NadekoBot/Db/Models/anti/AntiSpamIgnore.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/NadekoBot/Db/Models/anti/AntiSpamIgnore.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/NadekoBot/Db/Models/anti/AntiSpamSetting.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/NadekoBot/Db/Models/anti/AntiSpamSetting.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
public class AntiSpamSetting : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public int GuildConfigId { get; set; }
 | 
			
		||||
    public GuildConfig GuildConfig { get; set; }
 | 
			
		||||
 | 
			
		||||
    public PunishmentAction Action { get; set; }
 | 
			
		||||
    public int MessageThreshold { get; set; } = 3;
 | 
			
		||||
    public int MuteTime { get; set; }
 | 
			
		||||
    public ulong? RoleId { get; set; }
 | 
			
		||||
    public HashSet<AntiSpamIgnore> IgnoredChannels { get; set; } = new();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								src/NadekoBot/Db/Models/club/ClubInfo.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/NadekoBot/Db/Models/club/ClubInfo.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class ClubInfo : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    [MaxLength(20)]
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
    public string Description { get; set; }
 | 
			
		||||
    public string ImageUrl { get; set; } = string.Empty;
 | 
			
		||||
    
 | 
			
		||||
    public int Xp { get; set; } = 0;
 | 
			
		||||
    public int? OwnerId { get; set; }
 | 
			
		||||
    public DiscordUser Owner { get; set; }
 | 
			
		||||
 | 
			
		||||
    public List<DiscordUser> Members { get; set; } = new();
 | 
			
		||||
    public List<ClubApplicants> Applicants { get; set; } = new();
 | 
			
		||||
    public List<ClubBans> Bans { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
        => Name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class ClubApplicants
 | 
			
		||||
{
 | 
			
		||||
    public int ClubId { get; set; }
 | 
			
		||||
    public ClubInfo Club { get; set; }
 | 
			
		||||
 | 
			
		||||
    public int UserId { get; set; }
 | 
			
		||||
    public DiscordUser User { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class ClubBans
 | 
			
		||||
{
 | 
			
		||||
    public int ClubId { get; set; }
 | 
			
		||||
    public ClubInfo Club { get; set; }
 | 
			
		||||
 | 
			
		||||
    public int UserId { get; set; }
 | 
			
		||||
    public DiscordUser User { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/NadekoBot/Db/Models/currency/BankUser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/NadekoBot/Db/Models/currency/BankUser.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class BankUser : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public long Balance { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								src/NadekoBot/Db/Models/expr/NadekoExpression.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/NadekoBot/Db/Models/expr/NadekoExpression.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class NadekoExpression : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong? GuildId { get; set; }
 | 
			
		||||
    public string Response { get; set; }
 | 
			
		||||
    public string Trigger { get; set; }
 | 
			
		||||
 | 
			
		||||
    public bool AutoDeleteTrigger { get; set; }
 | 
			
		||||
    public bool DmResponse { get; set; }
 | 
			
		||||
    public bool ContainsAnywhere { get; set; }
 | 
			
		||||
    public bool AllowTarget { get; set; }
 | 
			
		||||
    public string Reactions { get; set; }
 | 
			
		||||
 | 
			
		||||
    public string[] GetReactions()
 | 
			
		||||
        => string.IsNullOrWhiteSpace(Reactions) ? Array.Empty<string>() : Reactions.Split("@@@");
 | 
			
		||||
 | 
			
		||||
    public bool IsGlobal()
 | 
			
		||||
        => GuildId is null or 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class ReactionResponse : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public bool OwnerOnly { get; set; }
 | 
			
		||||
    public string Text { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								src/NadekoBot/Db/Models/expr/Quote.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/NadekoBot/Db/Models/expr/Quote.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class Quote : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Required]
 | 
			
		||||
    public string Keyword { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Required]
 | 
			
		||||
    public string AuthorName { get; set; }
 | 
			
		||||
 | 
			
		||||
    public ulong AuthorId { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Required]
 | 
			
		||||
    public string Text { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum OrderType
 | 
			
		||||
{
 | 
			
		||||
    Id = -1,
 | 
			
		||||
    Keyword = -2
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								src/NadekoBot/Db/Models/filter/FilterChannelId.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/NadekoBot/Db/Models/filter/FilterChannelId.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class FilterChannelId : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public bool Equals(FilterChannelId other)
 | 
			
		||||
        => ChannelId == other.ChannelId;
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is FilterChannelId fci && Equals(fci);
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => ChannelId.GetHashCode();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class FilterWordsChannelId : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public bool Equals(FilterWordsChannelId other)
 | 
			
		||||
        => ChannelId == other.ChannelId;
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is FilterWordsChannelId fci && Equals(fci);
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => ChannelId.GetHashCode();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/NadekoBot/Db/Models/filter/FilterLinksChannelId.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/NadekoBot/Db/Models/filter/FilterLinksChannelId.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class FilterLinksChannelId : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is FilterLinksChannelId f && f.ChannelId == ChannelId;
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => ChannelId.GetHashCode();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								src/NadekoBot/Db/Models/filter/FilteredWord.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/NadekoBot/Db/Models/filter/FilteredWord.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class FilteredWord : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string Word { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/NadekoBot/Db/Models/giveaway/GiveawayModel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/NadekoBot/Db/Models/giveaway/GiveawayModel.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
public sealed class GiveawayModel
 | 
			
		||||
{
 | 
			
		||||
    public int Id { get; set; }
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong MessageId { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public string Message { get; set; }
 | 
			
		||||
 | 
			
		||||
    public IList<GiveawayUser> Participants { get; set; } = new List<GiveawayUser>();
 | 
			
		||||
    public DateTime EndsAt { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/NadekoBot/Db/Models/giveaway/GiveawayUser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/NadekoBot/Db/Models/giveaway/GiveawayUser.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
public sealed class GiveawayUser
 | 
			
		||||
{
 | 
			
		||||
    public int Id { get; set; }
 | 
			
		||||
    public int GiveawayId { get; set; }
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/NadekoBot/Db/Models/punish/BanTemplate.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/NadekoBot/Db/Models/punish/BanTemplate.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class BanTemplate : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public string Text { get; set; }
 | 
			
		||||
    public int? PruneDays { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/NadekoBot/Db/Models/punish/MutedUserId.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/NadekoBot/Db/Models/punish/MutedUserId.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class MutedUserId : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => UserId.GetHashCode();
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is MutedUserId mui ? mui.UserId == UserId : false;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/NadekoBot/Db/Models/punish/PunishmentAction.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/NadekoBot/Db/Models/punish/PunishmentAction.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public enum PunishmentAction
 | 
			
		||||
{
 | 
			
		||||
    Mute,
 | 
			
		||||
    Kick,
 | 
			
		||||
    Ban,
 | 
			
		||||
    Softban,
 | 
			
		||||
    RemoveRoles,
 | 
			
		||||
    ChatMute,
 | 
			
		||||
    VoiceMute,
 | 
			
		||||
    AddRole,
 | 
			
		||||
    Warn,
 | 
			
		||||
    TimeOut
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/NadekoBot/Db/Models/punish/WarnExpireAction.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/NadekoBot/Db/Models/punish/WarnExpireAction.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public enum WarnExpireAction
 | 
			
		||||
{
 | 
			
		||||
    Clear,
 | 
			
		||||
    Delete
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/NadekoBot/Db/Models/punish/Warning.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/NadekoBot/Db/Models/punish/Warning.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class Warning : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public string Reason { get; set; }
 | 
			
		||||
    public bool Forgiven { get; set; }
 | 
			
		||||
    public string ForgivenBy { get; set; }
 | 
			
		||||
    public string Moderator { get; set; }
 | 
			
		||||
    public long Weight { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/NadekoBot/Db/Models/punish/WarningPunishment.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/NadekoBot/Db/Models/punish/WarningPunishment.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class WarningPunishment : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public int Count { get; set; }
 | 
			
		||||
    public PunishmentAction Punishment { get; set; }
 | 
			
		||||
    public int Time { get; set; }
 | 
			
		||||
    public ulong? RoleId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								src/NadekoBot/Db/Models/roles/ReactionRole.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/NadekoBot/Db/Models/roles/ReactionRole.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class ReactionRoleV2 : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    public ulong MessageId { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    [MaxLength(100)]
 | 
			
		||||
    public string Emote { get; set; }
 | 
			
		||||
    public ulong RoleId { get; set; }
 | 
			
		||||
    public int Group { get; set; }
 | 
			
		||||
    public int LevelReq { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/NadekoBot/Db/Models/roles/SelfAssignableRole.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/NadekoBot/Db/Models/roles/SelfAssignableRole.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class SelfAssignedRole : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong RoleId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public int Group { get; set; }
 | 
			
		||||
    public int LevelRequirement { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/NadekoBot/Db/Models/roles/StickyRoles.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/NadekoBot/Db/Models/roles/StickyRoles.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
public class StickyRole : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public string RoleIds { get; set; }
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public ulong[] GetRoleIds()
 | 
			
		||||
        => string.IsNullOrWhiteSpace(RoleIds)
 | 
			
		||||
            ? []
 | 
			
		||||
            : RoleIds.Split(',').Select(ulong.Parse).ToArray();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								src/NadekoBot/Db/Models/slowmode/SlowmodeIgnoredRole.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/NadekoBot/Db/Models/slowmode/SlowmodeIgnoredRole.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class SlowmodeIgnoredRole : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong RoleId { get; set; }
 | 
			
		||||
 | 
			
		||||
    // override object.Equals
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
    {
 | 
			
		||||
        if (obj is null || GetType() != obj.GetType())
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return ((SlowmodeIgnoredRole)obj).RoleId == RoleId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // override object.GetHashCode
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => RoleId.GetHashCode();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								src/NadekoBot/Db/Models/slowmode/SlowmodeIgnoredUser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/NadekoBot/Db/Models/slowmode/SlowmodeIgnoredUser.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class SlowmodeIgnoredUser : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
 | 
			
		||||
    // override object.Equals
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
    {
 | 
			
		||||
        if (obj is null || GetType() != obj.GetType())
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return ((SlowmodeIgnoredUser)obj).UserId == UserId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // override object.GetHashCode
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => UserId.GetHashCode();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								src/NadekoBot/Db/Models/support/PatronQuota.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/NadekoBot/Db/Models/support/PatronQuota.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Contains data about usage of Patron-Only commands per user
 | 
			
		||||
/// in order to provide support for quota limitations
 | 
			
		||||
/// (allow user x who is pledging amount y to use the specified command only
 | 
			
		||||
///  x amount of times in the specified time period)
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class PatronQuota
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public FeatureType FeatureType { get; set; }
 | 
			
		||||
    public string Feature { get; set; }
 | 
			
		||||
    public uint HourlyCount { get; set; }
 | 
			
		||||
    public uint DailyCount { get; set; }
 | 
			
		||||
    public uint MonthlyCount { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum FeatureType
 | 
			
		||||
{
 | 
			
		||||
    Command,
 | 
			
		||||
    Group,
 | 
			
		||||
    Module,
 | 
			
		||||
    Limit
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PatronUser
 | 
			
		||||
{
 | 
			
		||||
    public string UniquePlatformUserId { get; set; }
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public int AmountCents { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    public DateTime LastCharge { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    // Date Only component
 | 
			
		||||
    public DateTime ValidThru { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    public PatronUser Clone()
 | 
			
		||||
        => new PatronUser()
 | 
			
		||||
        {
 | 
			
		||||
            UniquePlatformUserId = this.UniquePlatformUserId,
 | 
			
		||||
            UserId = this.UserId,
 | 
			
		||||
            AmountCents = this.AmountCents,
 | 
			
		||||
            LastCharge = this.LastCharge,
 | 
			
		||||
            ValidThru = this.ValidThru
 | 
			
		||||
        };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/NadekoBot/Db/Models/support/RewardedUser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/NadekoBot/Db/Models/support/RewardedUser.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class RewardedUser : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public string PlatformUserId { get; set; }
 | 
			
		||||
    public long AmountRewardedThisMonth { get; set; }
 | 
			
		||||
    public DateTime LastReward { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/NadekoBot/Db/Models/todo/ArchivedTodoListModel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/NadekoBot/Db/Models/todo/ArchivedTodoListModel.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
public sealed class ArchivedTodoListModel
 | 
			
		||||
{
 | 
			
		||||
    public int Id { get; set; }
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
    public List<TodoModel> Items { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/NadekoBot/Db/Models/todo/TodoModel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/NadekoBot/Db/Models/todo/TodoModel.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
public sealed class TodoModel
 | 
			
		||||
{
 | 
			
		||||
    public int Id { get; set; }
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public string Todo { get; set; }
 | 
			
		||||
 | 
			
		||||
    public DateTime DateAdded { get; set; }
 | 
			
		||||
    public bool IsDone { get; set; }
 | 
			
		||||
    public int? ArchiveId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/NadekoBot/Db/Models/untimer/UnbanTimer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/NadekoBot/Db/Models/untimer/UnbanTimer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class UnbanTimer : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public DateTime UnbanAt { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => UserId.GetHashCode();
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is UnbanTimer ut ? ut.UserId == UserId : false;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/NadekoBot/Db/Models/untimer/UnmuteTimer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/NadekoBot/Db/Models/untimer/UnmuteTimer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class UnmuteTimer : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public DateTime UnmuteAt { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => UserId.GetHashCode();
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is UnmuteTimer ut ? ut.UserId == UserId : false;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/NadekoBot/Db/Models/untimer/UnroleTimer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/NadekoBot/Db/Models/untimer/UnroleTimer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class UnroleTimer : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public ulong RoleId { get; set; }
 | 
			
		||||
    public DateTime UnbanAt { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => UserId.GetHashCode() ^ RoleId.GetHashCode();
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is UnroleTimer ut ? ut.UserId == UserId && ut.RoleId == RoleId : false;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/NadekoBot/Db/Models/xp/UserXpStats.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/NadekoBot/Db/Models/xp/UserXpStats.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class UserXpStats : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public long Xp { get; set; }
 | 
			
		||||
    public long AwardedXp { get; set; }
 | 
			
		||||
    public XpNotificationLocation NotifyOnLevelUp { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum XpNotificationLocation { None, Dm, Channel }
 | 
			
		||||
							
								
								
									
										62
									
								
								src/NadekoBot/Db/Models/xp/XpSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/NadekoBot/Db/Models/xp/XpSettings.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class XpSettings : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public int GuildConfigId { get; set; }
 | 
			
		||||
    public GuildConfig GuildConfig { get; set; }
 | 
			
		||||
 | 
			
		||||
    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 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								src/NadekoBot/Expressions/ExportedExpr.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/NadekoBot/Expressions/ExportedExpr.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.NadekoExpressions;
 | 
			
		||||
 | 
			
		||||
public class ExportedExpr
 | 
			
		||||
{
 | 
			
		||||
    public string Res { get; set; }
 | 
			
		||||
    public string Id { get; set; }
 | 
			
		||||
    public bool Ad { get; set; }
 | 
			
		||||
    public bool Dm { get; set; }
 | 
			
		||||
    public bool At { get; set; }
 | 
			
		||||
    public bool Ca { get; set; }
 | 
			
		||||
    public string[] React;
 | 
			
		||||
 | 
			
		||||
    public static ExportedExpr FromModel(NadekoExpression cr)
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            Res = cr.Response,
 | 
			
		||||
            Id = ((kwum)cr.Id).ToString(),
 | 
			
		||||
            Ad = cr.AutoDeleteTrigger,
 | 
			
		||||
            At = cr.AllowTarget,
 | 
			
		||||
            Ca = cr.ContainsAnywhere,
 | 
			
		||||
            Dm = cr.DmResponse,
 | 
			
		||||
            React = string.IsNullOrWhiteSpace(cr.Reactions) ? null : cr.GetReactions()
 | 
			
		||||
        };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/NadekoBot/Expressions/ExprField.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/NadekoBot/Expressions/ExprField.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
namespace NadekoBot.Modules.NadekoExpressions;
 | 
			
		||||
 | 
			
		||||
public enum ExprField
 | 
			
		||||
{
 | 
			
		||||
    AutoDelete,
 | 
			
		||||
    DmResponse,
 | 
			
		||||
    AllowTarget,
 | 
			
		||||
    ContainsAnywhere,
 | 
			
		||||
    Message
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										92
									
								
								src/NadekoBot/Expressions/NadekoExpressionExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/NadekoBot/Expressions/NadekoExpressionExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using NadekoBot.Common;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.NadekoExpressions;
 | 
			
		||||
 | 
			
		||||
public static class NadekoExpressionExtensions
 | 
			
		||||
{
 | 
			
		||||
    private static string ResolveTriggerString(this string str, DiscordSocketClient client)
 | 
			
		||||
        => str.Replace("%bot.mention%", client.CurrentUser.Mention, StringComparison.Ordinal);
 | 
			
		||||
 | 
			
		||||
    public static async Task<IUserMessage> Send(
 | 
			
		||||
        this NadekoExpression cr,
 | 
			
		||||
        IUserMessage ctx,
 | 
			
		||||
        IReplacementService repSvc,
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        bool sanitize)
 | 
			
		||||
    {
 | 
			
		||||
        var channel = cr.DmResponse ? await ctx.Author.CreateDMChannelAsync() : ctx.Channel;
 | 
			
		||||
 | 
			
		||||
        var trigger = cr.Trigger.ResolveTriggerString(client);
 | 
			
		||||
        var substringIndex = trigger.Length;
 | 
			
		||||
        if (cr.ContainsAnywhere)
 | 
			
		||||
        {
 | 
			
		||||
            var pos = ctx.Content.AsSpan().GetWordPosition(trigger);
 | 
			
		||||
            if (pos == WordPosition.Start)
 | 
			
		||||
                substringIndex += 1;
 | 
			
		||||
            else if (pos == WordPosition.End)
 | 
			
		||||
                substringIndex = ctx.Content.Length;
 | 
			
		||||
            else if (pos == WordPosition.Middle)
 | 
			
		||||
                substringIndex += ctx.Content.IndexOf(trigger, StringComparison.InvariantCulture);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var canMentionEveryone = (ctx.Author as IGuildUser)?.GuildPermissions.MentionEveryone ?? true;
 | 
			
		||||
 | 
			
		||||
        var repCtx = new ReplacementContext(client: client,
 | 
			
		||||
                guild: (ctx.Channel as ITextChannel)?.Guild as SocketGuild,
 | 
			
		||||
                channel: ctx.Channel,
 | 
			
		||||
                users: ctx.Author
 | 
			
		||||
            )
 | 
			
		||||
            .WithOverride("%target%",
 | 
			
		||||
                () => canMentionEveryone
 | 
			
		||||
                    ? ctx.Content[substringIndex..].Trim()
 | 
			
		||||
                    : ctx.Content[substringIndex..].Trim().SanitizeMentions(true));
 | 
			
		||||
        
 | 
			
		||||
        var text = SmartText.CreateFrom(cr.Response);
 | 
			
		||||
        text = await repSvc.ReplaceAsync(text, repCtx);
 | 
			
		||||
 | 
			
		||||
        return await channel.SendAsync(text, sanitize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
    public static WordPosition GetWordPosition(this ReadOnlySpan<char> str, in ReadOnlySpan<char> word)
 | 
			
		||||
    {
 | 
			
		||||
        var wordIndex = str.IndexOf(word, StringComparison.OrdinalIgnoreCase);
 | 
			
		||||
        if (wordIndex == -1)
 | 
			
		||||
            return WordPosition.None;
 | 
			
		||||
 | 
			
		||||
        if (wordIndex == 0)
 | 
			
		||||
        {
 | 
			
		||||
            if (word.Length < str.Length && str.IsValidWordDivider(word.Length))
 | 
			
		||||
                return WordPosition.Start;
 | 
			
		||||
        }
 | 
			
		||||
        else if (wordIndex + word.Length == str.Length)
 | 
			
		||||
        {
 | 
			
		||||
            if (str.IsValidWordDivider(wordIndex - 1))
 | 
			
		||||
                return WordPosition.End;
 | 
			
		||||
        }
 | 
			
		||||
        else if (str.IsValidWordDivider(wordIndex - 1) && str.IsValidWordDivider(wordIndex + word.Length))
 | 
			
		||||
            return WordPosition.Middle;
 | 
			
		||||
 | 
			
		||||
        return WordPosition.None;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static bool IsValidWordDivider(this in ReadOnlySpan<char> str, int index)
 | 
			
		||||
    {
 | 
			
		||||
        var ch = str[index];
 | 
			
		||||
        if (ch is >= 'a' and <= 'z' or >= 'A' and <= 'Z' or >= '1' and <= '9')
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum WordPosition
 | 
			
		||||
{
 | 
			
		||||
    None,
 | 
			
		||||
    Start,
 | 
			
		||||
    Middle,
 | 
			
		||||
    End
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										395
									
								
								src/NadekoBot/Expressions/NadekoExpressions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										395
									
								
								src/NadekoBot/Expressions/NadekoExpressions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,395 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
using NadekoBot.Common.Attributes;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.NadekoExpressions;
 | 
			
		||||
 | 
			
		||||
[Name("Expressions")]
 | 
			
		||||
public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
 | 
			
		||||
{
 | 
			
		||||
    public enum All
 | 
			
		||||
    {
 | 
			
		||||
        All
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private readonly IBotCredentials _creds;
 | 
			
		||||
    private readonly IHttpClientFactory _clientFactory;
 | 
			
		||||
 | 
			
		||||
    public NadekoExpressions(IBotCredentials creds, IHttpClientFactory clientFactory)
 | 
			
		||||
    {
 | 
			
		||||
        _creds = creds;
 | 
			
		||||
        _clientFactory = clientFactory;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private bool AdminInGuildOrOwnerInDm()
 | 
			
		||||
        => (ctx.Guild is null && _creds.IsOwner(ctx.User))
 | 
			
		||||
           || (ctx.Guild is not null && ((IGuildUser)ctx.User).GuildPermissions.Administrator);
 | 
			
		||||
 | 
			
		||||
    private async Task ExprAddInternalAsync(string key, string message)
 | 
			
		||||
    {
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ex = await _service.AddAsync(ctx.Guild?.Id, key, message);
 | 
			
		||||
 | 
			
		||||
        await ctx.Channel.EmbedAsync(_eb.Create()
 | 
			
		||||
                                        .WithOkColor()
 | 
			
		||||
                                        .WithTitle(GetText(strs.expr_new))
 | 
			
		||||
                                        .WithDescription($"#{new kwum(ex.Id)}")
 | 
			
		||||
                                        .AddField(GetText(strs.trigger), key)
 | 
			
		||||
                                        .AddField(GetText(strs.response),
 | 
			
		||||
                                            message.Length > 1024 ? GetText(strs.redacted_too_long) : message));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [UserPerm(GuildPerm.Administrator)]
 | 
			
		||||
    public async Task ExprToggleGlobal()
 | 
			
		||||
    {
 | 
			
		||||
        var result = await _service.ToggleGlobalExpressionsAsync(ctx.Guild.Id);
 | 
			
		||||
        if (result)
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.expr_global_disabled);
 | 
			
		||||
        else
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.expr_global_enabled);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [UserPerm(GuildPerm.Administrator)]
 | 
			
		||||
    public async Task ExprAddServer(string key, [Leftover] string message)
 | 
			
		||||
    {
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await ExprAddInternalAsync(key, message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public async Task ExprAdd(string key, [Leftover] string message)
 | 
			
		||||
    {
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!AdminInGuildOrOwnerInDm())
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await ExprAddInternalAsync(key, message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public async Task ExprEdit(kwum id, [Leftover] string message)
 | 
			
		||||
    {
 | 
			
		||||
        var channel = ctx.Channel as ITextChannel;
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(message) || id < 0)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((channel is null && !_creds.IsOwner(ctx.User))
 | 
			
		||||
            || (channel is not null && !((IGuildUser)ctx.User).GuildPermissions.Administrator))
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ex = await _service.EditAsync(ctx.Guild?.Id, id, message);
 | 
			
		||||
        if (ex is not null)
 | 
			
		||||
        {
 | 
			
		||||
            await ctx.Channel.EmbedAsync(_eb.Create()
 | 
			
		||||
                                            .WithOkColor()
 | 
			
		||||
                                            .WithTitle(GetText(strs.expr_edited))
 | 
			
		||||
                                            .WithDescription($"#{id}")
 | 
			
		||||
                                            .AddField(GetText(strs.trigger), ex.Trigger)
 | 
			
		||||
                                            .AddField(GetText(strs.response),
 | 
			
		||||
                                                message.Length > 1024 ? GetText(strs.redacted_too_long) : message));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [Priority(1)]
 | 
			
		||||
    public async Task ExprList(int page = 1)
 | 
			
		||||
    {
 | 
			
		||||
        if (--page < 0 || page > 999)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var expressions = _service.GetExpressionsFor(ctx.Guild?.Id);
 | 
			
		||||
 | 
			
		||||
        if (expressions is null || !expressions.Any())
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_no_found);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await ctx.SendPaginatedConfirmAsync(page,
 | 
			
		||||
            curPage =>
 | 
			
		||||
            {
 | 
			
		||||
                var desc = expressions.OrderBy(ex => ex.Trigger)
 | 
			
		||||
                                      .Skip(curPage * 20)
 | 
			
		||||
                                      .Take(20)
 | 
			
		||||
                                      .Select(ex => $"{(ex.ContainsAnywhere ? "🗯" : "◾")}"
 | 
			
		||||
                                                    + $"{(ex.DmResponse ? "✉" : "◾")}"
 | 
			
		||||
                                                    + $"{(ex.AutoDeleteTrigger ? "❌" : "◾")}"
 | 
			
		||||
                                                    + $"`{(kwum)ex.Id}` {ex.Trigger}"
 | 
			
		||||
                                                    + (string.IsNullOrWhiteSpace(ex.Reactions)
 | 
			
		||||
                                                        ? string.Empty
 | 
			
		||||
                                                        : " // " + string.Join(" ", ex.GetReactions())))
 | 
			
		||||
                                      .Join('\n');
 | 
			
		||||
 | 
			
		||||
                return _eb.Create().WithOkColor().WithTitle(GetText(strs.expressions)).WithDescription(desc);
 | 
			
		||||
            },
 | 
			
		||||
            expressions.Length,
 | 
			
		||||
            20);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public async Task ExprShow(kwum id)
 | 
			
		||||
    {
 | 
			
		||||
        var found = _service.GetExpression(ctx.Guild?.Id, id);
 | 
			
		||||
 | 
			
		||||
        if (found is null)
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await ctx.Channel.EmbedAsync(_eb.Create()
 | 
			
		||||
                                        .WithOkColor()
 | 
			
		||||
                                        .WithDescription($"#{id}")
 | 
			
		||||
                                        .AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
 | 
			
		||||
                                        .AddField(GetText(strs.response),
 | 
			
		||||
                                            found.Response.TrimTo(1000).Replace("](", "]\\(")));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task ExprDeleteInternalAsync(kwum id)
 | 
			
		||||
    {
 | 
			
		||||
        var ex = await _service.DeleteAsync(ctx.Guild?.Id, id);
 | 
			
		||||
 | 
			
		||||
        if (ex is not null)
 | 
			
		||||
        {
 | 
			
		||||
            await ctx.Channel.EmbedAsync(_eb.Create()
 | 
			
		||||
                                            .WithOkColor()
 | 
			
		||||
                                            .WithTitle(GetText(strs.expr_deleted))
 | 
			
		||||
                                            .WithDescription($"#{id}")
 | 
			
		||||
                                            .AddField(GetText(strs.trigger), ex.Trigger.TrimTo(1024))
 | 
			
		||||
                                            .AddField(GetText(strs.response), ex.Response.TrimTo(1024)));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [UserPerm(GuildPerm.Administrator)]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    public async Task ExprDeleteServer(kwum id)
 | 
			
		||||
        => await ExprDeleteInternalAsync(id);
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public async Task ExprDelete(kwum id)
 | 
			
		||||
    {
 | 
			
		||||
        if (!AdminInGuildOrOwnerInDm())
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await ExprDeleteInternalAsync(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public async Task ExprReact(kwum id, params string[] emojiStrs)
 | 
			
		||||
    {
 | 
			
		||||
        if (!AdminInGuildOrOwnerInDm())
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ex = _service.GetExpression(ctx.Guild?.Id, id);
 | 
			
		||||
        if (ex is null)
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (emojiStrs.Length == 0)
 | 
			
		||||
        {
 | 
			
		||||
            await _service.ResetExprReactions(ctx.Guild?.Id, id);
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.expr_reset(Format.Bold(id.ToString())));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var succ = new List<string>();
 | 
			
		||||
        foreach (var emojiStr in emojiStrs)
 | 
			
		||||
        {
 | 
			
		||||
            var emote = emojiStr.ToIEmote();
 | 
			
		||||
 | 
			
		||||
            // i should try adding these emojis right away to the message, to make sure the bot can react with these emojis. If it fails, skip that emoji
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await ctx.Message.AddReactionAsync(emote);
 | 
			
		||||
                await Task.Delay(100);
 | 
			
		||||
                succ.Add(emojiStr);
 | 
			
		||||
 | 
			
		||||
                if (succ.Count >= 3)
 | 
			
		||||
                {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch { }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (succ.Count == 0)
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.invalid_emojis);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await _service.SetExprReactions(ctx.Guild?.Id, id, succ);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        await ReplyConfirmLocalizedAsync(strs.expr_set(Format.Bold(id.ToString()),
 | 
			
		||||
            succ.Select(static x => x.ToString()).Join(", ")));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public Task ExprCa(kwum id)
 | 
			
		||||
        => InternalExprEdit(id, ExprField.ContainsAnywhere);
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public Task ExprDm(kwum id)
 | 
			
		||||
        => InternalExprEdit(id, ExprField.DmResponse);
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public Task ExprAd(kwum id)
 | 
			
		||||
        => InternalExprEdit(id, ExprField.AutoDelete);
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public Task ExprAt(kwum id)
 | 
			
		||||
        => InternalExprEdit(id, ExprField.AllowTarget);
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [OwnerOnly]
 | 
			
		||||
    public async Task ExprsReload()
 | 
			
		||||
    {
 | 
			
		||||
        await _service.TriggerReloadExpressions();
 | 
			
		||||
 | 
			
		||||
        await ctx.OkAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task InternalExprEdit(kwum id, ExprField option)
 | 
			
		||||
    {
 | 
			
		||||
        if (!AdminInGuildOrOwnerInDm())
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var (success, newVal) = await _service.ToggleExprOptionAsync(ctx.Guild?.Id, id, option);
 | 
			
		||||
        if (!success)
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (newVal)
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.option_enabled(Format.Code(option.ToString()),
 | 
			
		||||
                Format.Code(id.ToString())));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.option_disabled(Format.Code(option.ToString()),
 | 
			
		||||
                Format.Code(id.ToString())));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.Administrator)]
 | 
			
		||||
    public async Task ExprClear()
 | 
			
		||||
    {
 | 
			
		||||
        if (await PromptUserConfirmAsync(_eb.Create()
 | 
			
		||||
                                            .WithTitle("Expression clear")
 | 
			
		||||
                                            .WithDescription("This will delete all expressions on this server.")))
 | 
			
		||||
        {
 | 
			
		||||
            var count = _service.DeleteAllExpressions(ctx.Guild.Id);
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.exprs_cleared(count));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public async Task ExprsExport()
 | 
			
		||||
    {
 | 
			
		||||
        if (!AdminInGuildOrOwnerInDm())
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _ = ctx.Channel.TriggerTypingAsync();
 | 
			
		||||
 | 
			
		||||
        var serialized = _service.ExportExpressions(ctx.Guild?.Id);
 | 
			
		||||
        await using var stream = await serialized.ToStream();
 | 
			
		||||
        await ctx.Channel.SendFileAsync(stream, "exprs-export.yml");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
#if GLOBAL_NADEKO
 | 
			
		||||
    [OwnerOnly]
 | 
			
		||||
#endif
 | 
			
		||||
    public async Task ExprsImport([Leftover] string input = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (!AdminInGuildOrOwnerInDm())
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        input = input?.Trim();
 | 
			
		||||
 | 
			
		||||
        _ = ctx.Channel.TriggerTypingAsync();
 | 
			
		||||
 | 
			
		||||
        if (input is null)
 | 
			
		||||
        {
 | 
			
		||||
            var attachment = ctx.Message.Attachments.FirstOrDefault();
 | 
			
		||||
            if (attachment is null)
 | 
			
		||||
            {
 | 
			
		||||
                await ReplyErrorLocalizedAsync(strs.expr_import_no_input);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            using var client = _clientFactory.CreateClient();
 | 
			
		||||
            input = await client.GetStringAsync(attachment.Url);
 | 
			
		||||
 | 
			
		||||
            if (string.IsNullOrWhiteSpace(input))
 | 
			
		||||
            {
 | 
			
		||||
                await ReplyErrorLocalizedAsync(strs.expr_import_no_input);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var succ = await _service.ImportExpressionsAsync(ctx.Guild?.Id, input);
 | 
			
		||||
        if (!succ)
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.expr_import_invalid_data);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await ctx.OkAsync();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										777
									
								
								src/NadekoBot/Expressions/NadekoExpressionsService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										777
									
								
								src/NadekoBot/Expressions/NadekoExpressionsService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,777 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
using NadekoBot.Common.Yml;
 | 
			
		||||
using NadekoBot.Db;
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
using Serilog;
 | 
			
		||||
using YamlDotNet.Serialization;
 | 
			
		||||
using YamlDotNet.Serialization.NamingConventions;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.NadekoExpressions;
 | 
			
		||||
 | 
			
		||||
public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
 | 
			
		||||
{
 | 
			
		||||
    private const string MENTION_PH = "%bot.mention%";
 | 
			
		||||
 | 
			
		||||
    private const string PREPEND_EXPORT =
 | 
			
		||||
        """
 | 
			
		||||
        # Keys are triggers, Each key has a LIST of expressions in the following format:
 | 
			
		||||
        # - res: Response string
 | 
			
		||||
        #   id: Alphanumeric id used for commands related to the expression. (Note, when using .exprsimport, a new id will be generated.)
 | 
			
		||||
        #   react:
 | 
			
		||||
        #     - <List
 | 
			
		||||
        #     -  of
 | 
			
		||||
        #     - reactions>
 | 
			
		||||
        #   at: Whether expression allows targets (see .h .exprat)
 | 
			
		||||
        #   ca: Whether expression expects trigger anywhere (see .h .exprca)
 | 
			
		||||
        #   dm: Whether expression DMs the response (see .h .exprdm)
 | 
			
		||||
        #   ad: Whether expression automatically deletes triggering message (see .h .exprad)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        """;
 | 
			
		||||
 | 
			
		||||
    private static readonly ISerializer _exportSerializer = new SerializerBuilder()
 | 
			
		||||
        .WithEventEmitter(args
 | 
			
		||||
            => new MultilineScalarFlowStyleEmitter(args))
 | 
			
		||||
        .WithNamingConvention(CamelCaseNamingConvention.Instance)
 | 
			
		||||
        .WithIndentedSequences()
 | 
			
		||||
        .ConfigureDefaultValuesHandling(DefaultValuesHandling
 | 
			
		||||
            .OmitDefaults)
 | 
			
		||||
        .DisableAliases()
 | 
			
		||||
        .Build();
 | 
			
		||||
 | 
			
		||||
    public int Priority
 | 
			
		||||
        => 0;
 | 
			
		||||
 | 
			
		||||
    private readonly object _gexprWriteLock = new();
 | 
			
		||||
 | 
			
		||||
    private readonly TypedKey<NadekoExpression> _gexprAddedKey = new("gexpr.added");
 | 
			
		||||
    private readonly TypedKey<int> _gexprDeletedkey = new("gexpr.deleted");
 | 
			
		||||
    private readonly TypedKey<NadekoExpression> _gexprEditedKey = new("gexpr.edited");
 | 
			
		||||
    private readonly TypedKey<bool> _exprsReloadedKey = new("exprs.reloaded");
 | 
			
		||||
 | 
			
		||||
    // it is perfectly fine to have global expressions as an array
 | 
			
		||||
    // 1. expressions are almost never added (compared to how many times they are being looped through)
 | 
			
		||||
    // 2. only need write locks for this as we'll rebuild+replace the array on every edit
 | 
			
		||||
    // 3. there's never many of them (at most a thousand, usually < 100)
 | 
			
		||||
    private NadekoExpression[] globalExpressions = Array.Empty<NadekoExpression>();
 | 
			
		||||
    private ConcurrentDictionary<ulong, NadekoExpression[]> newguildExpressions = new();
 | 
			
		||||
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
 | 
			
		||||
    // private readonly PermissionService _perms;
 | 
			
		||||
    // private readonly GlobalPermissionService _gperm;
 | 
			
		||||
    // private readonly CmdCdService _cmdCds;
 | 
			
		||||
    private readonly IPermissionChecker _permChecker;
 | 
			
		||||
    private readonly ICommandHandler _cmd;
 | 
			
		||||
    private readonly IBotStrings _strings;
 | 
			
		||||
    private readonly IBot _bot;
 | 
			
		||||
    private readonly IPubSub _pubSub;
 | 
			
		||||
    private readonly IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly IReplacementService _repSvc;
 | 
			
		||||
    private readonly Random _rng;
 | 
			
		||||
 | 
			
		||||
    private bool ready;
 | 
			
		||||
    private ConcurrentHashSet<ulong> _disabledGlobalExpressionGuilds;
 | 
			
		||||
 | 
			
		||||
    public NadekoExpressionsService(
 | 
			
		||||
        DbService db,
 | 
			
		||||
        IBotStrings strings,
 | 
			
		||||
        IBot bot,
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        ICommandHandler cmd,
 | 
			
		||||
        IPubSub pubSub,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        IReplacementService repSvc,
 | 
			
		||||
        IPermissionChecker permChecker)
 | 
			
		||||
    {
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _cmd = cmd;
 | 
			
		||||
        _strings = strings;
 | 
			
		||||
        _bot = bot;
 | 
			
		||||
        _pubSub = pubSub;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        _repSvc = repSvc;
 | 
			
		||||
        _permChecker = permChecker;
 | 
			
		||||
        _rng = new NadekoRandom();
 | 
			
		||||
 | 
			
		||||
        _pubSub.Sub(_exprsReloadedKey, OnExprsShouldReload);
 | 
			
		||||
        pubSub.Sub(_gexprAddedKey, OnGexprAdded);
 | 
			
		||||
        pubSub.Sub(_gexprDeletedkey, OnGexprDeleted);
 | 
			
		||||
        pubSub.Sub(_gexprEditedKey, OnGexprEdited);
 | 
			
		||||
 | 
			
		||||
        bot.JoinedGuild += OnJoinedGuild;
 | 
			
		||||
        _client.LeftGuild += OnLeftGuild;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task ReloadInternal(IReadOnlyList<ulong> allGuildIds)
 | 
			
		||||
    {
 | 
			
		||||
        await using var uow = _db.GetDbContext();
 | 
			
		||||
        var guildItems = await uow.Set<NadekoExpression>().AsNoTracking()
 | 
			
		||||
            .Where(x => allGuildIds.Contains(x.GuildId.Value))
 | 
			
		||||
            .ToListAsync();
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
                    })
 | 
			
		||||
                    .ToArray())
 | 
			
		||||
            .ToConcurrent();
 | 
			
		||||
 | 
			
		||||
        _disabledGlobalExpressionGuilds = new(await uow.Set<GuildConfig>()
 | 
			
		||||
            .Where(x => x.DisableGlobalExpressions)
 | 
			
		||||
            .Select(x => x.GuildId)
 | 
			
		||||
            .ToListAsyncLinqToDB());
 | 
			
		||||
 | 
			
		||||
        lock (_gexprWriteLock)
 | 
			
		||||
        {
 | 
			
		||||
            var globalItems = uow.Set<NadekoExpression>().AsNoTracking()
 | 
			
		||||
                .Where(x => x.GuildId == null || x.GuildId == 0)
 | 
			
		||||
                .AsEnumerable()
 | 
			
		||||
                .Select(x =>
 | 
			
		||||
                {
 | 
			
		||||
                    x.Trigger = x.Trigger.Replace(MENTION_PH, _client.CurrentUser.Mention);
 | 
			
		||||
                    return x;
 | 
			
		||||
                })
 | 
			
		||||
                .ToArray();
 | 
			
		||||
 | 
			
		||||
            globalExpressions = globalItems;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ready = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private NadekoExpression TryGetExpression(IUserMessage umsg)
 | 
			
		||||
    {
 | 
			
		||||
        if (!ready)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        if (umsg.Channel is not SocketTextChannel channel)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        var content = umsg.Content.Trim().ToLowerInvariant();
 | 
			
		||||
 | 
			
		||||
        if (newguildExpressions.TryGetValue(channel.Guild.Id, out var expressions) && expressions.Length > 0)
 | 
			
		||||
        {
 | 
			
		||||
            var expr = MatchExpressions(content, expressions);
 | 
			
		||||
            if (expr is not null)
 | 
			
		||||
                return expr;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (_disabledGlobalExpressionGuilds.Contains(channel.Guild.Id))
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        var localGrs = globalExpressions;
 | 
			
		||||
 | 
			
		||||
        return MatchExpressions(content, localGrs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
    private NadekoExpression MatchExpressions(in ReadOnlySpan<char> content, NadekoExpression[] exprs)
 | 
			
		||||
    {
 | 
			
		||||
        var result = new List<NadekoExpression>(1);
 | 
			
		||||
        for (var i = 0; i < exprs.Length; i++)
 | 
			
		||||
        {
 | 
			
		||||
            var expr = exprs[i];
 | 
			
		||||
            var trigger = expr.Trigger;
 | 
			
		||||
            if (content.Length > trigger.Length)
 | 
			
		||||
            {
 | 
			
		||||
                // if input is greater than the trigger, it can only work if:
 | 
			
		||||
                // it has CA enabled
 | 
			
		||||
                if (expr.ContainsAnywhere)
 | 
			
		||||
                {
 | 
			
		||||
                    // if ca is enabled, we have to check if it is a word within the content
 | 
			
		||||
                    var wp = content.GetWordPosition(trigger);
 | 
			
		||||
 | 
			
		||||
                    // if it is, then that's valid
 | 
			
		||||
                    if (wp != WordPosition.None)
 | 
			
		||||
                        result.Add(expr);
 | 
			
		||||
 | 
			
		||||
                    // if it's not, then it cant' work under any circumstance,
 | 
			
		||||
                    // because content is greater than the trigger length
 | 
			
		||||
                    // so it can't be equal, and it's not contained as a word
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // if CA is disabled, and expr has AllowTarget, then the
 | 
			
		||||
                // content has to start with the trigger followed by a space
 | 
			
		||||
                if (expr.AllowTarget
 | 
			
		||||
                    && content.StartsWith(trigger, StringComparison.OrdinalIgnoreCase)
 | 
			
		||||
                    && content[trigger.Length] == ' ')
 | 
			
		||||
                    result.Add(expr);
 | 
			
		||||
            }
 | 
			
		||||
            else if (content.Length < expr.Trigger.Length)
 | 
			
		||||
            {
 | 
			
		||||
                // if input length is less than trigger length, it means
 | 
			
		||||
                // that the reaction can never be triggered
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                // if input length is the same as trigger length
 | 
			
		||||
                // reaction can only trigger if the strings are equal
 | 
			
		||||
                if (content.SequenceEqual(expr.Trigger))
 | 
			
		||||
                    result.Add(expr);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (result.Count == 0)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        var cancelled = result.FirstOrDefault(x => x.Response == "-");
 | 
			
		||||
        if (cancelled is not null)
 | 
			
		||||
            return cancelled;
 | 
			
		||||
 | 
			
		||||
        return result[_rng.Next(0, result.Count)];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> ExecOnMessageAsync(IGuild guild, IUserMessage msg)
 | 
			
		||||
    {
 | 
			
		||||
        // maybe this message is an expression
 | 
			
		||||
        var expr = TryGetExpression(msg);
 | 
			
		||||
 | 
			
		||||
        if (expr is null || expr.Response == "-")
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (guild is SocketGuild sg)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await _permChecker.CheckPermsAsync(
 | 
			
		||||
                    guild,
 | 
			
		||||
                    msg.Channel,
 | 
			
		||||
                    msg.Author,
 | 
			
		||||
                    "ACTUALEXPRESSIONS",
 | 
			
		||||
                    expr.Trigger
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                if (!result.IsAllowed)
 | 
			
		||||
                {
 | 
			
		||||
                    if (result.TryPickT3(out var disallowed, out _))
 | 
			
		||||
                    {
 | 
			
		||||
                        var permissionMessage = _strings.GetText(strs.perm_prevent(disallowed.PermIndex + 1,
 | 
			
		||||
                                Format.Bold(disallowed.PermText)),
 | 
			
		||||
                            sg.Id);
 | 
			
		||||
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            await msg.Channel.SendErrorAsync(_eb, permissionMessage);
 | 
			
		||||
                        }
 | 
			
		||||
                        catch
 | 
			
		||||
                        {
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        Log.Information("{PermissionMessage}", permissionMessage);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var sentMsg = await expr.Send(msg, _repSvc, _client, false);
 | 
			
		||||
 | 
			
		||||
            var reactions = expr.GetReactions();
 | 
			
		||||
            foreach (var reaction in reactions)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    await sentMsg.AddReactionAsync(reaction.ToIEmote());
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    Log.Warning("Unable to add reactions to message {Message} in server {GuildId}",
 | 
			
		||||
                        sentMsg.Id,
 | 
			
		||||
                        expr.GuildId);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                await Task.Delay(1000);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (expr.AutoDeleteTrigger)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    await msg.DeleteAsync();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Log.Information("s: {GuildId} c: {ChannelId} u: {UserId} | {UserName} executed expression {Expr}",
 | 
			
		||||
                guild.Id,
 | 
			
		||||
                msg.Channel.Id,
 | 
			
		||||
                msg.Author.Id,
 | 
			
		||||
                msg.Author.ToString(),
 | 
			
		||||
                expr.Trigger);
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Log.Warning(ex, "Error in Expression RunBehavior: {ErrorMessage}", ex.Message);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task ResetExprReactions(ulong? maybeGuildId, int id)
 | 
			
		||||
    {
 | 
			
		||||
        NadekoExpression expr;
 | 
			
		||||
        await using var uow = _db.GetDbContext();
 | 
			
		||||
        expr = uow.Set<NadekoExpression>().GetById(id);
 | 
			
		||||
        if (expr is null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        expr.Reactions = string.Empty;
 | 
			
		||||
 | 
			
		||||
        await uow.SaveChangesAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Task UpdateInternalAsync(ulong? maybeGuildId, NadekoExpression expr)
 | 
			
		||||
    {
 | 
			
		||||
        if (maybeGuildId is { } guildId)
 | 
			
		||||
            UpdateInternal(guildId, expr);
 | 
			
		||||
        else
 | 
			
		||||
            return _pubSub.Pub(_gexprEditedKey, expr);
 | 
			
		||||
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void UpdateInternal(ulong? maybeGuildId, NadekoExpression expr)
 | 
			
		||||
    {
 | 
			
		||||
        if (maybeGuildId is { } guildId)
 | 
			
		||||
        {
 | 
			
		||||
            newguildExpressions.AddOrUpdate(guildId,
 | 
			
		||||
                new[] { expr },
 | 
			
		||||
                (_, old) =>
 | 
			
		||||
                {
 | 
			
		||||
                    var newArray = old.ToArray();
 | 
			
		||||
                    for (var i = 0; i < newArray.Length; i++)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (newArray[i].Id == expr.Id)
 | 
			
		||||
                            newArray[i] = expr;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return newArray;
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            lock (_gexprWriteLock)
 | 
			
		||||
            {
 | 
			
		||||
                var exprs = globalExpressions;
 | 
			
		||||
                for (var i = 0; i < exprs.Length; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    if (exprs[i].Id == expr.Id)
 | 
			
		||||
                        exprs[i] = expr;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Task AddInternalAsync(ulong? maybeGuildId, NadekoExpression expr)
 | 
			
		||||
    {
 | 
			
		||||
        // only do this for perf purposes
 | 
			
		||||
        expr.Trigger = expr.Trigger.Replace(MENTION_PH, _client.CurrentUser.Mention);
 | 
			
		||||
 | 
			
		||||
        if (maybeGuildId is { } guildId)
 | 
			
		||||
            newguildExpressions.AddOrUpdate(guildId, new[] { expr }, (_, old) => old.With(expr));
 | 
			
		||||
        else
 | 
			
		||||
            return _pubSub.Pub(_gexprAddedKey, expr);
 | 
			
		||||
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Task DeleteInternalAsync(ulong? maybeGuildId, int id)
 | 
			
		||||
    {
 | 
			
		||||
        if (maybeGuildId is { } guildId)
 | 
			
		||||
        {
 | 
			
		||||
            newguildExpressions.AddOrUpdate(guildId,
 | 
			
		||||
                Array.Empty<NadekoExpression>(),
 | 
			
		||||
                (key, old) => DeleteInternal(old, id, out _));
 | 
			
		||||
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        lock (_gexprWriteLock)
 | 
			
		||||
        {
 | 
			
		||||
            var expr = Array.Find(globalExpressions, item => item.Id == id);
 | 
			
		||||
            if (expr is not null)
 | 
			
		||||
                return _pubSub.Pub(_gexprDeletedkey, expr.Id);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private NadekoExpression[] DeleteInternal(
 | 
			
		||||
        IReadOnlyList<NadekoExpression> exprs,
 | 
			
		||||
        int id,
 | 
			
		||||
        out NadekoExpression deleted)
 | 
			
		||||
    {
 | 
			
		||||
        deleted = null;
 | 
			
		||||
        if (exprs is null || exprs.Count == 0)
 | 
			
		||||
            return exprs as NadekoExpression[] ?? exprs?.ToArray();
 | 
			
		||||
 | 
			
		||||
        var newExprs = new NadekoExpression[exprs.Count - 1];
 | 
			
		||||
        for (int i = 0, k = 0; i < exprs.Count; i++, k++)
 | 
			
		||||
        {
 | 
			
		||||
            if (exprs[i].Id == id)
 | 
			
		||||
            {
 | 
			
		||||
                deleted = exprs[i];
 | 
			
		||||
                k--;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            newExprs[k] = exprs[i];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return newExprs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task SetExprReactions(ulong? guildId, int id, IEnumerable<string> emojis)
 | 
			
		||||
    {
 | 
			
		||||
        NadekoExpression expr;
 | 
			
		||||
        await using (var uow = _db.GetDbContext())
 | 
			
		||||
        {
 | 
			
		||||
            expr = uow.Set<NadekoExpression>().GetById(id);
 | 
			
		||||
            if (expr is null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            expr.Reactions = string.Join("@@@", emojis);
 | 
			
		||||
 | 
			
		||||
            await uow.SaveChangesAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await UpdateInternalAsync(guildId, expr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<(bool Sucess, bool NewValue)> ToggleExprOptionAsync(ulong? guildId, int id, ExprField field)
 | 
			
		||||
    {
 | 
			
		||||
        var newVal = false;
 | 
			
		||||
        NadekoExpression expr;
 | 
			
		||||
        await using (var uow = _db.GetDbContext())
 | 
			
		||||
        {
 | 
			
		||||
            expr = uow.Set<NadekoExpression>().GetById(id);
 | 
			
		||||
 | 
			
		||||
            if (expr is null || expr.GuildId != guildId)
 | 
			
		||||
                return (false, false);
 | 
			
		||||
            if (field == ExprField.AutoDelete)
 | 
			
		||||
                newVal = expr.AutoDeleteTrigger = !expr.AutoDeleteTrigger;
 | 
			
		||||
            else if (field == ExprField.ContainsAnywhere)
 | 
			
		||||
                newVal = expr.ContainsAnywhere = !expr.ContainsAnywhere;
 | 
			
		||||
            else if (field == ExprField.DmResponse)
 | 
			
		||||
                newVal = expr.DmResponse = !expr.DmResponse;
 | 
			
		||||
            else if (field == ExprField.AllowTarget)
 | 
			
		||||
                newVal = expr.AllowTarget = !expr.AllowTarget;
 | 
			
		||||
 | 
			
		||||
            await uow.SaveChangesAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await UpdateInternalAsync(guildId, expr);
 | 
			
		||||
 | 
			
		||||
        return (true, newVal);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NadekoExpression GetExpression(ulong? guildId, int id)
 | 
			
		||||
    {
 | 
			
		||||
        using var uow = _db.GetDbContext();
 | 
			
		||||
        var expr = uow.Set<NadekoExpression>().GetById(id);
 | 
			
		||||
        if (expr is null || expr.GuildId != guildId)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        return expr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int DeleteAllExpressions(ulong guildId)
 | 
			
		||||
    {
 | 
			
		||||
        using var uow = _db.GetDbContext();
 | 
			
		||||
        var count = uow.Set<NadekoExpression>().ClearFromGuild(guildId);
 | 
			
		||||
        uow.SaveChanges();
 | 
			
		||||
 | 
			
		||||
        newguildExpressions.TryRemove(guildId, out _);
 | 
			
		||||
 | 
			
		||||
        return count;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool ExpressionExists(ulong? guildId, string input)
 | 
			
		||||
    {
 | 
			
		||||
        input = input.ToLowerInvariant();
 | 
			
		||||
 | 
			
		||||
        var gexprs = globalExpressions;
 | 
			
		||||
        foreach (var t in gexprs)
 | 
			
		||||
        {
 | 
			
		||||
            if (t.Trigger == input)
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (guildId is ulong gid && newguildExpressions.TryGetValue(gid, out var guildExprs))
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var t in guildExprs)
 | 
			
		||||
            {
 | 
			
		||||
                if (t.Trigger == input)
 | 
			
		||||
                    return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public string ExportExpressions(ulong? guildId)
 | 
			
		||||
    {
 | 
			
		||||
        var exprs = GetExpressionsFor(guildId);
 | 
			
		||||
 | 
			
		||||
        var exprsDict = exprs.GroupBy(x => x.Trigger).ToDictionary(x => x.Key, x => x.Select(ExportedExpr.FromModel));
 | 
			
		||||
 | 
			
		||||
        return PREPEND_EXPORT + _exportSerializer.Serialize(exprsDict).UnescapeUnicodeCodePoints();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> ImportExpressionsAsync(ulong? guildId, string input)
 | 
			
		||||
    {
 | 
			
		||||
        Dictionary<string, List<ExportedExpr>> data;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            data = Yaml.Deserializer.Deserialize<Dictionary<string, List<ExportedExpr>>>(input);
 | 
			
		||||
            if (data.Sum(x => x.Value.Count) == 0)
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await using var uow = _db.GetDbContext();
 | 
			
		||||
        foreach (var entry in data)
 | 
			
		||||
        {
 | 
			
		||||
            var trigger = entry.Key;
 | 
			
		||||
            await uow.Set<NadekoExpression>().AddRangeAsync(entry.Value
 | 
			
		||||
                .Where(expr => !string.IsNullOrWhiteSpace(expr.Res))
 | 
			
		||||
                .Select(expr => new NadekoExpression
 | 
			
		||||
                {
 | 
			
		||||
                    GuildId = guildId,
 | 
			
		||||
                    Response = expr.Res,
 | 
			
		||||
                    Reactions = expr.React?.Join("@@@"),
 | 
			
		||||
                    Trigger = trigger,
 | 
			
		||||
                    AllowTarget = expr.At,
 | 
			
		||||
                    ContainsAnywhere = expr.Ca,
 | 
			
		||||
                    DmResponse = expr.Dm,
 | 
			
		||||
                    AutoDeleteTrigger = expr.Ad
 | 
			
		||||
                }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await uow.SaveChangesAsync();
 | 
			
		||||
        await TriggerReloadExpressions();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #region Event Handlers
 | 
			
		||||
 | 
			
		||||
    public async Task OnReadyAsync()
 | 
			
		||||
        => await OnExprsShouldReload(true);
 | 
			
		||||
 | 
			
		||||
    private ValueTask OnExprsShouldReload(bool _)
 | 
			
		||||
        => new(ReloadInternal(_bot.GetCurrentGuildIds()));
 | 
			
		||||
 | 
			
		||||
    private ValueTask OnGexprAdded(NadekoExpression c)
 | 
			
		||||
    {
 | 
			
		||||
        lock (_gexprWriteLock)
 | 
			
		||||
        {
 | 
			
		||||
            var newGlobalReactions = new NadekoExpression[globalExpressions.Length + 1];
 | 
			
		||||
            Array.Copy(globalExpressions, newGlobalReactions, globalExpressions.Length);
 | 
			
		||||
            newGlobalReactions[globalExpressions.Length] = c;
 | 
			
		||||
            globalExpressions = newGlobalReactions;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return default;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ValueTask OnGexprEdited(NadekoExpression c)
 | 
			
		||||
    {
 | 
			
		||||
        lock (_gexprWriteLock)
 | 
			
		||||
        {
 | 
			
		||||
            for (var i = 0; i < globalExpressions.Length; i++)
 | 
			
		||||
            {
 | 
			
		||||
                if (globalExpressions[i].Id == c.Id)
 | 
			
		||||
                {
 | 
			
		||||
                    globalExpressions[i] = c;
 | 
			
		||||
                    return default;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // if edited expr is not found?!
 | 
			
		||||
            // add it
 | 
			
		||||
            OnGexprAdded(c);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return default;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ValueTask OnGexprDeleted(int id)
 | 
			
		||||
    {
 | 
			
		||||
        lock (_gexprWriteLock)
 | 
			
		||||
        {
 | 
			
		||||
            var newGlobalReactions = DeleteInternal(globalExpressions, id, out _);
 | 
			
		||||
            globalExpressions = newGlobalReactions;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return default;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task TriggerReloadExpressions()
 | 
			
		||||
        => _pubSub.Pub(_exprsReloadedKey, true);
 | 
			
		||||
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    #region Client Event Handlers
 | 
			
		||||
 | 
			
		||||
    private Task OnLeftGuild(SocketGuild arg)
 | 
			
		||||
    {
 | 
			
		||||
        newguildExpressions.TryRemove(arg.Id, out _);
 | 
			
		||||
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task OnJoinedGuild(GuildConfig gc)
 | 
			
		||||
    {
 | 
			
		||||
        await using var uow = _db.GetDbContext();
 | 
			
		||||
        var exprs = await uow.Set<NadekoExpression>().AsNoTracking().Where(x => x.GuildId == gc.GuildId).ToArrayAsync();
 | 
			
		||||
 | 
			
		||||
        newguildExpressions[gc.GuildId] = exprs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    #region Basic Operations
 | 
			
		||||
 | 
			
		||||
    public async Task<NadekoExpression> AddAsync(ulong? guildId, string key, string message,
 | 
			
		||||
        bool ca = false, bool ad = false, bool dm = false)
 | 
			
		||||
    {
 | 
			
		||||
        key = key.ToLowerInvariant();
 | 
			
		||||
        var expr = new NadekoExpression
 | 
			
		||||
        {
 | 
			
		||||
            GuildId = guildId,
 | 
			
		||||
            Trigger = key,
 | 
			
		||||
            Response = message,
 | 
			
		||||
            ContainsAnywhere = ca,
 | 
			
		||||
            AutoDeleteTrigger = ad,
 | 
			
		||||
            DmResponse = dm
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (expr.Response.Contains("%target%", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            expr.AllowTarget = true;
 | 
			
		||||
 | 
			
		||||
        await using (var uow = _db.GetDbContext())
 | 
			
		||||
        {
 | 
			
		||||
            uow.Set<NadekoExpression>().Add(expr);
 | 
			
		||||
            await uow.SaveChangesAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await AddInternalAsync(guildId, expr);
 | 
			
		||||
 | 
			
		||||
        return expr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<NadekoExpression> EditAsync(ulong? guildId, int id, string message,
 | 
			
		||||
        bool? ca = null, bool? ad = null, bool? dm = null)
 | 
			
		||||
    {
 | 
			
		||||
        await using var uow = _db.GetDbContext();
 | 
			
		||||
        var expr = uow.Set<NadekoExpression>().GetById(id);
 | 
			
		||||
 | 
			
		||||
        if (expr is null || expr.GuildId != guildId)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        // disable allowtarget if message had target, but it was removed from it
 | 
			
		||||
        if (!message.Contains("%target%", StringComparison.OrdinalIgnoreCase)
 | 
			
		||||
            && expr.Response.Contains("%target%", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            expr.AllowTarget = false;
 | 
			
		||||
 | 
			
		||||
        expr.Response = message;
 | 
			
		||||
 | 
			
		||||
        // enable allow target if message is edited to contain target
 | 
			
		||||
        if (expr.Response.Contains("%target%", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            expr.AllowTarget = true;
 | 
			
		||||
 | 
			
		||||
        expr.ContainsAnywhere = ca ?? expr.ContainsAnywhere;
 | 
			
		||||
        expr.AutoDeleteTrigger = ad ?? expr.AutoDeleteTrigger;
 | 
			
		||||
        expr.DmResponse = dm ?? expr.DmResponse;
 | 
			
		||||
 | 
			
		||||
        await uow.SaveChangesAsync();
 | 
			
		||||
        await UpdateInternalAsync(guildId, expr);
 | 
			
		||||
 | 
			
		||||
        return expr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public async Task<NadekoExpression> DeleteAsync(ulong? guildId, int id)
 | 
			
		||||
    {
 | 
			
		||||
        await using var uow = _db.GetDbContext();
 | 
			
		||||
        var toDelete = uow.Set<NadekoExpression>().GetById(id);
 | 
			
		||||
 | 
			
		||||
        if (toDelete is null)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        if ((toDelete.IsGlobal() && guildId is null) || guildId == toDelete.GuildId)
 | 
			
		||||
        {
 | 
			
		||||
            uow.Set<NadekoExpression>().Remove(toDelete);
 | 
			
		||||
            await uow.SaveChangesAsync();
 | 
			
		||||
            await DeleteInternalAsync(guildId, id);
 | 
			
		||||
            return toDelete;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
    public NadekoExpression[] GetExpressionsFor(ulong? maybeGuildId)
 | 
			
		||||
    {
 | 
			
		||||
        if (maybeGuildId is { } guildId)
 | 
			
		||||
            return newguildExpressions.TryGetValue(guildId, out var exprs) ? exprs : Array.Empty<NadekoExpression>();
 | 
			
		||||
 | 
			
		||||
        return globalExpressions;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> ToggleGlobalExpressionsAsync(ulong guildId)
 | 
			
		||||
    {
 | 
			
		||||
        await using var ctx = _db.GetDbContext();
 | 
			
		||||
        var gc = ctx.GuildConfigsForId(guildId, set => set);
 | 
			
		||||
        var toReturn = gc.DisableGlobalExpressions = !gc.DisableGlobalExpressions;
 | 
			
		||||
        await ctx.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
        if (toReturn)
 | 
			
		||||
            _disabledGlobalExpressionGuilds.Add(guildId);
 | 
			
		||||
        else
 | 
			
		||||
            _disabledGlobalExpressionGuilds.TryRemove(guildId);
 | 
			
		||||
 | 
			
		||||
        return toReturn;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public async Task<(IReadOnlyCollection<NadekoExpression> Exprs, int TotalCount)> FindExpressionsAsync(ulong guildId,
 | 
			
		||||
        string query, int page)
 | 
			
		||||
    {
 | 
			
		||||
        await using var ctx = _db.GetDbContext();
 | 
			
		||||
 | 
			
		||||
        if (newguildExpressions.TryGetValue(guildId, out var exprs))
 | 
			
		||||
        {
 | 
			
		||||
            return (exprs.Where(x => x.Trigger.Contains(query))
 | 
			
		||||
                .Skip(page * 9)
 | 
			
		||||
                .Take(9)
 | 
			
		||||
                .ToArray(), exprs.Length);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ([], 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using NadekoBot.Modules.NadekoExpressions;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Common.TypeReaders;
 | 
			
		||||
 | 
			
		||||
public sealed class CommandOrExprTypeReader : NadekoTypeReader<CommandOrExprInfo>
 | 
			
		||||
{
 | 
			
		||||
    private readonly CommandService _cmds;
 | 
			
		||||
    private readonly ICommandHandler _commandHandler;
 | 
			
		||||
    private readonly NadekoExpressionsService _exprs;
 | 
			
		||||
 | 
			
		||||
    public CommandOrExprTypeReader(CommandService cmds, NadekoExpressionsService exprs, ICommandHandler commandHandler)
 | 
			
		||||
    {
 | 
			
		||||
        _cmds = cmds;
 | 
			
		||||
        _exprs = exprs;
 | 
			
		||||
        _commandHandler = commandHandler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override async ValueTask<TypeReaderResult<CommandOrExprInfo>> ReadAsync(ICommandContext ctx, string input)
 | 
			
		||||
    {
 | 
			
		||||
        if (_exprs.ExpressionExists(ctx.Guild?.Id, input))
 | 
			
		||||
            return TypeReaderResult.FromSuccess(new CommandOrExprInfo(input, CommandOrExprInfo.Type.Custom));
 | 
			
		||||
 | 
			
		||||
        var cmd = await new CommandTypeReader(_commandHandler, _cmds).ReadAsync(ctx, input);
 | 
			
		||||
        if (cmd.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            return TypeReaderResult.FromSuccess(new CommandOrExprInfo(((CommandInfo)cmd.Values.First().Value).Name,
 | 
			
		||||
                CommandOrExprInfo.Type.Normal));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return TypeReaderResult.FromError<CommandOrExprInfo>(CommandError.ParseFailed, "No such command or expression found.");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -12,7 +12,7 @@ global using Nadeko.Common; // new project
 | 
			
		||||
global using NadekoBot.Common; // old + nadekobot specific things
 | 
			
		||||
global using NadekoBot.Common.Attributes;
 | 
			
		||||
global using NadekoBot.Extensions;
 | 
			
		||||
global using Nadeko.Snake;
 | 
			
		||||
// global using Nadeko.Snake;
 | 
			
		||||
 | 
			
		||||
// discord
 | 
			
		||||
global using Discord;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
#nullable enable
 | 
			
		||||
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
 | 
			
		||||
[DIIgnore]
 | 
			
		||||
public sealed class BehaviorAdapter : ICustomBehavior
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
using Nadeko.Medusa;
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
 | 
			
		||||
internal class ContextAdapterFactory
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
 | 
			
		||||
public sealed class DmContextAdapter : DmContext
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
namespace Nadeko.Medusa.Adapters;
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Medusa.Adapters;
 | 
			
		||||
 | 
			
		||||
public class FilterAdapter : PreconditionAttribute
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
 | 
			
		||||
public sealed class GuildContextAdapter : GuildContext
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
public sealed class ParamParserAdapter<T> : TypeReader
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
 | 
			
		||||
public sealed class ParamParserAdapter<T> : TypeReader
 | 
			
		||||
{
 | 
			
		||||
    private readonly ParamParser<T> _parser;
 | 
			
		||||
    private readonly IMedusaStrings _strings;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using Ninject;
 | 
			
		||||
using Ninject.Activation;
 | 
			
		||||
using Ninject.Activation.Caching;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Medusa;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using Ninject.Modules;
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
using Ninject.Modules;
 | 
			
		||||
using System.Collections.Immutable;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Medusa;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using CommandStrings = Nadeko.Snake.CommandStrings;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Medusa;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
namespace Nadeko.Medusa;
 | 
			
		||||
using Nadeko.Snake;
 | 
			
		||||
 | 
			
		||||
namespace Nadeko.Medusa;
 | 
			
		||||
 | 
			
		||||
public sealed record SnekInfo(
 | 
			
		||||
    string Name,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										405
									
								
								src/NadekoBot/Modules/Administration/Administration.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										405
									
								
								src/NadekoBot/Modules/Administration/Administration.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,405 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using NadekoBot.Common.TypeReaders.Models;
 | 
			
		||||
using NadekoBot.Modules.Administration.Services;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Administration;
 | 
			
		||||
 | 
			
		||||
public partial class Administration : NadekoModule<AdministrationService>
 | 
			
		||||
{
 | 
			
		||||
    public enum Channel
 | 
			
		||||
    {
 | 
			
		||||
        Channel,
 | 
			
		||||
        Ch,
 | 
			
		||||
        Chnl,
 | 
			
		||||
        Chan
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum List
 | 
			
		||||
    {
 | 
			
		||||
        List = 0,
 | 
			
		||||
        Ls = 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum Server
 | 
			
		||||
    {
 | 
			
		||||
        Server
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum State
 | 
			
		||||
    {
 | 
			
		||||
        Enable,
 | 
			
		||||
        Disable,
 | 
			
		||||
        Inherit
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private readonly SomethingOnlyChannelService _somethingOnly;
 | 
			
		||||
    private readonly AutoPublishService _autoPubService;
 | 
			
		||||
 | 
			
		||||
    public Administration(SomethingOnlyChannelService somethingOnly, AutoPublishService autoPubService)
 | 
			
		||||
    {
 | 
			
		||||
        _somethingOnly = somethingOnly;
 | 
			
		||||
        _autoPubService = autoPubService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.Administrator)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageGuild)]
 | 
			
		||||
    public async Task ImageOnlyChannel(StoopidTime time = null)
 | 
			
		||||
    {
 | 
			
		||||
        var newValue = await _somethingOnly.ToggleImageOnlyChannelAsync(ctx.Guild.Id, ctx.Channel.Id);
 | 
			
		||||
        if (newValue)
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.imageonly_enable);
 | 
			
		||||
        else
 | 
			
		||||
            await ReplyPendingLocalizedAsync(strs.imageonly_disable);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.Administrator)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageGuild)]
 | 
			
		||||
    public async Task LinkOnlyChannel(StoopidTime time = null)
 | 
			
		||||
    {
 | 
			
		||||
        var newValue = await _somethingOnly.ToggleLinkOnlyChannelAsync(ctx.Guild.Id, ctx.Channel.Id);
 | 
			
		||||
        if (newValue)
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.linkonly_enable);
 | 
			
		||||
        else
 | 
			
		||||
            await ReplyPendingLocalizedAsync(strs.linkonly_disable);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(ChannelPerm.ManageChannels)]
 | 
			
		||||
    [BotPerm(ChannelPerm.ManageChannels)]
 | 
			
		||||
    public async Task Slowmode(StoopidTime time = null)
 | 
			
		||||
    {
 | 
			
		||||
        var seconds = (int?)time?.Time.TotalSeconds ?? 0;
 | 
			
		||||
        if (time is not null && (time.Time < TimeSpan.FromSeconds(0) || time.Time > TimeSpan.FromHours(6)))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        await ((ITextChannel)ctx.Channel).ModifyAsync(tcp =>
 | 
			
		||||
        {
 | 
			
		||||
            tcp.SlowModeInterval = seconds;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await ctx.OkAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.Administrator)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageMessages)]
 | 
			
		||||
    [Priority(2)]
 | 
			
		||||
    public async Task Delmsgoncmd(List _)
 | 
			
		||||
    {
 | 
			
		||||
        var guild = (SocketGuild)ctx.Guild;
 | 
			
		||||
        var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id);
 | 
			
		||||
 | 
			
		||||
        var embed = _eb.Create()
 | 
			
		||||
                       .WithOkColor()
 | 
			
		||||
                       .WithTitle(GetText(strs.server_delmsgoncmd))
 | 
			
		||||
                       .WithDescription(enabled ? "✅" : "❌");
 | 
			
		||||
 | 
			
		||||
        var str = string.Join("\n",
 | 
			
		||||
            channels.Select(x =>
 | 
			
		||||
            {
 | 
			
		||||
                var ch = guild.GetChannel(x.ChannelId)?.ToString() ?? x.ChannelId.ToString();
 | 
			
		||||
                var prefixSign = x.State ? "✅ " : "❌ ";
 | 
			
		||||
                return prefixSign + ch;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(str))
 | 
			
		||||
            str = "-";
 | 
			
		||||
 | 
			
		||||
        embed.AddField(GetText(strs.channel_delmsgoncmd), str);
 | 
			
		||||
 | 
			
		||||
        await ctx.Channel.EmbedAsync(embed);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.Administrator)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageMessages)]
 | 
			
		||||
    [Priority(1)]
 | 
			
		||||
    public async Task Delmsgoncmd(Server _ = Server.Server)
 | 
			
		||||
    {
 | 
			
		||||
        if (_service.ToggleDeleteMessageOnCommand(ctx.Guild.Id))
 | 
			
		||||
        {
 | 
			
		||||
            _service.DeleteMessagesOnCommand.Add(ctx.Guild.Id);
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.delmsg_on);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            _service.DeleteMessagesOnCommand.TryRemove(ctx.Guild.Id);
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.delmsg_off);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.Administrator)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageMessages)]
 | 
			
		||||
    [Priority(0)]
 | 
			
		||||
    public Task Delmsgoncmd(Channel _, State s, ITextChannel ch)
 | 
			
		||||
        => Delmsgoncmd(_, s, ch.Id);
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.Administrator)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageMessages)]
 | 
			
		||||
    [Priority(1)]
 | 
			
		||||
    public async Task Delmsgoncmd(Channel _, State s, ulong? chId = null)
 | 
			
		||||
    {
 | 
			
		||||
        var actualChId = chId ?? ctx.Channel.Id;
 | 
			
		||||
        await _service.SetDelMsgOnCmdState(ctx.Guild.Id, actualChId, s);
 | 
			
		||||
 | 
			
		||||
        if (s == State.Disable)
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.delmsg_channel_off);
 | 
			
		||||
        else if (s == State.Enable)
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.delmsg_channel_on);
 | 
			
		||||
        else
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.delmsg_channel_inherit);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.DeafenMembers)]
 | 
			
		||||
    [BotPerm(GuildPerm.DeafenMembers)]
 | 
			
		||||
    public async Task Deafen(params IGuildUser[] users)
 | 
			
		||||
    {
 | 
			
		||||
        await _service.DeafenUsers(true, users);
 | 
			
		||||
        await ReplyConfirmLocalizedAsync(strs.deafen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.DeafenMembers)]
 | 
			
		||||
    [BotPerm(GuildPerm.DeafenMembers)]
 | 
			
		||||
    public async Task UnDeafen(params IGuildUser[] users)
 | 
			
		||||
    {
 | 
			
		||||
        await _service.DeafenUsers(false, users);
 | 
			
		||||
        await ReplyConfirmLocalizedAsync(strs.undeafen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    public async Task DelVoiChanl([Leftover] IVoiceChannel voiceChannel)
 | 
			
		||||
    {
 | 
			
		||||
        await voiceChannel.DeleteAsync();
 | 
			
		||||
        await ReplyConfirmLocalizedAsync(strs.delvoich(Format.Bold(voiceChannel.Name)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    public async Task CreatVoiChanl([Leftover] string channelName)
 | 
			
		||||
    {
 | 
			
		||||
        var ch = await ctx.Guild.CreateVoiceChannelAsync(channelName);
 | 
			
		||||
        await ReplyConfirmLocalizedAsync(strs.createvoich(Format.Bold(ch.Name)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    public async Task DelTxtChanl([Leftover] ITextChannel toDelete)
 | 
			
		||||
    {
 | 
			
		||||
        await toDelete.DeleteAsync();
 | 
			
		||||
        await ReplyConfirmLocalizedAsync(strs.deltextchan(Format.Bold(toDelete.Name)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    public async Task CreaTxtChanl([Leftover] string channelName)
 | 
			
		||||
    {
 | 
			
		||||
        var txtCh = await ctx.Guild.CreateTextChannelAsync(channelName); 
 | 
			
		||||
        await ReplyConfirmLocalizedAsync(strs.createtextchan(Format.Bold(txtCh.Name)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    public async Task SetTopic([Leftover] string topic = null)
 | 
			
		||||
    {
 | 
			
		||||
        var channel = (ITextChannel)ctx.Channel;
 | 
			
		||||
        topic ??= "";
 | 
			
		||||
        await channel.ModifyAsync(c => c.Topic = topic);
 | 
			
		||||
        await ReplyConfirmLocalizedAsync(strs.set_topic);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    public async Task SetChanlName([Leftover] string name)
 | 
			
		||||
    {
 | 
			
		||||
        var channel = (ITextChannel)ctx.Channel;
 | 
			
		||||
        await channel.ModifyAsync(c => c.Name = name);
 | 
			
		||||
        await ReplyConfirmLocalizedAsync(strs.set_channel_name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageChannels)]
 | 
			
		||||
    public async Task AgeRestrictToggle()
 | 
			
		||||
    {
 | 
			
		||||
        var channel = (ITextChannel)ctx.Channel;
 | 
			
		||||
        var isEnabled = channel.IsNsfw;
 | 
			
		||||
 | 
			
		||||
        await channel.ModifyAsync(c => c.IsNsfw = !isEnabled);
 | 
			
		||||
 | 
			
		||||
        if (isEnabled)
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.nsfw_set_false);
 | 
			
		||||
        else
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.nsfw_set_true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(ChannelPerm.ManageMessages)]
 | 
			
		||||
    [Priority(0)]
 | 
			
		||||
    public Task Edit(ulong messageId, [Leftover] string text)
 | 
			
		||||
        => Edit((ITextChannel)ctx.Channel, messageId, text);
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [Priority(1)]
 | 
			
		||||
    public async Task Edit(ITextChannel channel, ulong messageId, [Leftover] string text)
 | 
			
		||||
    {
 | 
			
		||||
        var userPerms = ((SocketGuildUser)ctx.User).GetPermissions(channel);
 | 
			
		||||
        var botPerms = ((SocketGuild)ctx.Guild).CurrentUser.GetPermissions(channel);
 | 
			
		||||
        if (!userPerms.Has(ChannelPermission.ManageMessages))
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.insuf_perms_u);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!botPerms.Has(ChannelPermission.ViewChannel))
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.insuf_perms_i);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await _service.EditMessage(ctx, channel, messageId, text);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [UserPerm(ChannelPerm.ManageMessages)]
 | 
			
		||||
    [BotPerm(ChannelPerm.ManageMessages)]
 | 
			
		||||
    public Task Delete(ulong messageId, StoopidTime time = null)
 | 
			
		||||
        => Delete((ITextChannel)ctx.Channel, messageId, time);
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    public async Task Delete(ITextChannel channel, ulong messageId, StoopidTime time = null)
 | 
			
		||||
        => await InternalMessageAction(channel, messageId, time, msg => msg.DeleteAsync());
 | 
			
		||||
 | 
			
		||||
    private async Task InternalMessageAction(
 | 
			
		||||
        ITextChannel channel,
 | 
			
		||||
        ulong messageId,
 | 
			
		||||
        StoopidTime time,
 | 
			
		||||
        Func<IMessage, Task> func)
 | 
			
		||||
    {
 | 
			
		||||
        var userPerms = ((SocketGuildUser)ctx.User).GetPermissions(channel);
 | 
			
		||||
        var botPerms = ((SocketGuild)ctx.Guild).CurrentUser.GetPermissions(channel);
 | 
			
		||||
        if (!userPerms.Has(ChannelPermission.ManageMessages))
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.insuf_perms_u);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!botPerms.Has(ChannelPermission.ManageMessages))
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.insuf_perms_i);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var msg = await channel.GetMessageAsync(messageId);
 | 
			
		||||
        if (msg is null)
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.msg_not_found);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (time is null)
 | 
			
		||||
            await msg.DeleteAsync();
 | 
			
		||||
        else if (time.Time <= TimeSpan.FromDays(7))
 | 
			
		||||
        {
 | 
			
		||||
            _ = Task.Run(async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await Task.Delay(time.Time);
 | 
			
		||||
                await msg.DeleteAsync();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.time_too_long);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await ctx.OkAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [BotPerm(ChannelPermission.CreatePublicThreads)]
 | 
			
		||||
    [UserPerm(ChannelPermission.CreatePublicThreads)]
 | 
			
		||||
    public async Task ThreadCreate([Leftover] string name)
 | 
			
		||||
    {
 | 
			
		||||
        if (ctx.Channel is not SocketTextChannel stc)
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
        await stc.CreateThreadAsync(name, message: ctx.Message.ReferencedMessage);
 | 
			
		||||
        await ctx.OkAsync();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [BotPerm(ChannelPermission.ManageThreads)]
 | 
			
		||||
    [UserPerm(ChannelPermission.ManageThreads)]
 | 
			
		||||
    public async Task ThreadDelete([Leftover] string name)
 | 
			
		||||
    {
 | 
			
		||||
        if (ctx.Channel is not SocketTextChannel stc)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        var t = stc.Threads.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.InvariantCultureIgnoreCase));
 | 
			
		||||
 | 
			
		||||
        if (t is null)
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.not_found);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        await t.DeleteAsync();
 | 
			
		||||
        await ctx.OkAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [UserPerm(ChannelPerm.ManageMessages)]
 | 
			
		||||
    public async Task AutoPublish()
 | 
			
		||||
    {
 | 
			
		||||
        if (ctx.Channel.GetChannelType() != ChannelType.News)
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.req_announcement_channel);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var newState = await _autoPubService.ToggleAutoPublish(ctx.Guild.Id, ctx.Channel.Id);
 | 
			
		||||
 | 
			
		||||
        if (newState)
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.autopublish_enable);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.autopublish_disable);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										162
									
								
								src/NadekoBot/Modules/Administration/AdministrationService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/NadekoBot/Modules/Administration/AdministrationService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Common;
 | 
			
		||||
using NadekoBot.Db;
 | 
			
		||||
using Nadeko.Bot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Administration.Services;
 | 
			
		||||
 | 
			
		||||
public class AdministrationService : INService
 | 
			
		||||
{
 | 
			
		||||
    public ConcurrentHashSet<ulong> DeleteMessagesOnCommand { get; }
 | 
			
		||||
    public ConcurrentDictionary<ulong, bool> DeleteMessagesOnCommandChannels { get; }
 | 
			
		||||
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
    private readonly IReplacementService _repSvc;
 | 
			
		||||
    private readonly ILogCommandService _logService;
 | 
			
		||||
 | 
			
		||||
    public AdministrationService(
 | 
			
		||||
        IBot bot,
 | 
			
		||||
        CommandHandler cmdHandler,
 | 
			
		||||
        DbService db,
 | 
			
		||||
        IReplacementService repSvc,
 | 
			
		||||
        ILogCommandService logService)
 | 
			
		||||
    {
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _repSvc = repSvc;
 | 
			
		||||
        _logService = logService;
 | 
			
		||||
 | 
			
		||||
        DeleteMessagesOnCommand = new(bot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
 | 
			
		||||
 | 
			
		||||
        DeleteMessagesOnCommandChannels = new(bot.AllGuildConfigs.SelectMany(x => x.DelMsgOnCmdChannels)
 | 
			
		||||
                                                 .ToDictionary(x => x.ChannelId, x => x.State)
 | 
			
		||||
                                                 .ToConcurrent());
 | 
			
		||||
 | 
			
		||||
        cmdHandler.CommandExecuted += DelMsgOnCmd_Handler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public (bool DelMsgOnCmd, IEnumerable<DelMsgOnCmdChannel> channels) GetDelMsgOnCmdData(ulong guildId)
 | 
			
		||||
    {
 | 
			
		||||
        using var uow = _db.GetDbContext();
 | 
			
		||||
        var conf = uow.GuildConfigsForId(guildId, set => set.Include(x => x.DelMsgOnCmdChannels));
 | 
			
		||||
 | 
			
		||||
        return (conf.DeleteMessageOnCommand, conf.DelMsgOnCmdChannels);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 (state && cmd.Name != "prune" && cmd.Name != "pick")
 | 
			
		||||
                {
 | 
			
		||||
                    _logService.AddDeleteIgnore(msg.Id);
 | 
			
		||||
                    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")
 | 
			
		||||
            {
 | 
			
		||||
                _logService.AddDeleteIgnore(msg.Id);
 | 
			
		||||
                try { await msg.DeleteAsync(); }
 | 
			
		||||
                catch { }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 = conf.DelMsgOnCmdChannels.FirstOrDefault(x => x.ChannelId == chId);
 | 
			
		||||
            if (newState == Administration.State.Inherit)
 | 
			
		||||
            {
 | 
			
		||||
                if (old is not null)
 | 
			
		||||
                {
 | 
			
		||||
                    conf.DelMsgOnCmdChannels.Remove(old);
 | 
			
		||||
                    uow.Remove(old);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (old is null)
 | 
			
		||||
                {
 | 
			
		||||
                    old = new()
 | 
			
		||||
                    {
 | 
			
		||||
                        ChannelId = chId
 | 
			
		||||
                    };
 | 
			
		||||
                    conf.DelMsgOnCmdChannels.Add(old);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                old.State = newState == Administration.State.Enable;
 | 
			
		||||
                DeleteMessagesOnCommandChannels[chId] = newState == Administration.State.Enable;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await uow.SaveChangesAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (newState == Administration.State.Disable)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
        else if (newState == Administration.State.Enable)
 | 
			
		||||
            DeleteMessagesOnCommandChannels[chId] = true;
 | 
			
		||||
        else
 | 
			
		||||
            DeleteMessagesOnCommandChannels.TryRemove(chId, out _);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task DeafenUsers(bool value, params IGuildUser[] users)
 | 
			
		||||
    {
 | 
			
		||||
        if (!users.Any())
 | 
			
		||||
            return;
 | 
			
		||||
        foreach (var u in users)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await u.ModifyAsync(usr => usr.Deaf = value);
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                // ignored
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task EditMessage(
 | 
			
		||||
        ICommandContext context,
 | 
			
		||||
        ITextChannel chanl,
 | 
			
		||||
        ulong messageId,
 | 
			
		||||
        string input)
 | 
			
		||||
    {
 | 
			
		||||
        var msg = await chanl.GetMessageAsync(messageId);
 | 
			
		||||
 | 
			
		||||
        if (msg is not IUserMessage umsg || msg.Author.Id != context.Client.CurrentUser.Id)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        var repCtx = new ReplacementContext(context);
 | 
			
		||||
 | 
			
		||||
        var text = SmartText.CreateFrom(input);
 | 
			
		||||
        text = await _repSvc.ReplaceAsync(text, repCtx);
 | 
			
		||||
 | 
			
		||||
        await umsg.EditAsync(text);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user