mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	Nuked humanizer. Some of the strings might look worse but the output directory will no longer look terrible. Added stats for todo list command
This commit is contained in:
		@@ -3,7 +3,6 @@ global using NonBlocking;
 | 
			
		||||
 | 
			
		||||
// packages
 | 
			
		||||
global using Serilog;
 | 
			
		||||
global using Humanizer;
 | 
			
		||||
 | 
			
		||||
// nadekobot
 | 
			
		||||
global using NadekoBot;
 | 
			
		||||
 
 | 
			
		||||
@@ -344,7 +344,7 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (sr.GetContentLength() > 8.Megabytes().Bytes)
 | 
			
		||||
        if (sr.GetContentLength() > 8.Megabytes())
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using CommandLine;
 | 
			
		||||
using Humanizer.Localisation;
 | 
			
		||||
using NadekoBot.Common.TypeReaders.Models;
 | 
			
		||||
using NadekoBot.Modules.Administration.Services;
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
@@ -458,7 +457,7 @@ public partial class Administration
 | 
			
		||||
                         .AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
 | 
			
		||||
                         .AddField("ID", userId.ToString(), true)
 | 
			
		||||
                         .AddField(GetText(strs.duration),
 | 
			
		||||
                             time.Time.Humanize(3, minUnit: TimeUnit.Minute, culture: Culture),
 | 
			
		||||
                             time.Time.ToPrettyStringHm(),
 | 
			
		||||
                             true);
 | 
			
		||||
 | 
			
		||||
            if (dmFailed)
 | 
			
		||||
 
 | 
			
		||||
@@ -396,8 +396,8 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
            ("rps", _, _) => $"Rock Paper Scissors - {subType}",
 | 
			
		||||
            (null, _, _) => null,
 | 
			
		||||
            (_, null, _) => null,
 | 
			
		||||
            (_, _, ulong userId) => $"{type.Titleize()} - {subType.Titleize()} | [{userId}]",
 | 
			
		||||
            _ => $"{type.Titleize()} - {subType.Titleize()}"
 | 
			
		||||
            (_, _, ulong userId) => $"{type} - {subType} | [{userId}]",
 | 
			
		||||
            _ => $"{type} - {subType}"
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Humanizer.Localisation;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Common.TypeReaders;
 | 
			
		||||
using NadekoBot.Db;
 | 
			
		||||
@@ -81,7 +80,6 @@ public partial class Permissions
 | 
			
		||||
            if (--page < 0)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            var channel = (ITextChannel)ctx.Channel;
 | 
			
		||||
            var localSet = _service.GetCommandCooldowns(ctx.Guild.Id);
 | 
			
		||||
 | 
			
		||||
            if (!localSet.Any())
 | 
			
		||||
@@ -96,7 +94,7 @@ public partial class Permissions
 | 
			
		||||
                      .Page((items, _) =>
 | 
			
		||||
                      {
 | 
			
		||||
                          var output = items.Select(x =>
 | 
			
		||||
                              $"{Format.Code(x.CommandName)}: {x.Seconds.Seconds().Humanize(maxUnit: TimeUnit.Second, culture: Culture)}");
 | 
			
		||||
                              $"{Format.Code(x.CommandName)}: {x.Seconds}s");
 | 
			
		||||
 | 
			
		||||
                          return _sender.CreateEmbed()
 | 
			
		||||
                                 .WithOkColor()
 | 
			
		||||
 
 | 
			
		||||
@@ -105,10 +105,7 @@ public sealed class DefaultStockDataService : IStockDataService, INService
 | 
			
		||||
                   .ToList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static CsvConfiguration _csvConfig = new(CultureInfo.InvariantCulture)
 | 
			
		||||
    {
 | 
			
		||||
        PrepareHeaderForMatch = args => args.Header.Humanize(LetterCasing.Title)
 | 
			
		||||
    };
 | 
			
		||||
    private static CsvConfiguration _csvConfig = new(CultureInfo.InvariantCulture);
 | 
			
		||||
 | 
			
		||||
    public async Task<IReadOnlyCollection<CandleData>> GetCandleDataAsync(string query)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,7 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Humanizer.Localisation;
 | 
			
		||||
using NadekoBot.Db;
 | 
			
		||||
using NadekoBot.Modules.Utility.Services;
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
using System.Resources;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Utility;
 | 
			
		||||
 | 
			
		||||
@@ -118,7 +116,7 @@ public partial class Utility
 | 
			
		||||
                    var diff = when - DateTime.UtcNow;
 | 
			
		||||
                    embed.AddField(
 | 
			
		||||
                        $"#{++i + (page * 10)} {rem.When:HH:mm yyyy-MM-dd} UTC "
 | 
			
		||||
                        + $"(in {diff.Humanize(2, minUnit: TimeUnit.Minute, culture: Culture)})",
 | 
			
		||||
                        + $"(in {diff.ToPrettyStringHm()})",
 | 
			
		||||
                        $@"`Target:` {(rem.IsPrivate ? "DM" : "Channel")}
 | 
			
		||||
`TargetId:` {rem.ChannelId}
 | 
			
		||||
`Message:` {rem.Message?.TrimTo(50)}");
 | 
			
		||||
@@ -212,7 +210,7 @@ public partial class Utility
 | 
			
		||||
                      .Confirm($"\u23f0 {GetText(strs.remind(
 | 
			
		||||
                          Format.Bold(!isPrivate ? $"<#{targetId}>" : ctx.User.Username),
 | 
			
		||||
                          Format.Bold(message),
 | 
			
		||||
                          ts.Humanize(3, minUnit: TimeUnit.Second, culture: Culture),
 | 
			
		||||
                          ts.ToPrettyStringHm(),
 | 
			
		||||
                          gTime,
 | 
			
		||||
                          gTime))}")
 | 
			
		||||
                      .SendAsync();
 | 
			
		||||
 
 | 
			
		||||
