mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	Small refactoring. Finally made use of the type parameter in NadekoTypeReader<T> class
This commit is contained in:
		@@ -1,6 +1,5 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
#pragma warning disable
 | 
			
		||||
#pragma warning disable *
 | 
			
		||||
// License MIT
 | 
			
		||||
// Source: https://github.com/i3arnon/ConcurrentHashSet
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,19 +11,21 @@ namespace NadekoBot.Modules;
 | 
			
		||||
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 CommandHandler _cmdHandler { get; set; }
 | 
			
		||||
    public ILocalization _localization { get; set; }
 | 
			
		||||
    public IEmbedBuilderService _eb { get; set; }
 | 
			
		||||
 | 
			
		||||
    protected string prefix
 | 
			
		||||
        => CmdHandler.GetPrefix(ctx.Guild);
 | 
			
		||||
        => _cmdHandler.GetPrefix(ctx.Guild);
 | 
			
		||||
 | 
			
		||||
    protected ICommandContext ctx
 | 
			
		||||
        => Context;
 | 
			
		||||
 | 
			
		||||
    protected override void BeforeExecute(CommandInfo cmd)
 | 
			
		||||
        => Culture = Localization.GetCultureInfo(ctx.Guild?.Id);
 | 
			
		||||
    protected override void BeforeExecute(CommandInfo command)
 | 
			
		||||
        => Culture = _localization.GetCultureInfo(ctx.Guild?.Id);
 | 
			
		||||
 | 
			
		||||
    protected string GetText(in LocStr data)
 | 
			
		||||
        => Strings.GetText(data, Culture);
 | 
			
		||||
 
 | 
			
		||||
@@ -17,8 +17,8 @@ public static class OptionsParser
 | 
			
		||||
            x.HelpWriter = null;
 | 
			
		||||
        });
 | 
			
		||||
        var res = p.ParseArguments<T>(args);
 | 
			
		||||
        options = res.MapResult(x => x, _ => options);
 | 
			
		||||
        options.NormalizeOptions();
 | 
			
		||||
        return (options, res.Tag == ParserResultType.Parsed);
 | 
			
		||||
        var output = res.MapResult(x => x, _ => options);
 | 
			
		||||
        output.NormalizeOptions();
 | 
			
		||||
        return (output, res.Tag == ParserResultType.Parsed);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@ namespace NadekoBot.Common;
 | 
			
		||||
 | 
			
		||||
