mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	Lots more stuff
This commit is contained in:
		@@ -1,202 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Cloneable;
 | 
			
		||||
using NadekoBot.Common.Yml;
 | 
			
		||||
using SixLabors.ImageSharp.PixelFormats;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using YamlDotNet.Core;
 | 
			
		||||
using YamlDotNet.Serialization;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Common.Configs;
 | 
			
		||||
 | 
			
		||||
[Cloneable]
 | 
			
		||||
public sealed partial class BotConfig : ICloneable<BotConfig>
 | 
			
		||||
{
 | 
			
		||||
    [Comment("""DO NOT CHANGE""")]
 | 
			
		||||
    public int Version { get; set; } = 5;
 | 
			
		||||
 | 
			
		||||
    [Comment("""
 | 
			
		||||
        Most commands, when executed, have a small colored line
 | 
			
		||||
        next to the response. The color depends whether the command
 | 
			
		||||
        is completed, errored or in progress (pending)
 | 
			
		||||
        Color settings below are for the color of those lines.
 | 
			
		||||
        To get color's hex, you can go here https://htmlcolorcodes.com/
 | 
			
		||||
        and copy the hex code fo your selected color (marked as #)
 | 
			
		||||
        """)]
 | 
			
		||||
    public ColorConfig Color { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("Default bot language. It has to be in the list of supported languages (.langli)")]
 | 
			
		||||
    public CultureInfo DefaultLocale { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""
 | 
			
		||||
        Style in which executed commands will show up in the console.
 | 
			
		||||
        Allowed values: Simple, Normal, None
 | 
			
		||||
        """)]
 | 
			
		||||
    public ConsoleOutputType ConsoleOutputType { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""Whether the bot will check for new releases every hour""")]
 | 
			
		||||
    public bool CheckForUpdates { get; set; } = true;
 | 
			
		||||
 | 
			
		||||
    [Comment("""Do you want any messages sent by users in Bot's DM to be forwarded to the owner(s)?""")]
 | 
			
		||||
    public bool ForwardMessages { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""
 | 
			
		||||
            Do you want the message to be forwarded only to the first owner specified in the list of owners (in creds.yml),
 | 
			
		||||
            or all owners? (this might cause the bot to lag if there's a lot of owners specified)
 | 
			
		||||
            """)]
 | 
			
		||||
    public bool ForwardToAllOwners { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    [Comment("""
 | 
			
		||||
        Any messages sent by users in Bot's DM to be forwarded to the specified channel.
 | 
			
		||||
        This option will only work when ForwardToAllOwners is set to false
 | 
			
		||||
        """)]
 | 
			
		||||
    public ulong? ForwardToChannel { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""
 | 
			
		||||
        When a user DMs the bot with a message which is not a command
 | 
			
		||||
        they will receive this message. Leave empty for no response. The string which will be sent whenever someone DMs the bot.
 | 
			
		||||
        Supports embeds. How it looks: https://puu.sh/B0BLV.png
 | 
			
		||||
        """)]
 | 
			
		||||
    [YamlMember(ScalarStyle = ScalarStyle.Literal)]
 | 
			
		||||
    public string DmHelpText { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""
 | 
			
		||||
        Only users who send a DM to the bot containing one of the specified words will get a DmHelpText response.
 | 
			
		||||
        Case insensitive.
 | 
			
		||||
        Leave empty to reply with DmHelpText to every DM.
 | 
			
		||||
        """)]
 | 
			
		||||
    public List<string> DmHelpTextKeywords { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""This is the response for the .h command""")]
 | 
			
		||||
    [YamlMember(ScalarStyle = ScalarStyle.Literal)]
 | 
			
		||||
    public string HelpText { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""List of modules and commands completely blocked on the bot""")]
 | 
			
		||||
    public BlockedConfig Blocked { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""Which string will be used to recognize the commands""")]
 | 
			
		||||
    public string Prefix { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""
 | 
			
		||||
        Toggles whether your bot will group greet/bye messages into a single message every 5 seconds.
 | 
			
		||||
        1st user who joins will get greeted immediately
 | 
			
		||||
        If more users join within the next 5 seconds, they will be greeted in groups of 5.
 | 
			
		||||
        This will cause %user.mention% and other placeholders to be replaced with multiple users. 
 | 
			
		||||
        Keep in mind this might break some of your embeds - for example if you have %user.avatar% in the thumbnail,
 | 
			
		||||
        it will become invalid, as it will resolve to a list of avatars of grouped users.
 | 
			
		||||
        note: This setting is primarily used if you're afraid of raids, or you're running medium/large bots where some
 | 
			
		||||
              servers might get hundreds of people join at once. This is used to prevent the bot from getting ratelimited,
 | 
			
		||||
              and (slightly) reduce the greet spam in those servers.
 | 
			
		||||
        """)]
 | 
			
		||||
    public bool GroupGreets { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""
 | 
			
		||||
        Whether the bot will rotate through all specified statuses.
 | 
			
		||||
        This setting can be changed via .ropl command.
 | 
			
		||||
        See RotatingStatuses submodule in Administration.
 | 
			
		||||
        """)]
 | 
			
		||||
    public bool RotateStatuses { get; set; }
 | 
			
		||||
 | 
			
		||||
    public BotConfig()
 | 
			
		||||
    {
 | 
			
		||||
        var color = new ColorConfig();
 | 
			
		||||
        Color = color;
 | 
			
		||||
        DefaultLocale = new("en-US");
 | 
			
		||||
        ConsoleOutputType = ConsoleOutputType.Normal;
 | 
			
		||||
        ForwardMessages = false;
 | 
			
		||||
        ForwardToAllOwners = false;
 | 
			
		||||
        DmHelpText = """{"description": "Type `%prefix%h` for help."}""";
 | 
			
		||||
        HelpText = """
 | 
			
		||||
            {
 | 
			
		||||
              "title": "To invite me to your server, use this link",
 | 
			
		||||
              "description": "https://discordapp.com/oauth2/authorize?client_id={0}&scope=bot&permissions=66186303",
 | 
			
		||||
              "color": 53380,
 | 
			
		||||
              "thumbnail": "https://i.imgur.com/nKYyqMK.png",
 | 
			
		||||
              "fields": [
 | 
			
		||||
                {
 | 
			
		||||
                  "name": "Useful help commands",
 | 
			
		||||
                  "value": "`%bot.prefix%modules` Lists all bot modules.
 | 
			
		||||
            `%prefix%h CommandName` Shows some help about a specific command.
 | 
			
		||||
            `%prefix%commands ModuleName` Lists all commands in a module.",
 | 
			
		||||
                  "inline": false
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  "name": "List of all Commands",
 | 
			
		||||
                  "value": "https://nadeko.bot/commands",
 | 
			
		||||
                  "inline": false
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  "name": "Nadeko Support Server",
 | 
			
		||||
                  "value": "https://discord.nadeko.bot/ ",
 | 
			
		||||
                  "inline": true
 | 
			
		||||
                }
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
            """;
 | 
			
		||||
        var blocked = new BlockedConfig();
 | 
			
		||||
        Blocked = blocked;
 | 
			
		||||
        Prefix = ".";
 | 
			
		||||
        RotateStatuses = false;
 | 
			
		||||
        GroupGreets = false;
 | 
			
		||||
        DmHelpTextKeywords = new()
 | 
			
		||||
        {
 | 
			
		||||
            "help",
 | 
			
		||||
            "commands",
 | 
			
		||||
            "cmds",
 | 
			
		||||
            "module",
 | 
			
		||||
            "can you do"
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
//         [Comment(@"Whether the prefix will be a suffix, or prefix.
 | 
			
		||||
// For example, if your prefix is ! you will run a command called 'cash' by typing either
 | 
			
		||||
// '!cash @Someone' if your prefixIsSuffix: false or
 | 
			
		||||
// 'cash @Someone!' if your prefixIsSuffix: true")]
 | 
			
		||||
//         public bool PrefixIsSuffix { get; set; }
 | 
			
		||||
 | 
			
		||||
    // public string Prefixed(string text) => PrefixIsSuffix
 | 
			
		||||
    //     ? text + Prefix
 | 
			
		||||
    //     : Prefix + text;
 | 
			
		||||
 | 
			
		||||
    public string Prefixed(string text)
 | 
			
		||||
        => Prefix + text;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[Cloneable]
 | 
			
		||||
public sealed partial class BlockedConfig
 | 
			
		||||
{
 | 
			
		||||
    public HashSet<string> Commands { get; set; }
 | 
			
		||||
    public HashSet<string> Modules { get; set; }
 | 
			
		||||
 | 
			
		||||
    public BlockedConfig()
 | 
			
		||||
    {
 | 
			
		||||
        Modules = new();
 | 
			
		||||
        Commands = new();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[Cloneable]
 | 
			
		||||
public partial class ColorConfig
 | 
			
		||||
{
 | 
			
		||||
    [Comment("""Color used for embed responses when command successfully executes""")]
 | 
			
		||||
    public Rgba32 Ok { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""Color used for embed responses when command has an error""")]
 | 
			
		||||
    public Rgba32 Error { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""Color used for embed responses while command is doing work or is in progress""")]
 | 
			
		||||
    public Rgba32 Pending { get; set; }
 | 
			
		||||
 | 
			
		||||
    public ColorConfig()
 | 
			
		||||
    {
 | 
			
		||||
        Ok = Rgba32.ParseHex("00e584");
 | 
			
		||||
        Error = Rgba32.ParseHex("ee281f");
 | 
			
		||||
        Pending = Rgba32.ParseHex("faa61a");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum ConsoleOutputType
 | 
			
		||||
{
 | 
			
		||||
    Normal = 0,
 | 
			
		||||
    Simple = 1,
 | 
			
		||||
    None = 2
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
namespace NadekoBot.Common.Configs;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     Base interface for available config serializers
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IConfigSeria
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Serialize the object to string
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Serialize<T>(T obj)
 | 
			
		||||
        where T : notnull;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Deserialize string data into an object of the specified type
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public T Deserialize<T>(string data);
 | 
			
		||||
}
 | 
			
		||||
@@ -47,16 +47,16 @@ public sealed class Creds : IBotCredentials
 | 
			
		||||
            
 | 
			
		||||
            Do all steps again but enable image search for the ImageSearchId
 | 
			
		||||
            """)]
 | 
			
		||||
    public GoogleApiConfig Google { get; set; }
 | 
			
		||||
    public IGoogleApiConfig Google { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""Settings for voting system for discordbots. Meant for use on global Nadeko.""")]
 | 
			
		||||
    public VotesSettings Votes { get; set; }
 | 
			
		||||
    public IVotesSettings Votes { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""
 | 
			
		||||
        Patreon auto reward system settings.
 | 
			
		||||
        go to https://www.patreon.com/portal -> my clients -> create client
 | 
			
		||||
        """)]
 | 
			
		||||
    public PatreonSettings Patreon { get; set; }
 | 
			
		||||
    public IPatreonSettings Patreon { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""Api key for sending stats to DiscordBotList.""")]
 | 
			
		||||
    public string BotListToken { get; set; }
 | 
			
		||||
@@ -81,7 +81,7 @@ public sealed class Creds : IBotCredentials
 | 
			
		||||
    public string RedisOptions { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""Database options. Don't change if you don't know what you're doing. Leave null for default values""")]
 | 
			
		||||
    public DbOptions Db { get; set; }
 | 
			
		||||
    public IDbOptions Db { get; set; }
 | 
			
		||||
 | 
			
		||||
    [Comment("""
 | 
			
		||||
        Address and port of the coordinator endpoint. Leave empty for default.
 | 
			
		||||
@@ -143,7 +143,7 @@ public sealed class Creds : IBotCredentials
 | 
			
		||||
            cmd: NadekoBot.exe
 | 
			
		||||
            args: "{0}"
 | 
			
		||||
        """)]
 | 
			
		||||
    public RestartConfig RestartCommand { get; set; }
 | 
			
		||||
    public IRestartConfig RestartCommand { get; set; }
 | 
			
		||||
 | 
			
		||||
    public Creds()
 | 
			
		||||
    {
 | 
			
		||||
@@ -153,14 +153,14 @@ public sealed class Creds : IBotCredentials
 | 
			
		||||
        OwnerIds = new List<ulong>();
 | 
			
		||||
        TotalShards = 1;
 | 
			
		||||
        GoogleApiKey = string.Empty;
 | 
			
		||||
        Votes = new(string.Empty, string.Empty, string.Empty, string.Empty);
 | 
			
		||||
        Patreon = new(string.Empty, string.Empty, string.Empty, string.Empty);
 | 
			
		||||
        Votes = new VotesSettings(string.Empty, string.Empty, string.Empty, string.Empty);
 | 
			
		||||
        Patreon = new PatreonSettings(string.Empty, string.Empty, string.Empty, string.Empty);
 | 
			
		||||
        BotListToken = string.Empty;
 | 
			
		||||
        CleverbotApiKey = string.Empty;
 | 
			
		||||
        Gpt3ApiKey = string.Empty;
 | 
			
		||||
        BotCache = BotCacheImplemenation.Memory;
 | 
			
		||||
        RedisOptions = "localhost:6379,syncTimeout=30000,responseTimeout=30000,allowAdmin=true,password=";
 | 
			
		||||
        Db = new()
 | 
			
		||||
        Db = new DbOptions()
 | 
			
		||||
        {
 | 
			
		||||
            Type = "sqlite",
 | 
			
		||||
            ConnectionString = "Data Source=data/NadekoBot.db"
 | 
			
		||||
@@ -168,12 +168,12 @@ public sealed class Creds : IBotCredentials
 | 
			
		||||
 | 
			
		||||
        CoordinatorUrl = "http://localhost:3442";
 | 
			
		||||
 | 
			
		||||
        RestartCommand = new();
 | 
			
		||||
        Google = new();
 | 
			
		||||
        RestartCommand = new RestartConfig();
 | 
			
		||||
        Google = new GoogleApiConfig();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    public class DbOptions
 | 
			
		||||
        : IDbOptions
 | 
			
		||||
    {
 | 
			
		||||
        [Comment("""
 | 
			
		||||
            Database type. "sqlite", "mysql" and "postgresql" are supported.
 | 
			
		||||
@@ -191,7 +191,7 @@ public sealed class Creds : IBotCredentials
 | 
			
		||||
        public string ConnectionString { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public sealed record PatreonSettings
 | 
			
		||||
    public sealed record PatreonSettings : IPatreonSettings
 | 
			
		||||
    {
 | 
			
		||||
        public string ClientId { get; set; }
 | 
			
		||||
        public string AccessToken { get; set; }
 | 
			
		||||
@@ -219,7 +219,7 @@ public sealed class Creds : IBotCredentials
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public sealed record VotesSettings
 | 
			
		||||
    public sealed record VotesSettings : IVotesSettings
 | 
			
		||||
    {
 | 
			
		||||
        [Comment("""
 | 
			
		||||
            top.gg votes service url
 | 
			
		||||
@@ -265,14 +265,11 @@ public sealed class Creds : IBotCredentials
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class GoogleApiConfig
 | 
			
		||||
public class GoogleApiConfig : IGoogleApiConfig
 | 
			
		||||
{
 | 
			
		||||
    public string SearchId { get; init; }
 | 
			
		||||
    public string ImageSearchId { get; init; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum BotCacheImplemenation
 | 
			
		||||
{
 | 
			
		||||
    Memory,
 | 
			
		||||
    Redis
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public interface IBotCredentials
 | 
			
		||||
{
 | 
			
		||||
    string Token { get; }
 | 
			
		||||
    string GoogleApiKey { get; }
 | 
			
		||||
    ICollection<ulong> OwnerIds { get; }
 | 
			
		||||
    bool UsePrivilegedIntents { get; }
 | 
			
		||||
    string RapidApiKey { get; }
 | 
			
		||||
 | 
			
		||||
    Creds.DbOptions Db { get; }
 | 
			
		||||
    string OsuApiKey { get; }
 | 
			
		||||
    int TotalShards { get; }
 | 
			
		||||
    Creds.PatreonSettings Patreon { get; }
 | 
			
		||||
    string CleverbotApiKey { get; }
 | 
			
		||||
    string Gpt3ApiKey { get; }
 | 
			
		||||
    RestartConfig RestartCommand { get; }
 | 
			
		||||
    Creds.VotesSettings Votes { get; }
 | 
			
		||||
    string BotListToken { get; }
 | 
			
		||||
    string RedisOptions { get; }
 | 
			
		||||
    string LocationIqApiKey { get; }
 | 
			
		||||
    string TimezoneDbApiKey { get; }
 | 
			
		||||
    string CoinmarketcapApiKey { get; }
 | 
			
		||||
    string TrovoClientId { get; }
 | 
			
		||||
    string CoordinatorUrl { get; set; }
 | 
			
		||||
    string TwitchClientId { get; set; }
 | 
			
		||||
    string TwitchClientSecret { get; set; }
 | 
			
		||||
    GoogleApiConfig Google { get; set; }
 | 
			
		||||
    BotCacheImplemenation BotCache { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class RestartConfig
 | 
			
		||||
{
 | 
			
		||||
    public string Cmd { get; set; }
 | 
			
		||||
    public string Args { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Common;
 | 
			
		||||
 | 
			
		||||
public interface ICloneable<T>
 | 
			
		||||
    where T : new()
 | 
			
		||||
{
 | 
			
		||||
    public T Clone();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Common;
 | 
			
		||||
 | 
			
		||||
public interface ILogCommandService
 | 
			
		||||
{
 | 
			
		||||
    void AddDeleteIgnore(ulong xId);
 | 
			
		||||
    Task LogServer(ulong guildId, ulong channelId, bool actionValue);
 | 
			
		||||
    bool LogIgnore(ulong guildId, ulong itemId, IgnoredItemType itemType);
 | 
			
		||||
    LogSetting? GetGuildLogSettings(ulong guildId);
 | 
			
		||||
    bool Log(ulong guildId, ulong? channelId, LogType type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum LogType
 | 
			
		||||
{
 | 
			
		||||
    Other,
 | 
			
		||||
    MessageUpdated,
 | 
			
		||||
    MessageDeleted,
 | 
			
		||||
    UserJoined,
 | 
			
		||||
    UserLeft,
 | 
			
		||||
    UserBanned,
 | 
			
		||||
    UserUnbanned,
 | 
			
		||||
    UserUpdated,
 | 
			
		||||
    ChannelCreated,
 | 
			
		||||
    ChannelDestroyed,
 | 
			
		||||
    ChannelUpdated,
 | 
			
		||||
    UserPresence,
 | 
			
		||||
    VoicePresence,
 | 
			
		||||
    VoicePresenceTts,
 | 
			
		||||
    UserMuted,
 | 
			
		||||
    UserWarned,
 | 
			
		||||
    
 | 
			
		||||
    ThreadDeleted,
 | 
			
		||||
    ThreadCreated
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public interface INadekoInteractionService
 | 
			
		||||
{
 | 
			
		||||
    public NadekoInteraction Create<T>(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        SimpleInteraction<T> inter);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,82 +0,0 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public sealed class NadekoInteraction
 | 
			
		||||
{
 | 
			
		||||
    private readonly ulong _authorId;
 | 
			
		||||
    private readonly ButtonBuilder _button;
 | 
			
		||||
    private readonly Func<SocketMessageComponent, Task> _onClick;
 | 
			
		||||
    private readonly bool _onlyAuthor;
 | 
			
		||||
    public DiscordSocketClient Client { get; }
 | 
			
		||||
 | 
			
		||||
    private readonly TaskCompletionSource<bool> _interactionCompletedSource;
 | 
			
		||||
 | 
			
		||||
    private IUserMessage message = null!;
 | 
			
		||||
 | 
			
		||||
    public NadekoInteraction(DiscordSocketClient client,
 | 
			
		||||
        ulong authorId,
 | 
			
		||||
        ButtonBuilder button,
 | 
			
		||||
        Func<SocketMessageComponent, Task> onClick,
 | 
			
		||||
        bool onlyAuthor)
 | 
			
		||||
    {
 | 
			
		||||
        _authorId = authorId;
 | 
			
		||||
        _button = button;
 | 
			
		||||
        _onClick = onClick;
 | 
			
		||||
        _onlyAuthor = onlyAuthor;
 | 
			
		||||
        _interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
 | 
			
		||||
        
 | 
			
		||||
        Client = client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task RunAsync(IUserMessage msg)
 | 
			
		||||
    {
 | 
			
		||||
        message = msg;
 | 
			
		||||
 | 
			
		||||
        Client.InteractionCreated += OnInteraction;
 | 
			
		||||
        await Task.WhenAny(Task.Delay(15_000), _interactionCompletedSource.Task);
 | 
			
		||||
        Client.InteractionCreated -= OnInteraction;
 | 
			
		||||
 | 
			
		||||
        await msg.ModifyAsync(m => m.Components = new ComponentBuilder().Build());
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    private Task OnInteraction(SocketInteraction arg)
 | 
			
		||||
    {
 | 
			
		||||
        if (arg is not SocketMessageComponent smc)
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
        if (smc.Message.Id != message.Id)
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
        if (_onlyAuthor && smc.User.Id != _authorId)
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
        if (smc.Data.CustomId != _button.CustomId)
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
        _ = Task.Run(async () =>
 | 
			
		||||
        {
 | 
			
		||||
            await ExecuteOnActionAsync(smc);
 | 
			
		||||
            
 | 
			
		||||
            // this should only be a thing on single-response buttons
 | 
			
		||||
            _interactionCompletedSource.TrySetResult(true);
 | 
			
		||||
 | 
			
		||||
            if (!smc.HasResponded)
 | 
			
		||||
            {
 | 
			
		||||
                await smc.DeferAsync();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public MessageComponent CreateComponent()
 | 
			
		||||
    {
 | 
			
		||||
        var comp = new ComponentBuilder()
 | 
			
		||||
            .WithButton(_button);
 | 
			
		||||
 | 
			
		||||
        return comp.Build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task ExecuteOnActionAsync(SocketMessageComponent smc)
 | 
			
		||||
        => _onClick(smc);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents essential interacation data
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="Emote">Emote which will show on a button</param>
 | 
			
		||||
/// <param name="CustomId">Custom interaction id</param>
 | 
			
		||||
public record NadekoInteractionData(IEmote Emote, string CustomId, string? Text = null);
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public class NadekoInteractionService : INadekoInteractionService, INService
 | 
			
		||||
{
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
 | 
			
		||||
    public NadekoInteractionService(DiscordSocketClient client)
 | 
			
		||||
    {
 | 
			
		||||
        _client = client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NadekoInteraction Create<T>(
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        SimpleInteraction<T> inter)
 | 
			
		||||
        => new NadekoInteraction(_client,
 | 
			
		||||
            userId,
 | 
			
		||||
            inter.Button,
 | 
			
		||||
            inter.TriggerAsync,
 | 
			
		||||
            onlyAuthor: true);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public class SimpleInteraction<T>
 | 
			
		||||
{
 | 
			
		||||
    public ButtonBuilder Button { get; }
 | 
			
		||||
    private readonly Func<SocketMessageComponent, T, Task> _onClick;
 | 
			
		||||
    private readonly T? _state;
 | 
			
		||||
 | 
			
		||||
    public SimpleInteraction(ButtonBuilder button, Func<SocketMessageComponent, T?, Task> onClick, T? state = default)
 | 
			
		||||
    {
 | 
			
		||||
        Button = button;
 | 
			
		||||
        _onClick = onClick;
 | 
			
		||||
        _state = state;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task TriggerAsync(SocketMessageComponent smc)
 | 
			
		||||
    {
 | 
			
		||||
        await _onClick(smc, _state!);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -915,21 +915,4 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
 | 
			
		||||
Command: {m.Name}
 | 
			
		||||
ParamName: {pi.Name}
 | 
			
		||||
ParamType: {pi.ParameterType.Name}";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum MedusaLoadResult
 | 
			
		||||
{
 | 
			
		||||
    Success,
 | 
			
		||||
    NotFound,
 | 
			
		||||
    AlreadyLoaded,
 | 
			
		||||
    Empty,
 | 
			
		||||
    UnknownError,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum MedusaUnloadResult
 | 
			
		||||
{
 | 
			
		||||
    Success,
 | 
			
		||||
    NotLoaded,
 | 
			
		||||
    PossiblyUnable,
 | 
			
		||||
    NotFound,
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
namespace NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     Executed if no command was found for this message
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IExecNoCommand
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Executed at the end of the lifecycle if no command was found
 | 
			
		||||
    /// <see cref="IExecOnMessage"/> →
 | 
			
		||||
    /// <see cref="IInputTransformer"/> →
 | 
			
		||||
    /// <see cref="IExecPreCommand"/> →
 | 
			
		||||
    /// [<see cref="IExecPostCommand"/> | *<see cref="IExecNoCommand"/>*]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="guild"></param>
 | 
			
		||||
    /// <param name="msg"></param>
 | 
			
		||||
    /// <returns>A task representing completion</returns>
 | 
			
		||||
    Task ExecOnNoCommandAsync(IGuild guild, IUserMessage msg);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
namespace NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     Implemented by modules to handle non-bot messages received
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IExecOnMessage
 | 
			
		||||
{
 | 
			
		||||
    int Priority { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Ran after a non-bot message was received
 | 
			
		||||
    /// *<see cref="IExecOnMessage"/>* →
 | 
			
		||||
    /// <see cref="IInputTransformer"/> →
 | 
			
		||||
    /// <see cref="IExecPreCommand"/> →
 | 
			
		||||
    /// [<see cref="IExecPostCommand"/> | <see cref="IExecNoCommand"/>]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="guild">Guild where the message was sent</param>
 | 
			
		||||
    /// <param name="msg">The message that was received</param>
 | 
			
		||||
    /// <returns>Whether further processing of this message should be blocked</returns>
 | 
			
		||||
    Task<bool> ExecOnMessageAsync(IGuild guild, IUserMessage msg);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
namespace NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// This interface's method is executed after the command successfully finished execution.
 | 
			
		||||
/// ***There is no support for this method in NadekoBot services.***
 | 
			
		||||
/// It is only meant to be used in medusa system
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IExecPostCommand
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Executed after a command was successfully executed
 | 
			
		||||
    /// <see cref="IExecOnMessage"/> →
 | 
			
		||||
    /// <see cref="IInputTransformer"/> →
 | 
			
		||||
    /// <see cref="IExecPreCommand"/> →
 | 
			
		||||
    /// [*<see cref="IExecPostCommand"/>* | <see cref="IExecNoCommand"/>]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="ctx">Command context</param>
 | 
			
		||||
    /// <param name="moduleName">Module name</param>
 | 
			
		||||
    /// <param name="commandName">Command name</param>
 | 
			
		||||
    /// <returns>A task representing completion</returns>
 | 
			
		||||
    ValueTask ExecPostCommandAsync(ICommandContext ctx, string moduleName, string commandName);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,25 +0,0 @@
 | 
			
		||||
namespace NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// This interface's method is executed after a command was found but before it was executed.
 | 
			
		||||
/// Able to block further processing of a command
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IExecPreCommand
 | 
			
		||||
{
 | 
			
		||||
    public int Priority { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <para>
 | 
			
		||||
    /// Ran after a command was found but before execution.
 | 
			
		||||
    /// </para>
 | 
			
		||||
    /// <see cref="IExecOnMessage"/> →
 | 
			
		||||
    /// <see cref="IInputTransformer"/> →
 | 
			
		||||
    /// *<see cref="IExecPreCommand"/>* →
 | 
			
		||||
    /// [<see cref="IExecPostCommand"/> | <see cref="IExecNoCommand"/>]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="context">Command context</param>
 | 
			
		||||
    /// <param name="moduleName">Name of the module</param>
 | 
			
		||||
    /// <param name="command">Command info</param>
 | 
			
		||||
    /// <returns>Whether further processing of the command is blocked</returns>
 | 
			
		||||
    Task<bool> ExecPreCommandAsync(ICommandContext context, string moduleName, CommandInfo command);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,25 +0,0 @@
 | 
			
		||||
namespace NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Implemented by services which may transform input before a command is searched for
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IInputTransformer
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Ran after a non-bot message was received
 | 
			
		||||
    /// <see cref="IExecOnMessage"/> ->
 | 
			
		||||
    /// *<see cref="IInputTransformer"/>* ->
 | 
			
		||||
    /// <see cref="IExecPreCommand"/> ->
 | 
			
		||||
    /// [<see cref="IExecPostCommand"/> OR <see cref="IExecNoCommand"/>]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="guild">Guild</param>
 | 
			
		||||
    /// <param name="channel">Channel in which the message was sent</param>
 | 
			
		||||
    /// <param name="user">User who sent the message</param>
 | 
			
		||||
    /// <param name="input">Content of the message</param>
 | 
			
		||||
    /// <returns>New input, if any, otherwise null</returns>
 | 
			
		||||
    Task<string?> TransformInput(
 | 
			
		||||
        IGuild guild,
 | 
			
		||||
        IMessageChannel channel,
 | 
			
		||||
        IUser user,
 | 
			
		||||
        string input);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
namespace NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     All services which need to execute something after
 | 
			
		||||
///     the bot is ready should implement this interface
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IReadyExecutor
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Executed when bot is ready
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Task OnReadyAsync();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,142 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using MessageType = NadekoBot.Extensions.MessageType;
 | 
			
		||||
 | 
			
		||||
// ReSharper disable InconsistentNaming
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules;
 | 
			
		||||
 | 
			
		||||
[UsedImplicitly(ImplicitUseTargetFlags.Default
 | 
			
		||||
                | ImplicitUseTargetFlags.WithInheritors
 | 
			
		||||
                | ImplicitUseTargetFlags.WithMembers)]
 | 
			
		||||
public abstract class NadekoModule : ModuleBase
 | 
			
		||||
{
 | 
			
		||||
    protected CultureInfo Culture { get; set; }
 | 
			
		||||
 | 
			
		||||
    // Injected by Discord.net
 | 
			
		||||
    public IBotStrings Strings { get; set; }
 | 
			
		||||
    public CommandHandler _cmdHandler { get; set; }
 | 
			
		||||
    public ILocalization _localization { get; set; }
 | 
			
		||||
    public IEmbedBuilderService _eb { get; set; }
 | 
			
		||||
    public INadekoInteractionService _inter { get; set; }
 | 
			
		||||
 | 
			
		||||
    protected string prefix
 | 
			
		||||
        => _cmdHandler.GetPrefix(ctx.Guild);
 | 
			
		||||
 | 
			
		||||
    protected ICommandContext ctx
 | 
			
		||||
        => Context;
 | 
			
		||||
 | 
			
		||||
    protected override void BeforeExecute(CommandInfo command)
 | 
			
		||||
        => Culture = _localization.GetCultureInfo(ctx.Guild?.Id);
 | 
			
		||||
 | 
			
		||||
    protected string GetText(in LocStr data)
 | 
			
		||||
        => Strings.GetText(data, Culture);
 | 
			
		||||
    
 | 
			
		||||
    public Task<IUserMessage> SendErrorAsync(
 | 
			
		||||
        string title,
 | 
			
		||||
        string error,
 | 
			
		||||
        string url = null,
 | 
			
		||||
        string footer = null, 
 | 
			
		||||
        NadekoInteraction inter = null)
 | 
			
		||||
        => ctx.Channel.SendErrorAsync(_eb, title, error, url, footer);
 | 
			
		||||
    
 | 
			
		||||
    public Task<IUserMessage> SendConfirmAsync(
 | 
			
		||||
        string title,
 | 
			
		||||
        string text,
 | 
			
		||||
        string url = null,
 | 
			
		||||
        string footer = null)
 | 
			
		||||
        => ctx.Channel.SendConfirmAsync(_eb, title, text, url, footer);
 | 
			
		||||
 | 
			
		||||
    // 
 | 
			
		||||
    public Task<IUserMessage> SendErrorAsync(string text, NadekoInteraction inter = null)
 | 
			
		||||
        => ctx.Channel.SendAsync(_eb, text, MessageType.Error, inter);
 | 
			
		||||
    public Task<IUserMessage> SendConfirmAsync(string text, NadekoInteraction inter = null)
 | 
			
		||||
        => ctx.Channel.SendAsync(_eb, text, MessageType.Ok, inter);
 | 
			
		||||
    public Task<IUserMessage> SendPendingAsync(string text, NadekoInteraction inter = null)
 | 
			
		||||
        => ctx.Channel.SendAsync(_eb, text, MessageType.Pending, inter);
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    // localized normal
 | 
			
		||||
    public Task<IUserMessage> ErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendErrorAsync(GetText(str), inter);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> PendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendPendingAsync(GetText(str), inter);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> ConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendConfirmAsync(GetText(str), inter);
 | 
			
		||||
 | 
			
		||||
    // localized replies
 | 
			
		||||
    public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> PromptUserConfirmAsync(IEmbedBuilder embed)
 | 
			
		||||
    {
 | 
			
		||||
        embed.WithPendingColor().WithFooter("yes/no");
 | 
			
		||||
 | 
			
		||||
        var msg = await ctx.Channel.EmbedAsync(embed);
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var input = await GetUserInputAsync(ctx.User.Id, ctx.Channel.Id);
 | 
			
		||||
            input = input?.ToUpperInvariant();
 | 
			
		||||
 | 
			
		||||
            if (input != "YES" && input != "Y")
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            _ = Task.Run(() => msg.DeleteAsync());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TypeConverter typeConverter = TypeDescriptor.GetConverter(propType); ?
 | 
			
		||||
    public async Task<string> GetUserInputAsync(ulong userId, ulong channelId)
 | 
			
		||||
    {
 | 
			
		||||
        var userInputTask = new TaskCompletionSource<string>();
 | 
			
		||||
        var dsc = (DiscordSocketClient)ctx.Client;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            dsc.MessageReceived += MessageReceived;
 | 
			
		||||
 | 
			
		||||
            if (await Task.WhenAny(userInputTask.Task, Task.Delay(10000)) != userInputTask.Task)
 | 
			
		||||
                return null;
 | 
			
		||||
 | 
			
		||||
            return await userInputTask.Task;
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            dsc.MessageReceived -= MessageReceived;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Task MessageReceived(SocketMessage arg)
 | 
			
		||||
        {
 | 
			
		||||
            _ = Task.Run(() =>
 | 
			
		||||
            {
 | 
			
		||||
                if (arg is not SocketUserMessage userMsg
 | 
			
		||||
                    || userMsg.Channel is not ITextChannel
 | 
			
		||||
                    || userMsg.Author.Id != userId
 | 
			
		||||
                    || userMsg.Channel.Id != channelId)
 | 
			
		||||
                    return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
                if (userInputTask.TrySetResult(arg.Content))
 | 
			
		||||
                    userMsg.DeleteAfter(1);
 | 
			
		||||
 | 
			
		||||
                return Task.CompletedTask;
 | 
			
		||||
            });
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public abstract class NadekoModule<TService> : NadekoModule
 | 
			
		||||
{
 | 
			
		||||
    public TService _service { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,164 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using NadekoBot.Modules.Administration.Services;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Common;
 | 
			
		||||
 | 
			
		||||
public class ReplacementBuilder
 | 
			
		||||
{
 | 
			
		||||
    private static readonly Regex _rngRegex = new("%rng(?:(?<from>(?:-)?\\d+)-(?<to>(?:-)?\\d+))?%",
 | 
			
		||||
        RegexOptions.Compiled);
 | 
			
		||||
 | 
			
		||||
    private readonly ConcurrentDictionary<Regex, Func<Match, string>> _regex = new();
 | 
			
		||||
 | 
			
		||||
    private readonly ConcurrentDictionary<string, Func<string>> _reps = new();
 | 
			
		||||
 | 
			
		||||
    public ReplacementBuilder()
 | 
			
		||||
        => WithRngRegex();
 | 
			
		||||
 | 
			
		||||
    public ReplacementBuilder WithDefault(
 | 
			
		||||
        IUser usr,
 | 
			
		||||
        IMessageChannel ch,
 | 
			
		||||
        SocketGuild g,
 | 
			
		||||
        DiscordSocketClient client)
 | 
			
		||||
        => WithUser(usr).WithChannel(ch).WithServer(client, g).WithClient(client);
 | 
			
		||||
 | 
			
		||||
    public ReplacementBuilder WithDefault(ICommandContext ctx)
 | 
			
		||||
        => WithDefault(ctx.User, ctx.Channel, ctx.Guild as SocketGuild, (DiscordSocketClient)ctx.Client);
 | 
			
		||||
 | 
			
		||||
    public ReplacementBuilder WithMention(DiscordSocketClient client)
 | 
			
		||||
    {
 | 
			
		||||
        _reps.TryAdd("%bot.mention%", () => client.CurrentUser.Mention);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ReplacementBuilder WithClient(DiscordSocketClient client)
 | 
			
		||||
    {
 | 
			
		||||
        WithMention(client);
 | 
			
		||||
 | 
			
		||||
        _reps.TryAdd("%bot.status%", () => client.Status.ToString());
 | 
			
		||||
        _reps.TryAdd("%bot.latency%", () => client.Latency.ToString());
 | 
			
		||||
        _reps.TryAdd("%bot.name%", () => client.CurrentUser.Username);
 | 
			
		||||
        _reps.TryAdd("%bot.fullname%", () => client.CurrentUser.ToString());
 | 
			
		||||
        _reps.TryAdd("%bot.time%",
 | 
			
		||||
            () => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()));
 | 
			
		||||
        _reps.TryAdd("%bot.discrim%", () => client.CurrentUser.Discriminator);
 | 
			
		||||
        _reps.TryAdd("%bot.id%", () => client.CurrentUser.Id.ToString());
 | 
			
		||||
        _reps.TryAdd("%bot.avatar%", () => client.CurrentUser.RealAvatarUrl().ToString());
 | 
			
		||||
 | 
			
		||||
        WithStats(client);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ReplacementBuilder WithServer(DiscordSocketClient client, SocketGuild g)
 | 
			
		||||
    {
 | 
			
		||||
        _reps.TryAdd("%server%", () => g is null ? "DM" : g.Name);
 | 
			
		||||
        _reps.TryAdd("%server.id%", () => g is null ? "DM" : g.Id.ToString());
 | 
			
		||||
        _reps.TryAdd("%server.name%", () => g is null ? "DM" : g.Name);
 | 
			
		||||
        _reps.TryAdd("%server.icon%", () => g is null ? null : g.IconUrl);
 | 
			
		||||
        _reps.TryAdd("%server.members%", () => g is { } sg ? sg.MemberCount.ToString() : "?");
 | 
			
		||||
        _reps.TryAdd("%server.boosters%", () => g.PremiumSubscriptionCount.ToString());
 | 
			
		||||
        _reps.TryAdd("%server.boost_level%", () => ((int)g.PremiumTier).ToString());
 | 
			
		||||
        _reps.TryAdd("%server.time%",
 | 
			
		||||
            () =>
 | 
			
		||||
            {
 | 
			
		||||
                var to = TimeZoneInfo.Local;
 | 
			
		||||
                if (g is not null)
 | 
			
		||||
                {
 | 
			
		||||
                    if (GuildTimezoneService.AllServices.TryGetValue(client.CurrentUser.Id, out var tz))
 | 
			
		||||
                        to = tz.GetTimeZoneOrDefault(g.Id) ?? TimeZoneInfo.Local;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return TimeZoneInfo.ConvertTime(DateTime.UtcNow, TimeZoneInfo.Utc, to).ToString("HH:mm ")
 | 
			
		||||
                       + to.StandardName.GetInitials();
 | 
			
		||||
            });
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ReplacementBuilder WithChannel(IMessageChannel ch)
 | 
			
		||||
    {
 | 
			
		||||
        _reps.TryAdd("%channel%", () => ch.Name);
 | 
			
		||||
        _reps.TryAdd("%channel.mention%", () => (ch as ITextChannel)?.Mention ?? "#" + ch.Name);
 | 
			
		||||
        _reps.TryAdd("%channel.name%", () => ch.Name);
 | 
			
		||||
        _reps.TryAdd("%channel.id%", () => ch.Id.ToString());
 | 
			
		||||
        _reps.TryAdd("%channel.created%", () => ch.CreatedAt.ToString("HH:mm dd.MM.yyyy"));
 | 
			
		||||
        _reps.TryAdd("%channel.nsfw%", () => (ch as ITextChannel)?.IsNsfw.ToString() ?? "-");
 | 
			
		||||
        _reps.TryAdd("%channel.topic%", () => (ch as ITextChannel)?.Topic ?? "-");
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ReplacementBuilder WithUser(IUser user)
 | 
			
		||||
    {
 | 
			
		||||
        WithManyUsers(new[] { user });
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ReplacementBuilder WithManyUsers(IEnumerable<IUser> users)
 | 
			
		||||
    {
 | 
			
		||||
        _reps.TryAdd("%user%", () => string.Join(" ", users.Select(user => user.Mention)));
 | 
			
		||||
        _reps.TryAdd("%user.mention%", () => string.Join(" ", users.Select(user => user.Mention)));
 | 
			
		||||
        _reps.TryAdd("%user.fullname%", () => string.Join(" ", users.Select(user => user.ToString())));
 | 
			
		||||
        _reps.TryAdd("%user.name%", () => string.Join(" ", users.Select(user => user.Username)));
 | 
			
		||||
        _reps.TryAdd("%user.discrim%", () => string.Join(" ", users.Select(user => user.Discriminator)));
 | 
			
		||||
        _reps.TryAdd("%user.avatar%", () => string.Join(" ", users.Select(user => user.RealAvatarUrl().ToString())));
 | 
			
		||||
        _reps.TryAdd("%user.id%", () => string.Join(" ", users.Select(user => user.Id.ToString())));
 | 
			
		||||
        _reps.TryAdd("%user.created_time%",
 | 
			
		||||
            () => string.Join(" ", users.Select(user => user.CreatedAt.ToString("HH:mm"))));
 | 
			
		||||
        _reps.TryAdd("%user.created_date%",
 | 
			
		||||
            () => string.Join(" ", users.Select(user => user.CreatedAt.ToString("dd.MM.yyyy"))));
 | 
			
		||||
        _reps.TryAdd("%user.joined_time%",
 | 
			
		||||
            () => string.Join(" ", users.Select(user => (user as IGuildUser)?.JoinedAt?.ToString("HH:mm") ?? "-")));
 | 
			
		||||
        _reps.TryAdd("%user.joined_date%",
 | 
			
		||||
            () => string.Join(" ",
 | 
			
		||||
                users.Select(user => (user as IGuildUser)?.JoinedAt?.ToString("dd.MM.yyyy") ?? "-")));
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ReplacementBuilder WithStats(DiscordSocketClient c)
 | 
			
		||||
    {
 | 
			
		||||
        _reps.TryAdd("%shard.servercount%", () => c.Guilds.Count.ToString());
 | 
			
		||||
        _reps.TryAdd("%shard.usercount%", () => c.Guilds.Sum(g => g.MemberCount).ToString());
 | 
			
		||||
        _reps.TryAdd("%shard.id%", () => c.ShardId.ToString());
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ReplacementBuilder WithRngRegex()
 | 
			
		||||
    {
 | 
			
		||||
        var rng = new NadekoRandom();
 | 
			
		||||
        _regex.TryAdd(_rngRegex,
 | 
			
		||||
            match =>
 | 
			
		||||
            {
 | 
			
		||||
                if (!int.TryParse(match.Groups["from"].ToString(), out var from))
 | 
			
		||||
                    from = 0;
 | 
			
		||||
                if (!int.TryParse(match.Groups["to"].ToString(), out var to))
 | 
			
		||||
                    to = 0;
 | 
			
		||||
 | 
			
		||||
                if (from == 0 && to == 0)
 | 
			
		||||
                    return rng.Next(0, 11).ToString();
 | 
			
		||||
 | 
			
		||||
                if (from >= to)
 | 
			
		||||
                    return string.Empty;
 | 
			
		||||
 | 
			
		||||
                return rng.Next(from, to + 1).ToString();
 | 
			
		||||
            });
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ReplacementBuilder WithOverride(string key, Func<string> output)
 | 
			
		||||
    {
 | 
			
		||||
        _reps.AddOrUpdate(key, output, delegate { return output; });
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Replacer Build()
 | 
			
		||||
        => new(_reps.Select(x => (x.Key, x.Value)).ToArray(), _regex.Select(x => (x.Key, x.Value)).ToArray());
 | 
			
		||||
 | 
			
		||||
    public ReplacementBuilder WithProviders(IEnumerable<IPlaceholderProvider> phProviders)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var provider in phProviders)
 | 
			
		||||
        foreach (var ovr in provider.GetPlaceholders())
 | 
			
		||||
            _reps.TryAdd(ovr.Name, ovr.Func);
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,94 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using Nadeko.Common;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Common;
 | 
			
		||||
 | 
			
		||||
public class Replacer
 | 
			
		||||
{
 | 
			
		||||
    private readonly IEnumerable<(Regex Regex, Func<Match, string> Replacement)> _regex;
 | 
			
		||||
    private readonly IEnumerable<(string Key, Func<string> Text)> _replacements;
 | 
			
		||||
 | 
			
		||||
    public Replacer(IEnumerable<(string, Func<string>)> replacements, IEnumerable<(Regex, Func<Match, string>)> regex)
 | 
			
		||||
    {
 | 
			
		||||
        _replacements = replacements;
 | 
			
		||||
        _regex = regex;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public string Replace(string input)
 | 
			
		||||
    {
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(input))
 | 
			
		||||
            return input;
 | 
			
		||||
 | 
			
		||||
        foreach (var (key, text) in _replacements)
 | 
			
		||||
        {
 | 
			
		||||
            if (input.Contains(key))
 | 
			
		||||
                input = input.Replace(key, text(), StringComparison.InvariantCulture);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach (var item in _regex)
 | 
			
		||||
            input = item.Regex.Replace(input, m => item.Replacement(m));
 | 
			
		||||
 | 
			
		||||
        return input;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public SmartText Replace(SmartText data)
 | 
			
		||||
        => data switch
 | 
			
		||||
        {
 | 
			
		||||
            SmartEmbedText embedData => Replace(embedData) with
 | 
			
		||||
            {
 | 
			
		||||
                PlainText = Replace(embedData.PlainText),
 | 
			
		||||
                Color = embedData.Color
 | 
			
		||||
            },
 | 
			
		||||
            SmartPlainText plain => Replace(plain),
 | 
			
		||||
            SmartEmbedTextArray arr => Replace(arr), 
 | 
			
		||||
            _ => throw new ArgumentOutOfRangeException(nameof(data), "Unsupported argument type")
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    private SmartEmbedTextArray Replace(SmartEmbedTextArray embedArr)
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            Embeds = embedArr.Embeds.Map(e => Replace(e) with
 | 
			
		||||
            {
 | 
			
		||||
                Color = e.Color
 | 
			
		||||
            }),
 | 
			
		||||
            Content = Replace(embedArr.Content)
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    private SmartPlainText Replace(SmartPlainText plain)
 | 
			
		||||
        => Replace(plain.Text);
 | 
			
		||||
 | 
			
		||||
    private T Replace<T>(T embedData) where T: SmartEmbedTextBase, new()
 | 
			
		||||
    {
 | 
			
		||||
        var newEmbedData = new T
 | 
			
		||||
        {
 | 
			
		||||
            Description = Replace(embedData.Description),
 | 
			
		||||
            Title = Replace(embedData.Title),
 | 
			
		||||
            Thumbnail = Replace(embedData.Thumbnail),
 | 
			
		||||
            Image = Replace(embedData.Image),
 | 
			
		||||
            Url = Replace(embedData.Url),
 | 
			
		||||
            Author = embedData.Author is null
 | 
			
		||||
                ? null
 | 
			
		||||
                : new()
 | 
			
		||||
                {
 | 
			
		||||
                    Name = Replace(embedData.Author.Name),
 | 
			
		||||
                    IconUrl = Replace(embedData.Author.IconUrl)
 | 
			
		||||
                },
 | 
			
		||||
            Fields = embedData.Fields?.Map(f => new SmartTextEmbedField
 | 
			
		||||
            {
 | 
			
		||||
                Name = Replace(f.Name),
 | 
			
		||||
                Value = Replace(f.Value),
 | 
			
		||||
                Inline = f.Inline
 | 
			
		||||
            }),
 | 
			
		||||
            Footer = embedData.Footer is null
 | 
			
		||||
                ? null
 | 
			
		||||
                : new()
 | 
			
		||||
                {
 | 
			
		||||
                    Text = Replace(embedData.Footer.Text),
 | 
			
		||||
                    IconUrl = Replace(embedData.Footer.IconUrl)
 | 
			
		||||
                }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return newEmbedData;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,182 +0,0 @@
 | 
			
		||||
#nullable disable warnings
 | 
			
		||||
using SixLabors.ImageSharp.PixelFormats;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public sealed record SmartEmbedArrayElementText : SmartEmbedTextBase
 | 
			
		||||
{
 | 
			
		||||
    public string Color { get; init; } = string.Empty;
 | 
			
		||||
 | 
			
		||||
    public SmartEmbedArrayElementText() : base()
 | 
			
		||||
    {
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public SmartEmbedArrayElementText(IEmbed eb) : base(eb)
 | 
			
		||||
    {
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override EmbedBuilder GetEmbedInternal()
 | 
			
		||||
    {
 | 
			
		||||
        var embed = base.GetEmbedInternal();
 | 
			
		||||
        if (Rgba32.TryParseHex(Color, out var color))
 | 
			
		||||
            return embed.WithColor(color.ToDiscordColor());
 | 
			
		||||
 | 
			
		||||
        return embed;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public sealed record SmartEmbedText : SmartEmbedTextBase
 | 
			
		||||
{
 | 
			
		||||
    public string PlainText { get; init; }
 | 
			
		||||
 | 
			
		||||
    public uint Color { get; init; } = 7458112;
 | 
			
		||||
 | 
			
		||||
    public SmartEmbedText()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private SmartEmbedText(IEmbed eb, string? plainText = null)
 | 
			
		||||
        : base(eb)
 | 
			
		||||
        => (PlainText, Color) = (plainText, eb.Color?.RawValue ?? 0);
 | 
			
		||||
 | 
			
		||||
    public static SmartEmbedText FromEmbed(IEmbed eb, string? plainText = null)
 | 
			
		||||
        => new(eb, plainText);
 | 
			
		||||
 | 
			
		||||
    protected override EmbedBuilder GetEmbedInternal()
 | 
			
		||||
    {
 | 
			
		||||
        var embed = base.GetEmbedInternal();
 | 
			
		||||
        return embed.WithColor(Color);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public abstract record SmartEmbedTextBase : SmartText
 | 
			
		||||
{
 | 
			
		||||
    public string Title { get; init; }
 | 
			
		||||
    public string Description { get; init; }
 | 
			
		||||
    public string Url { get; init; }
 | 
			
		||||
    public string Thumbnail { get; init; }
 | 
			
		||||
    public string Image { get; init; }
 | 
			
		||||
 | 
			
		||||
    public SmartTextEmbedAuthor Author { get; init; }
 | 
			
		||||
    public SmartTextEmbedFooter Footer { get; init; }
 | 
			
		||||
    public SmartTextEmbedField[] Fields { get; init; }
 | 
			
		||||
 | 
			
		||||
    public bool IsValid
 | 
			
		||||
        => !string.IsNullOrWhiteSpace(Title)
 | 
			
		||||
           || !string.IsNullOrWhiteSpace(Description)
 | 
			
		||||
           || !string.IsNullOrWhiteSpace(Url)
 | 
			
		||||
           || !string.IsNullOrWhiteSpace(Thumbnail)
 | 
			
		||||
           || !string.IsNullOrWhiteSpace(Image)
 | 
			
		||||
           || (Footer is not null
 | 
			
		||||
               && (!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl)))
 | 
			
		||||
           || Fields is { Length: > 0 };
 | 
			
		||||
 | 
			
		||||
    protected SmartEmbedTextBase()
 | 
			
		||||
    {
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    protected SmartEmbedTextBase(IEmbed eb)
 | 
			
		||||
    {
 | 
			
		||||
        Title = eb.Title;
 | 
			
		||||
        Description = eb.Description;
 | 
			
		||||
        Url = eb.Url;
 | 
			
		||||
        Thumbnail = eb.Thumbnail?.Url;
 | 
			
		||||
        Image = eb.Image?.Url;
 | 
			
		||||
        Author = eb.Author is { } ea
 | 
			
		||||
            ? new()
 | 
			
		||||
            {
 | 
			
		||||
                Name = ea.Name,
 | 
			
		||||
                Url = ea.Url,
 | 
			
		||||
                IconUrl = ea.IconUrl
 | 
			
		||||
            }
 | 
			
		||||
            : null;
 | 
			
		||||
        Footer = eb.Footer is { } ef
 | 
			
		||||
            ? new()
 | 
			
		||||
            {
 | 
			
		||||
                Text = ef.Text,
 | 
			
		||||
                IconUrl = ef.IconUrl
 | 
			
		||||
            }
 | 
			
		||||
            : null;
 | 
			
		||||
        
 | 
			
		||||
        if (eb.Fields.Length > 0)
 | 
			
		||||
        {
 | 
			
		||||
            Fields = eb.Fields.Select(field
 | 
			
		||||
                               => new SmartTextEmbedField
 | 
			
		||||
                               {
 | 
			
		||||
                                   Inline = field.Inline,
 | 
			
		||||
                                   Name = field.Name,
 | 
			
		||||
                                   Value = field.Value
 | 
			
		||||
                               })
 | 
			
		||||
                           .ToArray();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder GetEmbed()
 | 
			
		||||
        => GetEmbedInternal();
 | 
			
		||||
    
 | 
			
		||||
    protected virtual EmbedBuilder GetEmbedInternal()
 | 
			
		||||
    {
 | 
			
		||||
        var embed = new EmbedBuilder();
 | 
			
		||||
 | 
			
		||||
        if (!string.IsNullOrWhiteSpace(Title))
 | 
			
		||||
            embed.WithTitle(Title);
 | 
			
		||||
 | 
			
		||||
        if (!string.IsNullOrWhiteSpace(Description))
 | 
			
		||||
            embed.WithDescription(Description);
 | 
			
		||||
 | 
			
		||||
        if (Url is not null && Uri.IsWellFormedUriString(Url, UriKind.Absolute))
 | 
			
		||||
            embed.WithUrl(Url);
 | 
			
		||||
 | 
			
		||||
        if (Footer is not null)
 | 
			
		||||
        {
 | 
			
		||||
            embed.WithFooter(efb =>
 | 
			
		||||
            {
 | 
			
		||||
                efb.WithText(Footer.Text);
 | 
			
		||||
                if (Uri.IsWellFormedUriString(Footer.IconUrl, UriKind.Absolute))
 | 
			
		||||
                    efb.WithIconUrl(Footer.IconUrl);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (Thumbnail is not null && Uri.IsWellFormedUriString(Thumbnail, UriKind.Absolute))
 | 
			
		||||
            embed.WithThumbnailUrl(Thumbnail);
 | 
			
		||||
 | 
			
		||||
        if (Image is not null && Uri.IsWellFormedUriString(Image, UriKind.Absolute))
 | 
			
		||||
            embed.WithImageUrl(Image);
 | 
			
		||||
 | 
			
		||||
        if (Author is not null && !string.IsNullOrWhiteSpace(Author.Name))
 | 
			
		||||
        {
 | 
			
		||||
            if (!Uri.IsWellFormedUriString(Author.IconUrl, UriKind.Absolute))
 | 
			
		||||
                Author.IconUrl = null;
 | 
			
		||||
            if (!Uri.IsWellFormedUriString(Author.Url, UriKind.Absolute))
 | 
			
		||||
                Author.Url = null;
 | 
			
		||||
 | 
			
		||||
            embed.WithAuthor(Author.Name, Author.IconUrl, Author.Url);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (Fields is not null)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var f in Fields)
 | 
			
		||||
            {
 | 
			
		||||
                if (!string.IsNullOrWhiteSpace(f.Name) && !string.IsNullOrWhiteSpace(f.Value))
 | 
			
		||||
                    embed.AddField(f.Name, f.Value, f.Inline);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return embed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void NormalizeFields()
 | 
			
		||||
    {
 | 
			
		||||
        if (Fields is { Length: > 0 })
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var f in Fields)
 | 
			
		||||
            {
 | 
			
		||||
                f.Name = f.Name.TrimTo(256);
 | 
			
		||||
                f.Value = f.Value.TrimTo(1024);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,31 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public sealed record SmartEmbedTextArray : SmartText
 | 
			
		||||
{
 | 
			
		||||
    public string Content { get; set; }
 | 
			
		||||
    public SmartEmbedArrayElementText[] Embeds { get; set; }
 | 
			
		||||
 | 
			
		||||
    public bool IsValid
 | 
			
		||||
        => Embeds?.All(x => x.IsValid) ?? false;
 | 
			
		||||
 | 
			
		||||
    public EmbedBuilder[] GetEmbedBuilders()
 | 
			
		||||
    {
 | 
			
		||||
        if (Embeds is null)
 | 
			
		||||
            return Array.Empty<EmbedBuilder>();
 | 
			
		||||
 | 
			
		||||
        return Embeds
 | 
			
		||||
            .Where(x => x.IsValid)
 | 
			
		||||
            .Select(em => em.GetEmbed())
 | 
			
		||||
            .ToArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void NormalizeFields()
 | 
			
		||||
    {
 | 
			
		||||
        if (Embeds is null)
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
        foreach(var eb in Embeds)
 | 
			
		||||
            eb.NormalizeFields();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public sealed record SmartPlainText : SmartText
 | 
			
		||||
{
 | 
			
		||||
    public string Text { get; init; }
 | 
			
		||||
 | 
			
		||||
    public SmartPlainText(string text)
 | 
			
		||||
        => Text = text;
 | 
			
		||||
 | 
			
		||||
    public static implicit operator SmartPlainText(string input)
 | 
			
		||||
        => new(input);
 | 
			
		||||
 | 
			
		||||
    public static implicit operator string(SmartPlainText input)
 | 
			
		||||
        => input.Text;
 | 
			
		||||
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
        => Text;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,85 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Newtonsoft.Json.Linq;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public abstract record SmartText
 | 
			
		||||
{
 | 
			
		||||
    public bool IsEmbed
 | 
			
		||||
        => this is SmartEmbedText;
 | 
			
		||||
 | 
			
		||||
    public bool IsPlainText
 | 
			
		||||
        => this is SmartPlainText;
 | 
			
		||||
 | 
			
		||||
    public bool IsEmbedArray
 | 
			
		||||
        => this is SmartEmbedTextArray;
 | 
			
		||||
    
 | 
			
		||||
    public static SmartText operator +(SmartText text, string input)
 | 
			
		||||
        => text switch
 | 
			
		||||
        {
 | 
			
		||||
            SmartEmbedText set => set with
 | 
			
		||||
            {
 | 
			
		||||
                PlainText = set.PlainText + input
 | 
			
		||||
            },
 | 
			
		||||
            SmartPlainText spt => new SmartPlainText(spt.Text + input),
 | 
			
		||||
            SmartEmbedTextArray arr => arr with
 | 
			
		||||
            {
 | 
			
		||||
                Content = arr.Content + input
 | 
			
		||||
            },
 | 
			
		||||
            _ => throw new ArgumentOutOfRangeException(nameof(text))
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public static SmartText operator +(string input, SmartText text)
 | 
			
		||||
        => text switch
 | 
			
		||||
        {
 | 
			
		||||
            SmartEmbedText set => set with
 | 
			
		||||
            {
 | 
			
		||||
                PlainText = input + set.PlainText
 | 
			
		||||
            },
 | 
			
		||||
            SmartPlainText spt => new SmartPlainText(input + spt.Text),
 | 
			
		||||
            SmartEmbedTextArray arr => arr with
 | 
			
		||||
            {
 | 
			
		||||
                Content = input + arr.Content
 | 
			
		||||
            },
 | 
			
		||||
            _ => throw new ArgumentOutOfRangeException(nameof(text))
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public static SmartText CreateFrom(string input)
 | 
			
		||||
    {
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(input))
 | 
			
		||||
            return new SmartPlainText(input);
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var doc = JObject.Parse(input);
 | 
			
		||||
            var root = doc.Root;
 | 
			
		||||
            if (root.Type == JTokenType.Object)
 | 
			
		||||
            {
 | 
			
		||||
                if (((JObject)root).TryGetValue("embeds", out _))
 | 
			
		||||
                {
 | 
			
		||||
                    var arr = root.ToObject<SmartEmbedTextArray>();
 | 
			
		||||
 | 
			
		||||
                    if (arr is null)
 | 
			
		||||
                        return new SmartPlainText(input);
 | 
			
		||||
 | 
			
		||||
                    arr!.NormalizeFields();
 | 
			
		||||
                    return arr;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var obj = root.ToObject<SmartEmbedText>();
 | 
			
		||||
 | 
			
		||||
                if (obj is null || !(obj.IsValid || !string.IsNullOrWhiteSpace(obj.PlainText)))
 | 
			
		||||
                    return new SmartPlainText(input);
 | 
			
		||||
 | 
			
		||||
                obj.NormalizeFields();
 | 
			
		||||
                return obj;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            return new SmartPlainText(input);
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return new SmartPlainText(input);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public class SmartTextEmbedAuthor
 | 
			
		||||
{
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
 | 
			
		||||
    [JsonProperty("icon_url")]
 | 
			
		||||
    public string IconUrl { get; set; }
 | 
			
		||||
 | 
			
		||||
    public string Url { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public class SmartTextEmbedField
 | 
			
		||||
{
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
    public string Value { get; set; }
 | 
			
		||||
    public bool Inline { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
public class SmartTextEmbedFooter
 | 
			
		||||
{
 | 
			
		||||
    public string Text { get; set; }
 | 
			
		||||
 | 
			
		||||
    [JsonProperty("icon_url")]
 | 
			
		||||
    public string IconUrl { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,48 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Common.Yml;
 | 
			
		||||
 | 
			
		||||
public class YamlHelper
 | 
			
		||||
{
 | 
			
		||||
    // https://github.com/aaubry/YamlDotNet/blob/0f4cc205e8b2dd8ef6589d96de32bf608a687c6f/YamlDotNet/Core/Scanner.cs#L1687
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     This is modified code from yamldotnet's repo which handles parsing unicode code points
 | 
			
		||||
    ///     it is needed as yamldotnet doesn't support unescaped unicode characters
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="point">Unicode code point</param>
 | 
			
		||||
    /// <returns>Actual character</returns>
 | 
			
		||||
    public static string UnescapeUnicodeCodePoint(string point)
 | 
			
		||||
    {
 | 
			
		||||
        var character = 0;
 | 
			
		||||
 | 
			
		||||
        // Scan the character value.
 | 
			
		||||
 | 
			
		||||
        foreach (var c in point)
 | 
			
		||||
        {
 | 
			
		||||
            if (!IsHex(c))
 | 
			
		||||
                return point;
 | 
			
		||||
 | 
			
		||||
            character = (character << 4) + AsHex(c);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check the value and write the character.
 | 
			
		||||
 | 
			
		||||
        if (character is (>= 0xD800 and <= 0xDFFF) or > 0x10FFFF)
 | 
			
		||||
            return point;
 | 
			
		||||
 | 
			
		||||
        return char.ConvertFromUtf32(character);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static bool IsHex(char c)
 | 
			
		||||
        => c is (>= '0' and <= '9') or (>= 'A' and <= 'F') or (>= 'a' and <= 'f');
 | 
			
		||||
 | 
			
		||||
    public static int AsHex(char c)
 | 
			
		||||
    {
 | 
			
		||||
        if (c <= '9')
 | 
			
		||||
            return c - '0';
 | 
			
		||||
 | 
			
		||||
        if (c <= 'F')
 | 
			
		||||
            return c - 'A' + 10;
 | 
			
		||||
 | 
			
		||||
        return c - 'a' + 10;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
#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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services.Database.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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services.Database.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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,130 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using LinqToDB;
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
using NadekoBot.Services.Database;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class DiscordUserExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static Task<DiscordUser> GetByUserIdAsync(
 | 
			
		||||
        this IQueryable<DiscordUser> set,
 | 
			
		||||
        ulong userId)
 | 
			
		||||
        => set.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId);
 | 
			
		||||
    
 | 
			
		||||
    public static void EnsureUserCreated(
 | 
			
		||||
        this NadekoContext ctx,
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        string username,
 | 
			
		||||
        string discrim,
 | 
			
		||||
        string avatarId)
 | 
			
		||||
        => ctx.DiscordUser.ToLinqToDBTable()
 | 
			
		||||
              .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 NadekoContext ctx,
 | 
			
		||||
        ulong userId)
 | 
			
		||||
        => ctx.DiscordUser
 | 
			
		||||
              .ToLinqToDBTable()
 | 
			
		||||
              .InsertOrUpdateAsync(
 | 
			
		||||
                  () => new()
 | 
			
		||||
                  {
 | 
			
		||||
                      UserId = userId,
 | 
			
		||||
                      Username = "Unknown",
 | 
			
		||||
                      Discriminator = "????",
 | 
			
		||||
                      AvatarId = string.Empty,
 | 
			
		||||
                      TotalXp = 0,
 | 
			
		||||
                      CurrencyAmount = 0
 | 
			
		||||
                  },
 | 
			
		||||
                  old => new()
 | 
			
		||||
                  {
 | 
			
		||||
 | 
			
		||||
                  },
 | 
			
		||||
                  () => new()
 | 
			
		||||
                  {
 | 
			
		||||
                      UserId = userId
 | 
			
		||||
                  });
 | 
			
		||||
    
 | 
			
		||||
    //temp is only used in updatecurrencystate, so that i don't overwrite real usernames/discrims with Unknown
 | 
			
		||||
    public static DiscordUser GetOrCreateUser(
 | 
			
		||||
        this NadekoContext ctx,
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        string username,
 | 
			
		||||
        string discrim,
 | 
			
		||||
        string avatarId,
 | 
			
		||||
        Func<IQueryable<DiscordUser>, IQueryable<DiscordUser>> includes = null)
 | 
			
		||||
    {
 | 
			
		||||
        ctx.EnsureUserCreated(userId, username, discrim, avatarId);
 | 
			
		||||
 | 
			
		||||
        IQueryable<DiscordUser> queryable = ctx.DiscordUser;
 | 
			
		||||
        if (includes is not null)
 | 
			
		||||
            queryable = includes(queryable);
 | 
			
		||||
        return queryable.First(u => u.UserId == userId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static DiscordUser GetOrCreateUser(this NadekoContext ctx, IUser original, Func<IQueryable<DiscordUser>, IQueryable<DiscordUser>> includes = null)
 | 
			
		||||
        => ctx.GetOrCreateUser(original.Id, original.Username, original.Discriminator, original.AvatarId, includes);
 | 
			
		||||
 | 
			
		||||
    public static int GetUserGlobalRank(this DbSet<DiscordUser> users, ulong id)
 | 
			
		||||
        => users.AsQueryable()
 | 
			
		||||
                .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)
 | 
			
		||||
        => users.AsQueryable().OrderByDescending(x => x.TotalXp).Skip(page * 9).Take(9).AsEnumerable().ToArray();
 | 
			
		||||
 | 
			
		||||
    public static List<DiscordUser> GetTopRichest(
 | 
			
		||||
        this DbSet<DiscordUser> users,
 | 
			
		||||
        ulong botId,
 | 
			
		||||
        int count,
 | 
			
		||||
        int page = 0)
 | 
			
		||||
        => users.AsQueryable()
 | 
			
		||||
                .Where(c => c.CurrencyAmount > 0 && botId != c.UserId)
 | 
			
		||||
                .OrderByDescending(c => c.CurrencyAmount)
 | 
			
		||||
                .Skip(page * 9)
 | 
			
		||||
                .Take(count)
 | 
			
		||||
                .ToList();
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,227 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
using NadekoBot.Services.Database;
 | 
			
		||||
using NadekoBot.Services.Database.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 NadekoContext 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,
 | 
			
		||||
        List<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 NadekoContext ctx,
 | 
			
		||||
        ulong guildId,
 | 
			
		||||
        Func<DbSet<GuildConfig>, IQueryable<GuildConfig>> includes)
 | 
			
		||||
    {
 | 
			
		||||
        GuildConfig config;
 | 
			
		||||
 | 
			
		||||
        if (includes is null)
 | 
			
		||||
            config = ctx.GuildConfigs.IncludeEverything().FirstOrDefault(c => c.GuildId == guildId);
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            var set = includes(ctx.GuildConfigs);
 | 
			
		||||
            config = set.FirstOrDefault(c => c.GuildId == guildId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (config is null)
 | 
			
		||||
        {
 | 
			
		||||
            ctx.GuildConfigs.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 NadekoContext ctx, ulong guildId)
 | 
			
		||||
    {
 | 
			
		||||
        var logSetting = ctx.LogSettings.AsQueryable()
 | 
			
		||||
                            .Include(x => x.LogIgnores)
 | 
			
		||||
                            .Where(x => x.GuildId == guildId)
 | 
			
		||||
                            .FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
        if (logSetting is null)
 | 
			
		||||
        {
 | 
			
		||||
            ctx.LogSettings.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 NadekoContext ctx, ulong guildId)
 | 
			
		||||
    {
 | 
			
		||||
        var config = ctx.GuildConfigs.AsQueryable()
 | 
			
		||||
                        .Where(gc => gc.GuildId == guildId)
 | 
			
		||||
                        .Include(gc => gc.Permissions)
 | 
			
		||||
                        .FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
        if (config is null) // if there is no guildconfig, create new one
 | 
			
		||||
        {
 | 
			
		||||
            ctx.GuildConfigs.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 NadekoContext 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; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class MusicPlayerSettingsExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static async Task<MusicPlayerSettings> ForGuildAsync(this DbSet<MusicPlayerSettings> settings, ulong guildId)
 | 
			
		||||
    {
 | 
			
		||||
        var toReturn = await settings.AsQueryable().FirstOrDefaultAsync(x => x.GuildId == guildId);
 | 
			
		||||
 | 
			
		||||
        if (toReturn is null)
 | 
			
		||||
        {
 | 
			
		||||
            var newSettings = new MusicPlayerSettings
 | 
			
		||||
            {
 | 
			
		||||
                GuildId = guildId,
 | 
			
		||||
                PlayerRepeat = PlayerRepeatType.Queue
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            await settings.AddAsync(newSettings);
 | 
			
		||||
            return newSettings;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return toReturn;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class MusicPlaylistExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static List<MusicPlaylist> GetPlaylistsOnPage(this DbSet<MusicPlaylist> playlists, int num)
 | 
			
		||||
    {
 | 
			
		||||
        if (num < 1)
 | 
			
		||||
            throw new ArgumentOutOfRangeException(nameof(num));
 | 
			
		||||
 | 
			
		||||
        return playlists.AsQueryable().Skip((num - 1) * 20).Take(20).Include(pl => pl.Songs).ToList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static MusicPlaylist GetWithSongs(this DbSet<MusicPlaylist> playlists, int id)
 | 
			
		||||
        => playlists.Include(mpl => mpl.Songs).FirstOrDefault(mpl => mpl.Id == id);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using LinqToDB;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services.Database.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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services.Database;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class PollExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static IEnumerable<Poll> GetAllPolls(this DbSet<Poll> polls)
 | 
			
		||||
        => polls.Include(x => x.Answers).Include(x => x.Votes).ToArray();
 | 
			
		||||
 | 
			
		||||
    public static void RemovePoll(this NadekoContext ctx, int id)
 | 
			
		||||
    {
 | 
			
		||||
        var p = ctx.Poll.Include(x => x.Answers).Include(x => x.Votes).FirstOrDefault(x => x.Id == id);
 | 
			
		||||
 | 
			
		||||
        if (p is null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (p.Votes is not null)
 | 
			
		||||
        {
 | 
			
		||||
            ctx.RemoveRange(p.Votes);
 | 
			
		||||
            p.Votes.Clear();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (p.Answers is not null)
 | 
			
		||||
        {
 | 
			
		||||
            ctx.RemoveRange(p.Answers);
 | 
			
		||||
            p.Answers.Clear();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ctx.Poll.Remove(p);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,57 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services.Database.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)
 | 
			
		||||
    {
 | 
			
		||||
        var rng = new NadekoRandom();
 | 
			
		||||
        return (await quotes.AsQueryable().Where(q => q.GuildId == guildId && q.Keyword == keyword).ToListAsync())
 | 
			
		||||
               .OrderBy(_ => rng.Next())
 | 
			
		||||
               .FirstOrDefault();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static async Task<Quote> SearchQuoteKeywordTextAsync(
 | 
			
		||||
        this DbSet<Quote> quotes,
 | 
			
		||||
        ulong guildId,
 | 
			
		||||
        string keyword,
 | 
			
		||||
        string text)
 | 
			
		||||
    {
 | 
			
		||||
        var rngk = new NadekoRandom();
 | 
			
		||||
        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)))
 | 
			
		||||
                            .ToListAsync())
 | 
			
		||||
               .OrderBy(_ => rngk.Next())
 | 
			
		||||
               .FirstOrDefault();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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));
 | 
			
		||||
}
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services.Database.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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services.Database.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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,63 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using LinqToDB;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services.Database;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class UserXpExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static UserXpStats GetOrCreateUserXpStats(this NadekoContext ctx, ulong guildId, ulong userId)
 | 
			
		||||
    {
 | 
			
		||||
        var usr = ctx.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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,145 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using LinqToDB;
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
using NadekoBot.Services.Database;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public class WaifuInfoStats
 | 
			
		||||
{
 | 
			
		||||
    public int WaifuId { get; init; }
 | 
			
		||||
    public string FullName { get; init; }
 | 
			
		||||
    public long Price { get; init; }
 | 
			
		||||
    public string ClaimerName { get; init; }
 | 
			
		||||
    public string AffinityName { get; init; }
 | 
			
		||||
    public int AffinityCount { get; init; }
 | 
			
		||||
    public int DivorceCount { get; init; }
 | 
			
		||||
    public int ClaimCount { get; init; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public static class WaifuExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static WaifuInfo ByWaifuUserId(
 | 
			
		||||
        this DbSet<WaifuInfo> waifus,
 | 
			
		||||
        ulong userId,
 | 
			
		||||
        Func<DbSet<WaifuInfo>, IQueryable<WaifuInfo>> includes = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (includes is null)
 | 
			
		||||
        {
 | 
			
		||||
            return waifus.Include(wi => wi.Waifu)
 | 
			
		||||
                         .Include(wi => wi.Affinity)
 | 
			
		||||
                         .Include(wi => wi.Claimer)
 | 
			
		||||
                         .Include(wi => wi.Items)
 | 
			
		||||
                         .FirstOrDefault(wi => wi.Waifu.UserId == userId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return includes(waifus).AsQueryable().FirstOrDefault(wi => wi.Waifu.UserId == userId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static IEnumerable<WaifuLbResult> GetTop(this DbSet<WaifuInfo> waifus, int count, int skip = 0)
 | 
			
		||||
    {
 | 
			
		||||
        if (count < 0)
 | 
			
		||||
            throw new ArgumentOutOfRangeException(nameof(count));
 | 
			
		||||
        if (count == 0)
 | 
			
		||||
            return new List<WaifuLbResult>();
 | 
			
		||||
 | 
			
		||||
        return waifus.Include(wi => wi.Waifu)
 | 
			
		||||
                     .Include(wi => wi.Affinity)
 | 
			
		||||
                     .Include(wi => wi.Claimer)
 | 
			
		||||
                     .OrderByDescending(wi => wi.Price)
 | 
			
		||||
                     .Skip(skip)
 | 
			
		||||
                     .Take(count)
 | 
			
		||||
                     .Select(x => new WaifuLbResult
 | 
			
		||||
                     {
 | 
			
		||||
                         Affinity = x.Affinity == null ? null : x.Affinity.Username,
 | 
			
		||||
                         AffinityDiscrim = x.Affinity == null ? null : x.Affinity.Discriminator,
 | 
			
		||||
                         Claimer = x.Claimer == null ? null : x.Claimer.Username,
 | 
			
		||||
                         ClaimerDiscrim = x.Claimer == null ? null : x.Claimer.Discriminator,
 | 
			
		||||
                         Username = x.Waifu.Username,
 | 
			
		||||
                         Discrim = x.Waifu.Discriminator,
 | 
			
		||||
                         Price = x.Price
 | 
			
		||||
                     })
 | 
			
		||||
                     .ToList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static decimal GetTotalValue(this DbSet<WaifuInfo> waifus)
 | 
			
		||||
        => waifus.AsQueryable().Where(x => x.ClaimerId != null).Sum(x => x.Price);
 | 
			
		||||
 | 
			
		||||
    public static ulong GetWaifuUserId(this DbSet<WaifuInfo> waifus, ulong ownerId, string name)
 | 
			
		||||
        => waifus.AsQueryable()
 | 
			
		||||
                 .AsNoTracking()
 | 
			
		||||
                 .Where(x => x.Claimer.UserId == ownerId && x.Waifu.Username + "#" + x.Waifu.Discriminator == name)
 | 
			
		||||
                 .Select(x => x.Waifu.UserId)
 | 
			
		||||
                 .FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
    public static async Task<WaifuInfoStats> GetWaifuInfoAsync(this NadekoContext ctx, ulong userId)
 | 
			
		||||
    {
 | 
			
		||||
        await ctx.WaifuInfo
 | 
			
		||||
                 .ToLinqToDBTable()
 | 
			
		||||
                 .InsertOrUpdateAsync(() => new()
 | 
			
		||||
                     {
 | 
			
		||||
                         AffinityId = null,
 | 
			
		||||
                         ClaimerId = null,
 | 
			
		||||
                         Price = 1,
 | 
			
		||||
                         WaifuId = ctx.DiscordUser.Where(x => x.UserId == userId).Select(x => x.Id).First()
 | 
			
		||||
                     },
 | 
			
		||||
                     _ => new(),
 | 
			
		||||
                     () => new()
 | 
			
		||||
                     {
 | 
			
		||||
                         WaifuId = ctx.DiscordUser.Where(x => x.UserId == userId).Select(x => x.Id).First()
 | 
			
		||||
                     });
 | 
			
		||||
 | 
			
		||||
        var toReturn = ctx.WaifuInfo.AsQueryable()
 | 
			
		||||
                          .Where(w => w.WaifuId
 | 
			
		||||
                                      == ctx.Set<DiscordUser>()
 | 
			
		||||
                                            .AsQueryable()
 | 
			
		||||
                                            .Where(u => u.UserId == userId)
 | 
			
		||||
                                            .Select(u => u.Id)
 | 
			
		||||
                                            .FirstOrDefault())
 | 
			
		||||
                          .Select(w => new WaifuInfoStats
 | 
			
		||||
                          {
 | 
			
		||||
                              WaifuId = w.WaifuId,
 | 
			
		||||
                              FullName =
 | 
			
		||||
                                  ctx.Set<DiscordUser>()
 | 
			
		||||
                                     .AsQueryable()
 | 
			
		||||
                                     .Where(u => u.UserId == userId)
 | 
			
		||||
                                     .Select(u => u.Username + "#" + u.Discriminator)
 | 
			
		||||
                                     .FirstOrDefault(),
 | 
			
		||||
                              AffinityCount =
 | 
			
		||||
                                  ctx.Set<WaifuUpdate>()
 | 
			
		||||
                                     .AsQueryable()
 | 
			
		||||
                                     .Count(x => x.UserId == w.WaifuId
 | 
			
		||||
                                                 && x.UpdateType == WaifuUpdateType.AffinityChanged
 | 
			
		||||
                                                 && x.NewId != null),
 | 
			
		||||
                              AffinityName =
 | 
			
		||||
                                  ctx.Set<DiscordUser>()
 | 
			
		||||
                                     .AsQueryable()
 | 
			
		||||
                                     .Where(u => u.Id == w.AffinityId)
 | 
			
		||||
                                     .Select(u => u.Username + "#" + u.Discriminator)
 | 
			
		||||
                                     .FirstOrDefault(),
 | 
			
		||||
                              ClaimCount = ctx.WaifuInfo.AsQueryable().Count(x => x.ClaimerId == w.WaifuId),
 | 
			
		||||
                              ClaimerName =
 | 
			
		||||
                                  ctx.Set<DiscordUser>()
 | 
			
		||||
                                     .AsQueryable()
 | 
			
		||||
                                     .Where(u => u.Id == w.ClaimerId)
 | 
			
		||||
                                     .Select(u => u.Username + "#" + u.Discriminator)
 | 
			
		||||
                                     .FirstOrDefault(),
 | 
			
		||||
                              DivorceCount =
 | 
			
		||||
                                  ctx.Set<WaifuUpdate>()
 | 
			
		||||
                                     .AsQueryable()
 | 
			
		||||
                                     .Count(x => x.OldId == w.WaifuId
 | 
			
		||||
                                                 && x.NewId == null
 | 
			
		||||
                                                 && x.UpdateType == WaifuUpdateType.Claimed),
 | 
			
		||||
                              Price = w.Price,
 | 
			
		||||
                          })
 | 
			
		||||
                          .FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
        if (toReturn is null)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        return toReturn;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,60 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services.Database.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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,65 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum PunishmentAction
 | 
			
		||||
{
 | 
			
		||||
    Mute,
 | 
			
		||||
    Kick,
 | 
			
		||||
    Ban,
 | 
			
		||||
    Softban,
 | 
			
		||||
    RemoveRoles,
 | 
			
		||||
    ChatMute,
 | 
			
		||||
    VoiceMute,
 | 
			
		||||
    AddRole,
 | 
			
		||||
    Warn,
 | 
			
		||||
    TimeOut
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 : false;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class AutoPublishChannel : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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>();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class BanTemplate : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public string Text { get; set; }
 | 
			
		||||
    public int? PruneDays { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class BankUser : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public long Balance { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class BlacklistEntry : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong ItemId { get; set; }
 | 
			
		||||
    public BlacklistType Type { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum BlacklistType
 | 
			
		||||
{
 | 
			
		||||
    Server,
 | 
			
		||||
    Channel,
 | 
			
		||||
    User
 | 
			
		||||
}
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class CommandAlias : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string Trigger { get; set; }
 | 
			
		||||
    public string Mapping { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class CommandCooldown : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public int Seconds { get; set; }
 | 
			
		||||
    public string CommandName { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class DbEntity
 | 
			
		||||
{
 | 
			
		||||
    [Key]
 | 
			
		||||
    public int Id { get; set; }
 | 
			
		||||
 | 
			
		||||
    public DateTime? DateAdded { get; set; } = DateTime.UtcNow;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class DiscordPermOverride : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public GuildPerm Perm { get; set; }
 | 
			
		||||
 | 
			
		||||
    public ulong? GuildId { get; set; }
 | 
			
		||||
    public string Command { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,32 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using NadekoBot.Services.Database.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()
 | 
			
		||||
        => Username + "#" + Discriminator;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,49 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class FilteredWord : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string Word { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using NadekoBot.Modules.Searches.Common;
 | 
			
		||||
using NadekoBot.Services.Database.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);
 | 
			
		||||
 | 
			
		||||
    public StreamDataKey CreateKey()
 | 
			
		||||
        => new(Type, Username.ToLower());
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class GamblingStats : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string Feature { get; set; }
 | 
			
		||||
    public decimal Bet { get; set; }
 | 
			
		||||
    public decimal PaidOut { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,108 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
 | 
			
		||||
    #endregion
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class IgnoredVoicePresenceChannel : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public LogSetting LogSetting { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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
 | 
			
		||||
}
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class MusicPlaylist : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
    public string Author { get; set; }
 | 
			
		||||
    public ulong AuthorId { get; set; }
 | 
			
		||||
    public List<PlaylistSong> Songs { get; set; } = new();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,61 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class MusicPlayerSettings
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Auto generated Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int Id { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Id of the guild
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Queue repeat type
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public PlayerRepeatType PlayerRepeat { get; set; } = PlayerRepeatType.Queue;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Channel id the bot will always try to send track related messages to
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ulong? MusicChannelId { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Default volume player will be created with
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int Volume { get; set; } = 100;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Whether the bot should auto disconnect from the voice channel once the queue is done
 | 
			
		||||
    ///     This only has effect if
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool AutoDisconnect { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Selected quality preset for the music player
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public QualityPreset QualityPreset { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Whether the bot will automatically queue related songs
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool AutoPlay { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum QualityPreset
 | 
			
		||||
{
 | 
			
		||||
    Highest,
 | 
			
		||||
    High,
 | 
			
		||||
    Medium,
 | 
			
		||||
    Low
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum PlayerRepeatType
 | 
			
		||||
{
 | 
			
		||||
    None,
 | 
			
		||||
    Track,
 | 
			
		||||
    Queue
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class NsfwBlacklistedTag : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public string Tag { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => Tag.GetHashCode(StringComparison.InvariantCulture);
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is NsfwBlacklistedTag x && x.Tag == Tag;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,48 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
        };
 | 
			
		||||
}
 | 
			
		||||
@@ -1,55 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using System.ComponentModel.DataAnnotations.Schema;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Database.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
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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,
 | 
			
		||||
    Soundcloud
 | 
			
		||||
}
 | 
			
		||||
@@ -1,17 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class Poll : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
    public ulong ChannelId { get; set; }
 | 
			
		||||
    public string Question { get; set; }
 | 
			
		||||
    public IndexedCollection<PollAnswer> Answers { get; set; }
 | 
			
		||||
    public HashSet<PollVote> Votes { get; set; } = new();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class PollAnswer : DbEntity, IIndexed
 | 
			
		||||
{
 | 
			
		||||
    public int Index { get; set; }
 | 
			
		||||
    public string Text { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class PollVote : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public int VoteIndex { get; set; }
 | 
			
		||||
 | 
			
		||||
    public override int GetHashCode()
 | 
			
		||||
        => UserId.GetHashCode();
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object obj)
 | 
			
		||||
        => obj is PollVote p ? p.UserId == UserId : false;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Database.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
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public class RotatingPlayingStatus : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public string Status { get; set; }
 | 
			
		||||
    public ActivityType Type { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
public enum ShopEntryType
 | 
			
		||||
{
 | 
			
		||||
    Role,
 | 
			
		||||
 | 
			
		||||
    List
 | 
			
		||||
    //Infinite_List,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using NadekoBot.Services.Database.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,68 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services.Database.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;
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user