using System.Diagnostics; using System.Globalization; using System.Text.Json; using System.Text.RegularExpressions; using Nadeko.Common.Medusa; namespace NadekoBot.Extensions; public static class Extensions { private static readonly Regex _urlRegex = new(@"^(https?|ftp)://(?[^\s/$.?#].[^\s]*)$", RegexOptions.Compiled); /// /// Converts to /// /// The to convert. /// The . public static DateOnly ToDateOnly(this DateTime dateTime) => DateOnly.FromDateTime(dateTime); /// /// Determines if is before today /// /// The to check. /// True if is before today. 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); /// /// Generates a string in the format HHH:mm if timespan is >= 2m. /// Generates a string in the format 00:mm:ss if timespan is less than 2m. /// /// Timespan to convert to string /// Formatted duration string 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); /// /// First 10 characters of teh bot token. /// 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, IMedusaLoaderService medusae, CultureInfo culture, string prefix) { string description; if (cmd.Remarks?.StartsWith("medusa///") ?? false) { // command method name is kept in Summary // medusa/// is kept in remarks // this way I can find the name of the medusa, and then name of the command for which // the description should be loaded var medusaName = cmd.Remarks.Split("///")[1]; description = medusae.GetCommandDescription(medusaName, 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, IMedusaLoaderService medusae, CultureInfo culture, string prefix) { string[] args; if (cmd.Remarks?.StartsWith("medusa///") ?? false) { // command method name is kept in Summary // medusa/// is kept in remarks // this way I can find the name of the medusa, // and command for which data should be loaded var medusaName = cmd.Remarks.Split("///")[1]; args = medusae.GetCommandExampleArgs(medusaName, 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> 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(this T any, JsonSerializerOptions? options = null) => JsonSerializer.Serialize(any, options); public static Stream ToStream(this IEnumerable bytes, bool canWrite = false) { var ms = new MemoryStream(bytes as byte[] ?? bytes.ToArray(), canWrite); ms.Seek(0, SeekOrigin.Begin); return ms; } public static IEnumerable 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(); } }