elliebot/src/EllieBot/_common/_Extensions/Extensions.cs
2024-06-26 19:44:07 +12:00

238 lines
No EOL
8.1 KiB
C#

using System.Diagnostics;
using System.Globalization;
using System.Text.Json;
using System.Text.RegularExpressions;
using Ellie.Common.Marmalade;
namespace EllieBot.Extensions;
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>
/// <param name="dateTime"> The <see cref="DateTime"/> to convert. </param>
/// <returns> The <see cref="DateOnly"/>. </returns>
public static DateOnly ToDateOnly(this DateTime dateTime)
=> DateOnly.FromDateTime(dateTime);
/// <summary>
/// Determines if <see cref="DateTime"/> is before today
/// </summary>
/// <param name="date"> The <see cref="DateTime"/> to check. </param>
/// <returns> True if <see cref="DateTime"/> is before today. </returns>
public static bool IsBeforeToday(this DateTime date)
=> date < DateTime.UtcNow.Date;
public static Task EditAsync(this IUserMessage msg, SmartText text)
=> text switch
{
SmartEmbedText set => msg.ModifyAsync(x =>
{
x.Embed = set.IsValid ? set.GetEmbed().Build() : null;
x.Content = set.PlainText?.SanitizeMentions() ?? "";
}),
SmartEmbedTextArray set => msg.ModifyAsync(x =>
{
x.Embeds = set.GetEmbedBuilders().Map(eb => eb.Build());
x.Content = set.Content?.SanitizeMentions() ?? "";
}),
SmartPlainText spt => msg.ModifyAsync(x =>
{
x.Content = spt.Text.SanitizeMentions();
x.Embed = null;
}),
_ => throw new ArgumentOutOfRangeException(nameof(text))
};
public static ulong[] GetGuildIds(this DiscordSocketClient client)
=> client.Guilds
.Map(x => x.Id);
/// <summary>
/// Generates a string in the format HHH:mm if timespan is &gt;= 2m.
/// Generates a string in the format 00:mm:ss if timespan is less than 2m.
/// </summary>
/// <param name="span">Timespan to convert to string</param>
/// <returns>Formatted duration string</returns>
public static string ToPrettyStringHm(this TimeSpan span)
{
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)
{
var match = _urlRegex.Match(input);
if (match.Success)
{
path = match.Groups["path"].Value;
return true;
}
path = string.Empty;
return false;
}
public static IEmote ToIEmote(this string emojiStr)
=> Emote.TryParse(emojiStr, out var maybeEmote) ? maybeEmote : new Emoji(emojiStr);
/// <summary>
/// First 10 characters of teh bot token.
/// </summary>
public static string RedisKey(this IBotCredentials bc)
=> bc.Token[..10];
public static bool IsAuthor(this IMessage msg, IDiscordClient client)
=> msg.Author?.Id == client.CurrentUser.Id;
public static string RealSummary(
this CommandInfo cmd,
IBotStrings strings,
IMarmaladeLoaderService marmalades,
CultureInfo culture,
string prefix)
{
string description;
if (cmd.Remarks?.StartsWith("marmalade///") ?? false)
{
// command method name is kept in Summary
// marmalade///<marmalade-name-here> is kept in remarks
// this way I can find the name of the marmalade, and then name of the command for which
// the description should be loaded
var marmaladeName = cmd.Remarks.Split("///")[1];
description = marmalades.GetCommandDescription(marmaladeName, cmd.Summary, culture);
}
else
{
description = strings.GetCommandStrings(cmd.Summary, culture).Desc;
}
return string.Format(description, prefix);
}
public static string[] RealRemarksArr(
this CommandInfo cmd,
IBotStrings strings,
IMarmaladeLoaderService marmalades,
CultureInfo culture,
string prefix)
{
string[] args;
if (cmd.Remarks?.StartsWith("marmalade///") ?? false)
{
// command method name is kept in Summary
// marmalade///<marmalade-name-here> is kept in remarks
// this way I can find the name of the marmalade,
// and command for which data should be loaded
var marmaladeName = cmd.Remarks.Split("///")[1];
args = marmalades.GetCommandExampleArgs(marmaladeName, cmd.Summary, culture);
}
else
{
args = strings.GetCommandStrings(cmd.Summary, culture).Examples;
}
return args.Map(arg => GetFullUsage(cmd.Aliases.First(), arg, prefix));
}
private static string GetFullUsage(string commandName, string args, string prefix)
=> $"{prefix}{commandName} {string.Format(args, prefix)}".TrimEnd();
public static EmbedBuilder AddPaginatedFooter(this EmbedBuilder embed, int curPage, int? lastPage)
{
if (lastPage is not null)
return embed.WithFooter($"{curPage + 1} / {lastPage + 1}");
return embed.WithFooter((curPage + 1).ToString());
}
// public static EmbedBuilder WithOkColor(this EmbedBuilder eb)
// => eb.WithColor(EmbedColor.Ok);
//
// public static EmbedBuilder WithPendingColor(this EmbedBuilder eb)
// => eb.WithColor(EmbedColor.Pending);
//
// public static EmbedBuilder WithErrorColor(this EmbedBuilder eb)
// => eb.WithColor(EmbedColor.Error);
//
public static IMessage DeleteAfter(this IUserMessage msg, float seconds, ILogCommandService? logService = null)
{
Task.Run(async () =>
{
await Task.Delay((int)(seconds * 1000));
if (logService is not null)
logService.AddDeleteIgnore(msg.Id);
try
{
await msg.DeleteAsync();
}
catch
{
}
});
return msg;
}
public static ModuleInfo GetTopLevelModule(this ModuleInfo module)
{
while (module.Parent is not null)
module = module.Parent;
return module;
}
public static string GetGroupName(this ModuleInfo module)
=> module.Name.Replace("Commands", "", StringComparison.InvariantCulture);
public static async Task<IEnumerable<IGuildUser>> GetMembersAsync(this IRole role)
{
var users = await role.Guild.GetUsersAsync(CacheMode.CacheOnly);
return users.Where(u => u.RoleIds.Contains(role.Id));
}
public static string ToJson<T>(this T any, JsonSerializerOptions? options = null)
=> JsonSerializer.Serialize(any, options);
public static Stream ToStream(this IEnumerable<byte> bytes, bool canWrite = false)
{
var ms = new MemoryStream(bytes as byte[] ?? bytes.ToArray(), canWrite);
ms.Seek(0, SeekOrigin.Begin);
return ms;
}
public static IEnumerable<IRole> GetRoles(this IGuildUser user)
=> user.RoleIds.Select(r => user.Guild.GetRole(r)).Where(r => r is not null);
// todo remove
public static void Lap(this Stopwatch sw, string checkpoint)
{
Log.Information("Checkpoint {CheckPoint}: {Time}ms", checkpoint, sw.Elapsed.TotalMilliseconds);
sw.Restart();
}
}