using Newtonsoft.Json; using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using NadekoBot.Common.Yml; namespace NadekoBot.Extensions; public static class StringExtensions { public static string PadBoth(this string str, int length) { var spaces = length - str.Length; var padLeft = spaces / 2 + str.Length; return str.PadLeft(padLeft).PadRight(length); } public static T MapJson(this string str) => JsonConvert.DeserializeObject(str); private static readonly HashSet lettersAndDigits = new HashSet(Enumerable.Range(48, 10) .Concat(Enumerable.Range(65, 26)) .Concat(Enumerable.Range(97, 26)) .Select(x => (char)x)); public static string StripHTML(this string input) { return Regex.Replace(input, "<.*?>", String.Empty); } // todo future maybe use humanizer lib for this kind of work public static string TrimTo(this string str, int maxLength, bool hideDots = false) { if (maxLength < 0) throw new ArgumentOutOfRangeException(nameof(maxLength), $"Argument {nameof(maxLength)} can't be negative."); if (maxLength == 0) return string.Empty; if (maxLength <= 3) return string.Concat(str.Select(c => '.')); if (str.Length < maxLength) return str; if (hideDots) { return string.Concat(str.Take(maxLength)); } else { return string.Concat(str.Take(maxLength - 1)) + "…"; } } public static string ToTitleCase(this string str) { var tokens = str.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries); for (var i = 0; i < tokens.Length; i++) { var token = tokens[i]; tokens[i] = token.Substring(0, 1).ToUpperInvariant() + token.Substring(1); } return string.Join(" ", tokens) .Replace(" Of ", " of ") .Replace(" The ", " the "); } /// /// Removes trailing S or ES (if specified) on the given string if the num is 1 /// /// /// /// /// String with the correct singular/plural form public static string SnPl(this string str, int? num, bool es = false) { if (str is null) throw new ArgumentNullException(nameof(str)); if (num is null) throw new ArgumentNullException(nameof(num)); return num == 1 ? str.Remove(str.Length - 1, es ? 2 : 1) : str; } //http://www.dotnetperls.com/levenshtein public static int LevenshteinDistance(this string s, string t) { var n = s.Length; var m = t.Length; var d = new int[n + 1, m + 1]; // Step 1 if (n == 0) { return m; } if (m == 0) { return n; } // Step 2 for (var i = 0; i <= n; d[i, 0] = i++) { } for (var j = 0; j <= m; d[0, j] = j++) { } // Step 3 for (var i = 1; i <= n; i++) { //Step 4 for (var j = 1; j <= m; j++) { // Step 5 var cost = t[j - 1] == s[i - 1] ? 0 : 1; // Step 6 d[i, j] = Math.Min( Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost); } } // Step 7 return d[n, m]; } public static async Task ToStream(this string str) { var ms = new MemoryStream(); var sw = new StreamWriter(ms); await sw.WriteAsync(str).ConfigureAwait(false); await sw.FlushAsync().ConfigureAwait(false); ms.Position = 0; return ms; } private static readonly Regex filterRegex = new Regex(@"discord(?:\.gg|\.io|\.me|\.li|(?:app)?\.com\/invite)\/(\w+)", RegexOptions.Compiled | RegexOptions.IgnoreCase); public static bool IsDiscordInvite(this string str) => filterRegex.IsMatch(str); public static string Unmention(this string str) => str.Replace("@", "ම", StringComparison.InvariantCulture); public static string SanitizeMentions(this string str, bool sanitizeRoleMentions = false) { str = str.Replace("@everyone", "@everyοne", StringComparison.InvariantCultureIgnoreCase) .Replace("@here", "@һere", StringComparison.InvariantCultureIgnoreCase); if (sanitizeRoleMentions) str = str.SanitizeRoleMentions(); return str; } public static string SanitizeRoleMentions(this string str) => str.Replace("<@&", "<ම&", StringComparison.InvariantCultureIgnoreCase); public static string SanitizeAllMentions(this string str) => str.SanitizeMentions().SanitizeRoleMentions(); public static string ToBase64(this string plainText) { var plainTextBytes = Encoding.UTF8.GetBytes(plainText); return Convert.ToBase64String(plainTextBytes); } public static string GetInitials(this string txt, string glue = "") => string.Join(glue, txt.Split(' ').Select(x => x.FirstOrDefault())); public static bool IsAlphaNumeric(this string txt) => txt.All(c => lettersAndDigits.Contains(c)); private static readonly Regex CodePointRegex = new Regex(@"(\\U(?[a-zA-Z0-9]{8})|\\u(?[a-zA-Z0-9]{4})|\\x(?[a-zA-Z0-9]{2}))", RegexOptions.Compiled); public static string UnescapeUnicodeCodePoints(this string input) { return CodePointRegex.Replace(input, me => { var str = me.Groups["code"].Value; var newString = YamlHelper.UnescapeUnicodeCodePoint(str); return newString; }); } }