using System.Text; using System.Text.RegularExpressions; using KfChatDotNetBot.Models.DbModels; namespace KfChatDotNetBot.Extensions; public static class Extensions { /// Emotes are encoded like [emote:161238:russW] which translates to -> https://files.kick.com/emotes/161238/fullsize public static string TranslateKickEmotes(this string s) { Regex regex = new Regex(@"\[(.+?):(\d+):(\S+?)\]"); var matches = regex.Matches(s); if (matches.Count == 0) { return s; } foreach (Match match in matches) { // First group is the whole matched string // 0 -> [emote:161238:russW] // 1 -> emote // 2 -> 161238 // 3 -> russW var emoteId = match.Groups[2]; s = s.Replace(match.Value, $"[img]https://files.kick.com/emotes/{emoteId}/fullsize[/img]"); } return s; } public static IEnumerable SplitMessage(this string s, int partLength = 500, int partLimit = 5) { if (s == null) throw new ArgumentNullException(nameof(s)); if (partLength <= 0) throw new ArgumentException("Part length has to be positive.", nameof(partLength)); var parts = 0; for (var i = 0; i < s.Length; i += partLength) { parts++; if (parts > partLimit) break; yield return s.Substring(i, Math.Min(partLength, s.Length - i)); } } /// /// Split messages to x number of bytes while avoiding splitting mid-word where possible /// /// String that should get split /// Length limit, no part should be > than the number of bytes specified /// Limit for how many parts to return (returns first n elements). Set to 0 to disable. /// Separator to use when splitting up parts of the message /// List of string values which represents the split up message public static List FancySplitMessage(this string s, int partLengthBytes = 1023, int partLimit = 5, string partSeparator = " ") { var output = new List(); var part = string.Empty; foreach (var word in s.Split(partSeparator)) { if (word.Utf8LengthBytes() > partLengthBytes) { // Add the part already in memory if there is one if (part != string.Empty) { output.Add(part.TrimEnd()); part = string.Empty; } // Breaks into chunks of x size which will break really long URLs etc. but no other way really output.AddRange(word.ChunkBytes(partLengthBytes)); continue; } if (part.Utf8LengthBytes() + word.Utf8LengthBytes() > partLengthBytes) { // TrimEnd() to remove trailing spaces output.Add(part.TrimEnd()); part = word + partSeparator; continue; } part += word + partSeparator; } // Add on whatever remains if (part != string.Empty) { output.Add(part.TrimEnd()); } if (partLimit != 0 && output.Count > partLimit) { return output.Take(partLimit).ToList(); } return output; } public static int Utf8LengthBytes(this string s) { return Encoding.UTF8.GetByteCount(s); } public static IEnumerable ChunkBytes(this string input, int bytesPerChunk) { var bytes = Encoding.UTF8.GetBytes(input); for (var i = 0; i < bytes.Length; i += bytesPerChunk) { var chunkSize = Math.Min(bytesPerChunk, bytes.Length - i); yield return Encoding.UTF8.GetString(bytes, i, chunkSize); } } public static string TruncateBytes(this string s, int limitBytes) { if (string.IsNullOrEmpty(s) || limitBytes <= 0) { return string.Empty; } if (s.Utf8LengthBytes() <= limitBytes) { return s; } var bytes = Encoding.UTF8.GetBytes(s); var charCount = Encoding.UTF8.GetCharCount(bytes, 0, limitBytes); return s.Substring(0, charCount); } // Placeholder for future expansion involving custom titles public static string FormatUsername(this UserDbModel user) { return $"@{user.KfUsername}"; } }