namespace Xorog.UniversalExtensions; public static class UniversalExtensions { /// /// Get the current CPU Usage on all plattforms /// /// public static async Task GetCpuUsageForProcess() { var startTime = DateTime.UtcNow; var startCpuUsage = Process.GetCurrentProcess().TotalProcessorTime; await Task.Delay(500); var endTime = DateTime.UtcNow; var endCpuUsage = Process.GetCurrentProcess().TotalProcessorTime; var cpuUsedMs = (endCpuUsage - startCpuUsage).TotalMilliseconds; var totalMsPassed = (endTime - startTime).TotalMilliseconds; var cpuUsageTotal = cpuUsedMs / (Environment.ProcessorCount * totalMsPassed); return cpuUsageTotal * 100; } /// /// Copy a directory recursively /// /// /// /// /// public static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs) { // Get the subdirectories for the specified directory. DirectoryInfo dir = new(sourceDirName); if (!dir.Exists) { throw new DirectoryNotFoundException( "Source directory does not exist or could not be found: " + sourceDirName); } DirectoryInfo[] dirs = dir.GetDirectories(); // If the destination directory doesn't exist, create it. Directory.CreateDirectory(destDirName); // Get the files in the directory and copy them to the new location. FileInfo[] files = dir.GetFiles(); foreach (FileInfo file in files) { string tempPath = Path.Combine(destDirName, file.Name); file.CopyTo(tempPath, false); } // If copying subdirectories, copy them and their contents to new location. if (copySubDirs) { foreach (DirectoryInfo subdir in dirs) { string tempPath = Path.Combine(destDirName, subdir.Name); DirectoryCopy(subdir.FullName, tempPath, copySubDirs); } } } /// /// Generate an ASCII Progressbar /// /// The current progress /// The maximum progress /// How long the ASCII Progressbar should be (default: 44) /// What character the filled part should be (default: ) /// What character the not-filled part should be (default: ) /// What character the start-part should be (default: [) /// What character the end-part part should be (default: ]) /// A progressbar public static string GenerateASCIIProgressbar(double current, double max, int charlength = 44, char fill = '█', char empty = '∙', char start = '[', char end = ']') { long first = (long)Math.Round((current / max) * charlength, 0); long second = charlength - first; string mediadisplay = start.ToString(); for (long i = 0; i < first; i++) mediadisplay += fill; for (long i = 0; i < second; i++) mediadisplay += empty; mediadisplay += end; return mediadisplay; } /// /// Get the URL a redirect leads to (limited to StatusCodes 301, 303, 307, 308) /// /// The shortened URL /// The URL the redirect leads to public static async Task UnshortenUrl(string url) { HttpClient client = new(new HttpClientHandler() { AllowAutoRedirect = false }); var request = await client.GetAsync(url); if (request.StatusCode is HttpStatusCode.Found or HttpStatusCode.Redirect or HttpStatusCode.RedirectKeepVerb or HttpStatusCode.RedirectMethod or HttpStatusCode.PermanentRedirect or HttpStatusCode.TemporaryRedirect) { if (request.Headers is not null && request.Headers.Location is not null) return await UnshortenUrl(request.Headers.Location.AbsoluteUri); else return url; } else return url; } /// /// Try deleting the given files and directories until able to /// /// A list of directories to clean up /// A list of files to clean up /// public static async Task CleanupFilesAndDirectories(List DirectoryPaths, List FilePaths) { foreach (string DirectoryPath in DirectoryPaths) { while (Directory.Exists(DirectoryPath)) { try { Directory.Delete(DirectoryPath, true); } catch (Exception) { await Task.Delay(5000); } } } foreach (string FilePath in FilePaths) { while (File.Exists(FilePath)) { try { File.Delete(FilePath); } catch (Exception) { await Task.Delay(5000); } } } } /// /// Runs a long non-blocking delay, a work-around for Task.Delay only supporting Int32 /// /// A timespan of how long the delay should last /// A cancellation token source to cancel the action /// internal static async Task LongDelay(TimeSpan delay, CancellationTokenSource token) { var st = new Stopwatch(); st.Start(); while (true && !token.IsCancellationRequested) { var remaining = (delay - st.Elapsed).TotalMilliseconds; if (remaining <= 0) break; if (remaining > Int16.MaxValue) remaining = Int16.MaxValue; await Task.Delay(TimeSpan.FromMilliseconds(remaining), token.Token); } } /// /// Create a scheduled task /// /// The task to run /// The time to run the task /// An unique identifier of the task public static string CreateScheduleTask(this Task task, DateTime runTime, string CustomId = "") { string UID = Guid.NewGuid().ToString(); CancellationTokenSource CancellationToken = new CancellationTokenSource(); if (Math.Ceiling(runTime.GetTimespanUntil().TotalMilliseconds) < 0) runTime = DateTime.UtcNow.AddSeconds(1); _ = LongDelay(runTime.GetTimespanUntil(), CancellationToken).ContinueWith(x => { if (registeredScheduledTasks.ContainsKey(UID)) registeredScheduledTasks.Remove(UID); if (x.IsCompletedSuccessfully) task.Start(); }); registeredScheduledTasks.Add(UID, new taskInfo { tokenSource = CancellationToken, customId = CustomId, runTime = runTime}); return UID; } /// /// Deletes a scheduled task /// /// The task's unique identifier /// Throws if the task hasn't been found or if an internal error occured public static void DeleteScheduleTask(string UID) { if (!registeredScheduledTasks.ContainsKey(UID)) throw new Exception($"No sheduled task has been found with UID '{UID}'"); if (registeredScheduledTasks[ UID ].tokenSource is null) throw new Exception($"Internal: There is no token source registered the specified task."); registeredScheduledTasks[ UID ].tokenSource?.Cancel(); registeredScheduledTasks.Remove(UID); return; } /// /// Gets a list of all registered tasks /// /// A list of all registered tasks public static List>? GetScheduleTasks() { return registeredScheduledTasks.ToList(); } /// /// Gets a specific task /// /// The unique identifier of what task to get /// The task /// Throws if the task has not been found public static taskInfo GetScheduleTask(string UID) { if (!registeredScheduledTasks.ContainsKey(UID)) throw new Exception($"The specified task doesn't exist."); return registeredScheduledTasks[UID]; } /// /// Compute the SHA256-Hash for a given file /// /// /// public static string ComputeSHA256Hash(string filePath) { using SHA256 _SHA256 = SHA256.Create(); using FileStream fileStream = File.OpenRead(filePath); return BitConverter.ToString(_SHA256.ComputeHash(fileStream)).Replace("-", "").ToLowerInvariant(); } /// /// Get a timespan from now to the given time. Negative on values in the past. /// /// /// public static TimeSpan GetTimespanUntil(this DateTime until) => (until.ToUniversalTime() - DateTime.UtcNow); /// /// /// /// /// public static TimeSpan GetTimespanUntil(this DateTimeOffset until) => (until.ToUniversalTime() - DateTime.UtcNow); /// /// Get the total seconds until a given DateTime. Negative on values in the past. /// /// /// public static long GetTotalSecondsUntil(this DateTime until) => ((long)Math.Ceiling((until.ToUniversalTime() - DateTime.UtcNow).TotalSeconds)); /// /// /// /// /// public static long GetTotalSecondsUntil(this DateTimeOffset until) => ((long)Math.Ceiling((until.ToUniversalTime() - DateTime.UtcNow).TotalSeconds)); /// /// Get a timespan from now to the given time. Negative on values in the future. /// /// /// public static TimeSpan GetTimespanSince(this DateTime until) => (DateTime.UtcNow - until.ToUniversalTime()); /// /// /// /// /// public static TimeSpan GetTimespanSince(this DateTimeOffset until) => (DateTime.UtcNow - until.ToUniversalTime()); /// /// Get the total seconds since a given DateTime. Negative on values in the future. /// /// /// public static long GetTotalSecondsSince(this DateTime until) => ((long)Math.Ceiling((DateTime.UtcNow - until.ToUniversalTime()).TotalSeconds)); /// /// /// /// /// public static long GetTotalSecondsSince(this DateTimeOffset until) => ((long)Math.Ceiling((DateTime.UtcNow - until.ToUniversalTime()).TotalSeconds)); /// /// Get a short human readable string for the given amount of seconds /// /// /// /// public static string GetShortHumanReadable(this int seconds, TimeFormat timeFormat = TimeFormat.DAYS) => TimeSpan.FromSeconds(seconds).GetShortTimeFormat(timeFormat); /// /// /// /// /// /// public static string GetShortHumanReadable(this long seconds, TimeFormat timeFormat = TimeFormat.DAYS) => TimeSpan.FromSeconds(seconds).GetShortTimeFormat(timeFormat); /// /// Get a human readable string for the given amount of seconds /// /// /// /// public static string GetHumanReadable(this int seconds, TimeFormat timeFormat = TimeFormat.DAYS) => TimeSpan.FromSeconds(seconds).GetTimeFormat(timeFormat); /// /// /// /// /// /// public static string GetHumanReadable(this long seconds, TimeFormat timeFormat = TimeFormat.DAYS) => TimeSpan.FromSeconds(seconds).GetTimeFormat(timeFormat); /// /// /// /// /// /// public static string GetHumanReadable(this TimeSpan timeSpan, TimeFormat timeFormat = TimeFormat.DAYS) => timeSpan.GetTimeFormat(timeFormat); /// /// Check if a string contains only digits /// /// /// public static bool IsDigitsOnly(this string str) { foreach (char c in str) { if (c is < '0' or > '9') return false; } return true; } /// /// Get all digits from a string /// /// /// public static string GetAllDigits(this string str) => new(str.Where(Char.IsDigit).ToArray()); } public static class StringExt { /// /// Shorten a string to the given length /// /// /// /// public static string Truncate(this string value, int maxLength) { if (string.IsNullOrEmpty(value)) return value; return value.Length <= maxLength ? value : value[ ..maxLength ]; } /// /// Shorten a string to the given length and add ".." at the end /// /// /// /// public static string TruncateWithIndication(this string value, int maxLength) { if (string.IsNullOrEmpty(value)) return value; return value.Length <= maxLength ? value : $"{value[ ..maxLength ]}.."; } /// /// Remove unsupported characters from string to generate a valid filename /// /// The string with potentionally unwanted characters /// The character the unwanted characters get replaced with (default: _) /// A valid filename public static string MakeValidFileName(this string name, char replace_char = '_') { string invalidChars = System.Text.RegularExpressions.Regex.Escape(new string(System.IO.Path.GetInvalidFileNameChars())); string invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars); return System.Text.RegularExpressions.Regex.Replace(name, invalidRegStr, replace_char.ToString()).Replace('&', replace_char); } /// /// Compute the SHA256-Hash for the given string /// /// /// public static string ComputeSHA256Hash(string str) { using SHA256 _SHA256 = SHA256.Create(); return BitConverter.ToString(_SHA256.ComputeHash(Encoding.ASCII.GetBytes(str))).Replace("-", "").ToLowerInvariant(); } public static string FirstLetterToUpper(this string str) { return $"{str.First().ToString().ToUpper()}{str.Remove(0, 1)}"; } }