From 34f25c0bdcf2858bc7ce533a3dac25883a4af9b8 Mon Sep 17 00:00:00 2001 From: Code A Software Date: Fri, 8 Aug 2025 23:51:56 +0200 Subject: [PATCH 1/4] feat: Updated nuget packages & Convert csproj to new SDK-file format --- AutomaticMailPrinter/App.config | 18 ++- .../AutomaticMailPrinter.csproj | 131 +++--------------- AutomaticMailPrinter/Program.cs | 12 +- AutomaticMailPrinter/packages.config | 16 --- 4 files changed, 48 insertions(+), 129 deletions(-) delete mode 100644 AutomaticMailPrinter/packages.config diff --git a/AutomaticMailPrinter/App.config b/AutomaticMailPrinter/App.config index 05e2f90..5de3a19 100644 --- a/AutomaticMailPrinter/App.config +++ b/AutomaticMailPrinter/App.config @@ -11,12 +11,28 @@ - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AutomaticMailPrinter/AutomaticMailPrinter.csproj b/AutomaticMailPrinter/AutomaticMailPrinter.csproj index e76e147..8d14519 100644 --- a/AutomaticMailPrinter/AutomaticMailPrinter.csproj +++ b/AutomaticMailPrinter/AutomaticMailPrinter.csproj @@ -1,17 +1,7 @@ - - - + - Debug - AnyCPU - {EBE756A9-E6C7-45D7-9575-769607F15C2F} + net48 WinExe - AutomaticMailPrinter - AutomaticMailPrinter - v4.8 - 512 - true - true false publish\ true @@ -27,25 +17,8 @@ 1.0.0.%2a false true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + true + true icon.ico @@ -57,78 +30,13 @@ app.manifest - - ..\packages\Portable.BouncyCastle.1.9.0\lib\net40\BouncyCastle.Crypto.dll - - - ..\packages\MailKit.3.4.1\lib\net48\MailKit.dll - - - ..\packages\Microsoft.Bcl.AsyncInterfaces.7.0.0-rc.2.22472.3\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll - - - ..\packages\MimeKit.3.4.1\lib\net48\MimeKit.dll - - - - ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll - - - - ..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll - - - - ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll - - - ..\packages\System.Runtime.CompilerServices.Unsafe.7.0.0-preview.2.22152.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll - - - ..\packages\System.Text.Encoding.CodePages.7.0.0-rc.2.22472.3\lib\net462\System.Text.Encoding.CodePages.dll - - - ..\packages\System.Text.Encodings.Web.7.0.0-rc.2.22472.3\lib\net462\System.Text.Encodings.Web.dll - - - ..\packages\System.Text.Json.7.0.0-rc.2.22472.3\lib\net462\System.Text.Json.dll - - - ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll - - - ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll - - - - - - - - - - - - - True - True - Resources.resx - - - True - True - Resources.de.resx - - - - - + Always @@ -165,10 +73,10 @@ Always - + Always - + Always @@ -185,14 +93,21 @@ - - PublicResXFileCodeGenerator - Resources.de.Designer.cs - - - PublicResXFileCodeGenerator - Resources.Designer.cs - + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/AutomaticMailPrinter/Program.cs b/AutomaticMailPrinter/Program.cs index 4f4460e..27fbc09 100644 --- a/AutomaticMailPrinter/Program.cs +++ b/AutomaticMailPrinter/Program.cs @@ -18,6 +18,7 @@ internal class Program private static readonly Mutex AppMutex = new Mutex(false, "c75adf4e-765c-4529-bf7a-90dd76cd386a"); private static string ImapServer, MailAddress, Password, PrinterName; + public static string WebHookUrl { get; private set; } private static string[] Filter = new string[0]; private static int ImapPort; @@ -170,10 +171,11 @@ private static void Timer_Tick(object state) { // Print text Console.ForegroundColor = ConsoleColor.Green; - Logger.LogInfo($"{string.Format(Properties.Resources.strFoundUnreadMail, Filter.Where(f => subject.Contains(f)).FirstOrDefault())} {message.Subject}"); + Logger.LogInfo($"{string.Format(Properties.Resources.strFoundUnreadMail, Filter.FirstOrDefault(f => subject.Contains(f)))} {message.Subject}"); // Print mail Logger.LogInfo(string.Format(Properties.Resources.strPrintMessage, message.Subject, PrinterName)); + PrintHtmlPage(message.HtmlBody); // Delete mail https://stackoverflow.com/a/24204804/6237448 @@ -234,14 +236,16 @@ public static void PrintHtmlPage(string htmlContent) public static bool? PrintHtmlPages(string printer, params string[] urls) { // Spawn the code to print the packing slips - var info = new ProcessStartInfo(); - info.Arguments = $"-p \"{printer}\" -a A4 \"{string.Join("\" \"", urls)}\""; + var info = new ProcessStartInfo + { + Arguments = $"-p \"{printer}\" -a A4 \"{string.Join("\" \"", urls)}\"" + }; var pathToExe = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); info.FileName = Path.Combine(pathToExe, "PrintHtml", "PrintHtml.exe"); Process.Start(info); - return null; + /*using (var p = Process.Start(info)) { // Wait until it is finished diff --git a/AutomaticMailPrinter/packages.config b/AutomaticMailPrinter/packages.config deleted file mode 100644 index 4c23c67..0000000 --- a/AutomaticMailPrinter/packages.config +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file From b3788c1ae35d7b2ad45cf203567a70ca278b221d Mon Sep 17 00:00:00 2001 From: Code A Software Date: Fri, 8 Aug 2025 23:53:12 +0200 Subject: [PATCH 2/4] fix: Assembly Info --- AutomaticMailPrinter/AutomaticMailPrinter.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/AutomaticMailPrinter/AutomaticMailPrinter.csproj b/AutomaticMailPrinter/AutomaticMailPrinter.csproj index 8d14519..f6b2034 100644 --- a/AutomaticMailPrinter/AutomaticMailPrinter.csproj +++ b/AutomaticMailPrinter/AutomaticMailPrinter.csproj @@ -19,6 +19,7 @@ true true true + false icon.ico From 4e736282c6bc88863af7492e60ccc0ed4d58e951 Mon Sep 17 00:00:00 2001 From: Floris <65296458+florijohn@users.noreply.github.com> Date: Sat, 9 Aug 2025 12:10:55 +0200 Subject: [PATCH 3/4] update --- AutomaticMailPrinter/Program.cs | 246 +++++++++++++++++++++++++++++--- AutomaticMailPrinter/config.yml | 14 ++ 2 files changed, 241 insertions(+), 19 deletions(-) create mode 100644 AutomaticMailPrinter/config.yml diff --git a/AutomaticMailPrinter/Program.cs b/AutomaticMailPrinter/Program.cs index 27fbc09..00cb7b5 100644 --- a/AutomaticMailPrinter/Program.cs +++ b/AutomaticMailPrinter/Program.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Threading; using System.Windows.Forms; +using MimeKit; namespace AutomaticMailPrinter { @@ -22,6 +23,7 @@ internal class Program public static string WebHookUrl { get; private set; } private static string[] Filter = new string[0]; private static int ImapPort; + private static bool PrintPdfAttachments = false; private static ImapClient client = new ImapClient(); private static IMailFolder inbox; @@ -40,29 +42,46 @@ static void Main(string[] args) int intervalInSecods = 60; try { - string configPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json"); - var configDocument = System.Text.Json.JsonSerializer.Deserialize(System.IO.File.ReadAllText(configPath)); + // Prefer YAML if present; else fallback to JSON + string baseDir = AppDomain.CurrentDomain.BaseDirectory; + string yamlPath = System.IO.Path.Combine(baseDir, "config.yml"); + string jsonPath = System.IO.Path.Combine(baseDir, "config.json"); - ImapServer = configDocument.RootElement.GetProperty("imap_server").GetString(); - ImapPort = configDocument.RootElement.GetProperty("imap_port").GetInt32(); - MailAddress = configDocument.RootElement.GetProperty("mail").GetString(); - Password = configDocument.RootElement.GetProperty("password").GetString(); - PrinterName = configDocument.RootElement.GetProperty("printer_name").GetString(); - - try + if (System.IO.File.Exists(yamlPath)) { - // Can be empty or even may not existing ... - WebHookUrl = configDocument.RootElement.GetProperty("webhook_url").GetString(); + LoadConfigFromYaml(yamlPath, ref intervalInSecods); } - catch { } + else + { + var configDocument = System.Text.Json.JsonSerializer.Deserialize(System.IO.File.ReadAllText(jsonPath)); - intervalInSecods = configDocument.RootElement.GetProperty("timer_interval_in_seconds").GetInt32(); + ImapServer = configDocument.RootElement.GetProperty("imap_server").GetString(); + ImapPort = configDocument.RootElement.GetProperty("imap_port").GetInt32(); + MailAddress = configDocument.RootElement.GetProperty("mail").GetString(); + Password = configDocument.RootElement.GetProperty("password").GetString(); + PrinterName = configDocument.RootElement.GetProperty("printer_name").GetString(); + + try + { + // Can be empty or even may not existing ... + WebHookUrl = configDocument.RootElement.GetProperty("webhook_url").GetString(); + } + catch { } - var filterProperty = configDocument.RootElement.GetProperty("filter"); - int counter = 0; - Filter = new string[filterProperty.GetArrayLength()]; - foreach (var word in filterProperty.EnumerateArray()) - Filter[counter++] = word.GetString().ToLower(); + intervalInSecods = configDocument.RootElement.GetProperty("timer_interval_in_seconds").GetInt32(); + + try + { + PrintPdfAttachments = configDocument.RootElement.GetProperty("print_pdf_attachments").GetBoolean(); + } + catch { } + + var filterProperty = configDocument.RootElement.GetProperty("filter"); + int counter = 0; + Filter = new string[filterProperty.GetArrayLength()]; + foreach (var word in filterProperty.EnumerateArray()) + Filter[counter++] = word.GetString().ToLower(); + } } catch (Exception ex) { @@ -176,7 +195,23 @@ private static void Timer_Tick(object state) // Print mail Logger.LogInfo(string.Format(Properties.Resources.strPrintMessage, message.Subject, PrinterName)); - PrintHtmlPage(message.HtmlBody); + if (PrintPdfAttachments) + { + var pdfs = SavePdfAttachmentsToTempFiles(message); + if (pdfs != null && pdfs.Any()) + { + PrintPdfFiles(PrinterName, pdfs.ToArray()); + } + else + { + // Fallback to HTML if no PDFs found + PrintHtmlPage(message.HtmlBody); + } + } + else + { + PrintHtmlPage(message.HtmlBody); + } // Delete mail https://stackoverflow.com/a/24204804/6237448 Logger.LogInfo(Properties.Resources.strMarkMailAsDeleted); @@ -256,5 +291,178 @@ public static void PrintHtmlPage(string htmlContent) return p.ExitCode == 0; }*/ } + + private static void LoadConfigFromYaml(string yamlPath, ref int intervalInSecods) + { + try + { + var lines = System.IO.File.ReadAllLines(yamlPath); + var values = ParseSimpleYaml(lines); + + ImapServer = GetDictString(values, "imap_server"); + ImapPort = GetDictInt(values, "imap_port", 993); + MailAddress = GetDictString(values, "mail"); + Password = GetDictString(values, "password"); + PrinterName = GetDictString(values, "printer_name"); + WebHookUrl = GetDictString(values, "webhook_url"); + intervalInSecods = GetDictInt(values, "timer_interval_in_seconds", intervalInSecods); + PrintPdfAttachments = GetDictBool(values, "print_pdf_attachments", false); + + if (values.TryGetValue("filter", out var filterObj) && filterObj is System.Collections.Generic.List list) + { + Filter = list.Select(s => (s ?? string.Empty).ToLower()).ToArray(); + } + else if (values.TryGetValue("filter", out var filterStr) && filterStr is string s && !string.IsNullOrWhiteSpace(s)) + { + Filter = s.Split(',').Select(x => x.Trim().ToLower()).Where(x => !string.IsNullOrEmpty(x)).ToArray(); + } + } + catch (Exception ex) + { + Logger.LogError("Failed to read YAML config.", ex); + throw; + } + } + + private static System.Collections.Generic.Dictionary ParseSimpleYaml(string[] lines) + { + var dict = new System.Collections.Generic.Dictionary(StringComparer.OrdinalIgnoreCase); + string currentListKey = null; + System.Collections.Generic.List currentList = null; + + foreach (var rawLine in lines) + { + var line = rawLine; + if (line == null) continue; + line = line.Trim(); + if (line.Length == 0) continue; + if (line.StartsWith("#")) continue; + + // list item + if (line.StartsWith("- ")) + { + if (currentListKey != null) + { + currentList = currentList ?? new System.Collections.Generic.List(); + var item = line.Substring(2).Trim(); + currentList.Add(Unquote(item)); + dict[currentListKey] = currentList; + } + continue; + } + + var idx = line.IndexOf(':'); + if (idx <= 0) + continue; + + var key = line.Substring(0, idx).Trim(); + var value = line.Substring(idx + 1).Trim(); + + if (string.IsNullOrEmpty(value)) + { + // start list or nested section; we only support simple lists here + currentListKey = key; + currentList = new System.Collections.Generic.List(); + dict[currentListKey] = currentList; + } + else + { + currentListKey = null; + dict[key] = Unquote(value); + } + } + + return dict; + } + + private static string Unquote(string v) + { + if (string.IsNullOrEmpty(v)) return v; + if ((v.StartsWith("\"") && v.EndsWith("\"")) || (v.StartsWith("'") && v.EndsWith("'"))) + return v.Substring(1, v.Length - 2); + return v; + } + + private static string GetDictString(System.Collections.Generic.Dictionary dict, string key) + { + if (dict.TryGetValue(key, out var v) && v != null) + return Convert.ToString(v); + return null; + } + + private static int GetDictInt(System.Collections.Generic.Dictionary dict, string key, int defaultValue) + { + if (dict.TryGetValue(key, out var v) && v != null) + { + if (v is int i) return i; + if (int.TryParse(Convert.ToString(v), out var parsed)) return parsed; + } + return defaultValue; + } + + private static bool GetDictBool(System.Collections.Generic.Dictionary dict, string key, bool defaultValue) + { + if (dict.TryGetValue(key, out var v) && v != null) + { + var s = Convert.ToString(v)?.Trim().ToLowerInvariant(); + if (s == "true" || s == "yes" || s == "1") return true; + if (s == "false" || s == "no" || s == "0") return false; + } + return defaultValue; + } + + private static System.Collections.Generic.List SavePdfAttachmentsToTempFiles(MimeMessage message) + { + var result = new System.Collections.Generic.List(); + if (message == null) return result; + + foreach (var attachment in message.Attachments) + { + if (attachment is MimePart part) + { + var mimeType = part.ContentType?.MimeType ?? string.Empty; + var fileName = part.FileName ?? string.Empty; + bool isPdf = mimeType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase) + || fileName.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase); + if (isPdf) + { + var tempPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString("N") + ".pdf"); + using (var stream = System.IO.File.Create(tempPath)) + { + part.Content.DecodeTo(stream); + } + result.Add(tempPath); + } + } + } + + return result; + } + + private static void PrintPdfFiles(string printer, params string[] pdfPaths) + { + if (pdfPaths == null || pdfPaths.Length == 0) return; + + foreach (var pdf in pdfPaths) + { + try + { + var psi = new ProcessStartInfo + { + FileName = pdf, + Verb = "printto", + UseShellExecute = true, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + Arguments = $"\"{printer}\"" + }; + Process.Start(psi); + } + catch (Exception ex) + { + Logger.LogError($"Failed to print PDF '{pdf}'", ex); + } + } + } } } \ No newline at end of file diff --git a/AutomaticMailPrinter/config.yml b/AutomaticMailPrinter/config.yml new file mode 100644 index 0000000..6ccc548 --- /dev/null +++ b/AutomaticMailPrinter/config.yml @@ -0,0 +1,14 @@ +imap_server: imap.gmail.com +imap_port: 993 +mail: youremail@example.com +password: yourpassword +printer_name: Brother HL-L5200DW series Printer +timer_interval_in_seconds: 60 +filter: + - Order + - Your Message +# Optional: URL für Webhooks +# webhook_url: "https://example.com/webhook" +# PDFs statt HTML drucken (Anhänge mit .pdf oder application/pdf) +print_pdf_attachments: true + From 00bbf120fbf5c4f100feb0f49f0498066c2b39d7 Mon Sep 17 00:00:00 2001 From: Floris <65296458+florijohn@users.noreply.github.com> Date: Sat, 9 Aug 2025 12:21:06 +0200 Subject: [PATCH 4/4] Add comment for print_pdf_attachments option Added a clarifying comment explaining how to disable PDF attachment printing by setting print_pdf_attachments to false. --- AutomaticMailPrinter/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/AutomaticMailPrinter/config.yml b/AutomaticMailPrinter/config.yml index 6ccc548..a159392 100644 --- a/AutomaticMailPrinter/config.yml +++ b/AutomaticMailPrinter/config.yml @@ -10,5 +10,6 @@ filter: # Optional: URL für Webhooks # webhook_url: "https://example.com/webhook" # PDFs statt HTML drucken (Anhänge mit .pdf oder application/pdf) +# Wenn du keine PDFs drucken willst, setze dies auf false print_pdf_attachments: true