public readonly struct TypedKey<TData>
 | 
			
		||||
{
 | 
			
		||||
    public readonly string Key;
 | 
			
		||||
    public string Key { get; }
 | 
			
		||||
 | 
			
		||||
    public TypedKey(in string key)
 | 
			
		||||
        => Key = key;
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ public abstract record SmartText
 | 
			
		||||
            var smartEmbedText = JsonConvert.DeserializeObject<SmartEmbedText>(input);
 | 
			
		||||
 | 
			
		||||
            if (smartEmbedText is null)
 | 
			
		||||
                throw new();
 | 
			
		||||
                throw new FormatException();
 | 
			
		||||
 | 
			
		||||
            smartEmbedText.NormalizeFields();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,20 +14,20 @@ public sealed class CommandTypeReader : NadekoTypeReader<CommandInfo>
 | 
			
		||||
        _cmds = cmds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    public override ValueTask<TypeReaderResult<CommandInfo>> ReadAsync(ICommandContext ctx, string input)
 | 
			
		||||
    {
 | 
			
		||||
        input = input.ToUpperInvariant();
 | 
			
		||||
        var prefix = _handler.GetPrefix(context.Guild);
 | 
			
		||||
        var prefix = _handler.GetPrefix(ctx.Guild);
 | 
			
		||||
        if (!input.StartsWith(prefix.ToUpperInvariant(), StringComparison.InvariantCulture))
 | 
			
		||||
            return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No such command found."));
 | 
			
		||||
            return new(TypeReaderResult.FromError<CommandInfo>(CommandError.ParseFailed, "No such command found."));
 | 
			
		||||
 | 
			
		||||
        input = input[prefix.Length..];
 | 
			
		||||
 | 
			
		||||
        var cmd = _cmds.Commands.FirstOrDefault(c => c.Aliases.Select(a => a.ToUpperInvariant()).Contains(input));
 | 
			
		||||
        if (cmd is null)
 | 
			
		||||
            return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No such command found."));
 | 
			
		||||
            return new(TypeReaderResult.FromError<CommandInfo>(CommandError.ParseFailed, "No such command found."));
 | 
			
		||||
 | 
			
		||||
        return Task.FromResult(TypeReaderResult.FromSuccess(cmd));
 | 
			
		||||
        return new(TypeReaderResult.FromSuccess(cmd));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -44,19 +44,19 @@ public sealed class CommandOrCrTypeReader : NadekoTypeReader<CommandOrCrInfo>
 | 
			
		||||
        _commandHandler = commandHandler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override async Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    public override async ValueTask<TypeReaderResult<CommandOrCrInfo>> ReadAsync(ICommandContext ctx, string input)
 | 
			
		||||
    {
 | 
			
		||||
        input = input.ToUpperInvariant();
 | 
			
		||||
 | 
			
		||||
        if (_exprs.ExpressionExists(context.Guild?.Id, input))
 | 
			
		||||
        if (_exprs.ExpressionExists(ctx.Guild?.Id, input))
 | 
			
		||||
            return TypeReaderResult.FromSuccess(new CommandOrCrInfo(input, CommandOrCrInfo.Type.Custom));
 | 
			
		||||
 | 
			
		||||
        var cmd = await new CommandTypeReader(_commandHandler, _cmds).ReadAsync(context, input);
 | 
			
		||||
        var cmd = await new CommandTypeReader(_commandHandler, _cmds).ReadAsync(ctx, input);
 | 
			
		||||
        if (cmd.IsSuccess)
 | 
			
		||||
            return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name,
 | 
			
		||||
                CommandOrCrInfo.Type.Normal));
 | 
			
		||||
 | 
			
		||||
        return TypeReaderResult.FromError(CommandError.ParseFailed, "No such command or cr found.");
 | 
			
		||||
        return TypeReaderResult.FromError<CommandOrCrInfo>(CommandError.ParseFailed, "No such command or cr found.");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,11 @@ namespace NadekoBot.Common.TypeReaders;
 | 
			
		||||
 | 
			
		||||
public sealed class EmoteTypeReader : NadekoTypeReader<Emote>
 | 
			
		||||
{
 | 
			
		||||
    public override Task<TypeReaderResult> ReadAsync(ICommandContext ctx, string input)
 | 
			
		||||
    public override ValueTask<TypeReaderResult<Emote>> ReadAsync(ICommandContext ctx, string input)
 | 
			
		||||
    {
 | 
			
		||||
        if (!Emote.TryParse(input, out var emote))
 | 
			
		||||
            return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Input is not a valid emote"));
 | 
			
		||||
            return new(TypeReaderResult.FromError<Emote>(CommandError.ParseFailed, "Input is not a valid emote"));
 | 
			
		||||
 | 
			
		||||
        return Task.FromResult(TypeReaderResult.FromSuccess(emote));
 | 
			
		||||
        return new(TypeReaderResult.FromSuccess(emote));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -10,14 +10,14 @@ public sealed class GuildDateTimeTypeReader : NadekoTypeReader<GuildDateTime>
 | 
			
		||||
    public GuildDateTimeTypeReader(GuildTimezoneService gts)
 | 
			
		||||
        => _gts = gts;
 | 
			
		||||
 | 
			
		||||
    public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    public override ValueTask<TypeReaderResult<GuildDateTime>> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    {
 | 
			
		||||
        var gdt = Parse(context.Guild.Id, input);
 | 
			
		||||
        if (gdt is null)
 | 
			
		||||
            return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed,
 | 
			
		||||
            return new(TypeReaderResult.FromError<GuildDateTime>(CommandError.ParseFailed,
 | 
			
		||||
                "Input string is in an incorrect format."));
 | 
			
		||||
 | 
			
		||||
        return Task.FromResult(TypeReaderResult.FromSuccess(gdt));
 | 
			
		||||
        return new(TypeReaderResult.FromSuccess(gdt));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private GuildDateTime Parse(ulong guildId, string input)
 | 
			
		||||
 
 | 
			
		||||
@@ -8,18 +8,17 @@ public sealed class GuildTypeReader : NadekoTypeReader<IGuild>
 | 
			
		||||
    public GuildTypeReader(DiscordSocketClient client)
 | 
			
		||||
        => _client = client;
 | 
			
		||||
 | 
			
		||||
    public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    public override ValueTask<TypeReaderResult<IGuild>> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    {
 | 
			
		||||
        input = input.Trim().ToUpperInvariant();
 | 
			
		||||
        var guilds = _client.Guilds;
 | 
			
		||||
        var guild = guilds.FirstOrDefault(g => g.Id.ToString().Trim().ToUpperInvariant() == input)
 | 
			
		||||
                    ?? //by id
 | 
			
		||||
                    guilds.FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == input); //by name
 | 
			
		||||
        IGuild guild = guilds.FirstOrDefault(g => g.Id.ToString().Trim().ToUpperInvariant() == input) //by id
 | 
			
		||||
                       ?? guilds.FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == input); //by name
 | 
			
		||||
 | 
			
		||||
        if (guild is not null)
 | 
			
		||||
            return Task.FromResult(TypeReaderResult.FromSuccess(guild));
 | 
			
		||||
            return new(TypeReaderResult.FromSuccess(guild));
 | 
			
		||||
 | 
			
		||||
        return Task.FromResult(
 | 
			
		||||
            TypeReaderResult.FromError(CommandError.ParseFailed, "No guild by that name or Id found"));
 | 
			
		||||
        return new(
 | 
			
		||||
            TypeReaderResult.FromError<IGuild>(CommandError.ParseFailed, "No guild by that name or Id found"));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,17 +3,17 @@ namespace NadekoBot.Common.TypeReaders;
 | 
			
		||||
 | 
			
		||||
public sealed class KwumTypeReader : NadekoTypeReader<kwum>
 | 
			
		||||
{
 | 
			
		||||
    public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    public override ValueTask<TypeReaderResult<kwum>> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    {
 | 
			
		||||
        if (kwum.TryParse(input, out var val))
 | 
			
		||||
            return Task.FromResult(TypeReaderResult.FromSuccess(val));
 | 
			
		||||
            return new(TypeReaderResult.FromSuccess(val));
 | 
			
		||||
 | 
			
		||||
        return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Input is not a valid kwum"));
 | 
			
		||||
        return new(TypeReaderResult.FromError<kwum>(CommandError.ParseFailed, "Input is not a valid kwum"));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public sealed class SmartTextTypeReader : NadekoTypeReader<SmartText>
 | 
			
		||||
{
 | 
			
		||||
    public override Task<TypeReaderResult> ReadAsync(ICommandContext ctx, string input)
 | 
			
		||||
        => Task.FromResult(TypeReaderResult.FromSuccess(SmartText.CreateFrom(input)));
 | 
			
		||||
    public override ValueTask<TypeReaderResult<SmartText>> ReadAsync(ICommandContext ctx, string input)
 | 
			
		||||
        => new(TypeReaderResult.FromSuccess(SmartText.CreateFrom(input)));
 | 
			
		||||
}
 | 
			
		||||
@@ -8,16 +8,16 @@ public sealed class ModuleTypeReader : NadekoTypeReader<ModuleInfo>
 | 
			
		||||
    public ModuleTypeReader(CommandService cmds)
 | 
			
		||||
        => _cmds = cmds;
 | 
			
		||||
 | 
			
		||||
    public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    public override ValueTask<TypeReaderResult<ModuleInfo>> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    {
 | 
			
		||||
        input = input.ToUpperInvariant();
 | 
			
		||||
        var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule())
 | 
			
		||||
                          .FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input)
 | 
			
		||||
                          ?.Key;
 | 
			
		||||
        if (module is null)
 | 
			
		||||
            return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No such module found."));
 | 
			
		||||
            return new(TypeReaderResult.FromError<ModuleInfo>(CommandError.ParseFailed, "No such module found."));
 | 
			
		||||
 | 
			
		||||
        return Task.FromResult(TypeReaderResult.FromSuccess(module));
 | 
			
		||||
        return new(TypeReaderResult.FromSuccess(module));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -28,16 +28,16 @@ public sealed class ModuleOrCrTypeReader : NadekoTypeReader<ModuleOrCrInfo>
 | 
			
		||||
    public ModuleOrCrTypeReader(CommandService cmds)
 | 
			
		||||
        => _cmds = cmds;
 | 
			
		||||
 | 
			
		||||
    public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    public override ValueTask<TypeReaderResult<ModuleOrCrInfo>> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    {
 | 
			
		||||
        input = input.ToUpperInvariant();
 | 
			
		||||
        var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule())
 | 
			
		||||
                          .FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input)
 | 
			
		||||
                          ?.Key;
 | 
			
		||||
        if (module is null && input != "ACTUALEXPRESSIONS")
 | 
			
		||||
            return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No such module found."));
 | 
			
		||||
            return new(TypeReaderResult.FromError<ModuleOrCrInfo>(CommandError.ParseFailed, "No such module found."));
 | 
			
		||||
 | 
			
		||||
        return Task.FromResult(TypeReaderResult.FromSuccess(new ModuleOrCrInfo { Name = input }));
 | 
			
		||||
        return new(TypeReaderResult.FromSuccess(new ModuleOrCrInfo { Name = input }));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,40 @@ namespace NadekoBot.Common.TypeReaders;
 | 
			
		||||
[MeansImplicitUse(ImplicitUseTargetFlags.Default | ImplicitUseTargetFlags.WithInheritors)]
 | 
			
		||||
public abstract class NadekoTypeReader<T> : TypeReader
 | 
			
		||||
{
 | 
			
		||||
    public abstract Task<TypeReaderResult> ReadAsync(ICommandContext ctx, string input);
 | 
			
		||||
    public abstract ValueTask<TypeReaderResult<T>> ReadAsync(ICommandContext ctx, string input);
 | 
			
		||||
 | 
			
		||||
    public override Task<TypeReaderResult> ReadAsync(ICommandContext ctx, string input, IServiceProvider services)
 | 
			
		||||
        => ReadAsync(ctx, input);
 | 
			
		||||
    public override async Task<Discord.Commands.TypeReaderResult> ReadAsync(
 | 
			
		||||
        ICommandContext ctx,
 | 
			
		||||
        string input,
 | 
			
		||||
        IServiceProvider services)
 | 
			
		||||
        => await ReadAsync(ctx, input);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public static class TypeReaderResult
 | 
			
		||||
{
 | 
			
		||||
    public static TypeReaderResult<T> FromError<T>(CommandError error, string reason)
 | 
			
		||||
        => Discord.Commands.TypeReaderResult.FromError(error, reason);
 | 
			
		||||
 | 
			
		||||
    public static TypeReaderResult<T> FromSuccess<T>(in T value)
 | 
			
		||||
        => Discord.Commands.TypeReaderResult.FromSuccess(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public readonly struct TypeReaderResult<T>
 | 
			
		||||
{
 | 
			
		||||
    public bool IsSuccess
 | 
			
		||||
        => _result.IsSuccess;
 | 
			
		||||
 | 
			
		||||
    public IReadOnlyCollection<TypeReaderValue> Values
 | 
			
		||||
        => _result.Values;
 | 
			
		||||
 | 
			
		||||
    private readonly Discord.Commands.TypeReaderResult _result;
 | 
			
		||||
 | 
			
		||||
    private TypeReaderResult(in Discord.Commands.TypeReaderResult result)
 | 
			
		||||
        => _result = result;
 | 
			
		||||
 | 
			
		||||
    public static implicit operator TypeReaderResult<T>(in Discord.Commands.TypeReaderResult result)
 | 
			
		||||
        => new(result);
 | 
			
		||||
 | 
			
		||||
    public static implicit operator Discord.Commands.TypeReaderResult(in TypeReaderResult<T> wrapper)
 | 
			
		||||
        => wrapper._result;
 | 
			
		||||
}
 | 
			
		||||
@@ -8,7 +8,7 @@ namespace NadekoBot.Common.TypeReaders;
 | 
			
		||||
/// </summary>
 | 
			
		||||
public sealed class PermissionActionTypeReader : NadekoTypeReader<PermissionAction>
 | 
			
		||||
{
 | 
			
		||||
    public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    public override ValueTask<TypeReaderResult<PermissionAction>> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    {
 | 
			
		||||
        input = input.ToUpperInvariant();
 | 
			
		||||
        switch (input)
 | 
			
		||||
@@ -21,7 +21,7 @@ public sealed class PermissionActionTypeReader : NadekoTypeReader<PermissionActi
 | 
			
		||||
            case "ALLOW":
 | 
			
		||||
            case "PERMIT":
 | 
			
		||||
            case "UNBAN":
 | 
			
		||||
                return Task.FromResult(TypeReaderResult.FromSuccess(PermissionAction.Enable));
 | 
			
		||||
                return new(TypeReaderResult.FromSuccess(PermissionAction.Enable));
 | 
			
		||||
            case "0":
 | 
			
		||||
            case "F":
 | 
			
		||||
            case "FALSE":
 | 
			
		||||
@@ -30,9 +30,9 @@ public sealed class PermissionActionTypeReader : NadekoTypeReader<PermissionActi
 | 
			
		||||
            case "DISABLED":
 | 
			
		||||
            case "DISALLOW":
 | 
			
		||||
            case "BAN":
 | 
			
		||||
                return Task.FromResult(TypeReaderResult.FromSuccess(PermissionAction.Disable));
 | 
			
		||||
                return new(TypeReaderResult.FromSuccess(PermissionAction.Disable));
 | 
			
		||||
            default:
 | 
			
		||||
                return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed,
 | 
			
		||||
                return new(TypeReaderResult.FromError<PermissionAction>(CommandError.ParseFailed,
 | 
			
		||||
                    "Did not receive a valid boolean value"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace NadekoBot.Common.TypeReaders;
 | 
			
		||||
 | 
			
		||||
public sealed class Rgba32TypeReader : NadekoTypeReader<Color>
 | 
			
		||||
{
 | 
			
		||||
    public override async Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    public override async ValueTask<TypeReaderResult<Color>> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    {
 | 
			
		||||
        await Task.Yield();
 | 
			
		||||
 | 
			
		||||
@@ -16,7 +16,7 @@ public sealed class Rgba32TypeReader : NadekoTypeReader<Color>
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return TypeReaderResult.FromError(CommandError.ParseFailed, "Parameter is not a valid color hex.");
 | 
			
		||||
            return TypeReaderResult.FromError<Color>(CommandError.ParseFailed, "Parameter is not a valid color hex.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -18,12 +18,10 @@ public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
 | 
			
		||||
        _gambling = gambling;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override async Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    public override ValueTask<TypeReaderResult<ShmartNumber>> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    {
 | 
			
		||||
        await Task.Yield();
 | 
			
		||||
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(input))
 | 
			
		||||
            return TypeReaderResult.FromError(CommandError.ParseFailed, "Input is empty.");
 | 
			
		||||
            return new(TypeReaderResult.FromError<ShmartNumber>(CommandError.ParseFailed, "Input is empty."));
 | 
			
		||||
 | 
			
		||||
        var i = input.Trim().ToUpperInvariant();
 | 
			
		||||
 | 
			
		||||
@@ -32,17 +30,17 @@ public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
 | 
			
		||||
        //can't add m because it will conflict with max atm
 | 
			
		||||
 | 
			
		||||
        if (TryHandlePercentage(context, i, out var num))
 | 
			
		||||
            return TypeReaderResult.FromSuccess(new ShmartNumber(num, i));
 | 
			
		||||
            return new(TypeReaderResult.FromSuccess(new ShmartNumber(num, i)));
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var expr = new Expression(i, EvaluateOptions.IgnoreCase);
 | 
			
		||||
            expr.EvaluateParameter += (str, ev) => EvaluateParam(str, ev, context);
 | 
			
		||||
            var lon = (long)decimal.Parse(expr.Evaluate().ToString());
 | 
			
		||||
            return TypeReaderResult.FromSuccess(new ShmartNumber(lon, input));
 | 
			
		||||
            return new(TypeReaderResult.FromSuccess(new ShmartNumber(lon, input)));
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception)
 | 
			
		||||
        {
 | 
			
		||||
            return TypeReaderResult.FromError(CommandError.ParseFailed, $"Invalid input: {input}");
 | 
			
		||||
            return ValueTask.FromResult(TypeReaderResult.FromError<Common.ShmartNumber>(CommandError.ParseFailed, $"Invalid input: {input}"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,18 +5,18 @@ namespace NadekoBot.Common.TypeReaders;
 | 
			
		||||
 | 
			
		||||
public sealed class StoopidTimeTypeReader : NadekoTypeReader<StoopidTime>
 | 
			
		||||
{
 | 
			
		||||
    public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    public override ValueTask<TypeReaderResult<StoopidTime>> ReadAsync(ICommandContext context, string input)
 | 
			
		||||
    {
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(input))
 | 
			
		||||
            return Task.FromResult(TypeReaderResult.FromError(CommandError.Unsuccessful, "Input is empty."));
 | 
			
		||||
            return new(TypeReaderResult.FromError<StoopidTime>(CommandError.Unsuccessful, "Input is empty."));
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var time = StoopidTime.FromInput(input);
 | 
			
		||||
            return Task.FromResult(TypeReaderResult.FromSuccess(time));
 | 
			
		||||
            return new(TypeReaderResult.FromSuccess(time));
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return Task.FromResult(TypeReaderResult.FromError(CommandError.Exception, ex.Message));
 | 
			
		||||
            return new(TypeReaderResult.FromError<StoopidTime>(CommandError.Exception, ex.Message));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Common.Yml;
 | 
			
		||||
 | 
			
		||||
[AttributeUsage(AttributeTargets.Property)]
 | 
			
		||||
public class CommentAttribute : Attribute
 | 
			
		||||
{
 | 
			
		||||
    public string Comment { get; }
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ public class CommentGatheringTypeInspector : TypeInspectorSkeleton
 | 
			
		||||
    private readonly ITypeInspector _innerTypeDescriptor;
 | 
			
		||||
 | 
			
		||||
    public CommentGatheringTypeInspector(ITypeInspector innerTypeDescriptor)
 | 
			
		||||
        => _innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException("innerTypeDescriptor");
 | 
			
		||||
        => _innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException(nameof(innerTypeDescriptor));
 | 
			
		||||
 | 
			
		||||
    public override IEnumerable<IPropertyDescriptor> GetProperties(Type type, object container)
 | 
			
		||||
        => _innerTypeDescriptor.GetProperties(type, container).Select(d => new CommentsPropertyDescriptor(d));
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ public static class MusicPlaylistExtensions
 | 
			
		||||
    public static List<MusicPlaylist> GetPlaylistsOnPage(this DbSet<MusicPlaylist> playlists, int num)
 | 
			
		||||
    {
 | 
			
		||||
        if (num < 1)
 | 
			
		||||
            throw new IndexOutOfRangeException();
 | 
			
		||||
            throw new ArgumentOutOfRangeException(nameof(num));
 | 
			
		||||
 | 
			
		||||
        return playlists.AsQueryable().Skip((num - 1) * 20).Take(20).Include(pl => pl.Songs).ToList();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ public class AntiSpamSetting : DbEntity
 | 
			
		||||
 | 
			
		||||
    public PunishmentAction Action { get; set; }
 | 
			
		||||
    public int MessageThreshold { get; set; } = 3;
 | 
			
		||||
    public int MuteTime { get; set; } = 0;
 | 
			
		||||
    public int MuteTime { get; set; }
 | 
			
		||||
    public ulong? RoleId { get; set; }
 | 
			
		||||
    public HashSet<AntiSpamIgnore> IgnoredChannels { get; set; } = new();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ public class GuildConfig : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong GuildId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public string Prefix { get; set; } = null;
 | 
			
		||||
    public string Prefix { get; set; }
 | 
			
		||||
 | 
			
		||||
    public bool DeleteMessageOnCommand { get; set; }
 | 
			
		||||
    public HashSet<DelMsgOnCmdChannel> DelMsgOnCmdChannels { get; set; } = new();
 | 
			
		||||
@@ -16,8 +16,6 @@ public class GuildConfig : DbEntity
 | 
			
		||||
    public string AutoAssignRoleIds { get; set; }
 | 
			
		||||
 | 
			
		||||
    //greet stuff
 | 
			
		||||
    public bool AutoDeleteGreetMessages { get; set; } //unused
 | 
			
		||||
    public bool AutoDeleteByeMessages { get; set; } // unused
 | 
			
		||||
    public int AutoDeleteGreetMessagesTimer { get; set; } = 30;
 | 
			
		||||
    public int AutoDeleteByeMessagesTimer { get; set; } = 30;
 | 
			
		||||
 | 
			
		||||
@@ -45,7 +43,7 @@ public class GuildConfig : DbEntity
 | 
			
		||||
 | 
			
		||||
    public List<Permissionv2> Permissions { get; set; }
 | 
			
		||||
    public bool VerbosePermissions { get; set; } = true;
 | 
			
		||||
    public string PermissionRole { get; set; } = null;
 | 
			
		||||
    public string PermissionRole { get; set; }
 | 
			
		||||
 | 
			
		||||
    public HashSet<CommandCooldown> CommandCooldowns { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
@@ -71,8 +69,8 @@ public class GuildConfig : DbEntity
 | 
			
		||||
    public AntiSpamSetting AntiSpamSetting { get; set; }
 | 
			
		||||
    public AntiAltSetting AntiAltSetting { get; set; }
 | 
			
		||||
 | 
			
		||||
    public string Locale { get; set; } = null;
 | 
			
		||||
    public string TimeZoneId { get; set; } = null;
 | 
			
		||||
    public string Locale { get; set; }
 | 
			
		||||
    public string TimeZoneId { get; set; }
 | 
			
		||||
 | 
			
		||||
    public HashSet<UnmuteTimer> UnmuteTimers { get; set; } = new();
 | 
			
		||||
    public HashSet<UnbanTimer> UnbanTimer { get; set; } = new();
 | 
			
		||||
@@ -85,8 +83,8 @@ public class GuildConfig : DbEntity
 | 
			
		||||
    public HashSet<SlowmodeIgnoredRole> SlowmodeIgnoredRoles { get; set; }
 | 
			
		||||
 | 
			
		||||
    public List<ShopEntry> ShopEntries { get; set; }
 | 
			
		||||
    public ulong? GameVoiceChannel { get; set; } = null;
 | 
			
		||||
    public bool VerboseErrors { get; set; } = false;
 | 
			
		||||
    public ulong? GameVoiceChannel { get; set; }
 | 
			
		||||
    public bool VerboseErrors { get; set; }
 | 
			
		||||
 | 
			
		||||
    public StreamRoleSettings StreamRole { get; set; }
 | 
			
		||||
 | 
			
		||||
@@ -95,7 +93,7 @@ public class GuildConfig : DbEntity
 | 
			
		||||
    public IndexedCollection<ReactionRoleMessage> ReactionRoleMessages { get; set; } = new();
 | 
			
		||||
    public bool NotifyStreamOffline { get; set; }
 | 
			
		||||
    public List<GroupName> SelfAssignableRoleGroupNames { get; set; }
 | 
			
		||||
    public int WarnExpireHours { get; set; } = 0;
 | 
			
		||||
    public int WarnExpireHours { get; set; }
 | 
			
		||||
    public WarnExpireAction WarnExpireAction { get; set; } = WarnExpireAction.Clear;
 | 
			
		||||
 | 
			
		||||
    #region Boost Message
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ public class MusicPlayerSettings
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Channel id the bot will always try to send track related messages to
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ulong? MusicChannelId { get; set; } = null;
 | 
			
		||||
    public ulong? MusicChannelId { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Default volume player will be created with
 | 
			
		||||
@@ -32,7 +32,7 @@ public class MusicPlayerSettings
 | 
			
		||||
    ///     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; } = false;
 | 
			
		||||
    public bool AutoDisconnect { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Selected quality preset for the music player
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ global using GuildPerm = Discord.GuildPermission;
 | 
			
		||||
global using ChannelPerm = Discord.ChannelPermission;
 | 
			
		||||
global using BotPermAttribute = Discord.Commands.RequireBotPermissionAttribute;
 | 
			
		||||
global using LeftoverAttribute = Discord.Commands.RemainderAttribute;
 | 
			
		||||
global using TypeReaderResult = NadekoBot.Common.TypeReaders.TypeReaderResult;
 | 
			
		||||
 | 
			
		||||
// non-essential
 | 
			
		||||
global using JetBrains.Annotations;
 | 
			
		||||
@@ -341,38 +341,6 @@ public class GreetService : INService, IReadyExecutor
 | 
			
		||||
        return settings;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> SetSettings(ulong guildId, GreetSettings settings)
 | 
			
		||||
    {
 | 
			
		||||
        if (settings.AutoDeleteByeMessagesTimer is > 600 or < 0
 | 
			
		||||
            || settings.AutoDeleteGreetMessagesTimer is > 600 or < 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        await using var uow = _db.GetDbContext();
 | 
			
		||||
        var conf = uow.GuildConfigsForId(guildId, set => set);
 | 
			
		||||
        conf.DmGreetMessageText = settings.DmGreetMessageText?.SanitizeMentions();
 | 
			
		||||
        conf.ChannelGreetMessageText = settings.ChannelGreetMessageText?.SanitizeMentions();
 | 
			
		||||
        conf.ChannelByeMessageText = settings.ChannelByeMessageText?.SanitizeMentions();
 | 
			
		||||
 | 
			
		||||
        conf.AutoDeleteGreetMessagesTimer = settings.AutoDeleteGreetMessagesTimer;
 | 
			
		||||
        conf.AutoDeleteGreetMessages = settings.AutoDeleteGreetMessagesTimer > 0;
 | 
			
		||||
 | 
			
		||||
        conf.AutoDeleteByeMessagesTimer = settings.AutoDeleteByeMessagesTimer;
 | 
			
		||||
        conf.AutoDeleteByeMessages = settings.AutoDeleteByeMessagesTimer > 0;
 | 
			
		||||
 | 
			
		||||
        conf.GreetMessageChannelId = settings.GreetMessageChannelId;
 | 
			
		||||
        conf.ByeMessageChannelId = settings.ByeMessageChannelId;
 | 
			
		||||
 | 
			
		||||
        conf.SendChannelGreetMessage = settings.SendChannelGreetMessage;
 | 
			
		||||
        conf.SendChannelByeMessage = settings.SendChannelByeMessage;
 | 
			
		||||
 | 
			
		||||
        await uow.SaveChangesAsync();
 | 
			
		||||
 | 
			
		||||
        var toAdd = GreetSettings.Create(conf);
 | 
			
		||||
        _guildConfigsCache[guildId] = toAdd;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> SetGreet(ulong guildId, ulong channelId, bool? value = null)
 | 
			
		||||
    {
 | 
			
		||||
        await using var uow = _db.GetDbContext();
 | 
			
		||||
 
 | 
			
		||||
@@ -56,13 +56,13 @@ public partial class Administration
 | 
			
		||||
                CultureInfo ci;
 | 
			
		||||
                if (name.Trim().ToLowerInvariant() == "default")
 | 
			
		||||
                {
 | 
			
		||||
                    Localization.RemoveGuildCulture(ctx.Guild);
 | 
			
		||||
                    ci = Localization.DefaultCultureInfo;
 | 
			
		||||
                    _localization.RemoveGuildCulture(ctx.Guild);
 | 
			
		||||
                    ci = _localization.DefaultCultureInfo;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    ci = new(name);
 | 
			
		||||
                    Localization.SetGuildCulture(ctx.Guild, ci);
 | 
			
		||||
                    _localization.SetGuildCulture(ctx.Guild, ci);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                await ReplyConfirmLocalizedAsync(strs.lang_set(Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)));
 | 
			
		||||
@@ -76,7 +76,7 @@ public partial class Administration
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        public async partial Task LanguageSetDefault()
 | 
			
		||||
        {
 | 
			
		||||
            var cul = Localization.DefaultCultureInfo;
 | 
			
		||||
            var cul = _localization.DefaultCultureInfo;
 | 
			
		||||
            await ReplyErrorLocalizedAsync(strs.lang_set_bot_show(cul, cul.NativeName));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -89,13 +89,13 @@ public partial class Administration
 | 
			
		||||
                CultureInfo ci;
 | 
			
		||||
                if (name.Trim().ToLowerInvariant() == "default")
 | 
			
		||||
                {
 | 
			
		||||
                    Localization.ResetDefaultCulture();
 | 
			
		||||
                    ci = Localization.DefaultCultureInfo;
 | 
			
		||||
                    _localization.ResetDefaultCulture();
 | 
			
		||||
                    ci = _localization.DefaultCultureInfo;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    ci = new(name);
 | 
			
		||||
                    Localization.SetDefaultCulture(ci);
 | 
			
		||||
                    _localization.SetDefaultCulture(ci);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                await ReplyConfirmLocalizedAsync(strs.lang_set_bot(Format.Bold(ci.ToString()),
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ public partial class Administration
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        [Priority(1)]
 | 
			
		||||
        public async partial Task Prefix()
 | 
			
		||||
            => await ReplyConfirmLocalizedAsync(strs.prefix_current(Format.Code(CmdHandler.GetPrefix(ctx.Guild))));
 | 
			
		||||
            => await ReplyConfirmLocalizedAsync(strs.prefix_current(Format.Code(_cmdHandler.GetPrefix(ctx.Guild))));
 | 
			
		||||
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        [RequireContext(ContextType.Guild)]
 | 
			
		||||
@@ -33,7 +33,7 @@ public partial class Administration
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            var oldPrefix = prefix;
 | 
			
		||||
            var newPrefix = CmdHandler.SetPrefix(ctx.Guild, toSet);
 | 
			
		||||
            var newPrefix = _cmdHandler.SetPrefix(ctx.Guild, toSet);
 | 
			
		||||
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.prefix_new(Format.Code(oldPrefix), Format.Code(newPrefix)));
 | 
			
		||||
        }
 | 
			
		||||
@@ -44,12 +44,12 @@ public partial class Administration
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrWhiteSpace(toSet))
 | 
			
		||||
            {
 | 
			
		||||
                await ReplyConfirmLocalizedAsync(strs.defprefix_current(CmdHandler.GetPrefix()));
 | 
			
		||||
                await ReplyConfirmLocalizedAsync(strs.defprefix_current(_cmdHandler.GetPrefix()));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var oldPrefix = CmdHandler.GetPrefix();
 | 
			
		||||
            var newPrefix = CmdHandler.SetDefaultPrefix(toSet);
 | 
			
		||||
            var oldPrefix = _cmdHandler.GetPrefix();
 | 
			
		||||
            var newPrefix = _cmdHandler.SetDefaultPrefix(toSet);
 | 
			
		||||
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.defprefix_new(Format.Code(oldPrefix), Format.Code(newPrefix)));
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -130,7 +130,7 @@ public partial class Utility
 | 
			
		||||
            var startCount = page * activityPerPage;
 | 
			
		||||
 | 
			
		||||
            var str = new StringBuilder();
 | 
			
		||||
            foreach (var kvp in CmdHandler.UserMessagesSent.OrderByDescending(kvp => kvp.Value)
 | 
			
		||||
            foreach (var kvp in _cmdHandler.UserMessagesSent.OrderByDescending(kvp => kvp.Value)
 | 
			
		||||
                                          .Skip(page * activityPerPage)
 | 
			
		||||
                                          .Take(activityPerPage))
 | 
			
		||||
                str.AppendLine(GetText(strs.activity_line(++startCount,
 | 
			
		||||
@@ -142,7 +142,7 @@ public partial class Utility
 | 
			
		||||
                                            .WithTitle(GetText(strs.activity_page(page + 1)))
 | 
			
		||||
                                            .WithOkColor()
 | 
			
		||||
                                            .WithFooter(GetText(
 | 
			
		||||
                                                strs.activity_users_total(CmdHandler.UserMessagesSent.Count)))
 | 
			
		||||
                                                strs.activity_users_total(_cmdHandler.UserMessagesSent.Count)))
 | 
			
		||||
                                            .WithDescription(str.ToString()));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,17 @@
 | 
			
		||||
    <Nullable>enable</Nullable>
 | 
			
		||||
    <EnablePreviewFeatures>True</EnablePreviewFeatures>
 | 
			
		||||
    <ImplicitUsings>true</ImplicitUsings>
 | 
			
		||||
    
 | 
			
		||||
    <!-- Output/build -->
 | 
			
		||||
    <RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
 | 
			
		||||
    <OutputType>exe</OutputType>
 | 
			
		||||
    <ApplicationIcon>nadeko_icon.ico</ApplicationIcon>
 | 
			
		||||
    <NoWarn>CS1066,CA1069</NoWarn>
 | 
			
		||||
    <AnalysisMode>Recommended</AnalysisMode>
 | 
			
		||||
    
 | 
			
		||||
    <!-- Analysis/Warnings -->
 | 
			
		||||
<!--    <AnalysisMode>Recommended</AnalysisMode>-->
 | 
			
		||||
<!--    <AnalysisModeGlobalization>None</AnalysisModeGlobalization>-->
 | 
			
		||||
<!--    <AnalysisModeNaming>None</AnalysisModeNaming>    -->
 | 
			
		||||
    <NoWarn>CS1066</NoWarn>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
  
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
using NadekoBot.Services.Currency;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
namespace NadekoBot.Services;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user