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, bool UseHeadMethod = true)
{
HttpClient client = new(new HttpClientHandler()
{
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.GZip,
});
client.Timeout = TimeSpan.FromSeconds(10);
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36");
client.DefaultRequestHeaders.Add("upgrade-insecure-requests", "1");
client.DefaultRequestHeaders.Add("accept-encoding", "gzip, deflate, br");
client.DefaultRequestHeaders.Add("accept-language", "en-US,en;q=0.9");
client.MaxResponseContentBufferSize = 4096;
HttpRequestMessage requestMessage = new HttpRequestMessage((UseHeadMethod ? HttpMethod.Head : HttpMethod.Get), url);
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
var request_task = client.SendAsync(requestMessage, cancellationTokenSource.Token);
try
{
await request_task.WaitAsync(TimeSpan.FromSeconds(3));
}
catch (Exception)
{
if (UseHeadMethod)
return await UnshortenUrl(url, false);
throw;
}
if (!request_task.IsCompleted)
cancellationTokenSource.Cancel();
if (UseHeadMethod && request_task.IsFaulted && request_task.Exception.InnerException.GetType().FullName == "System.Net.Http.HttpRequestException")
return await UnshortenUrl(url, false);
var statuscode = request_task.Result.StatusCode;
var header = request_task.Result.Headers;
if (UseHeadMethod && statuscode is HttpStatusCode.NotFound or HttpStatusCode.InternalServerError)
return await UnshortenUrl(url, false);
if (statuscode is HttpStatusCode.Found
or HttpStatusCode.Redirect
or HttpStatusCode.SeeOther
or HttpStatusCode.RedirectKeepVerb
or HttpStatusCode.RedirectMethod
or HttpStatusCode.PermanentRedirect
or HttpStatusCode.TemporaryRedirect)
{
if (header is not null && header.Location is not null)
return await UnshortenUrl(header.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());
///
/// Get country flag emoji based on Iso Country Code
///
///
///
public static string IsoCountryCodeToFlagEmoji(this string country)
{
return string.Concat(country.ToUpper().Select(x => char.ConvertFromUtf32(x + 0x1F1A5)));
}
///
/// Get closest Color to given Color
///
///
///
///
public static Color GetClosestColor(List colorArray, Color baseColor)
{
var colors = colorArray.Select(x => new { Value = x, Diff = Internal.GetDiff(x, baseColor) }).ToList();
var min = colors.Min(x => x.Diff);
return colors.Find(x => x.Diff == min).Value;
}
}
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)}";
}
}