@@ -11,8 +11,7 @@ namespace NadekoBot.Modules.Utility.Services;
 | 
			
		||||
public class RemindService : INService, IReadyExecutor, IRemindService
 | 
			
		||||
{
 | 
			
		||||
    private readonly Regex _regex =
 | 
			
		||||
        new(
 | 
			
		||||
            @"^(?:(?:at|on(?:\sthe)?)?\s*(?<date>(?:\d{2}:\d{2}\s)?\d{1,2}\.\d{1,2}(?:\.\d{2,4})?)|(?:in\s?)?\s*(?:(?<mo>\d+)(?:\s?(?:months?|mos?),?))?(?:(?:\sand\s|\s*)?(?<w>\d+)(?:\s?(?:weeks?|w),?))?(?:(?:\sand\s|\s*)?(?<d>\d+)(?:\s?(?:days?|d),?))?(?:(?:\sand\s|\s*)?(?<h>\d+)(?:\s?(?:hours?|h),?))?(?:(?:\sand\s|\s*)?(?<m>\d+)(?:\s?(?:minutes?|mins?|m),?))?)\s+(?:to:?\s+)?(?<what>(?:\r\n|[\r\n]|.)+)",
 | 
			
		||||
        new(@"^(?:(?:at|on(?:\sthe)?)?\s*(?<date>(?:\d{2}:\d{2}\s)?\d{1,2}\.\d{1,2}(?:\.\d{2,4})?)|(?:in\s?)?\s*(?:(?<mo>\d+)(?:\s?(?:months?|mos?),?))?(?:(?:\sand\s|\s*)?(?<w>\d+)(?:\s?(?:weeks?|w),?))?(?:(?:\sand\s|\s*)?(?<d>\d+)(?:\s?(?:days?|d),?))?(?:(?:\sand\s|\s*)?(?<h>\d+)(?:\s?(?:hours?|h),?))?(?:(?:\sand\s|\s*)?(?<m>\d+)(?:\s?(?:minutes?|mins?|m),?))?)\s+(?:to:?\s+)?(?<what>(?:\r\n|[\r\n]|.)+)",
 | 
			
		||||
            RegexOptions.Compiled | RegexOptions.Multiline);
 | 
			
		||||
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
@@ -62,7 +61,7 @@ public class RemindService : INService, IReadyExecutor, IRemindService
 | 
			
		||||
 | 
			
		||||
            Log.Information("Executing {ReminderCount} reminders", reminders.Count);
 | 
			
		||||
 | 
			
		||||
            // make groups of 5, with 1.5 second inbetween each one to ensure against ratelimits
 | 
			
		||||
            // make groups of 5, with 1.5 second in between each one to ensure against ratelimits
 | 
			
		||||
            foreach (var group in reminders.Chunk(5))
 | 
			
		||||
            {
 | 
			
		||||
                var executedReminders = group.ToList();
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,26 @@ public partial class Utility
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await ShowTodosAsync(todos);
 | 
			
		||||
            await Response()
 | 
			
		||||
                  .Paginated()
 | 
			
		||||
                  .Items(todos)
 | 
			
		||||
                  .PageSize(9)
 | 
			
		||||
                  .AddFooter(false)
 | 
			
		||||
                  .Page((items, _) =>
 | 
			
		||||
                  {
 | 
			
		||||
                      var eb = _sender.CreateEmbed()
 | 
			
		||||
                                      .WithOkColor()
 | 
			
		||||
                                      .WithTitle(GetText(strs.todo_list));
 | 
			
		||||
 | 
			
		||||
                      ShowTodoItem(items, eb);
 | 
			
		||||
 | 
			
		||||
                      eb.WithFooter(GetText(strs.todo_stats(todos.Length,
 | 
			
		||||
                          todos.Count(x => x.IsDone),
 | 
			
		||||
                          todos.Count(x => !x.IsDone))));
 | 
			
		||||
 | 
			
		||||
                      return eb;
 | 
			
		||||
                  })
 | 
			
		||||
                  .SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -81,23 +100,6 @@ public partial class Utility
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        private Task ShowTodosAsync(TodoModel[] todos)
 | 
			
		||||
            => Response()
 | 
			
		||||
               .Paginated()
 | 
			
		||||
               .Items(todos)
 | 
			
		||||
               .PageSize(9)
 | 
			
		||||
               .Page((items, _) =>
 | 
			
		||||
               {
 | 
			
		||||
                   var eb = _sender.CreateEmbed()
 | 
			
		||||
                            .WithOkColor()
 | 
			
		||||
                            .WithTitle(GetText(strs.todo_list));
 | 
			
		||||
 | 
			
		||||
                   ShowTodoItem(items, eb);
 | 
			
		||||
 | 
			
		||||
                   return eb;
 | 
			
		||||
               })
 | 
			
		||||
               .SendAsync();
 | 
			
		||||
 | 
			
		||||
        private static void ShowTodoItem(IReadOnlyCollection<TodoModel> todos, EmbedBuilder eb)
 | 
			
		||||
        {
 | 
			
		||||
            var sb = new StringBuilder();
 | 
			
		||||
@@ -155,8 +157,8 @@ public partial class Utility
 | 
			
		||||
                      .Page((items, _) =>
 | 
			
		||||
                      {
 | 
			
		||||
                          var eb = _sender.CreateEmbed()
 | 
			
		||||
                                   .WithTitle(GetText(strs.todo_archive_list))
 | 
			
		||||
                                   .WithOkColor();
 | 
			
		||||
                                          .WithTitle(GetText(strs.todo_archive_list))
 | 
			
		||||
                                          .WithOkColor();
 | 
			
		||||
 | 
			
		||||
                          foreach (var archivedList in items)
 | 
			
		||||
                          {
 | 
			
		||||
@@ -182,14 +184,19 @@ public partial class Utility
 | 
			
		||||
                      .Paginated()
 | 
			
		||||
                      .Items(list.Items)
 | 
			
		||||
                      .PageSize(9)
 | 
			
		||||
                      .AddFooter(false)
 | 
			
		||||
                      .Page((items, _) =>
 | 
			
		||||
                      {
 | 
			
		||||
                          var eb = _sender.CreateEmbed()
 | 
			
		||||
                                   .WithOkColor()
 | 
			
		||||
                                   .WithTitle(GetText(strs.todo_list));
 | 
			
		||||
                                          .WithOkColor()
 | 
			
		||||
                                          .WithTitle(GetText(strs.todo_archived_list));
 | 
			
		||||
 | 
			
		||||
                          ShowTodoItem(items, eb);
 | 
			
		||||
 | 
			
		||||
                          eb.WithFooter(GetText(strs.todo_stats(list.Items.Count,
 | 
			
		||||
                              list.Items.Count(x => x.IsDone),
 | 
			
		||||
                              list.Items.Count(x => !x.IsDone))));
 | 
			
		||||
 | 
			
		||||
                          return eb;
 | 
			
		||||
                      })
 | 
			
		||||
                      .SendAsync();
 | 
			
		||||
 
 | 
			
		||||
@@ -110,7 +110,7 @@ public class ClubService : INService, IClubService
 | 
			
		||||
            if (!temp.IsImage()) 
 | 
			
		||||
                return SetClubIconResult.InvalidFileType;
 | 
			
		||||
            
 | 
			
		||||
            if (temp.GetContentLength() > 5.Megabytes().Bytes)
 | 
			
		||||
            if (temp.GetContentLength() > 5.Megabytes())
 | 
			
		||||
                return SetClubIconResult.TooLarge;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -127,7 +127,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
			
		||||
    {
 | 
			
		||||
        _ = Task.Run(() => _levelUpQueue.RunAsync());
 | 
			
		||||
 | 
			
		||||
        using var timer = new PeriodicTimer(5.Seconds());
 | 
			
		||||
        using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));
 | 
			
		||||
        while (await timer.WaitForNextTickAsync())
 | 
			
		||||
        {
 | 
			
		||||
            await UpdateXp();
 | 
			
		||||
@@ -1364,7 +1364,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
 | 
			
		||||
                    using (var http = _httpFactory.CreateClient())
 | 
			
		||||
                    using (var temp = await http.GetAsync(imgUrl, HttpCompletionOption.ResponseHeadersRead))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (!temp.IsImage() || temp.GetContentLength() > 11.Megabytes().Bytes)
 | 
			
		||||
                        if (!temp.IsImage() || temp.GetContentLength() > 11 * 1024 * 1024)
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        var imgData = await temp.Content.ReadAsByteArrayAsync();
 | 
			
		||||
 
 | 
			
		||||
@@ -77,11 +77,6 @@
 | 
			
		||||
        <PackageReference Include="YamlDotNet" Version="15.1.2" />
 | 
			
		||||
        <PackageReference Include="SharpToken" Version="2.0.2" />
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="Humanizer" Version="2.14.1">
 | 
			
		||||
            <PrivateAssets>all</PrivateAssets>
 | 
			
		||||
            <Publish>True</Publish>
 | 
			
		||||
        </PackageReference>
 | 
			
		||||
 | 
			
		||||
        <PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,3 @@
 | 
			
		||||
using Humanizer.Localisation;
 | 
			
		||||
using System.Resources;
 | 
			
		||||
 | 
			
		||||
var pid = Environment.ProcessId;
 | 
			
		||||
 | 
			
		||||
var shardId = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ public sealed class ImageCache : IImageCache, INService
 | 
			
		||||
                var bytes = await http.GetByteArrayAsync(url);
 | 
			
		||||
                return bytes;
 | 
			
		||||
            },
 | 
			
		||||
            expiry: 48.Hours());
 | 
			
		||||
            expiry: TimeSpan.FromHours(48));
 | 
			
		||||
 | 
			
		||||
    private async Task<byte[]?> GetRandomImageDataAsync(Uri[] urls)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,17 @@ public static class StringExtensions
 | 
			
		||||
        => Regex.Replace(input, "<.*?>", string.Empty);
 | 
			
		||||
 | 
			
		||||
    public static string? TrimTo(this string? str, int maxLength, bool hideDots = false)
 | 
			
		||||
        => hideDots ? str?.Truncate(maxLength, string.Empty) : str?.Truncate(maxLength);
 | 
			
		||||
    {
 | 
			
		||||
        if (hideDots)
 | 
			
		||||
        {
 | 
			
		||||
            return str?.Substring(0, maxLength);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (str is null || str.Length <= maxLength)
 | 
			
		||||
            return str;
 | 
			
		||||
 | 
			
		||||
        return string.Concat(str.AsSpan(0, maxLength - 1), "…");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static string ToTitleCase(this string str)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,13 @@ public static class PatronExtensions
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public static string ToFullName(this QuotaPer per)
 | 
			
		||||
        => per.Humanize(LetterCasing.LowerCase);
 | 
			
		||||
        => per switch
 | 
			
		||||
        {
 | 
			
		||||
            QuotaPer.PerDay => "per day",
 | 
			
		||||
            QuotaPer.PerHour => "per hour",
 | 
			
		||||
            QuotaPer.PerMonth => "per month",
 | 
			
		||||
            _ => "Unknown",
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public static DateTime DayOfNextMonth(this DateTime date, int day)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Humanizer.Localisation;
 | 
			
		||||
using NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
@@ -178,13 +177,23 @@ public sealed class StatsService : IStatsService, IReadyExecutor, INService
 | 
			
		||||
    public string GetUptimeString(string separator = ", ")
 | 
			
		||||
    {
 | 
			
		||||
        var time = GetUptime();
 | 
			
		||||
        return time.Humanize(3, maxUnit: TimeUnit.Day, minUnit: TimeUnit.Minute);
 | 
			
		||||
        
 | 
			
		||||
        if (time.Days > 0)
 | 
			
		||||
            return $"{time.Days}d {time.Hours}h {time.Minutes}m";
 | 
			
		||||
        
 | 
			
		||||
        if (time.Hours > 0)
 | 
			
		||||
            return $"{time.Hours}h {time.Minutes}m";
 | 
			
		||||
        
 | 
			
		||||
        if (time.Minutes > 0)
 | 
			
		||||
            return $"{time.Minutes}m {time.Seconds}s";
 | 
			
		||||
        
 | 
			
		||||
        return $"{time.Seconds}s";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    public double GetPrivateMemoryMegabytes()
 | 
			
		||||
    {
 | 
			
		||||
        _currentProcess.Refresh();
 | 
			
		||||
        return _currentProcess.PrivateMemorySize64 / 1.Megabytes().Bytes;
 | 
			
		||||
        return _currentProcess.PrivateMemorySize64 / 1.Megabytes();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public GuildInfo GetGuildInfo(string name)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
using Humanizer.Localisation;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
@@ -11,7 +10,7 @@ public static class Extensions
 | 
			
		||||
{
 | 
			
		||||
    private static readonly Regex _urlRegex =
 | 
			
		||||
        new(@"^(https?|ftp)://(?<path>[^\s/$.?#].[^\s]*)$", RegexOptions.Compiled);
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Converts <see cref="DateTime"/> to <see cref="DateOnly"/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -51,7 +50,7 @@ public static class Extensions
 | 
			
		||||
 | 
			
		||||
    public static ulong[] GetGuildIds(this DiscordSocketClient client)
 | 
			
		||||
        => client.Guilds
 | 
			
		||||
            .Map(x => x.Id);
 | 
			
		||||
                 .Map(x => x.Id);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Generates a string in the format HHH:mm if timespan is >= 2m.
 | 
			
		||||
@@ -60,7 +59,30 @@ public static class Extensions
 | 
			
		||||
    /// <param name="span">Timespan to convert to string</param>
 | 
			
		||||
    /// <returns>Formatted duration string</returns>
 | 
			
		||||
    public static string ToPrettyStringHm(this TimeSpan span)
 | 
			
		||||
        => span.Humanize(2, minUnit: TimeUnit.Second);
 | 
			
		||||
    {
 | 
			
		||||
        if(span > TimeSpan.FromHours(24))
 | 
			
		||||
            return $"{span.Days:00}d:{span.Hours:00}h";
 | 
			
		||||
        
 | 
			
		||||
        if (span > TimeSpan.FromMinutes(2))
 | 
			
		||||
            return $"{span.Hours:00}h:{span.Minutes:00}m";
 | 
			
		||||
 | 
			
		||||
        return $"{span.Minutes:00}m:{span.Seconds:00}s";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static double Megabytes(this int mb)
 | 
			
		||||
        => mb * 1024d * 1024;
 | 
			
		||||
 | 
			
		||||
    public static TimeSpan Hours(this int hours)
 | 
			
		||||
        => TimeSpan.FromHours(hours);
 | 
			
		||||
 | 
			
		||||
    public static TimeSpan Minutes(this int minutes)
 | 
			
		||||
        => TimeSpan.FromMinutes(minutes);
 | 
			
		||||
    
 | 
			
		||||
    public static TimeSpan Days(this int days)
 | 
			
		||||
        => TimeSpan.FromDays(days);
 | 
			
		||||
 | 
			
		||||
    public static TimeSpan Seconds(this int seconds)
 | 
			
		||||
        => TimeSpan.FromSeconds(seconds);
 | 
			
		||||
 | 
			
		||||
    public static bool TryGetUrlPath(this string input, out string path)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -1080,12 +1080,14 @@
 | 
			
		||||
  "giveaway_list": "List of active giveways",
 | 
			
		||||
  "todo_list_empty": "Your todo list is empty." ,
 | 
			
		||||
  "todo_list": "Todo List",
 | 
			
		||||
  "todo_add_max_limit": "You've reached the maximum amount of todos you can have.",
 | 
			
		||||
  "todo_stats": "{0} items | {1} completed | {2} remaining",
 | 
			
		||||
  "todo_add_max_limit": "You'reached the maximum amount of todos you can have.",
 | 
			
		||||
  "todo_not_found": "Todo not found.",
 | 
			
		||||
  "todo_cleared": "All unarchived todos have been cleared.",
 | 
			
		||||
  "todo_no_todos": "There are no todos in your todo list.",
 | 
			
		||||
  "todo_archive_max_limit": "You've reached the maximum amount of archived todos you can have.",
 | 
			
		||||
  "todo_archive_empty": "You have no archived todos.",
 | 
			
		||||
  "todo_archive_list": "Archived Todo Lists",
 | 
			
		||||
  "todo_archive_not_found": "Archived todo list not found."
 | 
			
		||||
  "todo_archive_not_found": "Archived todo list not found.",
 | 
			
		||||
  "todo_archived_list": "Archived Todo List"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user