// Copyright (c) 2020-present schick Informatik // Description: using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Globalization; using System.ServiceProcess; using System.Text.RegularExpressions; using System.Timers; using bsmd.email; using bsmd.database; using log4net; namespace bsmd.POService { public partial class POService : ServiceBase { #region Fields private Timer _timer; private readonly ILog _log = LogManager.GetLogger(typeof(POService)); #endregion #region Construction public POService() { Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); InitializeComponent(); } #endregion #region Start/Stop Service protected override void OnStart(string[] args) { this.EventLog.Source = this.ServiceName; this.EventLog.Log = "Application"; this.Init(); this.EventLog.WriteEntry("NSW PO Number Service started.", EventLogEntryType.Information); System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly(); FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location); string version = fvi.FileVersion; _log.InfoFormat("Starting PO Number Service. v.{0} -------------- ", version); this.DoOnce(); } protected override void OnStop() { this._log.Info("Stopping PO Number Service"); } #endregion #region helper private void Init() { this._timer = new Timer { Interval = Properties.Settings.Default.SleepSeconds * 1000 }; this._timer.Elapsed += _timer_Elapsed; this._timer.Enabled = true; } internal void DoOnce() { this._timer_Elapsed(null, null); } internal static bool ParsePO(string inputValue, ref string poNumber, ref string port, ref string shipname, ref string something, ref string dateString) { bool result = false; if (inputValue.IsNullOrEmpty()) return result; // WuselString parsen. Ein Beispiel: // "WG: PO:8204730095 DEWVNTM-ADELINA D-005E-310120"; // Hier kann man designen: https://regex101.com/ const string poPattern = @"PO:(\d+) ([A-Z]+)-(.*?)-(.*?)-(\d{6})"; Regex poRegex = new Regex(poPattern); Match aMatch = poRegex.Match(inputValue); if (aMatch.Success) { poNumber = aMatch.Groups[1].Captures[0].Value; port = aMatch.Groups[2].Captures[0].Value; shipname = aMatch.Groups[3].Captures[0].Value; something = aMatch.Groups[4].Captures[0].Value; dateString = aMatch.Groups[5].Captures[0].Value; result = true; } return result; } #endregion #region timer loop handler private void _timer_Elapsed(object sender, ElapsedEventArgs e) { if (DBManager.Instance.Connect(Properties.Settings.Default.ConnectionString)) { try { string messageId = ""; string mailSender = ""; const string receiptSubject = "PO number service INFO"; string mailSubject = ""; string body = ""; using (BSMDPopClient bsmdPopClient = new BSMDPopClient()) { if (bsmdPopClient.IsConnected) { // check and download next e-Mail, saving attachment while (bsmdPopClient.ReceiveSingleMailText(out messageId, out mailSender, out mailSubject, out body)) { string receiptText = ""; // only a valid sender gets a reply bool isValidSender = false; foreach (string aValidSender in Properties.Settings.Default.ValidSender) { if (mailSender.Equals(aValidSender, StringComparison.OrdinalIgnoreCase)) { isValidSender = true; break; } } if (!isValidSender) { receiptText = string.Format("ignored e - mail from illegal sender: {0}", mailSender); _log.Warn(receiptText); } else { try { string poNumber = null; string port = null; string dateString = null; string shipname = null; string something = null; // Mail nach dem PO Nummerntext durchsuchen // Der Nummerstring ist vielleicht im Betreff, im Body oder im Dateinamen des // PDF Attachments. In den PDF's selbst habe ich den (kompletten) String aktuell nicht gefunden. try { if (!ParsePO(mailSubject, ref poNumber, ref port, ref shipname, ref something, ref dateString)) { if (!ParsePO(body, ref poNumber, ref port, ref shipname, ref something, ref dateString)) { string attachmentFileName = bsmdPopClient.GetNameOfFirstMailEmailPDFAttachment(messageId); if (attachmentFileName != null) ParsePO(attachmentFileName, ref poNumber, ref port, ref shipname, ref something, ref dateString); } } } catch(Exception ex) { _log.WarnFormat("Exception looking for PO number: {0}", ex.ToString()); } if (poNumber != null) { // aus (ungefährem) Datum, Name und Hafen den MessageCore suchen uint? from = null, to = null; if (DateTime.TryParseExact(dateString, "ddMMyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime etaDate)) { // Alle Anmeldungen des betreffenden Tages laden Dictionary filterDict = new Dictionary(); // Datum filtern from = etaDate.ToUniversalTime().Subtract(new TimeSpan(2, 0, 0, 0)).ToUnixTimeStamp(); // 2 Tage vorher to = etaDate.ToUniversalTime().Add(new TimeSpan(23, 59, 59)).ToUnixTimeStamp(); // 1 Tag später filterDict.Add(MessageCore.SearchFilterType.FILTER_ETA, string.Format("{0}:{1}", from?.ToString() ?? "", to?.ToString() ?? "")); List anmeldungen = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessageCoresWithFilters(filterDict, 100); List possibleMatches = new List(); // nach Hafen und Schiffsnamen ausfiltern if (port.Length > 5) port = port.Substring(0, 5); foreach (MessageCore foundCore in anmeldungen) { if (foundCore.PoC.Equals(port) && foundCore.Shipname.Equals(shipname, StringComparison.OrdinalIgnoreCase)) possibleMatches.Add(foundCore); } if (possibleMatches.Count == 0) { // nix gefunden receiptText = string.Format("no match found for PO number {0} ship {1} date {2} port {3}", poNumber, shipname, etaDate, port); _log.Warn(receiptText); } else if (possibleMatches.Count > 1) { // nicht eindeutig receiptText = string.Format("{0} matches found for PO number {1} ship {2} date {3} port {4}", possibleMatches.Count, poNumber, shipname, etaDate, port); _log.Warn(receiptText); } else { // PO-Nummer und Schiffs/Anlaufklassifizierung eintragen possibleMatches[0].PONumber = poNumber; DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(possibleMatches[0]); _log.InfoFormat("MessageCore {0} updated with po number {1}", possibleMatches[0].Id, poNumber); } } else { _log.WarnFormat("Error parsing date component of PO string: {0}", dateString); } } else { _log.Warn("incoming e-mail did not contain a po number"); } } catch (Exception someException) { receiptText = string.Format("Error processing po mail: {0}", someException.Message); _log.Error(receiptText); } } if (receiptText.Length > 0) { _log.Debug("sending system info email"); BSMDMail.SendSystemInfo(receiptSubject, receiptText, mailSender); } // remove e-Mail _log.InfoFormat("deleting mail with messageId {0}", messageId); _log.InfoFormat("mail delete {0}", bsmdPopClient.DeleteMessageByMessageId(messageId) ? "successful" : "failed"); } } } DBManager.Instance.Disconnect(); } catch (Exception ex) { _log.ErrorFormat("Exception occurred: {0}", ex.ToString()); } } else { _log.Error("DB Connection failure"); } } #endregion } }