diff --git a/SharpENDEC/App.config b/SharpENDEC/App.config index 560f8b5..883ef30 100644 --- a/SharpENDEC/App.config +++ b/SharpENDEC/App.config @@ -1,13 +1,13 @@ - + -
-
+
+
- + @@ -141,12 +141,12 @@ - + - + @@ -154,4 +154,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SharpENDEC/ENDEC/AlertProcessor.cs b/SharpENDEC/ENDEC/AlertProcessor.cs index 255838c..e1ccb3b 100644 --- a/SharpENDEC/ENDEC/AlertProcessor.cs +++ b/SharpENDEC/ENDEC/AlertProcessor.cs @@ -1,11 +1,15 @@ -using SharpENDEC.Properties; +using NAudio.Wave.SampleProviders; +using NAudio.Wave; +using SharpENDEC.Properties; using System; using System.Collections.Generic; -using System.Drawing; +using System.Globalization; using System.IO; +using System.Linq; +using System.Net.Http; +using System.Speech.Synthesis; using System.Text.RegularExpressions; using System.Threading; -using System.Threading.Tasks; namespace SharpENDEC { @@ -33,37 +37,52 @@ public static void AlertProcessor() public static void ProcessAlertItem(SharpDataItem relayItem) { - bool IsUI = Settings.Default.WirelessAlertMode; - foreach (Match match in Regex.Matches(relayItem.Data, @"([^<]+)\s*([^<]+)", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline)) + // check if file is even valid + if (!relayItem.Data.StartsWith("\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); - Match statusMatch = Regex.Match(relayItem.Data, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); - Match messageTypeMatch = Regex.Match(relayItem.Data, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); - MatchCollection broadcastImmediatelyMatches = Regex.Matches(relayItem.Data, @"layer:SOREM:1.0:Broadcast_Immediately\s*\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); - MatchCollection urgencyMatches = Regex.Matches(relayItem.Data, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); - MatchCollection severityMatches = Regex.Matches(relayItem.Data, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + //bool IsUI = Settings.Default.WirelessAlertMode; + //foreach (Match match in ValueNameRegex.Matches(relayItem.Data)) + //{ + // if (match.Groups[1].Value == "layer:SOREM:2.0:WirelessText") + // { + // if (!string.IsNullOrWhiteSpace(match.Groups[2].Value)) + // { + // IsUI = true; + // break; + // } + // } + + // //ConsoleExt.WriteLine($"valueName: {match.Groups[1].Value}"); + // //ConsoleExt.WriteLine($"value: {match.Groups[2].Value}"); + //} + + Match sentMatch = SentRegex.Match(relayItem.Data); + Match statusMatch = StatusRegex.Match(relayItem.Data); + Match messageTypeMatch = MessageTypeRegex.Match(relayItem.Data); + MatchCollection broadcastImmediatelyMatches = BroadcastImmediatelyRegex.Matches(relayItem.Data); + MatchCollection urgencyMatches = UrgencyRegex.Matches(relayItem.Data); + MatchCollection severityMatches = InfoRegex.Matches(relayItem.Data); bool final = false; for (int i = 0; i < severityMatches.Count; i++) { - if (Check.Config(relayItem.Data, statusMatch.Groups[1].Value, messageTypeMatch.Groups[1].Value, severityMatches[i].Groups[1].Value, urgencyMatches[i].Groups[1].Value, broadcastImmediatelyMatches[i].Groups[1].Value)) + try { - final = true; - break; + if (Check.Config(relayItem.Data, statusMatch.Groups[1].Value, messageTypeMatch.Groups[1].Value, severityMatches[i].Groups[1].Value, urgencyMatches[i].Groups[1].Value, broadcastImmediatelyMatches[i].Groups[1].Value)) + { + final = true; + break; + } + } + catch (Exception e) + { + ConsoleExt.WriteLineErr($"[Alert Processor] An incompatible or damaged XML was detected, and won't be processed further. {e.Message}"); + return; } } @@ -73,7 +92,7 @@ public static void ProcessAlertItem(SharpDataItem relayItem) return; } - MatchCollection infoMatches = Regex.Matches(relayItem.Data, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + MatchCollection infoMatches = InfoRegex.Matches(relayItem.Data); int infoProc = 0; foreach (Match infoMatch in infoMatches) @@ -82,7 +101,7 @@ public static void ProcessAlertItem(SharpDataItem relayItem) ConsoleExt.WriteLine($"[Alert Processor] {LanguageStrings.GenericProcessingValueOfValue(Settings.Default.CurrentLanguage, infoProc, infoMatches.Count)}"); string infoEN = $"{infoMatch.Groups[1].Value}"; string lang = "en"; - if (Regex.Match(infoEN, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value == "fr-CA") + if (LanguageRegex.Match(infoEN).Groups[1].Value == "fr-CA") { lang = "fr"; } @@ -93,12 +112,13 @@ public static void ProcessAlertItem(SharpDataItem relayItem) string Status = statusMatch.Groups[1].Value; string MsgType = messageTypeMatch.Groups[1].Value; - string EventType = Regex.Match(infoEN, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value; - string urgency = Regex.Match(infoEN, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value; - string severity = Regex.Match(infoEN, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value; - //string when = Regex.Match(@"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value; + Match effectiveMatch = EffectiveRegex.Match(relayItem.Data); + Match expiryMatch = ExpiresRegex.Match(relayItem.Data); + string EventType = EventRegex.Match(infoEN).Groups[1].Value; + string urgency = UrgencyRegex.Match(infoEN).Groups[1].Value; + string severity = SeverityRegex.Match(infoEN).Groups[1].Value; string broadcastImmediately; - Match broadcastImmediatelyMatch = Regex.Match(infoEN, @"layer:SOREM:1.0:Broadcast_Immediately\s*\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + Match broadcastImmediatelyMatch = BroadcastImmediatelyRegex.Match(infoEN); if (broadcastImmediatelyMatch.Success) { broadcastImmediately = broadcastImmediatelyMatch.Groups[1].Value; @@ -110,107 +130,140 @@ public static void ProcessAlertItem(SharpDataItem relayItem) EventDetails EventInfo = GetEventDetails(EventType); - ConsoleExt.WriteLine(Status, ConsoleColor.DarkGray); - ConsoleExt.WriteLine(MsgType, ConsoleColor.DarkGray); - ConsoleExt.WriteLine($"{EventType} | {EventInfo.FriendlyName}", ConsoleColor.DarkGray); - ConsoleExt.WriteLine(urgency, ConsoleColor.DarkGray); - ConsoleExt.WriteLine(severity, ConsoleColor.DarkGray); - ConsoleExt.WriteLine(broadcastImmediately, ConsoleColor.DarkGray); - ConsoleExt.WriteLine(sentMatch.Groups[1].Value, ConsoleColor.DarkGray); + ConsoleExt.WriteLine($"Status: {Status}", ConsoleColor.Green); + ConsoleExt.WriteLine($"Message Type: {MsgType}", ConsoleColor.Green); + ConsoleExt.WriteLine($"Event Type: {EventType}", ConsoleColor.Green); + ConsoleExt.WriteLine($"Urgency: {urgency}", ConsoleColor.Green); + ConsoleExt.WriteLine($"Severity: {severity}", ConsoleColor.Green); + ConsoleExt.WriteLine($"Broadcast Immediately: {broadcastImmediately}", ConsoleColor.Green); + ConsoleExt.WriteLine($"Sent: {sentMatch.Groups[1].Value}", ConsoleColor.Green); + ConsoleExt.WriteLine($"Effective: {effectiveMatch.Groups[1].Value}", ConsoleColor.Green); + ConsoleExt.WriteLine($"Expires: {expiryMatch.Groups[1].Value}", ConsoleColor.Green); + + if (DateTime.Parse(expiryMatch.Groups[1].Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal) <= DateTime.Now) + { + ConsoleExt.WriteLine($"[Alert Processor] {LanguageStrings.AlertIgnoredDueToExpiry(Settings.Default.CurrentLanguage)}", ConsoleColor.DarkGray); + continue; + } bool Stop = false; if (Check.Config(infoEN, statusMatch.Groups[1].Value, MsgType, severity, urgency, broadcastImmediately)) { + //foreach (var handler in handlers) + //{ + // var handlerInstance = (ISharpPlugin)Activator.CreateInstance(handler); + // if (!handlerInstance.RelayAlert(infoEN, statusMatch.Groups[1].Value, MsgType, severity, urgency, broadcastImmediately)) + // Stop = true; + //} + foreach (string EventName in Settings.Default.EnforceEventBlacklist) { if (EventType.ToLower() == EventName.ToLower()) { Stop = true; + break; } } if (Stop) { ConsoleExt.WriteLine($"[Alert Processor] {LanguageStrings.AlertIgnoredDueToBlacklist(Settings.Default.CurrentLanguage)}", ConsoleColor.DarkGray); + //foreach (var handler in handlers) + //{ + // var handlerInstance = (ISharpPlugin)Activator.CreateInstance(handler); + // handlerInstance.AlertBlacklisted(); + //} continue; } ConsoleExt.WriteLine($"[Alert Processor] {LanguageStrings.GeneratingProductText(Settings.Default.CurrentLanguage)}"); Generate gen = new Generate(infoEN, MsgType, sentMatch.Groups[1].Value); - var info = gen.BroadcastInfo(lang); //if (true) //(!string.IsNullOrWhiteSpace(info.BroadcastText)) { //ConsoleExt.WriteLine($"[Alert Processor] {LanguageStrings.GeneratingProductAudio}"); File.WriteAllText($"{AssemblyDirectory}\\inactive-text.txt", string.Empty); - File.WriteAllText($"{AssemblyDirectory}\\active-text.txt", $"{info.BroadcastText}\x20"); - File.WriteAllText($"{AssemblyDirectory}\\static-text.txt", $"{info.BroadcastText}\x20"); + File.WriteAllText($"{AssemblyDirectory}\\active-text.txt", $"{info}\x20"); + File.WriteAllText($"{AssemblyDirectory}\\static-text.txt", $"{info}\x20"); - ConsoleExt.WriteLine($"[Alert Processor] -> {info.BroadcastText}", ConsoleColor.Magenta); + ConsoleExt.WriteLine($"[Alert Processor] -> {info}", ConsoleColor.Magenta); - gen.GenerateAudio(info.BroadcastText, lang); + //foreach (var handler in handlers) + //{ + // var handlerInstance = (ISharpPlugin)Activator.CreateInstance(handler); + // handlerInstance.ProvideBroadcastText(info.BroadcastText); + //} - if (IsUI) - { - Color BackColor; - Color ForeColor; + gen.GenerateAudio(info, lang); - switch (severity.ToLower()) - { - case "extreme": - BackColor = Color.Red; - ForeColor = Color.Yellow; - break; - case "severe": - BackColor = Color.OrangeRed; - ForeColor = Color.Black; - break; - case "moderate": - BackColor = Color.Gold; - ForeColor = Color.Black; - break; - case "minor": - BackColor = Color.LightGreen; - ForeColor = Color.Black; - break; - case "unknown": - default: - BackColor = Color.White; - ForeColor = Color.Black; - break; - } + //foreach (var handler in handlers) + //{ + // var handlerInstance = (ISharpPlugin)Activator.CreateInstance(handler); + // handlerInstance.AlertRelayingNow(info.BroadcastText); + //} - //Task.Run(() => - //{ - // AlertForm af = new AlertForm - // { - // PlayAudio = () => Play($"{AudioDirectory}\\audio.wav"), - // EventBackColor = BackColor, - // EventForeColor = ForeColor, - // EventTextContent = EventInfo.FriendlyName - // }; - // af.Show(); - //}).Wait(30000); - - Task.Run(() => - { - NotifyOverlay no = new NotifyOverlay - { - EventShortInfoText = $"{EventInfo.FriendlyName}", - EventLongInfoText = $"{info.BroadcastText}", - EventTypeText = $"{EventInfo.FriendlyName}" - }; - no.ShowDialog(); - }).Wait(30000); - } - else + //if (IsUI) + //{ + // Color BackColor; + // Color ForeColor; + + // switch (severity.ToLower()) + // { + // case "extreme": + // BackColor = Color.Red; + // ForeColor = Color.Yellow; + // break; + // case "severe": + // BackColor = Color.OrangeRed; + // ForeColor = Color.Black; + // break; + // case "moderate": + // BackColor = Color.Gold; + // ForeColor = Color.Black; + // break; + // case "minor": + // BackColor = Color.LightGreen; + // ForeColor = Color.Black; + // break; + // case "unknown": + // default: + // BackColor = Color.White; + // ForeColor = Color.Black; + // break; + // } + + // //Task.Run(() => + // //{ + // // AlertForm af = new AlertForm + // // { + // // PlayAudio = () => Play($"{AudioDirectory}\\audio.wav"), + // // EventBackColor = BackColor, + // // EventForeColor = ForeColor, + // // EventTextContent = EventInfo.FriendlyName + // // }; + // // af.Show(); + // //}).Wait(30000); + + // Task.Run(() => + // { + // NotifyOverlay no = new NotifyOverlay + // { + // EventShortInfoText = $"{EventInfo.FriendlyName}", + // EventLongInfoText = $"{info}", + // EventTypeText = $"{EventInfo.FriendlyName}" + // }; + // no.ShowDialog(); + // }).Wait(30000); + //} + //else { + ConsoleExt.WriteLine($"[Alert Processor] {LanguageStrings.PlayingAudio(Settings.Default.CurrentLanguage)}"); ConsoleExt.WriteLine($"[Alert Processor] {Play($"{AudioDirectory}\\in.wav").AudioLength.TotalMilliseconds} millisecond(s) played"); - if (EventInfo.Severity.Contains("Severe") || EventInfo.Severity.Contains("Extreme")) + if (EventInfo.Severity.ToLower().Contains("severe") || EventInfo.Severity.ToLower().Contains("extreme")) ConsoleExt.WriteLine($"[Alert Processor] {Play($"{AudioDirectory}\\attn.wav").AudioLength.TotalMilliseconds} millisecond(s) played."); else { @@ -226,7 +279,7 @@ public static void ProcessAlertItem(SharpDataItem relayItem) } File.WriteAllText($"{AssemblyDirectory}\\active-text.txt", string.Empty); - File.WriteAllText($"{AssemblyDirectory}\\inactive-text.txt", $"{info.BroadcastText}\x20"); + File.WriteAllText($"{AssemblyDirectory}\\inactive-text.txt", $"{info}\x20"); } //else //{ @@ -240,5 +293,691 @@ public static void ProcessAlertItem(SharpDataItem relayItem) } //ConsoleExt.WriteLine("[Alert Processor] Processed all available entries.", ConsoleColor.DarkGray); } + + public class Generate + { + private readonly string InfoData; + private readonly string MsgType; + private readonly string Sent; + //private readonly string Effective; + //private readonly string Expires; + + public Generate(string InfoDataZ, string MsgTypeZ, string SentDate) + { + InfoData = InfoDataZ; + MsgType = MsgTypeZ; + Sent = SentDate; + //Effective = EffectiveDate; + //Expires = ExpiryDate; + } + + public string BroadcastInfo(string lang) + { + string BroadcastText = ""; + + string SentenceAppendEnd(string value) + { + value = value.Trim(); + if (value.EndsWith(".") || value.EndsWith("!") || value.EndsWith(",")) return value; + else return value += "."; + } + + string SentenceAppendSpace(string value) + { + value = value.Trim(); + if (string.IsNullOrWhiteSpace(value)) return string.Empty; + else return value += "\x20"; + } + + string SentencePuncuationCorrection(string value) + { + value = value.Trim(); + while (value.EndsWith("\x20.") || value.EndsWith("\x20,")) + { + value = value.Substring(0, value.Length - 1); + } + return value = SentenceAppendEnd(value.Substring(0, value.Length - 2)); + } + + string issue, update, cancel; + if (lang == "fr") + { + issue = "émis"; + update = "mis à jour"; + cancel = "annulé"; + } + else + { + issue = "issued"; + update = "updated"; + cancel = "cancelled"; + } + + string MsgPrefix; + switch (MsgType.ToLower()) + { + case "alert": + MsgPrefix = issue; + break; + case "update": + MsgPrefix = update; + break; + case "cancel": + MsgPrefix = cancel; + break; + default: + MsgPrefix = "issued"; + break; + } + + DateTime sentDate; + try + { + sentDate = DateTime.Parse(Sent, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + } + catch (Exception e) + { + ConsoleExt.WriteLine(e.Message); + sentDate = DateTime.Now; + } + + DateTime effectiveDate; + try + { + effectiveDate = DateTime.Parse(Sent, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + } + catch (Exception e) + { + ConsoleExt.WriteLine(e.Message); + effectiveDate = DateTime.Now; + } + + DateTime expiryDate; + try + { + expiryDate = DateTime.Parse(Sent, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + } + catch (Exception e) + { + ConsoleExt.WriteLine(e.Message); + expiryDate = DateTime.Now.AddHours(1); + } + + //DateTime sentDate = DateTime.Parse(Sent, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal).ToUniversalTime(); + + string TimeZoneName = "Unknown Time Zone"; + + switch (lang) + { + case "fr": + //TimeZoneName = OffsetToTimeZoneName(int.Parse(sentISO)); + TimeZoneName = "Temps Universel Coordonné"; + break; + case "en": + default: + //TimeZoneName = OffsetToTimeZoneName(int.Parse(sentISO)); + TimeZoneName = "Coordinated Universal Time"; + break; + } + + string SentFormatted = lang == "fr" ? $"{sentDate:HH}'{sentDate:h}'{sentDate:mm} {TimeZoneName}" : $"{sentDate:HH:mm} {TimeZoneName}, {sentDate:MMMM dd}, {sentDate:yyyy}"; + string BeginFormatted = lang == "fr" ? $"{effectiveDate:HH}'{effectiveDate:h}'{effectiveDate:mm} {TimeZoneName}" : $"{effectiveDate:HH:mm} {TimeZoneName}, {effectiveDate:MMMM dd}, {effectiveDate:yyyy}"; + string EndFormatted = lang == "fr" ? $"{expiryDate:HH}'{expiryDate:h}'{expiryDate:mm} {TimeZoneName}" : $"{expiryDate:HH:mm} {TimeZoneName}, {expiryDate:MMMM dd}, {expiryDate:yyyy}"; + + string EventType; + try + { + EventType = EventTypeRegex.Match(InfoData).Groups[1].Value; + EventType = Regex.Replace(EventType.ToLower(), @"(^\w)|(\s\w)", m => m.Value.ToUpper()); + EventType = lang == "fr" ? $"Le type d'événement est {EventType}." : $"Event type is {EventType}."; + } + catch (Exception) + { + EventType = lang == "fr" ? $"Le type d'événement n'a pas été spécifié." : $"Event type is not known."; + } + + string Coverage; + try + { + Coverage = CoverageRegex.Match(InfoData).Groups[1].Value; + Coverage = lang == "fr" ? $"Pour {Coverage} :" : $"For {Coverage}:"; + } + catch (Exception) + { + Coverage = lang == "fr" ? "Pour :" : "For:"; + } + + string[] areaDescMatches = AreaDescriptionRegex.Matches(InfoData) + .Cast() + .Select(m => m.Groups[1].Value) + .ToArray(); + + string AreaDesc = string.Join(", ", areaDescMatches) + "."; + + string SenderName; + + try + { + SenderName = SenderNameRegex.Match(InfoData).Groups[1].Value; + } + catch (Exception) + { + SenderName = "an unknown issuer"; + } + + string Description; + + try + { + Description = SentenceAppendEnd(DescriptionRegex.Match(InfoData).Groups[1].Value.Replace("\r\n", " ").Replace("\n", " ")); + } + catch (Exception) + { + Description = ""; + } + + string Instruction; + + try + { + Instruction = SentenceAppendEnd(InstructionRegex.Match(InfoData).Groups[1].Value.Replace("\r\n", " ").Replace("\n", " ")); + } + catch (Exception) + { + Instruction = ""; + } + + string Effective; + + try + { + Effective = effectiveDate.ToString(); + } + catch (Exception) + { + Effective = "currently"; + } + + string Expiry; + + try + { + Expiry = expiryDate.ToString(); + } + catch (Exception) + { + Expiry = "soon"; + } + + // Effective {Effective}, and expiring {Expires}. + + BroadcastText += SentenceAppendSpace(EventType); + BroadcastText += SentenceAppendSpace(Coverage); + BroadcastText += SentenceAppendSpace(SentenceAppendEnd(AreaDesc)); + + if (BeginFormatted != EndFormatted) + { + switch (lang) + { + case "fr": + BroadcastText += SentenceAppendSpace($"Alerte émise le {SentFormatted}, {MsgPrefix} par {SenderName}."); + BroadcastText += SentenceAppendSpace($"Cette alerte prend effet le {BeginFormatted}, et expire le {EndFormatted}."); + break; + case "en": + default: + BroadcastText += SentenceAppendSpace($"Alertez {MsgPrefix} sur {SentFormatted}, par {SenderName}."); + BroadcastText += SentenceAppendSpace($"This alert takes effect on {BeginFormatted}, and expires on {EndFormatted}."); + break; + } + } + + BroadcastText += SentenceAppendSpace(SentenceAppendEnd(Description)); + BroadcastText += SentenceAppendSpace(SentenceAppendEnd(Instruction)); + + Match match = BroadcastTextRegex.Match(InfoData); + + if (match.Success) + { + BroadcastText = match.Groups[1].Value.Replace("\r\n", "\x20").Replace("\n", "\x20").Replace("\x20\x20\x20", "\x20").Replace("\x20\x20", "\x20").Trim(); + } + + BroadcastText = SentencePuncuationCorrection(BroadcastText.Replace("###", string.Empty).Replace("\x20\x20\x20", "\x20").Replace("\x20\x20", "\x20").Trim()); + BroadcastText = SentenceAppendEnd(BroadcastText); + + return BroadcastText; + } + + //public string LegacyBroadcastInfo(string lang) + //{ + // string BroadcastText = ""; + + // string SentenceAppendEnd(string value) + // { + // value = value.Trim(); + // if (value.EndsWith(".") || value.EndsWith("!") || value.EndsWith(",")) return value; + // else return value += "."; + // } + + // string SentenceAppendSpace(string value) + // { + // value = value.Trim(); + // if (string.IsNullOrWhiteSpace(value)) return string.Empty; + // else return value += "\x20"; + // } + + // string SentencePuncuationCorrection(string value) + // { + // value = value.Trim(); + // while (value.EndsWith("\x20.") || + // value.EndsWith("\x20,") || + // value.EndsWith("\x20!") || + // value.EndsWith("\x20?") || + // value.EndsWith("\x20?") || + // value.EndsWith("\x20")) + // { + // value = value.Substring(0, value.Length - 1); + // } + // return value = SentenceAppendEnd(value.Substring(0, value.Length - 2)); + // } + + // Match match = BroadcastTextRegex.Match(InfoData); + + // if (match.Success) + // { + // BroadcastText = match.Groups[1].Value.Replace("\r\n", "\x20").Replace("\n", "\x20").Replace("\x20\x20\x20", "\x20").Replace("\x20\x20", "\x20").Trim(); + // } + // else + // { + // string issue, update, cancel; + // if (lang == "fr") + // { + // issue = "émis"; + // update = "mis à jour"; + // cancel = "annulé"; + // } + // else + // { + // issue = "issued"; + // update = "updated"; + // cancel = "cancelled"; + // } + + // string MsgPrefix; + // switch (MsgType.ToLower()) + // { + // case "alert": + // MsgPrefix = issue; + // break; + // case "update": + // MsgPrefix = update; + // break; + // case "cancel": + // MsgPrefix = cancel; + // break; + // default: + // MsgPrefix = "issued"; + // break; + // } + + // DateTime sentDate; + // try + // { + // sentDate = DateTime.Parse(Sent, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); + // } + // catch (Exception e) + // { + // ConsoleExt.WriteLine(e.Message); + // sentDate = DateTime.Now; + // } + + // DateTime effectiveDate; + // try + // { + // effectiveDate = DateTime.Parse(Sent, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); + // } + // catch (Exception e) + // { + // ConsoleExt.WriteLine(e.Message); + // effectiveDate = DateTime.Now; + // } + + // DateTime expiryDate; + // try + // { + // expiryDate = DateTime.Parse(Sent, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); + // } + // catch (Exception e) + // { + // ConsoleExt.WriteLine(e.Message); + // expiryDate = DateTime.Now.AddHours(1); + // } + + // //DateTime sentDate = DateTime.Parse(Sent, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToUniversalTime(); + + // string TimeZoneName = "Unknown Time Zone"; + + // string OffsetToTimeZoneName(int utcOffset) + // { + // utcOffset = +1; + // DateTime now = DateTime.UtcNow; + // List matchingTimeZones = new List(); + // foreach (TimeZoneInfo tz in TimeZoneInfo.GetSystemTimeZones()) + // { + // TimeSpan offset = tz.BaseUtcOffset; + + // if (offset.Hours == utcOffset) + // { + // ConsoleExt.WriteLine($"{tz.DaylightName} {tz.DaylightName}", ConsoleColor.DarkGray); + // if (tz.IsDaylightSavingTime(now)) + // { + // matchingTimeZones.Add(tz.DaylightName); + // } + // else + // { + // matchingTimeZones.Add(tz.StandardName); + // } + // } + // } + + // return matchingTimeZones.Count > 0 ? matchingTimeZones[0] : $"UTC -{utcOffset}"; + // } + + // string sentISO = sentDate.ToString("zz"); + + // switch (lang) + // { + // case "fr": + // TimeZoneName = OffsetToTimeZoneName(int.Parse(sentISO)); + // break; + // case "en": + // default: + // TimeZoneName = OffsetToTimeZoneName(int.Parse(sentISO)); + // break; + // } + + // string SentFormatted = lang == "fr" ? $"{sentDate:HH}'{sentDate:h}'{sentDate:mm} {TimeZoneName}." : $"{sentDate:HH:mm} {TimeZoneName}, {sentDate:MMMM dd}, {sentDate:yyyy}."; + // string BeginFormatted = lang == "fr" ? $"{effectiveDate:HH}'{effectiveDate:h}'{effectiveDate:mm} {TimeZoneName}." : $"{effectiveDate:HH:mm} {TimeZoneName}, {effectiveDate:MMMM dd}, {effectiveDate:yyyy}."; + // string EndFormatted = lang == "fr" ? $"{expiryDate:HH}'{expiryDate:h}'{expiryDate:mm} {TimeZoneName}." : $"{expiryDate:HH:mm} {TimeZoneName}, {expiryDate:MMMM dd}, {expiryDate:yyyy}."; + + // string EventType; + // try + // { + // EventType = EventRegex.Match(InfoData).Groups[1].Value; + // } + // catch (Exception) + // { + // // why + // EventType = EventRegex.Match(InfoData).Groups[1].Value; + // EventType = lang == "fr" ? $"alerte {EventType}" : $"{EventType} alert"; + // } + + // string Coverage; + // try + // { + // Coverage = CoverageRegex.Match(InfoData).Groups[1].Value; + // Coverage = lang == "fr" ? $"en {Coverage} pour:" : $"in {Coverage} for:"; + // } + // catch (Exception) + // { + // Coverage = lang == "fr" ? "pour:" : "for:"; + // } + + // string[] areaDescMatches = AreaDescriptionRegex.Matches(InfoData) + // .Cast() + // .Select(m => m.Groups[1].Value) + // .ToArray(); + + // string AreaDesc = string.Join(", ", areaDescMatches) + "."; + + // string SenderName; + + // try + // { + // SenderName = SenderNameRegex.Match(InfoData).Groups[1].Value; + // } + // catch (Exception) + // { + // SenderName = "an alert issuer"; + // } + + // string Description; + + // try + // { + // Description = SentenceAppendEnd(DescriptionRegex.Match(InfoData).Groups[1].Value.Replace("\n", " ")); + // } + // catch (Exception) + // { + // Description = ""; + // } + + // string Instruction; + + // try + // { + // Instruction = SentenceAppendEnd(InstructionRegex.Match(InfoData).Groups[1].Value.Replace("\n", " ")); + // } + // catch (Exception) + // { + // Instruction = ""; + // } + + // string Effective; + + // try + // { + // Effective = effectiveDate.ToString(); + // } + // catch (Exception) + // { + // Effective = "currently"; + // } + + // string Expiry; + + // try + // { + // Expiry = expiryDate.ToString(); + // } + // catch (Exception) + // { + // Expiry = "soon"; + // } + + // // Effective {Effective}, and expiring {Expires}. + + // switch (lang) + // { + // case "fr": + // BroadcastText = SentenceAppendSpace("À"); + // BroadcastText += SentenceAppendSpace(SentFormatted); + // BroadcastText += SentenceAppendSpace(SenderName); + // BroadcastText += SentenceAppendSpace("a"); + // BroadcastText += SentenceAppendSpace(MsgPrefix); + // BroadcastText += SentenceAppendSpace("une"); + // break; + // case "en": + // default: + // BroadcastText = SentenceAppendSpace("At"); + // BroadcastText += SentenceAppendSpace(SentFormatted); + // BroadcastText += SentenceAppendSpace(SenderName); + // BroadcastText += SentenceAppendSpace("has"); + // BroadcastText += SentenceAppendSpace(MsgPrefix); + // BroadcastText += SentenceAppendSpace("a"); + // break; + // } + // BroadcastText += SentenceAppendSpace(EventType); + // BroadcastText += SentenceAppendSpace(Coverage); + // BroadcastText += SentenceAppendSpace(SentenceAppendEnd(AreaDesc)); + // BroadcastText += SentenceAppendSpace(SentenceAppendEnd(Description)); + // BroadcastText += SentenceAppendSpace(SentenceAppendEnd(Instruction)); + // BroadcastText = SentencePuncuationCorrection(BroadcastText.Replace("###", string.Empty).Replace("\x20\x20\x20", "\x20").Replace("\x20\x20", "\x20").Trim()); + // //BroadcastText = lang == "fr" ? + // // $"À {SentFormatted} {SenderName} a {MsgPrefix} une {EventType} {Coverage} {AreaDesc}. {Description} {Instruction}".Replace("###", "").Replace(" ", " ").Trim() : + // // $"At {SentFormatted} {SenderName} has {MsgPrefix} a {EventType} {Coverage} {AreaDesc}. {Description} {Instruction}".Replace("###", "").Replace(" ", " ").Trim(); + // } + + // BroadcastText = SentenceAppendEnd(BroadcastText); + // //if (BroadcastText.EndsWith("\x20.")) BroadcastText = BroadcastText.TrimEnd('\x20', '.'); + // //if (BroadcastText.EndsWith(".")) BroadcastText = BroadcastText.TrimEnd('.'); + // //if (!BroadcastText.EndsWith(".") || !BroadcastText.EndsWith("!")) BroadcastText += "."; + + // return BroadcastText; + //} + + public bool GetAudio(string audioLink, string output, int decodeType) + { + if (decodeType == 1) + { + ConsoleExt.WriteLine("Decoding audio from Base64."); + try + { + byte[] audioData = Convert.FromBase64String(audioLink); + File.WriteAllBytes(output, audioData); + return true; + } + catch (Exception ex) + { + ConsoleExt.WriteLine($"Decoder failed: {ex.Message}"); + return false; + } + } + else if (decodeType == 0) + { + ConsoleExt.WriteLine("Downloading audio."); + try + { + using (HttpClient webClient = new HttpClient()) + { + File.WriteAllBytes(output, webClient.GetByteArrayAsync(audioLink).Result); + } + return true; + } + catch (Exception ex) + { + ConsoleExt.WriteLine($"Downloader failed: {ex.Message}"); + } + return false; + } + else + { + ConsoleExt.WriteLine("Invalid DecodeType specified."); + return false; + } + } + + private readonly SpeechSynthesizer engine = new SpeechSynthesizer(); + + public void GenerateAudio(string text, string lang) + { + try + { + Match Resource = ResourceDescriptionRegex.Match(InfoData); + if (!Resource.Success) throw new Exception("[Alert Processor] Audio field not found. TTS will be generated instead."); + string broadcastAudioResource = Resource.Groups[1].Value; + string audioLink = string.Empty; + string audioType = string.Empty; + int decode = -1; + + if (broadcastAudioResource.Contains("")) + { + Match Link = DerefURIRegex.Match(broadcastAudioResource); + Match Type = MimeRegex.Match(broadcastAudioResource); + decode = 1; + if (Link.Success && Type.Success) + { + audioLink = Link.Groups[1].Value; + audioType = Type.Groups[1].Value; + } + } + else + { + Match Link = TypeURIRegex.Match(broadcastAudioResource); + Match Type = MimeRegex.Match(broadcastAudioResource); + decode = 0; + if (Link.Success && Type.Success) + { + audioLink = Link.Groups[1].Value; + audioType = Type.Groups[1].Value; + } + } + + ConsoleExt.WriteLine(audioLink); + //Thread.Sleep(5000); + + string audioFile; + switch (audioType) + { + case "audio/mpeg": + audioFile = "PreAudio.mp3"; + break; + case "audio/x-ms-wma": + audioFile = "PreAudio.wma"; + break; + default: + audioFile = "PreAudio.wav"; + break; + } + + if (File.Exists($"{AudioDirectory}\\{audioFile}")) File.Delete($"{AudioDirectory}\\{audioFile}"); + if (File.Exists($"{AudioDirectory}\\audio.wav")) File.Delete($"{AudioDirectory}\\audio.wav"); + + if (GetAudio(audioLink, audioFile, decode)) + { + using (var audioFileReader = new AudioFileReader(audioFile)) + { + var volumeSampleProvider = new VolumeSampleProvider(audioFileReader.ToSampleProvider()) + { + Volume = 2.5f, + }; + WaveFileWriter.CreateWaveFile16($"{AudioDirectory}\\audio.wav", volumeSampleProvider); + } + + if (!File.Exists($"{AudioDirectory}\\audio.wav")) + { + ConsoleExt.WriteLine("Post processing failed."); + File.Move(audioFile, $"{AudioDirectory}\\audio.wav"); + } + + //string ffmpegCmd = $"{AssemblyDirectory}\\ffmpeg.exe -y -i {audioFile} -filter:a volume=2.5 {AssemblyDirectory.Replace("\\", "/")}/Audio/audio.wav"; + //Process p = Process.Start("cmd.exe", $"/c {ffmpegCmd}"); + //p.WaitForExit(12000); + //if (p.ExitCode != 0 || !File.Exists($"{AssemblyDirectory}\\Audio\\audio.wav")) + //{ + // ConsoleExt.WriteLine("Post processing failed. Please make sure ffmpeg is in the program folder."); + // File.Move(audioFile, $"{AssemblyDirectory}\\Audio\\audio.wav"); + //} + //new SoundPlayer($"{AssemblyDirectory}\\Audio\\audio.wav").PlaySync(); + } + else + { + throw new Exception(); + } + } + catch (Exception ex) + { + ConsoleExt.WriteLine(ex.Message); + + //ConsoleExt.WriteLine(lang); + foreach (var voice in engine.GetInstalledVoices()) + { + //ConsoleExt.WriteLine(voice.VoiceInfo.Culture.TwoLetterISOLanguageName.ToLower()); + if (voice.VoiceInfo.Name.Contains(Settings.Default.SpeechVoice) && voice.VoiceInfo.Culture.TwoLetterISOLanguageName.ToLower() == lang) + { + //ConsoleExt.WriteLine(voice.VoiceInfo.Name, ConsoleColor.Magenta); + engine.SelectVoice(voice.VoiceInfo.Name); + break; + } + } + + text = text.Replace("#", "hashtag\x20"); + text = text.Replace("*", "star\x20"); + + engine.SetOutputToWaveFile($"{AudioDirectory}\\audio.wav"); + engine.Speak(text); + engine.Dispose(); + } + } + } } } diff --git a/SharpENDEC/ENDEC/DataProcessor.cs b/SharpENDEC/ENDEC/DataProcessor.cs index 88fbb28..b2d6606 100644 --- a/SharpENDEC/ENDEC/DataProcessor.cs +++ b/SharpENDEC/ENDEC/DataProcessor.cs @@ -1,18 +1,13 @@ -using NAudio.Wave.SampleProviders; -using NAudio.Wave; +using NAudio.Wave; using SharpENDEC.Properties; using System; -using System.Drawing; -using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; -using System.Speech.Synthesis; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using NAudio.Utils; -using System.Collections.Generic; namespace SharpENDEC { @@ -25,15 +20,24 @@ public static void DataProcessor() while (true) { SharpDataItem relayItem = Check.WatchForItemsInList(); - ConsoleExt.WriteLine($"[Data Processor] {LanguageStrings.CapturedFromFileWatcher(Settings.Default.CurrentLanguage)}", ConsoleColor.Cyan); + + if (relayItem.IsNull()) continue; + + ConsoleExt.WriteLine($"[Data Processor] {LanguageStrings.CapturedFromFileWatcher(Settings.Default.CurrentLanguage, DateTime.Now)}", ConsoleColor.Cyan); lock (SharpDataHistory) SharpDataHistory.Add(relayItem); lock (SharpDataQueue) SharpDataQueue.Remove(relayItem); + // trim history for memory saving + if (SharpDataHistory.Count > 50) + { + SharpDataHistory.RemoveRange(50, SharpDataHistory.Count - 50); + } + if (relayItem.Data.Contains("NAADS-Heartbeat")) { ConsoleExt.WriteLine($"[Data Processor] {LanguageStrings.HeartbeatDetected(Settings.Default.CurrentLanguage)}", ConsoleColor.Green); - Match referencesMatch = Regex.Match(relayItem.Data, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + Match referencesMatch = ReferencesRegex.Match(relayItem.Data); if (referencesMatch.Success) { string references = referencesMatch.Groups[1].Value; @@ -45,10 +49,10 @@ public static void DataProcessor() else { lock (SharpAlertQueue) SharpAlertQueue.Add(relayItem); - ConsoleExt.WriteLine(LanguageStrings.AlertQueued(Settings.Default.CurrentLanguage)); + ConsoleExt.WriteLine($"[Data Processor] {LanguageStrings.AlertQueued(Settings.Default.CurrentLanguage)}"); } - ConsoleExt.WriteLine($"[Data Processor] {LanguageStrings.LastDataReceived(Settings.Default.CurrentLanguage)} {DateTime.Now:yyyy-MM-dd HH:mm:ss}."); + //ConsoleExt.WriteLine($"[Data Processor] {LanguageStrings.LastDataReceived(Settings.Default.CurrentLanguage)} {DateTime.Now:yyyy-MM-dd HH:mm:ss}."); } } //catch (ThreadAbortException) @@ -70,7 +74,7 @@ private static (bool FilePlayed, TimeSpan AudioLength) Play(string filePath, flo if (!DefaultDevice) outputDevice.DeviceNumber = Settings.Default.SoundDevice; outputDevice.Init(audioFile); outputDevice.Play(); - ConsoleExt.WriteLine($"[Data Processor] -> {filePath}."); + ConsoleExt.WriteLine($"[Audio Player] -> {filePath}."); while (outputDevice.PlaybackState == PlaybackState.Playing) { if (SkipPlayback) @@ -195,7 +199,7 @@ public static bool Config(string InfoX, string Status, string MsgType, Final = true; } } - catch + catch (Exception) { Final = false; } @@ -211,9 +215,7 @@ public static bool Config(string InfoX, string Status, string MsgType, { try { - MatchCollection matches = Regex.Matches(InfoX, - @"\s*profile:CAP-CP:Location:0.3\s*\s*(.*?)\s*", - RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + MatchCollection matches = LocationRegex.Matches(InfoX); bool GeoMatch = false; foreach (Match match in matches) { @@ -231,7 +233,7 @@ public static bool Config(string InfoX, string Status, string MsgType, } catch (Exception ex) { - ConsoleExt.WriteLine($"[Data Processor] {ex.Message}", ConsoleColor.Red); + ConsoleExt.WriteLineErr($"[Data Processor] {ex.Message}"); return false; } } @@ -248,14 +250,14 @@ public static bool Config(string InfoX, string Status, string MsgType, public static void Heartbeat(string References) { - ConsoleExt.WriteLine($"[Heartbeat] {LanguageStrings.DownloadingFiles(Settings.Default.CurrentLanguage)}", ConsoleColor.DarkYellow); + ConsoleExt.WriteLine($"[Heartbeat] {LanguageStrings.DownloadingFiles(Settings.Default.CurrentLanguage)}", ConsoleColor.Yellow); string[] RefList = References.Split(' '); int DataMatched = 0; int Total = 0; + client.DefaultRequestHeaders.UserAgent.ParseAdd($"Mozilla/5.0 (compatible; SharpENDEC/{VersionInfo.ReleaseVersion}.{VersionInfo.MinorVersion})"); foreach (string i in RefList) { Total++; - ConsoleExt.WriteLine($"[Heartbeat] {LanguageStrings.GenericProcessingValueOfValue(Settings.Default.CurrentLanguage, Total, RefList.Length)}", ConsoleColor.DarkYellow); string filename = string.Empty; string[] k = i.Split(','); string sentDTFull = k[2].Replace("-", "_").Replace(":", "_").Replace("+", "p"); @@ -270,18 +272,18 @@ public static void Heartbeat(string References) if (SharpDataQueue.Any(x => x.Name == filename) || SharpDataHistory.Any(x => x.Name == filename)) { - ConsoleExt.WriteLine($"[Heartbeat] {LanguageStrings.DataPreviouslyProcessed(Settings.Default.CurrentLanguage)}", ConsoleColor.Yellow); + ConsoleExt.WriteLine($"[Heartbeat] -x {filename}", ConsoleColor.DarkGray); DataMatched++; } else { - ConsoleExt.WriteLine($"[Heartbeat] {filename}...", ConsoleColor.Yellow); + //ConsoleExt.WriteLine($"[Heartbeat] {LanguageStrings.GenericProcessingValueOfValue(Settings.Default.CurrentLanguage, Total, RefList.Length)}", ConsoleColor.Yellow); string url1 = $"http://{Dom1}/{sentDT}/{filename}"; string url2 = $"http://{Dom2}/{sentDT}/{filename}"; try { - ConsoleExt.WriteLine($"-> {url1}", ConsoleColor.Yellow); + ConsoleExt.WriteLine($"-> {filename}", ConsoleColor.DarkYellow); Task xml = client.GetStringAsync(url1); xml.Wait(); lock (SharpDataQueue) SharpDataQueue.Add(new SharpDataItem(filename, xml.Result)); @@ -291,9 +293,8 @@ public static void Heartbeat(string References) { try { - ConsoleExt.WriteLine($"[Heartbeat] {e1.Message}", ConsoleColor.Red); - ConsoleExt.WriteLine($"[Heartbeat] {filename}...", ConsoleColor.Yellow); - ConsoleExt.WriteLine($"-> {url2}", ConsoleColor.Yellow); + ConsoleExt.WriteLineErr($"[Heartbeat] {e1.Message}"); + ConsoleExt.WriteLine($"-> {url2}", ConsoleColor.DarkYellow); Task xml = client.GetStringAsync(url2); xml.Wait(); lock (SharpDataQueue) SharpDataQueue.Add(new SharpDataItem(filename, xml.Result)); @@ -301,8 +302,8 @@ public static void Heartbeat(string References) } catch (Exception e2) { - ConsoleExt.WriteLine($"[Heartbeat] {e2.Message}", ConsoleColor.Red); - ConsoleExt.WriteLine($"[Heartbeat] {LanguageStrings.DownloadFailure(Settings.Default.CurrentLanguage)}", ConsoleColor.Red); + ConsoleExt.WriteLineErr($"[Heartbeat] {e2.Message}"); + ConsoleExt.WriteLine($"[Heartbeat] {LanguageStrings.DownloadFailure(Settings.Default.CurrentLanguage)}", ConsoleColor.DarkBlue); } } } @@ -313,7 +314,6 @@ public static void Heartbeat(string References) public static SharpDataItem WatchForItemsInList() { - //ConsoleExt.WriteLine($"[Data Processor] Watching for new strings in FileStringListTempName."); while (true) { if (SharpDataQueue.Count != 0) @@ -324,338 +324,7 @@ public static SharpDataItem WatchForItemsInList() return data; } } - Thread.Sleep(50); - } - return null; - } - } - - public class Generate - { - private readonly string InfoData; - private readonly string MsgType; - private readonly string Sent; - - public Generate(string InfoDataZ, string MsgTypeZ, string SentDate) - { - InfoData = InfoDataZ; - MsgType = MsgTypeZ; - Sent = SentDate; - } - - public (string BroadcastText, bool) BroadcastInfo(string lang) - { - string BroadcastText = ""; - - Match match = Regex.Match(InfoData, @"layer:SOREM:1.0:Broadcast_Text\s*\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); - if (match.Success) - { - BroadcastText = match.Groups[1].Value.Replace("\r\n", " ").Replace("\n", " ").Replace(" ", " ").Trim(); - } - else - { - string issue, update, cancel; - if (lang == "fr") - { - issue = "émis"; - update = "mis à jour"; - cancel = "annulé"; - } - else - { - issue = "issued"; - update = "updated"; - cancel = "cancelled"; - } - - string MsgPrefix; - switch (MsgType.ToLower()) - { - case "alert": - MsgPrefix = issue; - break; - case "update": - MsgPrefix = update; - break; - case "cancel": - MsgPrefix = cancel; - break; - default: - MsgPrefix = "issued"; - break; - } - - DateTime sentDate; - try - { - // .ToUniversalTime() - sentDate = DateTime.Parse(Sent, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); - } - catch (Exception e) - { - ConsoleExt.WriteLine(e.Message); - sentDate = DateTime.Now; - } - - //DateTime sentDate = DateTime.Parse(Sent, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToUniversalTime(); - - string SentFormatted = lang == "fr" ? $"{sentDate:HH}'{sentDate:h}'{sentDate:mm zzz}." : $"{sentDate:HH:mm z}, {sentDate:MMMM dd}, {sentDate:yyyy}."; - - string EventType; - try - { - EventType = Regex.Match(InfoData, @"layer:EC-MSC-SMC:1.0:Alert_Name\s*\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value; - } - catch (Exception) - { - EventType = Regex.Match(InfoData, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value; - EventType = lang == "fr" ? $"alerte {EventType}" : $"{EventType} alert"; - } - - string Coverage; - try - { - Coverage = Regex.Match(InfoData, @"layer:EC-MSC-SMC:1.0:Alert_Coverage\s*\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value; - Coverage = lang == "fr" ? $"en {Coverage} pour:" : $"in {Coverage} for:"; - } - catch (Exception) - { - Coverage = lang == "fr" ? "pour:" : "for:"; - } - - string[] areaDescMatches = Regex.Matches(InfoData, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline) - .Cast() - .Select(m => m.Groups[1].Value) - .ToArray(); - - string AreaDesc = string.Join(", ", areaDescMatches) + "."; - - string SenderName; - - try - { - SenderName = Regex.Match(InfoData, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value; - } - catch (Exception) - { - SenderName = "an alert issuer"; - } - - string Description; - - try - { - Description = Regex.Match(InfoData, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value.Replace("\n", " "); - if (!Description.EndsWith(".")) Description += "."; - } - catch (Exception) - { - Description = ""; - } - - string Instruction; - - try - { - Instruction = Regex.Match(InfoData, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value.Replace("\n", " "); - if (!Instruction.EndsWith(".")) Instruction += "."; - } - catch (Exception) - { - Instruction = ""; - } - - string Effective; - - try - { - Effective = Regex.Match(InfoData, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value.Replace("\n", " "); - DateTime.Parse(Effective, CultureInfo.InvariantCulture); - } - catch (Exception) - { - Effective = "currently"; - } - - string Expires; - - try - { - Expires = Regex.Match(InfoData, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value.Replace("\n", " "); - DateTime.Parse(Expires, CultureInfo.InvariantCulture); - } - catch (Exception) - { - Expires = "soon"; - } - - // Effective {Effective}, and expiring {Expires}. - - BroadcastText = lang == "fr" ? - $"À {SentFormatted} {SenderName} a {MsgPrefix} une {EventType} {Coverage} {AreaDesc}. {Description} {Instruction}".Replace("###", "").Replace(" ", " ").Trim() : - $"At {SentFormatted} {SenderName} has {MsgPrefix} a {EventType} {Coverage} {AreaDesc}. {Description} {Instruction}".Replace("###", "").Replace(" ", " ").Trim(); - } - - if (BroadcastText.EndsWith("\x20.")) BroadcastText = BroadcastText.TrimEnd('\x20', '.'); - if (BroadcastText.EndsWith(".")) BroadcastText = BroadcastText.TrimEnd('.'); - if (!BroadcastText.EndsWith(".") || !BroadcastText.EndsWith("!")) BroadcastText += "."; - - //if (Debugger.IsAttached) BroadcastText += "\x20| Debugging in progress"; - - return (BroadcastText, true); - } - - public bool GetAudio(string audioLink, string output, int decodeType) - { - if (decodeType == 1) - { - ConsoleExt.WriteLine("Decoding audio from Base64."); - try - { - byte[] audioData = Convert.FromBase64String(audioLink); - File.WriteAllBytes(output, audioData); - return true; - } - catch (Exception ex) - { - ConsoleExt.WriteLine($"Decoder failed: {ex.Message}"); - return false; - } - } - else if (decodeType == 0) - { - ConsoleExt.WriteLine("Downloading audio."); - try - { - using (HttpClient webClient = new HttpClient()) - { - File.WriteAllBytes(output, webClient.GetByteArrayAsync(audioLink).Result); - } - return true; - } - catch (Exception ex) - { - ConsoleExt.WriteLine($"Downloader failed: {ex.Message}"); - } - return false; - } - else - { - ConsoleExt.WriteLine("Invalid DecodeType specified."); - return false; - } - } - - private readonly SpeechSynthesizer engine = new SpeechSynthesizer(); - - public void GenerateAudio(string text, string lang) - { - try - { - Match Resource = Regex.Match(InfoData, @"\s*(.*?)\s*\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); - if (!Resource.Success) throw new Exception("Audio field not found. TTS will be generated instead."); - string broadcastAudioResource = Resource.Groups[1].Value; - string audioLink = string.Empty; - string audioType = string.Empty; - int decode = -1; - - if (broadcastAudioResource.Contains("")) - { - Match Link = Regex.Match(broadcastAudioResource, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); - Match Type = Regex.Match(broadcastAudioResource, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); - decode = 1; - if (Link.Success && Type.Success) - { - audioLink = Link.Groups[1].Value; - audioType = Type.Groups[1].Value; - } - } - else - { - Match Link = Regex.Match(broadcastAudioResource, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); - Match Type = Regex.Match(broadcastAudioResource, @"\s*(.*?)\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); - decode = 0; - if (Link.Success && Type.Success) - { - audioLink = Link.Groups[1].Value; - audioType = Type.Groups[1].Value; - } - } - - ConsoleExt.WriteLine(audioLink); - //Thread.Sleep(5000); - - string audioFile; - switch (audioType) - { - case "audio/mpeg": - audioFile = "PreAudio.mp3"; - break; - case "audio/x-ms-wma": - audioFile = "PreAudio.wma"; - break; - default: - audioFile = "PreAudio.wav"; - break; - } - - if (File.Exists($"{AudioDirectory}\\{audioFile}")) File.Delete($"{AudioDirectory}\\{audioFile}"); - if (File.Exists($"{AudioDirectory}\\audio.wav")) File.Delete($"{AudioDirectory}\\audio.wav"); - - if (GetAudio(audioLink, audioFile, decode)) - { - using (var audioFileReader = new AudioFileReader(audioFile)) - { - var volumeSampleProvider = new VolumeSampleProvider(audioFileReader.ToSampleProvider()) - { - Volume = 2.5f, - }; - WaveFileWriter.CreateWaveFile16($"{AudioDirectory}\\audio.wav", volumeSampleProvider); - } - - if (!File.Exists($"{AudioDirectory}\\audio.wav")) - { - ConsoleExt.WriteLine("Post processing failed."); - File.Move(audioFile, $"{AudioDirectory}\\audio.wav"); - } - - //string ffmpegCmd = $"{AssemblyDirectory}\\ffmpeg.exe -y -i {audioFile} -filter:a volume=2.5 {AssemblyDirectory.Replace("\\", "/")}/Audio/audio.wav"; - //Process p = Process.Start("cmd.exe", $"/c {ffmpegCmd}"); - //p.WaitForExit(12000); - //if (p.ExitCode != 0 || !File.Exists($"{AssemblyDirectory}\\Audio\\audio.wav")) - //{ - // ConsoleExt.WriteLine("Post processing failed. Please make sure ffmpeg is in the program folder."); - // File.Move(audioFile, $"{AssemblyDirectory}\\Audio\\audio.wav"); - //} - //new SoundPlayer($"{AssemblyDirectory}\\Audio\\audio.wav").PlaySync(); - } - else - { - throw new Exception(); - } - } - catch (Exception ex) - { - ConsoleExt.WriteLine(ex.Message); - - //ConsoleExt.WriteLine(lang); - foreach (var voice in engine.GetInstalledVoices()) - { - //ConsoleExt.WriteLine(voice.VoiceInfo.Culture.TwoLetterISOLanguageName.ToLower()); - if (voice.VoiceInfo.Name.Contains(Settings.Default.SpeechVoice) && voice.VoiceInfo.Culture.TwoLetterISOLanguageName.ToLower() == lang) - { - //ConsoleExt.WriteLine(voice.VoiceInfo.Name, ConsoleColor.Magenta); - engine.SelectVoice(voice.VoiceInfo.Name); - break; - } - } - - text = text.Replace("#", "hashtag\x20"); - text = text.Replace("*", "star\x20"); - - engine.SetOutputToWaveFile($"{AudioDirectory}\\audio.wav"); - engine.Speak(text); - engine.Dispose(); + Thread.Sleep(100); } } } diff --git a/SharpENDEC/ENDEC/FeedCapture.cs b/SharpENDEC/ENDEC/FeedCapture.cs index b7c04ee..35bbd17 100644 --- a/SharpENDEC/ENDEC/FeedCapture.cs +++ b/SharpENDEC/ENDEC/FeedCapture.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Net.Sockets; using System.Text; -using System.Text.RegularExpressions; using System.Threading; namespace SharpENDEC @@ -64,7 +63,7 @@ private void Receive(string host, int port, string delimiter) } catch (IOException e) { - ConsoleExt.WriteLine($"[{host}:{port}] {e.Message}", ConsoleColor.Red); + ConsoleExt.WriteLineErr($"[{host}:{port}] {e.Message}"); return; } Thread.Sleep(1000); @@ -93,8 +92,8 @@ private void Receive(string host, int port, string delimiter) if (chunk.Contains(delimiter)) { ConsoleExt.WriteLine($"[{host}:{port}] {LanguageStrings.ProcessedStream(Settings.Default.CurrentLanguage, data.Count, now)}"); - string capturedSent = Regex.Match(dataReceived, @"\s*(.*?)\s*", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline).Groups[1].Value.Replace("-", "_").Replace("+", "p").Replace(":", "_"); - string capturedIdent = Regex.Match(dataReceived, @"\s*(.*?)\s*", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline).Groups[1].Value.Replace("-", "_").Replace("+", "p").Replace(":", "_"); + string capturedSent = SentRegex.Match(dataReceived).Groups[1].Value.Replace("-", "_").Replace("+", "p").Replace(":", "_"); + string capturedIdent = IdentifierRegex.Match(dataReceived).Groups[1].Value.Replace("-", "_").Replace("+", "p").Replace(":", "_"); string filename = $"{capturedSent}I{capturedIdent}.xml"; if (SharpDataQueue.Any(x => x.Name == filename) || SharpDataHistory.Any(x => x.Name == filename)) @@ -104,7 +103,7 @@ private void Receive(string host, int port, string delimiter) else { SharpDataQueue.Add(new SharpDataItem(filename, dataReceived)); - ConsoleExt.WriteLine($"[{host}:{port}] {LanguageStrings.FileDownloaded(Settings.Default.CurrentLanguage, host)}"); + ConsoleExt.WriteLine($"[{host}:{port}] {LanguageStrings.FileDownloaded(Settings.Default.CurrentLanguage)}"); } dataReceived = string.Empty; } @@ -112,7 +111,9 @@ private void Receive(string host, int port, string delimiter) { if (data.Count > 10000000) { - throw new Exception($"[{host}:{port}] The data exceeds the 10 MB limit. The server may be malfunctioning."); + //throw new OverflowException($"[{host}:{port}] The data exceeds the 10 MB limit. The server may be malfunctioning."); + ConsoleExt.WriteLineErr($"[{host}:{port}] The data exceeds the 10 MB limit. The server may be malfunctioning."); + return; } else ConsoleExt.WriteLine($"[{host}:{port}] {data.Count} bytes total including the current chunk."); } @@ -121,13 +122,13 @@ private void Receive(string host, int port, string delimiter) } catch (SocketException e) { - ConsoleExt.WriteLine($"[{host}:{port}] {e.Message}"); + ConsoleExt.WriteLineErr($"[{host}:{port}] {e.Message}"); Thread.Sleep(1000); return; } catch (TimeoutException) { - ConsoleExt.WriteLine($"[{host}:{port}] {LanguageStrings.HostTimedOut(Settings.Default.CurrentLanguage, host)}"); + ConsoleExt.WriteLineErr($"[{host}:{port}] {LanguageStrings.HostTimedOut(Settings.Default.CurrentLanguage, host)}"); return; } catch (ThreadAbortException) @@ -140,6 +141,8 @@ private void Receive(string host, int port, string delimiter) public bool Main() { + if (!SharpDataQueue.IsNull()) SharpDataQueue.Clear(); + if (!SharpDataHistory.IsNull()) SharpDataHistory.Clear(); SharpDataQueue = new List(); SharpDataHistory = new List(); @@ -149,7 +152,7 @@ void StartServerConnection() { Thread thread = new Thread(() => Receive(server, 8080, "")); thread.Start(); - ClientThreads.Add(thread); + CaptureThreads.Add(thread); ConsoleExt.WriteLine($"{LanguageStrings.StartingConnection(Settings.Default.CurrentLanguage, server, 8080)}", ConsoleColor.DarkGray); Thread.Sleep(250); } @@ -161,9 +164,9 @@ void StartServerConnection() { while (true) { - for (int i = 0; i < ClientThreads.Count; i++) + for (int i = 0; i < CaptureThreads.Count; i++) { - if (!ClientThreads[i].IsAlive) + if (!CaptureThreads[i].IsAlive) { if (!ShutdownCapture) { @@ -171,15 +174,15 @@ void StartServerConnection() string server = Settings.Default.CanadianServers[i]; Thread newThread = new Thread(() => Receive(server, 8080, "")); newThread.Start(); - ClientThreads[i] = newThread; + CaptureThreads[i] = newThread; } } } if (ShutdownCapture) { - lock (ClientThreads) - foreach (var thread in ClientThreads) + lock (CaptureThreads) + foreach (var thread in CaptureThreads) { try { diff --git a/SharpENDEC/ENDEC/RegexList.cs b/SharpENDEC/ENDEC/RegexList.cs new file mode 100644 index 0000000..c58a5a3 --- /dev/null +++ b/SharpENDEC/ENDEC/RegexList.cs @@ -0,0 +1,110 @@ +using System.Text.RegularExpressions; + +namespace SharpENDEC +{ + public static partial class ENDEC + { + //public static readonly Regex ValueNameRegex = new Regex( + // @"([^<]+)\s*([^<]+)", + // RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex SentRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex StatusRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex MessageTypeRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex BroadcastImmediatelyRegex = new Regex( + @"layer:SOREM:1.0:Broadcast_Immediately\s*\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex UrgencyRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex SeverityRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex InfoRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex EffectiveRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex ExpiresRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex EventRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + public static readonly Regex EventTypeRegex = new Regex( + @"layer:EC-MSC-SMC:1.0:Alert_Name\s*\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex LanguageRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex SenderNameRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex CoverageRegex = new Regex( + @"layer:EC-MSC-SMC:1.0:Alert_Coverage\s*\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex DescriptionRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex InstructionRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex BroadcastTextRegex = new Regex( + @"layer:SOREM:1.0:Broadcast_Text\s*\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex AreaDescriptionRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex ResourceDescriptionRegex = new Regex( + @"\s*(.*?)\s*\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex DerefURIRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex TypeURIRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex MimeRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex IdentifierRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex ReferencesRegex = new Regex( + @"\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + + public static readonly Regex LocationRegex = new Regex( + @"\s*profile:CAP-CP:Location:0.3\s*\s*(.*?)\s*", + RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Singleline); + } +} diff --git a/SharpENDEC/ENDEC/ServerRunner.cs b/SharpENDEC/ENDEC/ServerRunner.cs new file mode 100644 index 0000000..d8cae9a --- /dev/null +++ b/SharpENDEC/ENDEC/ServerRunner.cs @@ -0,0 +1,304 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace SharpENDEC +{ + public static partial class ENDEC + { + internal static MainExt.RandomGenerator rnd = new MainExt.RandomGenerator(); + internal static List AuthenticatedCookies = new List(); + + public static void HTTPServerProcessor() + { + lock (AuthenticatedCookies) AuthenticatedCookies.Clear(); + + try + { + HttpListener listener = new HttpListener(); + listener.Prefixes.Add("http://localhost:8050/"); + listener.Start(); + + while (true) + { + HttpListenerContext context = listener.GetContext(); + Task.Run(() => + { + string path = context.Request.Url.AbsolutePath.ToLower(); + HttpListenerResponse response = context.Response; + + List QueryString = new List(); + + foreach (string query in context.Request.QueryString) + { + QueryString.Add(query); + } + + switch (path) + { + case "/": + Endpoints.HandleHomeEndpoint(response, context.Request.HttpMethod, QueryString); + break; + case "/version": + Endpoints.HandleVersionEndpoint(response, context.Request.HttpMethod, QueryString); + break; + case "/login": + Endpoints.HandleLoginEndpoint(response, context.Request.HttpMethod, QueryString); + break; + default: + HandleNotFound(response); + break; + } + }); + } + } + catch (Exception) + { + lock (AuthenticatedCookies) AuthenticatedCookies.Clear(); + } + } + + internal static void WriteAndClose(HttpListenerResponse response, byte[] buffer, int code) + { + response.StatusCode = code; + response.ContentLength64 = buffer.Length; + response.OutputStream.Write(buffer, 0, buffer.Length); + response.OutputStream.Close(); + } + + private static class Endpoints + { + public static void HandleHomeEndpoint(HttpListenerResponse response, string method, List query) + { + switch (method.ToUpper()) + { + case "GET": + byte[] buffer = Encoding.UTF8.GetBytes($"The version is here. Query: {query.ListToString()}"); + WriteAndClose(response, buffer, 200); + break; + default: + HandleWrongMethod(response); + break; + } + } + + public static void HandleHealthDataEndpoint(HttpListenerResponse response, string method, List query) + { + switch (method.ToUpper()) + { + case "GET": + lock (CaptureThreads) + { + foreach (Thread thread in CaptureThreads) + { + if (thread.IsAlive) ConsoleExt.WriteLine(thread.Name + " is alive"); + } + } + byte[] buffer = Encoding.UTF8.GetBytes(""); + WriteAndClose(response, buffer, 200); + break; + default: + HandleWrongMethod(response); + break; + } + } + + internal static RandomGenerator rnd = new RandomGenerator(); + + public static void HandleLoginEndpoint(HttpListenerResponse response, string method, List query) + { + switch (method.ToUpper()) + { + case "GET": + byte[] buffer_GET = Encoding.UTF8.GetBytes($"{rnd.Next(0, 10000)} {query}"); + WriteAndClose(response, buffer_GET, 200); + break; + case "POST": + byte[] buffer_POST = Encoding.UTF8.GetBytes($"{rnd.Next(0, 10000)} {query}"); + int cookieContent = rnd.NextLarge(0, 1000); + AuthenticatedCookies.Add($"{cookieContent}"); + response.SetCookie(new Cookie("Authentify", "")); + WriteAndClose(response, buffer_POST, 200); + break; + default: + HandleWrongMethod(response); + break; + } + } + + public static void HandleVersionEndpoint(HttpListenerResponse response, string method, List query) + { + switch (method.ToUpper()) + { + case "GET": + byte[] buffer = Encoding.UTF8.GetBytes($"You queried \"{query.ListToString()}\"! Here's what you asked for though: {VersionInfo.FriendlyVersion}"); + WriteAndClose(response, buffer, 200); + break; + default: + HandleWrongMethod(response); + break; + } + } + } + + private static void HandleNotFound(HttpListenerResponse response) + { + string responseString = $"{VersionInfo.FriendlyVersion}Endpoint Not Found"; + byte[] buffer = Encoding.UTF8.GetBytes(responseString); + response.StatusCode = 404; + response.ContentLength64 = buffer.Length; + response.OutputStream.Write(buffer, 0, buffer.Length); + response.OutputStream.Close(); + } + + private static void HandleBadRequest(HttpListenerResponse response) + { + string responseString = $"{VersionInfo.FriendlyVersion}Bad Request"; + byte[] buffer = Encoding.UTF8.GetBytes(responseString); + response.StatusCode = 400; + response.ContentLength64 = buffer.Length; + response.OutputStream.Write(buffer, 0, buffer.Length); + response.OutputStream.Close(); + } + + private static void HandleUnauthorized(HttpListenerResponse response) + { + string responseString = $"{VersionInfo.FriendlyVersion}Request Unauthorized"; + byte[] buffer = Encoding.UTF8.GetBytes(responseString); + response.StatusCode = 401; + response.ContentLength64 = buffer.Length; + response.OutputStream.Write(buffer, 0, buffer.Length); + response.OutputStream.Close(); + } + + private static void HandleWrongMethod(HttpListenerResponse response) + { + string responseString = $"{VersionInfo.FriendlyVersion}Wrong Method"; + byte[] buffer = Encoding.UTF8.GetBytes(responseString); + response.StatusCode = 405; + response.ContentLength64 = buffer.Length; + response.OutputStream.Write(buffer, 0, buffer.Length); + response.OutputStream.Close(); + } + } + + /// + /// Secure random generator + /// + /// + /// + /// + public class RandomGenerator : IDisposable + { + private readonly RNGCryptoServiceProvider csp; + + /// + /// Constructor + /// + public RandomGenerator() + { + csp = new RNGCryptoServiceProvider(); + } + + /// + /// Get the next random value between minValue and maxExclusiveValue. + /// + public int Next(int minValue, int maxExclusiveValue) + { + if (minValue == maxExclusiveValue) + return minValue; + + if (minValue > maxExclusiveValue) + { + throw new ArgumentOutOfRangeException($"{nameof(minValue)} must be lower than {nameof(maxExclusiveValue)}"); + } + + var diff = (long)maxExclusiveValue - minValue; + var upperBound = uint.MaxValue / diff * diff; + + uint ui; + do + { + ui = GetRandomUInt(); + } while (ui >= upperBound); + + return (int)(minValue + (ui % diff)); + } + + /// + /// Get the next random value between (minValue * random) and (maxExclusiveValue * random) with random attempts. + /// + public int NextLarge(int minValue, int maxExclusiveValue) + { + if (minValue == maxExclusiveValue) + return minValue; + + if (minValue > maxExclusiveValue) + { + throw new ArgumentOutOfRangeException($"{nameof(minValue)} must be lower than {nameof(maxExclusiveValue)}"); + } + + minValue *= 1000 * Next(minValue, maxExclusiveValue); + maxExclusiveValue *= 1000 * Next(minValue, maxExclusiveValue); + + var diff = (long)maxExclusiveValue - minValue; + var upperBound = uint.MaxValue / diff * diff; + + uint ui; + do + { + ui = GetRandomUInt(); + } while (ui >= upperBound); + + return (int)(minValue + (ui % diff)); + } + + private uint GetRandomUInt() + { + var randomBytes = GenerateRandomBytes(sizeof(uint)); + return BitConverter.ToUInt32(randomBytes, 0); + } + + private byte[] GenerateRandomBytes(int bytesNumber) + { + var buffer = new byte[bytesNumber]; + csp.GetBytes(buffer); + return buffer; + } + + private bool _disposed; + + /// + /// Public implementation of Dispose pattern callable by consumers. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Protected implementation of Dispose pattern. + /// + /// + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + // Dispose managed state (managed objects). + csp?.Dispose(); + } + + _disposed = true; + } + } +} diff --git a/SharpENDEC/ILLink/ILLink.Descriptors.LibraryBuild.xml b/SharpENDEC/ILLink/ILLink.Descriptors.LibraryBuild.xml new file mode 100644 index 0000000..a42d7f0 --- /dev/null +++ b/SharpENDEC/ILLink/ILLink.Descriptors.LibraryBuild.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/SharpENDEC/InternalExtensions/ConsoleExt.cs b/SharpENDEC/InternalExtensions/ConsoleExt.cs index 8708c42..0fd3e56 100644 --- a/SharpENDEC/InternalExtensions/ConsoleExt.cs +++ b/SharpENDEC/InternalExtensions/ConsoleExt.cs @@ -1,4 +1,5 @@ using System; +using System.IO; namespace SharpENDEC { @@ -21,5 +22,14 @@ public static void Write(string value = "", ConsoleColor foreground = ConsoleCol Console.Write(value); Console.ForegroundColor = og; } + + public static void WriteLineErr(string value = "") + { + File.AppendAllText("error.log", $"{DateTime.Now:F} | {value}"); + og = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.DarkRed; + Console.WriteLine(value); + Console.ForegroundColor = og; + } } } diff --git a/SharpENDEC/InternalExtensions/MainExt.cs b/SharpENDEC/InternalExtensions/MainExt.cs index 4c54de1..3900042 100644 --- a/SharpENDEC/InternalExtensions/MainExt.cs +++ b/SharpENDEC/InternalExtensions/MainExt.cs @@ -1,14 +1,15 @@ using System; -using System.Diagnostics; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Security.Cryptography; using System.Threading; -using System.Threading.Tasks; -using ThreadState = System.Threading.ThreadState; namespace SharpENDEC { public static class MainExt { - private static Exception ex = new Exception(); + public static Exception LastException { get; private set; } /// /// This method is for dummy purposes, and does not perform any tasks. @@ -22,7 +23,7 @@ public static void UnknownCaller() /// public static Exception UnknownException() { - return new Exception("DummyException"); + return new Exception("UnknownException"); } /// @@ -31,51 +32,75 @@ public static Exception UnknownException() /// The method that called the exception. /// The exception that caused the unsafe state. /// The reason why the unsafe state was invoked. - /// Whether or not to restart and ignore the infinite rule. + /// Whether or not to restart and ignore the infinite wait rule. public static void UnsafeStateShutdown(Action caller, Exception exception, string reason, bool restart = true) { - lock (ex) + LastException = exception; + lock (LastException) { if (caller.IsNull()) caller = UnknownCaller; if (exception.IsNull()) exception = UnknownException(); if (string.IsNullOrEmpty(reason)) reason = "No reason provided."; - ex = exception; + try { if (!Program.WatchdogService.IsNull()) Program.WatchdogService.Abort(LastException); } catch (Exception) { } - lock (ENDEC.ClientThreads) + DateTime ExceptionTime = DateTime.Now; + + lock (ENDEC.CaptureThreads) { - foreach (Thread thread in ENDEC.ClientThreads) - try { if (!thread.IsNull()) thread.Abort(ex); } catch (Exception) { } - ENDEC.ClientThreads.Clear(); + foreach (Thread thread in ENDEC.CaptureThreads) + try { if (!thread.IsNull()) thread.Abort(LastException); } catch (Exception) { } + ENDEC.CaptureThreads.Clear(); } lock (Program.MainThreads) { - foreach (var (thread, method) in Program.MainThreads) - try { if (!thread.IsNull()) thread.Abort(ex); } catch (Exception) { } + foreach (var (thread, method, isMTA) in Program.MainThreads) + try { if (!thread.IsNull()) thread.Abort(LastException); } catch (Exception) { } Program.MainThreads.Clear(); } ENDEC.Capture = null; + ENDEC.SharpDataQueue = null; + ENDEC.SharpDataHistory = null; + + Thread.Sleep(100); Console.Clear(); Console.ForegroundColor = ConsoleColor.DarkRed; - string full = ($"{DateTime.Now:G} | Called by {caller.Method.Name}\r\n" + - $"Stack Trace: {ex.StackTrace}\r\n" + - $"Source: {ex.Source}\r\n" + - $"Message: {ex.Message}\r\n" + - $"{reason}"); + string full = $"{ExceptionTime:G} | Called by {caller.Method.Name}\r\n" + + $"Exception Stack Trace: {LastException.StackTrace}\r\n" + + $"Exception Source: {LastException.Source}\r\n" + + $"Exception Message: {LastException.Message}\r\n" + + $"Caller Reason: {reason}"; Console.WriteLine(full); - Thread.Sleep(1000); + try + { + string ExceptionDateTime = ExceptionTime.ToString("s").Replace(":", "-"); + File.WriteAllText(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + $"\\exception_{ExceptionDateTime}.txt", full + "\r\n"); + } + catch (Exception e) + { + Console.WriteLine($"Another exception occurred while trying to write an exception.\r\n{e.Message}"); + } +#if DEBUG Debug.Assert(false, "The program went into an unsafe state.", full); - Console.Clear(); +#else + Thread.Sleep(5000); +#endif if (restart) { Console.Clear(); - new Thread(() => Program.Main()).Start(); +#if !DEBUG + Environment.Exit(1984); +#else + new Thread(() => Program.Main(null)).Start(); +#endif + } + else + { + Environment.Exit(1985); } - else Thread.Sleep(Timeout.Infinite); - } } @@ -84,5 +109,56 @@ public static bool IsNull(this object obj) if (obj == null) return true; return false; } + + public static string ListToString(this List obj) + { + if (obj == null) return string.Empty; + if (obj.Count == 0) return string.Empty; + string all = string.Empty; + foreach (string str in obj) + { + all += $"{str},\x20"; + } + return all.Substring(all.Length, all.Length - 2); + } + + public class RandomGenerator + { + readonly RNGCryptoServiceProvider csp; + + public RandomGenerator() + { + csp = new RNGCryptoServiceProvider(); + } + + public int Next(int minValue, int maxExclusiveValue) + { + if (minValue >= maxExclusiveValue) + throw new ArgumentOutOfRangeException("minValue must be lower than maxExclusiveValue"); + + long diff = (long)maxExclusiveValue - minValue; + long upperBound = uint.MaxValue / diff * diff; + + uint ui; + do + { + ui = GetRandomUInt(); + } while (ui >= upperBound); + return (int)(minValue + (ui % diff)); + } + + private uint GetRandomUInt() + { + var randomBytes = GenerateRandomBytes(sizeof(uint)); + return BitConverter.ToUInt32(randomBytes, 0); + } + + private byte[] GenerateRandomBytes(int bytesNumber) + { + byte[] buffer = new byte[bytesNumber]; + csp.GetBytes(buffer); + return buffer; + } + } } } diff --git a/SharpENDEC/LanguageStrings.cs b/SharpENDEC/LanguageStrings.cs index 9454b45..6e2cb58 100644 --- a/SharpENDEC/LanguageStrings.cs +++ b/SharpENDEC/LanguageStrings.cs @@ -125,17 +125,17 @@ public static string RestartingAfterException(string lang) } } - public static string FileDownloaded(string lang, string host) + public static string FileDownloaded(string lang) { switch (lang) { case "french": - return $"Données enregistrées. | De : {host}"; + return $"Données enregistrées."; case "inuktitut": - return $"ᑎᑎᕋᖅᓯᒪᔪᑦ ᖃᐅᔨᓴᖅᑕᐅᓂᑯᐃᑦ ᑐᖅᑯᖅᑕᐅᓯᒪᔪᑦ. | ᐅᕙᙵᑦ: {host}"; + return $"ᑎᑎᕋᖅᓯᒪᔪᑦ ᖃᐅᔨᓴᖅᑕᐅᓂᑯᐃᑦ ᑐᖅᑯᖅᑕᐅᓯᒪᔪᑦ."; case "english": default: - return $"Data saved. | From: {host}"; + return $"File saved."; } } @@ -172,12 +172,22 @@ public static string DataIgnoredDueToMatchingPairs(string lang, int count) switch (lang) { case "french": - return $"{count} chaîne(s) avaient des paires correspondantes et n'ont pas été traités."; - case "inuktitut": - return $"{count} ᐊᒃᖢᓇᐅᔭᑦ ᐊᔾᔨᒌᓂᒃ ᐱᖃᑎᒌᓚᐅᖅᐳᑦ, ᐱᓕᕆᐊᖑᓚᐅᙱᖦᖢᑎᒡᓗ."; + return $"{count} alerte(s) avaient des noms correspondants et n'ont pas été téléchargées."; case "english": default: - return $"{count} string(s) had matching pairs, and were not processed."; + return $"{count} alert(s) had matching names, and were not downloaded."; + } + } + + public static string AlertInvalid(string lang) + { + switch (lang) + { + case "french": + return "Cette alerte n'est pas valide et ne peut pas être traitée."; + case "english": + default: + return "This alert is invalid and cannot be processed."; } } @@ -187,8 +197,6 @@ public static string AlertIgnoredDueToPreferences(string lang) { case "french": return "Cette alerte ne sera pas traitée en raison des préférences de l'utilisateur."; - case "inuktitut": - return "ᑖᓐᓇ ᖃᐅᔨᓴᕈᑎ ᐱᓕᕆᐊᖑᔾᔮᙱᑦᑐᖅ ᐊᑐᖅᑎᑦ ᓂᕈᐊᕆᔭᖏᑦ ᐱᔾᔪᑎᒋᓪᓗᒋᑦ."; case "english": default: return "This alert won't be processed due to the user preferences."; @@ -251,7 +259,7 @@ public static string PlayingAudio(string lang) } } - public static string CapturedFromFileWatcher(string lang) + public static string CapturedFromFileWatcher(string lang, DateTime dt) { switch (lang) { @@ -261,7 +269,7 @@ public static string CapturedFromFileWatcher(string lang) return "ᓄᑖᑦ ᓈᓴᐅᑏᑦ/ᑎᑎᖅᑲᐃᑦ ᐱᔭᐅᓚᐅᖅᑐᑦ."; case "english": default: - return "New data was captured."; + return $"Data captured at {dt:T}."; } } @@ -354,14 +362,12 @@ public static string ConfigurationLossProblem(string lang) switch (lang) { case "french": - return $"Vous perdrez probablement votre configuration parce que vous utilisez un compte invité." + + return $"Vous perdrez probablement votre configuration parce que vous utilisez un compte invité.\r\n" + $"S’il vous plaît exécuter SharpENDEC sous un utilisateur normal pour garder votre configuration !"; - case "inuktitut": - return ""; case "english": default: - return $"ᐋᖅᑭᒃᓯᒪᓂᕆᔭᐃᑦ ᐊᓯᐅᔨᑐᐃᓐᓇᕆᐊᖃᖅᑕᑦ ᓲᖃᐃᒻᒪ ᐊᑐᕋᕕᑦ ᖃᐃᖁᔭᐅᓯᒪᔪᒥᒃ ᓇᓕᖅᑲᒥᒃ.\r\n" + - $"SharpENDEC-ᑯᑦ ᐃᖏᕐᕋᑎᖃᑦᑕᕐᓂᐊᖅᐸᑎᑦ ᐊᑐᒐᔪᒃᑕᒃᑯᑦ ᐋᖅᑭᒃᓯᒪᓂᕆᔭᐃᑦᑏᓐᓇᕋᓱᒡᓗᒍ!"; + return $"You will lose your configuration because you are using a guest account.\r\n" + + $"Please run SharpENDEC under a normal user to keep your configuration!"; } } @@ -370,12 +376,10 @@ public static string DataPreviouslyProcessed(string lang) switch (lang) { case "french": - return "Les données ont été ignorées car elles se trouvaient déjà dans la file d’attente ou dans l’historique."; - case "inuktitut": - return "ᑖᒃᑯᐊ ᑎᑎᕋᖅᓯᒪᔪᑦ ᐲᖅᑕᐅᑐᐃᓐᓇᓚᐅᖅᑐᑦ ᐱᔾᔪᑎᒋᓪᓗᒍ ᐱᑕᖃᕇᕐᒪᑦ ᐅᕝᕙᓘᓐᓃᑦ ᐅᐊᑦᑎᐊᕈᓐᓂᓴᕐᓂᒃ."; + return "Les données ont été ignorées en raison d'une correspondance file d'attente/historique."; case "english": default: - return "The data was skipped because it's already in either the queue or history."; + return "The data was skipped due to a queue/history match."; } } @@ -385,8 +389,6 @@ public static string GenericProcessingValueOfValue(string lang, int x, int y) { case "french": return $"Traitement {x} de {y}."; - case "inuktitut": - return $"ᐱᓕᕆᐊᖃᕐᓂᖅ {x} ᑲᑎᖦᖢᒋᑦ {y}."; case "english": default: return $"Processing {x} of {y}."; @@ -400,8 +402,6 @@ public static string DownloadFailure(string lang) { case "french": return $"Impossible de télécharger le fichier."; - case "inuktitut": - return $"ᒥᓇᕆᙱᑕᖓ ᑎᑎᖅᑲᖓ."; case "english": default: return $"Failed to download the file."; @@ -414,11 +414,21 @@ public static string AlertIgnoredDueToBlacklist(string lang) { case "french": return "Cette alerte ne sera pas traitée en raison de la liste noire."; - case "inuktitut": - return "ᑖᓐᓇ ᖃᐅᔨᓴᕈᑎ ᐱᓕᕆᐊᖑᔾᔮᙱᑦᑐᖅ ᕿᕐᓂᖅᑕᒧᑦ."; case "english": default: - return "This alert won't be processed due to the blacklist."; + return "This alert won't be processed further due to the blacklist."; + } + } + + public static string AlertIgnoredDueToExpiry(string lang) + { + switch (lang) + { + case "french": + return "Cette alerte ne sera plus traitée parce qu'elle a expiré."; + case "english": + default: + return "This alert won't be processed further because it has expired."; } } } diff --git a/SharpENDEC/Plugins/DiscordWebhook.cs b/SharpENDEC/Plugins/DiscordWebhook.cs new file mode 100644 index 0000000..47b4f08 --- /dev/null +++ b/SharpENDEC/Plugins/DiscordWebhook.cs @@ -0,0 +1,25 @@ +using System.Diagnostics; + +namespace SharpENDEC +{ + public class DiscordWebhook : ISharpPlugin + { + public void AlertBlacklisted() + { + } + + public void AlertRelayingNow(string BroadcastText) + { + //Process.Start("cmd.exe", $"/c start cmd.exe /k echo {BroadcastText}"); + } + + public void ProvideBroadcastText(string text) + { + } + + public bool RelayAlert(string info, string status, string MsgType, string severity, string urgency, string broadcastImmediately) + { + return true; + } + } +} diff --git a/SharpENDEC/Plugins/ISharpPlugin.cs b/SharpENDEC/Plugins/ISharpPlugin.cs new file mode 100644 index 0000000..a07ee0e --- /dev/null +++ b/SharpENDEC/Plugins/ISharpPlugin.cs @@ -0,0 +1,10 @@ +namespace SharpENDEC +{ + internal interface ISharpPlugin + { + void AlertBlacklisted(); + void AlertRelayingNow(string BroadcastText); + void ProvideBroadcastText(string text); + bool RelayAlert(string info, string status, string MsgType, string severity, string urgency, string broadcastImmediately); + } +} diff --git a/SharpENDEC/Program.cs b/SharpENDEC/Program.cs index 34a427f..43f2983 100644 --- a/SharpENDEC/Program.cs +++ b/SharpENDEC/Program.cs @@ -2,12 +2,19 @@ using SharpENDEC.Properties; using System; using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; using System.IO; +using System.Linq; +using System.Management; using System.Reflection; +using System.Runtime.InteropServices; using System.Security.Principal; using System.Speech.Synthesis; using System.Threading; +using System.Windows.Forms; using static SharpENDEC.VersionInfo; +using ThreadState = System.Threading.ThreadState; namespace SharpENDEC { @@ -55,12 +62,16 @@ public static void ClearFolder(string directory) /// public static void Init(string VersionID, bool RecoveredFromProblem) { - Console.Title = VersionID; + EnablePerformanceProcessor = false; + Console.Title = VersionID + " | *"; + EnablePerformanceProcessor = true; + ConsoleExt.WriteLine($"{VersionID}\r\n" + - $"Source code forked from QuantumENDEC 4.\r\n\r\n" + + $"This project wouldn't have been possible without ApatheticDELL's QuantumENDEC!\r\n\r\n" + $"Project created by BunnyTub.\r\n" + $"Logo created by ApatheticDELL.\r\n" + - $"Translations may not be 100% accurate due to language deviations.\r\n"); + $"Translations may not be 100% accurate due to language deviations.\r\n" + + $"Translations are not fully complete, and may be undone.\r\n"); if (RecoveredFromProblem) ConsoleExt.WriteLine(LanguageStrings.RecoveredFromFailure(Settings.Default.CurrentLanguage), ConsoleColor.DarkRed); if (IsAdministrator) ConsoleExt.WriteLine(LanguageStrings.ElevationSecurityProblem(Settings.Default.CurrentLanguage), ConsoleColor.Yellow); @@ -70,15 +81,15 @@ public static void Init(string VersionID, bool RecoveredFromProblem) if (!File.Exists($"{AudioDirectory}\\attn.wav")) { + ConsoleExt.WriteLine("The attention tone audio \"attn.wav\" doesn't exist. The default one will be extracted and used instead."); MemoryStream mem = new MemoryStream(); Resources.attn.CopyTo(mem); File.WriteAllBytes("Audio\\attn.wav", mem.ToArray()); mem.Dispose(); - ConsoleExt.WriteLine("The attention tone audio \"attn.wav\" doesn't exist. The default one will be used instead."); } //ConsoleExt.WriteLine(); - ConsoleExt.WriteLine($"Press SPACE to pause for 30 seconds."); + ConsoleExt.WriteLine($"Press SPACE to pause for 15 seconds."); bool alreadyPaused = false; @@ -92,8 +103,8 @@ public static void Init(string VersionID, bool RecoveredFromProblem) switch (Console.ReadKey(true).Key) { case ConsoleKey.Spacebar: - ConsoleExt.WriteLine("Paused for 30 seconds."); - Thread.Sleep(30000); + ConsoleExt.WriteLine("Paused for 15 seconds."); + Thread.Sleep(15000); alreadyPaused = true; continue; } @@ -471,36 +482,7 @@ public static void StreamProcessor() } public static FeedCapture Capture; - public static List ClientThreads = new List(); - - //public static Thread MethodToThread(Action method) - //{ - // return new Thread(() => - // { - // try - // { - // method(); - // } - // catch (ThreadAbortException ex) - // { - // ConsoleExt.WriteLine($"Shutdown caught in thread: {ex.Message}"); - // File.AppendAllText($"{AssemblyDirectory}\\exception.log", - // $"{DateTime.Now:G} | Shutdown in {method.Method.Name}\r\n" + - // $"{ex.StackTrace}\r\n" + - // $"{ex.Source}\r\n" + - // $"{ex.Message}\r\n"); - // } - // catch (Exception ex) - // { - // ConsoleExt.WriteLine($"Exception caught in thread: {ex.Message}"); - // File.AppendAllText($"{AssemblyDirectory}\\exception.log", - // $"{DateTime.Now:G} | Exception in {method.Method.Name}\r\n" + - // $"{ex.StackTrace}\r\n" + - // $"{ex.Source}\r\n" + - // $"{ex.Message}\r\n"); - // } - // }); - //} + public static List CaptureThreads = new List(); public static void KeyboardProcessor() { @@ -541,6 +523,43 @@ public static void KeyboardProcessor() SkipPlayback = true; ConsoleExt.WriteLine("Audio playback has been disabled for this time only."); break; + case ConsoleKey.O: + OpenFileDialog AlertFileDialog = new OpenFileDialog + { + //InitialDirectory = "C:\\", + Filter = "CAP files (*.xml, *.cap)|*.xml;*.cap", + FilterIndex = 0, + CheckFileExists = true, + Multiselect = false + }; + //AlertFileDialog.RestoreDirectory = true; + + ConsoleExt.WriteLine("Opening file picker."); + + if (AlertFileDialog.ShowDialog() != DialogResult.OK) + { + ConsoleExt.WriteLine("No file chosen."); + break; + } + + try + { + string data = File.ReadAllText(AlertFileDialog.FileName); + lock (SharpDataQueue) + SharpDataQueue.Add(new SharpDataItem(AlertFileDialog.SafeFileName, data)); + ConsoleExt.WriteLine("Added file to queue."); + } + catch (Exception e) + { + ConsoleExt.WriteLineErr(e.Message); + } + finally + { + AlertFileDialog.Dispose(); + } + + break; + //case ConsoleKey.G: // ConsoleExt.WriteLine("GUI shown."); // break; @@ -551,7 +570,70 @@ public static void KeyboardProcessor() } } - public static void AddThread(ThreadStart method) + public static bool EnablePerformanceProcessor = false; + + public static void TitleProcessor() + { + string originalTitle = Console.Title; + Process thisProcess = Process.GetCurrentProcess(); + PerformanceCounter cpuCounter; + cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); + + while (true) + { + if (EnablePerformanceProcessor) + { + Console.Title = originalTitle.Replace("*", $"{cpuCounter.NextValue() / Environment.ProcessorCount:0.00}% CPU - {thisProcess.WorkingSet64 / (1024 * 1024)} MB"); + thisProcess.Refresh(); + } + Thread.Sleep(1000); + } + } + + public static void BatteryProcessor() + { + // if (Battery.UsageEnabled) then DoBatteryStuff + //try + { + while (true) + { + try + { + foreach (ManagementObject battery in new ManagementObjectSearcher("Select * from Win32_Battery") + .Get() + .Cast()) + { + string name = battery["Name"]?.ToString(); + string status = battery["Status"]?.ToString(); + int estimatedChargeRemaining = Convert.ToInt32(battery["EstimatedChargeRemaining"]); + int estimatedRunTime = Convert.ToInt32(battery["EstimatedRunTime"]); + + Console.WriteLine($"Battery Name: {name}"); + Console.WriteLine($"Status: {status}"); + Console.WriteLine($"Charge Remaining: {estimatedChargeRemaining}%"); + + // Estimated run time in minutes, -1 means unknown + if (estimatedRunTime != -1) + { + Console.WriteLine($"Estimated Run Time: {TimeSpan.FromMinutes(estimatedRunTime)}"); + } + else + { + Console.WriteLine("Estimated Run Time: Unknown"); + } + } + } + catch (Exception ex) + { + Console.WriteLine($"An error occurred: {ex.Message}"); + } + //Thread.Sleep(10000); + Thread.Sleep(1000); + } + } + } + + public static Thread AddThread(ThreadStart method, bool isMTA) { Thread thread = new Thread(() => { @@ -559,19 +641,43 @@ public static void AddThread(ThreadStart method) catch (ThreadAbortException) { } catch (Exception e) { MainExt.UnsafeStateShutdown(null, e, e.Message); } }); - Program.MainThreads.Add((thread, method)); + if (isMTA) thread.SetApartmentState(ApartmentState.MTA); + else thread.SetApartmentState(ApartmentState.STA); + Program.MainThreads.Add((thread, method, isMTA)); thread.Start(); + return thread; } + public static void KillAllThreads() + { + var threadList = new List(); + foreach (var (thread, _, _) in Program.MainThreads) + { + threadList.Add(thread); + } + Program.MainThreads.Clear(); + foreach (var thread in threadList) + { + thread.Abort(); + } + } + public static void RestartAllThreads() { - foreach (var (thread, method) in Program.MainThreads) + var threadList = new List<(Thread thread, ThreadStart method, bool isMTA)>(); + foreach (var (thread, method, isMTA) in Program.MainThreads) + { + threadList.Add((thread, method, isMTA)); + RestartThread(thread, method, isMTA); + } + Program.MainThreads.Clear(); + foreach (var (thread, method, isMTA) in threadList) { - RestartThread(thread, method); + Program.MainThreads.Add((thread, method, isMTA)); } } - public static void RestartThread(Thread serviceThread, ThreadStart method) + public static void RestartThread(Thread serviceThread, ThreadStart method, bool IsMTAThread) { if (serviceThread != null) { @@ -579,55 +685,102 @@ public static void RestartThread(Thread serviceThread, ThreadStart method) serviceThread.Abort(); } - Thread newThread = new Thread(method); - newThread.Start(); - - int index = Program.MainThreads.IndexOf((serviceThread, method)); - if (index >= 0) - { - Program.MainThreads[index] = (newThread, method); - } - else + Thread thread = new Thread(() => { - Program.MainThreads.Add((newThread, method)); - } + try { method(); } + catch (ThreadAbortException) { } + catch (Exception e) { MainExt.UnsafeStateShutdown(null, e, e.Message); } + }); + if (IsMTAThread) thread.SetApartmentState(ApartmentState.MTA); + else thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + //int index = Program.MainThreads.IndexOf((serviceThread, method)); + //if (index >= 0) + //{ + // Program.MainThreads[index] = (newThread, method); + //} + //else + //{ + // Program.MainThreads.Add((newThread, method)); + //} } + //public static IEnumerable handlers = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes()) + // .Where(p => typeof(ISharpPlugin).IsAssignableFrom(p) && p.IsClass); + public static Thread Watchdog() { return new Thread(() => { - string ProgramVersion = FriendlyVersion; - Init(ProgramVersion, false); - - void ShutdownCapture() + try { - if (Capture != null) + string ProgramVersion = FriendlyVersion; + Init(ProgramVersion, false); + + void ShutdownCapture() { - Capture.ShutdownCapture = true; - while (Capture.ShutdownCapture) + if (Capture != null) { - Thread.Sleep(500); + Capture.ShutdownCapture = true; + while (Capture.ShutdownCapture) + { + Thread.Sleep(500); + } } + ConsoleExt.WriteLine($"{LanguageStrings.ThreadShutdown(Settings.Default.CurrentLanguage, $"Data Processor")}"); } - ConsoleExt.WriteLine($"{LanguageStrings.ThreadShutdown(Settings.Default.CurrentLanguage, $"Data Processor")}"); - } - Config(); + Config(); - while (true) - { - Check.LastHeartbeat = DateTime.Now; + //foreach (var handler in handlers) + //{ + // var handlerInstance = (ISharpPlugin)Activator.CreateInstance(handler); + // //handlerInstance.AlertBlacklisted(); + //} + +#if !DEBUG + Process parent; + + try + { + var myId = Process.GetCurrentProcess().Id; + var search = new ManagementObjectSearcher("root\\CIMV2", + $"SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {Process.GetCurrentProcess().Id}"); + var results = search.Get().GetEnumerator(); + results.MoveNext(); + parent = Process.GetProcessById((int)(uint)results.Current["ParentProcessId"]); + } + catch (Exception) + { + ConsoleExt.WriteLine("Process is malfunctioning."); + return; + } +#endif //if (!Program.MainThreads.Contains(Thread.CurrentThread)) Program.MainThreads.Add(Thread.CurrentThread); - AddThread(StreamProcessor); - AddThread(DataProcessor); - AddThread(AlertProcessor); - AddThread(KeyboardProcessor); + AddThread(StreamProcessor, true); + AddThread(DataProcessor, true); + AddThread(AlertProcessor, true); + AddThread(KeyboardProcessor, false); + AddThread(TitleProcessor, true); + //AddThread(BatteryProcessor); + AddThread(HTTPServerProcessor, true); + + Check.LastHeartbeat = DateTime.Now; while (true) { +#if !DEBUG + if (parent.HasExited) + { + ShutdownCapture(); + KillAllThreads(); + Environment.Exit(1985); + } +#endif + if ((DateTime.Now - Check.LastHeartbeat).TotalMinutes >= 5) { if ((DateTime.Now - Check.LastHeartbeat).TotalMinutes >= 10) @@ -643,9 +796,9 @@ void ShutdownCapture() } catch (Exception) { - + } - + break; } //else ConsoleExt.WriteLine($"[Watchdog] {LanguageStrings.WatchdogForcedRestartWarning(Settings.Default.CurrentLanguage)}", ConsoleColor.Red); @@ -653,6 +806,8 @@ void ShutdownCapture() Thread.Sleep(5000); } } + catch (ThreadAbortException) { } + catch (Exception e) { MainExt.UnsafeStateShutdown(null, e, e.Message); } }); } } @@ -660,30 +815,103 @@ void ShutdownCapture() internal static class Program { internal static Thread WatchdogService; - //internal static Thread StreamService; - //internal static Thread RelayService; - //internal static Thread KeyboardProc; - //internal static Thread BatteryProc; - internal static List<(Thread, ThreadStart)> MainThreads = new List<(Thread, ThreadStart)>(); + internal static List<(Thread, ThreadStart, bool)> MainThreads = new List<(Thread, ThreadStart, bool)>(); + internal const uint ENABLE_QUICK_EDIT = 0x0040; + internal const uint ENABLE_EXTENDED_FLAGS = 0x0080; + public const int STD_INPUT_HANDLE = -10; + + [DllImport("kernel32.dll", SetLastError = true)] + internal static extern IntPtr GetStdHandle(int nStdHandle); + + [DllImport("kernel32.dll")] + internal static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); + + [DllImport("kernel32.dll")] + internal static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); [STAThread] - internal static void Main() + internal static void Main(string[] args) { Console.Title = "SharpENDEC"; Console.ForegroundColor = ConsoleColor.White; - // PLUGIN IMPLEMENTATION!!! - // BATTERY IMPLEMENTATION!!! - // GUI IMPLEMENTATION!!! - //Thread thread = new Thread(() => new ConsoleForm().ShowDialog()); - //thread.Start(); - WatchdogService = ENDEC.Watchdog(); - WatchdogService.Start(); - Console.CancelKeyPress += CancelAllOperations; + +#if !DEBUG + string WatchdogArgument = "--redundant-watchdog"; + + if (args.Length == 1) + { + if (args[0] == WatchdogArgument) + { +#endif + DisableConsoleHighlighting(); + // BATTERY IMPLEMENTATION!!! - BUSY + // GUI IMPLEMENTATION!!! - UNKNOWN / BUSY + WatchdogService = ENDEC.Watchdog(); + WatchdogService.Start(); +#if !DEBUG + } + } + else + { + bool restart = false; + + do + { + using (var p = new Process + { + StartInfo = new ProcessStartInfo(Assembly.GetExecutingAssembly().Location) + { + Arguments = WatchdogArgument, + UseShellExecute = false + } + }) + { + p.Start(); + p.WaitForExit(); + + if (p.ExitCode == 1984) + { + restart = true; + } + else + { + restart = false; + } + } + } while (restart); + } +#endif } - private static void CancelAllOperations(object sender, ConsoleCancelEventArgs e) + internal static bool DisableConsoleHighlighting() { + IntPtr consoleHandle = GetStdHandle(STD_INPUT_HANDLE); + + if (!GetConsoleMode(consoleHandle, out uint mode)) + { + var MarshalException = new Win32Exception(unchecked(Marshal.GetLastWin32Error())); + ConsoleExt.WriteLineErr($"Could not get the console mode. This may cause problems if you select text often.\r\n" + + $"You can ignore this message if you're not running Windows 10 or above.\r\n" + + $"Reason: {MarshalException.Message}"); + Thread.Sleep(5000); + return false; + } + + mode &= ~ENABLE_QUICK_EDIT; + mode |= ENABLE_EXTENDED_FLAGS; + + if (!SetConsoleMode(consoleHandle, mode)) + { + var MarshalException = new Win32Exception(unchecked(Marshal.GetLastWin32Error())); + ConsoleExt.WriteLineErr($"Could not set the console mode. This may cause problems if you select text often.\r\n" + + $"You can ignore this message if you're not running Windows 10 or above.\r\n" + + $"Reason: {MarshalException.Message}"); + Thread.Sleep(5000); + return false; + } + // if we don't use unchecked(), the error might cause an error + return true; } } } \ No newline at end of file diff --git a/SharpENDEC/SharpENDEC.csproj b/SharpENDEC/SharpENDEC.csproj index 3fe8c0c..5abbdb2 100644 --- a/SharpENDEC/SharpENDEC.csproj +++ b/SharpENDEC/SharpENDEC.csproj @@ -66,13 +66,37 @@ ..\packages\Costura.Fody.5.7.0\lib\netstandard1.0\Costura.dll + + ..\packages\Microsoft.Bcl.AsyncInterfaces.9.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll + ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll True True - - ..\packages\NAudio.1.10.0\lib\net35\NAudio.dll + + ..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll + + + ..\packages\NAudio.2.2.1\lib\net472\NAudio.dll + + + ..\packages\NAudio.Asio.2.2.1\lib\netstandard2.0\NAudio.Asio.dll + + + ..\packages\NAudio.Core.2.2.1\lib\netstandard2.0\NAudio.Core.dll + + + ..\packages\NAudio.Midi.2.2.1\lib\netstandard2.0\NAudio.Midi.dll + + + ..\packages\NAudio.Wasapi.2.2.1\lib\netstandard2.0\NAudio.Wasapi.dll + + + ..\packages\NAudio.WinForms.2.2.1\lib\net472\NAudio.WinForms.dll + + + ..\packages\NAudio.WinMM.2.2.1\lib\netstandard2.0\NAudio.WinMM.dll ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll @@ -83,15 +107,18 @@ True True + + ..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll + - - ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll + + ..\packages\System.Console.4.3.1\lib\net46\System.Console.dll True True - - ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll + + ..\packages\System.Diagnostics.DiagnosticSource.9.0.0\lib\net462\System.Diagnostics.DiagnosticSource.dll ..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll @@ -129,6 +156,9 @@ True True + + ..\packages\System.IO.Pipelines.9.0.0\lib\net462\System.IO.Pipelines.dll + ..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll True @@ -140,8 +170,11 @@ True - - ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll + + ..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll + + + ..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll True True @@ -151,18 +184,24 @@ True + + ..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll + ..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll True True - - ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll + + ..\packages\System.Runtime.4.3.1\lib\net462\System.Runtime.dll True True - - ..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Runtime.Extensions.4.3.1\lib\net462\System.Runtime.Extensions.dll True True @@ -176,8 +215,12 @@ True True + + ..\packages\System.Security.AccessControl.6.0.1\lib\net461\System.Security.AccessControl.dll + - ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll + ..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net463\System.Security.Cryptography.Algorithms.dll + True True @@ -190,17 +233,32 @@ True True - - ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll True True + + ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll + + + ..\packages\System.Text.Encodings.Web.9.0.0\lib\net462\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.9.0.0\lib\net462\System.Text.Json.dll + - ..\packages\System.Text.RegularExpressions.4.3.0\lib\net463\System.Text.RegularExpressions.dll + ..\packages\System.Text.RegularExpressions.4.3.1\lib\net463\System.Text.RegularExpressions.dll True True + + ..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + @@ -210,13 +268,14 @@ - ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll + ..\packages\System.Xml.ReaderWriter.4.3.1\lib\net46\System.Xml.ReaderWriter.dll True True + Form @@ -229,7 +288,10 @@ + + + Form @@ -307,6 +369,7 @@ + @@ -321,30 +384,29 @@ false + - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + + REM del "$(TargetDir)SharpENDEC.pdb" -del "$(TargetDir)SharpENDEC.exe.config" - -del "$(TargetDir)NAudio.xml" - -del "$(TargetDir)Newtonsoft.Json.xml" +del "$(TargetDir)*.config" -del "$(TargetDir)System.Diagnostics.DiagnosticSource.xml" +del "$(TargetDir)*.xml" cd $(ProjectDir) powershell -ExecutionPolicy Bypass -File "$(ProjectDir)UpdateVersionInfo.ps1" + + \ No newline at end of file diff --git a/SharpENDEC/VersionInfo.cs b/SharpENDEC/VersionInfo.cs index f0a2847..ca54395 100644 --- a/SharpENDEC/VersionInfo.cs +++ b/SharpENDEC/VersionInfo.cs @@ -1,5 +1,3 @@ -using System.Diagnostics; - namespace SharpENDEC { public static class VersionInfo @@ -8,11 +6,11 @@ public static class VersionInfo // You can change the release, minor, and cutting edge variables. // --- // Use VersionInfoTemplate.cs! - public const int BuildNumber = 596; - public const string BuiltOnDate = "2024-10-20"; - public const string BuiltOnTime = "09:14"; + public const int BuildNumber = 704; + public const string BuiltOnDate = "2024-11-28"; + public const string BuiltOnTime = "18:30"; public const string BuiltTimeZone = "Eastern Standard Time"; - public static readonly int ReleaseVersion = 2; + public static readonly int ReleaseVersion = 3; public static readonly int MinorVersion = 0; public static readonly bool IsCuttingEdge = false; public static string FriendlyVersion @@ -25,9 +23,7 @@ public static string FriendlyVersion } else { - if (Debugger.IsAttached) return $"SharpENDEC | Cutting Edge {ReleaseVersion}.{MinorVersion}-c (Build {BuildNumber}) | Built on {BuiltOnDate} {BuiltOnTime} ({BuiltTimeZone})"; - else return $"SharpENDEC | Cutting Edge {ReleaseVersion}.{MinorVersion}-c (Build {BuildNumber}) | Built on {BuiltOnDate} {BuiltOnTime} ({BuiltTimeZone})\r\n" + - $"Debugger Attached | Is Logging: {Debugger.IsLogging()}"; + return $"SharpENDEC | Cutting Edge {ReleaseVersion}.{MinorVersion}-c (Build {BuildNumber}) | Built on {BuiltOnDate} {BuiltOnTime} ({BuiltTimeZone})"; } } } diff --git a/SharpENDEC/VersionInfoTemplate.cs b/SharpENDEC/VersionInfoTemplate.cs index def75a8..a519a92 100644 --- a/SharpENDEC/VersionInfoTemplate.cs +++ b/SharpENDEC/VersionInfoTemplate.cs @@ -1,6 +1,4 @@ -using System.Diagnostics; - -namespace SharpENDEC +namespace SharpENDEC { public static class VersionInfoTemplate { @@ -12,7 +10,7 @@ public static class VersionInfoTemplate public const string BuiltOnDate = ""; public const string BuiltOnTime = ""; public const string BuiltTimeZone = ""; - public static readonly int ReleaseVersion = 2; + public static readonly int ReleaseVersion = 3; public static readonly int MinorVersion = 0; public static readonly bool IsCuttingEdge = false; public static string FriendlyVersion @@ -25,9 +23,7 @@ public static string FriendlyVersion } else { - if (Debugger.IsAttached) return $"SharpENDEC | Cutting Edge {ReleaseVersion}.{MinorVersion}-c (Build {BuildNumber}) | Built on {BuiltOnDate} {BuiltOnTime} ({BuiltTimeZone})"; - else return $"SharpENDEC | Cutting Edge {ReleaseVersion}.{MinorVersion}-c (Build {BuildNumber}) | Built on {BuiltOnDate} {BuiltOnTime} ({BuiltTimeZone})\r\n" + - $"Debugger Attached | Is Logging: {Debugger.IsLogging()}"; + return $"SharpENDEC | Cutting Edge {ReleaseVersion}.{MinorVersion}-c (Build {BuildNumber}) | Built on {BuiltOnDate} {BuiltOnTime} ({BuiltTimeZone})"; } } } diff --git a/SharpENDEC/packages.config b/SharpENDEC/packages.config index 7daa006..bc02615 100644 --- a/SharpENDEC/packages.config +++ b/SharpENDEC/packages.config @@ -1,18 +1,27 @@  - - + + + - - + + + + + + + + + + - + - + @@ -22,32 +31,42 @@ + - - + + + + - - + + + - + + - + + - + + + + - + + \ No newline at end of file