diff --git a/.gitignore b/.gitignore index 3c4efe2..fc68272 100644 --- a/.gitignore +++ b/.gitignore @@ -258,4 +258,7 @@ paket-files/ # Python Tools for Visual Studio (PTVS) __pycache__/ -*.pyc \ No newline at end of file +*.pyc + +# Claude Code project-specific instructions +CLAUDE.md \ No newline at end of file diff --git a/README.md b/README.md index b0a7c58..63c889a 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,21 @@ CgLogListener ![demo](https://i.imgur.com/xT5ZAwe.png) + 目錄下可替換wav + +York +20260128 +1. 增加 smtp mailsettings (暫時只使用 gmail 進行測試)。 +2. 新增物品損壞通知。 +3. 設定應用來源,方便信件過濾。 +4. 新增吃料偵測,秒數循環提醒。 + +20260203 +1. 預設選項可由 setting.ini 中自由修改命名、regex 規則。 +2. 吃料偵測改成計時器功能,可自定義秒數、提示訊息、regex 規則,若須恢復吃料判斷,案預設即可。 +3. custom notify 路徑設定改成跟選擇魔力資料夾相同,不用再手動打。 +4. custom notify 是否啟用通知,改成跟 sound, mail 一樣,可自行勾選是否啟用。 +5. 自訂關鍵字功能,可自行增加、修改,並增加 sound mail, notify, regex 彈性設定。 + +![demo](https://i.meee.com.tw/QBmHzKn.png) diff --git a/src/CgLogListener/CgLogListener.csproj b/src/CgLogListener/CgLogListener.csproj index 7685266..26029f2 100644 --- a/src/CgLogListener/CgLogListener.csproj +++ b/src/CgLogListener/CgLogListener.csproj @@ -22,9 +22,11 @@ false false true - 0 + true + 1 2.1.0.%2a false + true true @@ -51,6 +53,18 @@ icon.ico + + 76921EC744D0212480757F6181BD1C3D8ADB9734 + + + CgLogListener_TemporaryKey.pfx + + + true + + + true + ..\packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll @@ -96,7 +110,10 @@ Resource.resx + + + @@ -116,6 +133,7 @@ Resource.Designer.cs + diff --git a/src/CgLogListener/CgLogListenerControls.cs b/src/CgLogListener/CgLogListenerControls.cs index 9badeb0..807fcaf 100644 --- a/src/CgLogListener/CgLogListenerControls.cs +++ b/src/CgLogListener/CgLogListenerControls.cs @@ -12,18 +12,18 @@ public class CgLogListenerCheckBox : CheckBox, INotifyMessage public string NameInSetting { get; set; } public string RegexPattern { get; set; } - public bool Notify(string message) + public NotifyResult Notify(string message) { var settings = Settings.GetInstance(); - if (settings.StandardTips.TryGetValue(NameInSetting, out bool value) && - value && + if (settings.StandardTips.TryGetValue(NameInSetting, out TipNotifyOptions options) && + options.Enabled && Regex.IsMatch(message, RegexPattern)) { - return true; + return NotifyResult.Match(options.PlaySound, options.SendMail, options.CustomNotify); } - return false; + return NotifyResult.NoMatch; } } @@ -36,29 +36,54 @@ public class CgLogListenerListBox : ListBox, INotifyMessage { public NotifyIcon NotifyIcon { get; set; } - public bool Notify(string message) + public NotifyResult Notify(string message) { var settings = Settings.GetInstance(); - foreach (var s in settings.CustomizeTips) + foreach (var kv in settings.CustomizeTips) { - var split = s.Split('|'); + var keyword = kv.Key; + var options = kv.Value; - if (message.Contains(split[0])) + if (!options.Enabled) continue; + + var split = keyword.Split('|'); + var pattern = split[0]; + + bool isMatch; + if (options.IsRegex) + { + try + { + isMatch = Regex.IsMatch(message, pattern); + } + catch + { + isMatch = false; + } + } + else + { + isMatch = message.Contains(pattern); + } + + if (isMatch) { if (split.Length > 1) { var exps = split[1].Split(','); - - return !exps.Any(x => message.Contains(x));// !message.Contains(split[1]); + if (!exps.Any(x => message.Contains(x))) + { + return NotifyResult.Match(options.PlaySound, options.SendMail, options.CustomNotify); + } } else { - return true; + return NotifyResult.Match(options.PlaySound, options.SendMail, options.CustomNotify); } } } - return false; + return NotifyResult.NoMatch; } } } diff --git a/src/CgLogListener/FormMain.Designer.cs b/src/CgLogListener/FormMain.Designer.cs index f8daf15..6117a38 100644 --- a/src/CgLogListener/FormMain.Designer.cs +++ b/src/CgLogListener/FormMain.Designer.cs @@ -1,4 +1,4 @@ -namespace CgLogListener +namespace CgLogListener { partial class FormMain { @@ -38,10 +38,18 @@ private void InitializeComponent() this.toolExit = new System.Windows.Forms.ToolStripMenuItem(); this.txtCgLogPath = new System.Windows.Forms.TextBox(); this.btnSelectLogPath = new System.Windows.Forms.Button(); + this.lblAppName = new System.Windows.Forms.Label(); + this.txtAppName = new System.Windows.Forms.TextBox(); this.panel1 = new System.Windows.Forms.Panel(); - this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.cgLogListenerCheckBox7 = new CgLogListener.CgLogListenerCheckBox(); + this.chkCookingReminder = new System.Windows.Forms.CheckBox(); + this.txtCookingInterval = new System.Windows.Forms.TextBox(); + this.lblCookingUnit = new System.Windows.Forms.Label(); + this.txtCookingPattern = new System.Windows.Forms.TextBox(); + this.txtCookingMessage = new System.Windows.Forms.TextBox(); + this.btnCookingDefault = new System.Windows.Forms.Button(); this.cgLogListenerTrackBar = new CgLogListener.CgLogListenerTrackBar(); - this.cgLogListenerSettingCheckBox1 = new CgLogListener.CgLogListenerCheckBox(); + this.lblSoundVol = new System.Windows.Forms.Label(); this.cgLogListenerCheckBox6 = new CgLogListener.CgLogListenerCheckBox(); this.cgLogListenerCheckBox5 = new CgLogListener.CgLogListenerCheckBox(); this.cgLogListenerCheckBox4 = new CgLogListener.CgLogListenerCheckBox(); @@ -49,9 +57,15 @@ private void InitializeComponent() this.label1 = new System.Windows.Forms.Label(); this.cgLogListenerListBox = new CgLogListener.CgLogListenerListBox(); this.btnDelCus = new System.Windows.Forms.Button(); + this.btnEditCus = new System.Windows.Forms.Button(); this.btnAddCus = new System.Windows.Forms.Button(); this.cgLogListenerCheckBox2 = new CgLogListener.CgLogListenerCheckBox(); this.cgLogListenerCheckBox1 = new CgLogListener.CgLogListenerCheckBox(); + this.txtCustomNotifier = new System.Windows.Forms.TextBox(); + this.lblCustomNotifier = new System.Windows.Forms.Label(); + this.btnSelectNotifier = new System.Windows.Forms.Button(); + this.timerCooking = new System.Windows.Forms.Timer(this.components); + this.btnSaveAppName = new System.Windows.Forms.Button(); this.btnExit = new System.Windows.Forms.Button(); this.linkLabel1 = new System.Windows.Forms.LinkLabel(); this.notifyIconContextMenu.SuspendLayout(); @@ -78,46 +92,49 @@ private void InitializeComponent() this.toolExit}); this.notifyIconContextMenu.Name = "notifyIconContextMenu"; this.notifyIconContextMenu.ShowImageMargin = false; - this.notifyIconContextMenu.Size = new System.Drawing.Size(86, 76); + this.notifyIconContextMenu.Size = new System.Drawing.Size(99, 82); // // toolOpen // this.toolOpen.Name = "toolOpen"; - this.toolOpen.Size = new System.Drawing.Size(85, 22); + this.toolOpen.Size = new System.Drawing.Size(98, 24); this.toolOpen.Text = "開啟"; this.toolOpen.Click += new System.EventHandler(this.ToolOpen_Click); // // toolMinsize // this.toolMinsize.Name = "toolMinsize"; - this.toolMinsize.Size = new System.Drawing.Size(85, 22); + this.toolMinsize.Size = new System.Drawing.Size(98, 24); this.toolMinsize.Text = "最小化"; this.toolMinsize.Click += new System.EventHandler(this.ToolMinsize_Click); // // toolSep1 // this.toolSep1.Name = "toolSep1"; - this.toolSep1.Size = new System.Drawing.Size(82, 6); + this.toolSep1.Size = new System.Drawing.Size(95, 6); // // toolExit // this.toolExit.Name = "toolExit"; - this.toolExit.Size = new System.Drawing.Size(85, 22); + this.toolExit.Size = new System.Drawing.Size(98, 24); this.toolExit.Text = "結束"; this.toolExit.Click += new System.EventHandler(this.ToolExit_Click); // // txtCgLogPath // + this.txtCgLogPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.txtCgLogPath.Location = new System.Drawing.Point(11, 9); this.txtCgLogPath.Margin = new System.Windows.Forms.Padding(2); this.txtCgLogPath.Name = "txtCgLogPath"; this.txtCgLogPath.ReadOnly = true; - this.txtCgLogPath.Size = new System.Drawing.Size(219, 22); + this.txtCgLogPath.Size = new System.Drawing.Size(375, 25); this.txtCgLogPath.TabIndex = 1; // // btnSelectLogPath // - this.btnSelectLogPath.Location = new System.Drawing.Point(234, 8); + this.btnSelectLogPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnSelectLogPath.Location = new System.Drawing.Point(390, 8); this.btnSelectLogPath.Margin = new System.Windows.Forms.Padding(2); this.btnSelectLogPath.Name = "btnSelectLogPath"; this.btnSelectLogPath.Size = new System.Drawing.Size(53, 22); @@ -126,11 +143,40 @@ private void InitializeComponent() this.btnSelectLogPath.UseVisualStyleBackColor = true; this.btnSelectLogPath.Click += new System.EventHandler(this.BtnSelectLogPath_Click); // + // lblAppName + // + this.lblAppName.AutoSize = true; + this.lblAppName.Location = new System.Drawing.Point(11, 72); + this.lblAppName.Name = "lblAppName"; + this.lblAppName.Size = new System.Drawing.Size(71, 15); + this.lblAppName.TabIndex = 20; + this.lblAppName.Text = "應用名稱:"; + // + // txtAppName + // + this.txtAppName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtAppName.Location = new System.Drawing.Point(85, 68); + this.txtAppName.Margin = new System.Windows.Forms.Padding(2); + this.txtAppName.Name = "txtAppName"; + this.txtAppName.Size = new System.Drawing.Size(299, 25); + this.txtAppName.TabIndex = 21; + this.txtAppName.Leave += new System.EventHandler(this.TxtAppName_Leave); + // // panel1 // - this.panel1.Controls.Add(this.checkBox1); + this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panel1.Controls.Add(this.cgLogListenerCheckBox7); + this.panel1.Controls.Add(this.chkCookingReminder); + this.panel1.Controls.Add(this.txtCookingInterval); + this.panel1.Controls.Add(this.lblCookingUnit); + this.panel1.Controls.Add(this.txtCookingPattern); + this.panel1.Controls.Add(this.txtCookingMessage); + this.panel1.Controls.Add(this.btnCookingDefault); this.panel1.Controls.Add(this.cgLogListenerTrackBar); - this.panel1.Controls.Add(this.cgLogListenerSettingCheckBox1); + this.panel1.Controls.Add(this.lblSoundVol); this.panel1.Controls.Add(this.cgLogListenerCheckBox6); this.panel1.Controls.Add(this.cgLogListenerCheckBox5); this.panel1.Controls.Add(this.cgLogListenerCheckBox4); @@ -138,56 +184,112 @@ private void InitializeComponent() this.panel1.Controls.Add(this.label1); this.panel1.Controls.Add(this.cgLogListenerListBox); this.panel1.Controls.Add(this.btnDelCus); + this.panel1.Controls.Add(this.btnEditCus); this.panel1.Controls.Add(this.btnAddCus); this.panel1.Controls.Add(this.cgLogListenerCheckBox2); this.panel1.Controls.Add(this.cgLogListenerCheckBox1); - this.panel1.Location = new System.Drawing.Point(11, 38); + this.panel1.Location = new System.Drawing.Point(11, 100); this.panel1.Margin = new System.Windows.Forms.Padding(2); this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(276, 255); + this.panel1.Size = new System.Drawing.Size(433, 315); this.panel1.TabIndex = 6; // - // checkBox1 - // - this.checkBox1.AutoSize = true; - this.checkBox1.Location = new System.Drawing.Point(2, 230); - this.checkBox1.Name = "checkBox1"; - this.checkBox1.Size = new System.Drawing.Size(93, 16); - this.checkBox1.TabIndex = 13; - this.checkBox1.Text = "Custom Notify"; - this.checkBox1.UseVisualStyleBackColor = true; + // cgLogListenerCheckBox7 + // + this.cgLogListenerCheckBox7.AutoSize = true; + this.cgLogListenerCheckBox7.Location = new System.Drawing.Point(2, 147); + this.cgLogListenerCheckBox7.Name = "cgLogListenerCheckBox7"; + this.cgLogListenerCheckBox7.NameInSetting = "7"; + this.cgLogListenerCheckBox7.RegexPattern = "^(?!.*×\\d+).*壞掉了"; + this.cgLogListenerCheckBox7.Size = new System.Drawing.Size(119, 19); + this.cgLogListenerCheckBox7.TabIndex = 18; + this.cgLogListenerCheckBox7.Text = "物品損壞通知"; + this.cgLogListenerCheckBox7.UseVisualStyleBackColor = true; + // + // chkCookingReminder + // + this.chkCookingReminder.AutoSize = true; + this.chkCookingReminder.Location = new System.Drawing.Point(1, 205); + this.chkCookingReminder.Name = "chkCookingReminder"; + this.chkCookingReminder.Size = new System.Drawing.Size(104, 19); + this.chkCookingReminder.TabIndex = 15; + this.chkCookingReminder.Text = "計時器通知"; + this.chkCookingReminder.UseVisualStyleBackColor = true; + this.chkCookingReminder.CheckedChanged += new System.EventHandler(this.ChkCookingReminder_CheckedChanged); + // + // txtCookingInterval + // + this.txtCookingInterval.Location = new System.Drawing.Point(109, 203); + this.txtCookingInterval.Name = "txtCookingInterval"; + this.txtCookingInterval.Size = new System.Drawing.Size(40, 25); + this.txtCookingInterval.TabIndex = 16; + this.txtCookingInterval.Text = "180"; + this.txtCookingInterval.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.txtCookingInterval.TextChanged += new System.EventHandler(this.txtCookingInterval_TextChanged); + // + // lblCookingUnit + // + this.lblCookingUnit.AutoSize = true; + this.lblCookingUnit.Location = new System.Drawing.Point(155, 206); + this.lblCookingUnit.Name = "lblCookingUnit"; + this.lblCookingUnit.Size = new System.Drawing.Size(22, 15); + this.lblCookingUnit.TabIndex = 17; + this.lblCookingUnit.Text = "秒"; + // + // txtCookingPattern + // + this.txtCookingPattern.Location = new System.Drawing.Point(1, 230); + this.txtCookingPattern.Name = "txtCookingPattern"; + this.txtCookingPattern.Size = new System.Drawing.Size(254, 25); + this.txtCookingPattern.TabIndex = 19; + this.txtCookingPattern.Text = "恢復了\\d+魔力"; + // + // txtCookingMessage + // + this.txtCookingMessage.Location = new System.Drawing.Point(2, 261); + this.txtCookingMessage.Name = "txtCookingMessage"; + this.txtCookingMessage.Size = new System.Drawing.Size(200, 25); + this.txtCookingMessage.TabIndex = 20; + this.txtCookingMessage.Text = "時間到了,吃料理~"; + // + // btnCookingDefault + // + this.btnCookingDefault.Location = new System.Drawing.Point(205, 264); + this.btnCookingDefault.Name = "btnCookingDefault"; + this.btnCookingDefault.Size = new System.Drawing.Size(50, 22); + this.btnCookingDefault.TabIndex = 21; + this.btnCookingDefault.Text = "預設"; + this.btnCookingDefault.UseVisualStyleBackColor = true; + this.btnCookingDefault.Click += new System.EventHandler(this.BtnCookingDefault_Click); // // cgLogListenerTrackBar // this.cgLogListenerTrackBar.AutoSize = false; this.cgLogListenerTrackBar.LargeChange = 1; - this.cgLogListenerTrackBar.Location = new System.Drawing.Point(2, 197); + this.cgLogListenerTrackBar.Location = new System.Drawing.Point(50, 172); this.cgLogListenerTrackBar.Name = "cgLogListenerTrackBar"; this.cgLogListenerTrackBar.NameInSetting = "SoundVol"; - this.cgLogListenerTrackBar.Size = new System.Drawing.Size(108, 27); + this.cgLogListenerTrackBar.Size = new System.Drawing.Size(70, 27); this.cgLogListenerTrackBar.TabIndex = 12; this.cgLogListenerTrackBar.Value = 5; // - // cgLogListenerSettingCheckBox1 + // lblSoundVol // - this.cgLogListenerSettingCheckBox1.AutoSize = true; - this.cgLogListenerSettingCheckBox1.Location = new System.Drawing.Point(2, 172); - this.cgLogListenerSettingCheckBox1.Name = "cgLogListenerSettingCheckBox1"; - this.cgLogListenerSettingCheckBox1.NameInSetting = "PlaySound"; - this.cgLogListenerSettingCheckBox1.RegexPattern = null; - this.cgLogListenerSettingCheckBox1.Size = new System.Drawing.Size(72, 16); - this.cgLogListenerSettingCheckBox1.TabIndex = 8; - this.cgLogListenerSettingCheckBox1.Text = "播放音效"; - this.cgLogListenerSettingCheckBox1.UseVisualStyleBackColor = true; + this.lblSoundVol.AutoSize = true; + this.lblSoundVol.Location = new System.Drawing.Point(2, 177); + this.lblSoundVol.Name = "lblSoundVol"; + this.lblSoundVol.Size = new System.Drawing.Size(52, 15); + this.lblSoundVol.TabIndex = 14; + this.lblSoundVol.Text = "音量:"; // // cgLogListenerCheckBox6 // this.cgLogListenerCheckBox6.AutoSize = true; this.cgLogListenerCheckBox6.Location = new System.Drawing.Point(2, 124); this.cgLogListenerCheckBox6.Name = "cgLogListenerCheckBox6"; - this.cgLogListenerCheckBox6.NameInSetting = "ReMaze"; + this.cgLogListenerCheckBox6.NameInSetting = "6"; this.cgLogListenerCheckBox6.RegexPattern = "你感覺到一股不可思議的力量,而『.*』好像快(要?)消失了。"; - this.cgLogListenerCheckBox6.Size = new System.Drawing.Size(96, 16); + this.cgLogListenerCheckBox6.Size = new System.Drawing.Size(119, 19); this.cgLogListenerCheckBox6.TabIndex = 8; this.cgLogListenerCheckBox6.Text = "迷宮重組通知"; this.cgLogListenerCheckBox6.UseVisualStyleBackColor = true; @@ -197,9 +299,9 @@ private void InitializeComponent() this.cgLogListenerCheckBox5.AutoSize = true; this.cgLogListenerCheckBox5.Location = new System.Drawing.Point(2, 99); this.cgLogListenerCheckBox5.Name = "cgLogListenerCheckBox5"; - this.cgLogListenerCheckBox5.NameInSetting = "Sell"; + this.cgLogListenerCheckBox5.NameInSetting = "5"; this.cgLogListenerCheckBox5.RegexPattern = "您順利賣掉了一個.*,(收入|獲得).*魔幣!"; - this.cgLogListenerCheckBox5.Size = new System.Drawing.Size(96, 16); + this.cgLogListenerCheckBox5.Size = new System.Drawing.Size(119, 19); this.cgLogListenerCheckBox5.TabIndex = 8; this.cgLogListenerCheckBox5.Text = "擺攤售出通知"; this.cgLogListenerCheckBox5.UseVisualStyleBackColor = true; @@ -209,9 +311,9 @@ private void InitializeComponent() this.cgLogListenerCheckBox4.AutoSize = true; this.cgLogListenerCheckBox4.Location = new System.Drawing.Point(2, 74); this.cgLogListenerCheckBox4.Name = "cgLogListenerCheckBox4"; - this.cgLogListenerCheckBox4.NameInSetting = "PlayerJoin"; + this.cgLogListenerCheckBox4.NameInSetting = "4"; this.cgLogListenerCheckBox4.RegexPattern = "加入了(你|您)的隊伍。"; - this.cgLogListenerCheckBox4.Size = new System.Drawing.Size(108, 16); + this.cgLogListenerCheckBox4.Size = new System.Drawing.Size(134, 19); this.cgLogListenerCheckBox4.TabIndex = 8; this.cgLogListenerCheckBox4.Text = "被加入隊伍通知"; this.cgLogListenerCheckBox4.UseVisualStyleBackColor = true; @@ -221,47 +323,63 @@ private void InitializeComponent() this.cgLogListenerCheckBox3.AutoSize = true; this.cgLogListenerCheckBox3.Location = new System.Drawing.Point(2, 49); this.cgLogListenerCheckBox3.Name = "cgLogListenerCheckBox3"; - this.cgLogListenerCheckBox3.NameInSetting = "MP0"; + this.cgLogListenerCheckBox3.NameInSetting = "3"; this.cgLogListenerCheckBox3.RegexPattern = "魔力不足。"; - this.cgLogListenerCheckBox3.Size = new System.Drawing.Size(96, 16); + this.cgLogListenerCheckBox3.Size = new System.Drawing.Size(119, 19); this.cgLogListenerCheckBox3.TabIndex = 8; this.cgLogListenerCheckBox3.Text = "魔力不足通知"; this.cgLogListenerCheckBox3.UseVisualStyleBackColor = true; // // label1 // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(126, 2); + this.label1.Location = new System.Drawing.Point(275, 2); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(65, 12); + this.label1.Size = new System.Drawing.Size(82, 15); this.label1.TabIndex = 11; this.label1.Text = "自訂關鍵字"; // // cgLogListenerListBox // + this.cgLogListenerListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.cgLogListenerListBox.FormattingEnabled = true; - this.cgLogListenerListBox.ItemHeight = 12; - this.cgLogListenerListBox.Location = new System.Drawing.Point(127, 20); + this.cgLogListenerListBox.ItemHeight = 15; + this.cgLogListenerListBox.Location = new System.Drawing.Point(275, 20); this.cgLogListenerListBox.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3); this.cgLogListenerListBox.Name = "cgLogListenerListBox"; this.cgLogListenerListBox.NotifyIcon = this.notifyIcon; - this.cgLogListenerListBox.Size = new System.Drawing.Size(147, 148); + this.cgLogListenerListBox.Size = new System.Drawing.Size(147, 169); this.cgLogListenerListBox.TabIndex = 9; // // btnDelCus - // - this.btnDelCus.Location = new System.Drawing.Point(178, 181); + // + this.btnDelCus.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnDelCus.Location = new System.Drawing.Point(371, 194); this.btnDelCus.Margin = new System.Windows.Forms.Padding(2); this.btnDelCus.Name = "btnDelCus"; this.btnDelCus.Size = new System.Drawing.Size(47, 22); - this.btnDelCus.TabIndex = 10; + this.btnDelCus.TabIndex = 11; this.btnDelCus.Text = "移除"; this.btnDelCus.UseVisualStyleBackColor = true; this.btnDelCus.Click += new System.EventHandler(this.BtnDelCus_Click); - // + // + // btnEditCus + // + this.btnEditCus.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnEditCus.Location = new System.Drawing.Point(323, 194); + this.btnEditCus.Margin = new System.Windows.Forms.Padding(2); + this.btnEditCus.Name = "btnEditCus"; + this.btnEditCus.Size = new System.Drawing.Size(47, 22); + this.btnEditCus.TabIndex = 10; + this.btnEditCus.Text = "修改"; + this.btnEditCus.UseVisualStyleBackColor = true; + this.btnEditCus.Click += new System.EventHandler(this.BtnEditCus_Click); + // // btnAddCus // - this.btnAddCus.Location = new System.Drawing.Point(127, 181); + this.btnAddCus.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnAddCus.Location = new System.Drawing.Point(275, 194); this.btnAddCus.Margin = new System.Windows.Forms.Padding(2); this.btnAddCus.Name = "btnAddCus"; this.btnAddCus.Size = new System.Drawing.Size(47, 22); @@ -276,9 +394,9 @@ private void InitializeComponent() this.cgLogListenerCheckBox2.Location = new System.Drawing.Point(2, 25); this.cgLogListenerCheckBox2.Margin = new System.Windows.Forms.Padding(2); this.cgLogListenerCheckBox2.Name = "cgLogListenerCheckBox2"; - this.cgLogListenerCheckBox2.NameInSetting = "ItemFull"; + this.cgLogListenerCheckBox2.NameInSetting = "2"; this.cgLogListenerCheckBox2.RegexPattern = "物品欄沒有空位。"; - this.cgLogListenerCheckBox2.Size = new System.Drawing.Size(84, 16); + this.cgLogListenerCheckBox2.Size = new System.Drawing.Size(104, 19); this.cgLogListenerCheckBox2.TabIndex = 1; this.cgLogListenerCheckBox2.Text = "道具滿通知"; this.cgLogListenerCheckBox2.UseVisualStyleBackColor = true; @@ -289,17 +407,66 @@ private void InitializeComponent() this.cgLogListenerCheckBox1.Location = new System.Drawing.Point(2, 2); this.cgLogListenerCheckBox1.Margin = new System.Windows.Forms.Padding(2); this.cgLogListenerCheckBox1.Name = "cgLogListenerCheckBox1"; - this.cgLogListenerCheckBox1.NameInSetting = "Health"; + this.cgLogListenerCheckBox1.NameInSetting = "1"; this.cgLogListenerCheckBox1.RegexPattern = "在工作時不小心受傷了。"; - this.cgLogListenerCheckBox1.Size = new System.Drawing.Size(96, 16); + this.cgLogListenerCheckBox1.Size = new System.Drawing.Size(119, 19); this.cgLogListenerCheckBox1.TabIndex = 1; this.cgLogListenerCheckBox1.Text = "採集受傷通知"; this.cgLogListenerCheckBox1.UseVisualStyleBackColor = true; // + // txtCustomNotifier + // + this.txtCustomNotifier.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtCustomNotifier.Location = new System.Drawing.Point(132, 39); + this.txtCustomNotifier.Margin = new System.Windows.Forms.Padding(2); + this.txtCustomNotifier.Name = "txtCustomNotifier"; + this.txtCustomNotifier.Size = new System.Drawing.Size(254, 25); + this.txtCustomNotifier.TabIndex = 13; + this.txtCustomNotifier.Leave += new System.EventHandler(this.TxtCustomNotifier_Leave); + // + // lblCustomNotifier + // + this.lblCustomNotifier.AutoSize = true; + this.lblCustomNotifier.Location = new System.Drawing.Point(11, 42); + this.lblCustomNotifier.Name = "lblCustomNotifier"; + this.lblCustomNotifier.Size = new System.Drawing.Size(116, 15); + this.lblCustomNotifier.TabIndex = 22; + this.lblCustomNotifier.Text = "自定義通知路徑:"; + // + // btnSelectNotifier + // + this.btnSelectNotifier.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnSelectNotifier.Location = new System.Drawing.Point(390, 38); + this.btnSelectNotifier.Margin = new System.Windows.Forms.Padding(2); + this.btnSelectNotifier.Name = "btnSelectNotifier"; + this.btnSelectNotifier.Size = new System.Drawing.Size(53, 22); + this.btnSelectNotifier.TabIndex = 23; + this.btnSelectNotifier.Text = "選擇"; + this.btnSelectNotifier.UseVisualStyleBackColor = true; + this.btnSelectNotifier.Click += new System.EventHandler(this.BtnSelectNotifier_Click); + // + // timerCooking + // + this.timerCooking.Interval = 180000; + this.timerCooking.Tick += new System.EventHandler(this.TimerCooking_Tick); + // + // btnSaveAppName + // + this.btnSaveAppName.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnSaveAppName.Location = new System.Drawing.Point(390, 68); + this.btnSaveAppName.Margin = new System.Windows.Forms.Padding(2); + this.btnSaveAppName.Name = "btnSaveAppName"; + this.btnSaveAppName.Size = new System.Drawing.Size(53, 22); + this.btnSaveAppName.TabIndex = 22; + this.btnSaveAppName.Text = "儲存"; + this.btnSaveAppName.UseVisualStyleBackColor = true; + this.btnSaveAppName.Click += new System.EventHandler(this.BtnSaveAppName_Click); + // // btnExit // - this.btnExit.Anchor = System.Windows.Forms.AnchorStyles.Bottom; - this.btnExit.Location = new System.Drawing.Point(198, 300); + this.btnExit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnExit.Location = new System.Drawing.Point(355, 335); this.btnExit.Margin = new System.Windows.Forms.Padding(2); this.btnExit.Name = "btnExit"; this.btnExit.Size = new System.Drawing.Size(89, 22); @@ -310,10 +477,11 @@ private void InitializeComponent() // // linkLabel1 // + this.linkLabel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.linkLabel1.AutoSize = true; - this.linkLabel1.Location = new System.Drawing.Point(9, 311); + this.linkLabel1.Location = new System.Drawing.Point(8, 391); this.linkLabel1.Name = "linkLabel1"; - this.linkLabel1.Size = new System.Drawing.Size(65, 12); + this.linkLabel1.Size = new System.Drawing.Size(82, 15); this.linkLabel1.TabIndex = 7; this.linkLabel1.TabStop = true; this.linkLabel1.Text = "關於本程式"; @@ -321,18 +489,23 @@ private void InitializeComponent() // // FormMain // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(298, 333); + this.ClientSize = new System.Drawing.Size(455, 415); this.Controls.Add(this.linkLabel1); this.Controls.Add(this.panel1); + this.Controls.Add(this.lblAppName); + this.Controls.Add(this.btnSaveAppName); + this.Controls.Add(this.txtAppName); this.Controls.Add(this.btnSelectLogPath); this.Controls.Add(this.txtCgLogPath); + this.Controls.Add(this.txtCustomNotifier); + this.Controls.Add(this.lblCustomNotifier); + this.Controls.Add(this.btnSelectNotifier); this.Controls.Add(this.btnExit); this.Font = new System.Drawing.Font("新細明體", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3); - this.MaximizeBox = false; + this.MinimumSize = new System.Drawing.Size(360, 325); this.Name = "FormMain"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "魔力Log監視"; @@ -354,8 +527,11 @@ private void InitializeComponent() private System.Windows.Forms.NotifyIcon notifyIcon; private System.Windows.Forms.TextBox txtCgLogPath; private System.Windows.Forms.Button btnSelectLogPath; + private System.Windows.Forms.Label lblAppName; + private System.Windows.Forms.TextBox txtAppName; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Button btnDelCus; + private System.Windows.Forms.Button btnEditCus; private System.Windows.Forms.Button btnAddCus; private System.Windows.Forms.Button btnExit; private System.Windows.Forms.ContextMenuStrip notifyIconContextMenu; @@ -371,10 +547,20 @@ private void InitializeComponent() private CgLogListenerCheckBox cgLogListenerCheckBox4; private CgLogListenerCheckBox cgLogListenerCheckBox5; private System.Windows.Forms.LinkLabel linkLabel1; - private CgLogListenerCheckBox cgLogListenerSettingCheckBox1; private CgLogListenerCheckBox cgLogListenerCheckBox6; private CgLogListenerTrackBar cgLogListenerTrackBar; - private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.TextBox txtCustomNotifier; + private System.Windows.Forms.Label lblCustomNotifier; + private System.Windows.Forms.Button btnSelectNotifier; + private System.Windows.Forms.Label lblSoundVol; + private System.Windows.Forms.CheckBox chkCookingReminder; + private System.Windows.Forms.TextBox txtCookingInterval; + private System.Windows.Forms.Label lblCookingUnit; + private System.Windows.Forms.TextBox txtCookingPattern; + private System.Windows.Forms.TextBox txtCookingMessage; + private System.Windows.Forms.Button btnCookingDefault; + private System.Windows.Forms.Timer timerCooking; + private CgLogListenerCheckBox cgLogListenerCheckBox7; + private System.Windows.Forms.Button btnSaveAppName; } } - diff --git a/src/CgLogListener/FormMain.cs b/src/CgLogListener/FormMain.cs index cecb49b..26016e6 100644 --- a/src/CgLogListener/FormMain.cs +++ b/src/CgLogListener/FormMain.cs @@ -1,7 +1,10 @@ -using System; +using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Drawing; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Web; using System.Windows.Forms; using System.Windows.Media; @@ -13,6 +16,9 @@ public partial class FormMain : Form private Settings settings; private CgLogHandler watcher; private readonly MediaPlayer mp = new MediaPlayer(); + private readonly Dictionary soundCheckBoxes = new Dictionary(); + private readonly Dictionary mailCheckBoxes = new Dictionary(); + private readonly Dictionary customNotifyCheckBoxes = new Dictionary(); public FormMain() { @@ -28,6 +34,10 @@ private void FrmMain_Load(object sender, EventArgs e) { settings = Settings.GetInstance(); + // 設定通知標題 + txtAppName.Text = settings.AppName; + UpdateAppTitle(); + if (string.IsNullOrEmpty(settings.CgLogPath)) { string cgLogPath = settings.CgLogPath; @@ -43,7 +53,6 @@ private void FrmMain_Load(object sender, EventArgs e) if (!Directory.Exists(settings.CgLogPath) || !CgLogHandler.ValidationPath(settings.CgLogPath)) { - // the dir path invalid, set to default and exit settings.SetCgLogPath(string.Empty); MessageBox.Show(this, "設定檔路徑錯誤, 請重新啟動", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); this.Close(); @@ -52,56 +61,153 @@ private void FrmMain_Load(object sender, EventArgs e) BindWatcher(); - // set playsound check - cgLogListenerSettingCheckBox1.Checked = settings.PlaySound; - // set playsound vol cgLogListenerTrackBar.Value = settings.SoundVol; - // set line notify - checkBox1.Checked = settings.CustomNotify; + // set custom notifier path + txtCustomNotifier.Text = settings.CustomNotifier ?? ""; - // set default tips check - foreach (var chk in panel1.Controls.OfType()) - { - // skip playsound - if (chk == cgLogListenerSettingCheckBox1) { continue; } + // 設定標準關鍵字及其音效/郵件選項 + SetupStandardTips(); - settings.StandardTips.TryGetValue(chk.NameInSetting, out bool isEnable); - chk.Checked = isEnable; - } + // 設定自訂關鍵字 + SetupCustomTips(); - // set custom tips items - settings.CustomizeTips - .ForEach(s => - { - if (!string.IsNullOrEmpty(s)) - { - cgLogListenerListBox.Items.Add(s); - } - }); - - cgLogListenerCheckBox1.CheckedChanged += CgLogListenerCheckBox_CheckedChanged; - cgLogListenerCheckBox2.CheckedChanged += CgLogListenerCheckBox_CheckedChanged; - cgLogListenerCheckBox3.CheckedChanged += CgLogListenerCheckBox_CheckedChanged; - cgLogListenerCheckBox4.CheckedChanged += CgLogListenerCheckBox_CheckedChanged; - cgLogListenerCheckBox5.CheckedChanged += CgLogListenerCheckBox_CheckedChanged; - cgLogListenerCheckBox6.CheckedChanged += CgLogListenerCheckBox_CheckedChanged; - cgLogListenerSettingCheckBox1.CheckedChanged += CgLogListenerSettingCheckBox1_CheckedChanged; cgLogListenerTrackBar.ValueChanged += CgLogListenerTrackBar_ValueChanged; - checkBox1.CheckedChanged += CheckBox1_CheckedChanged; + txtCustomNotifier.Leave += TxtCustomNotifier_Leave; } - private void CgLogListenerCheckBox_CheckedChanged(object sender, EventArgs e) + private void SetupStandardTips() { - var chk = (CgLogListenerCheckBox)sender; - settings.SetStandardTip(chk.NameInSetting, chk.Checked); + var standardCheckBoxes = new[] + { + cgLogListenerCheckBox1, + cgLogListenerCheckBox2, + cgLogListenerCheckBox3, + cgLogListenerCheckBox4, + cgLogListenerCheckBox5, + cgLogListenerCheckBox6, + cgLogListenerCheckBox7 + }; + + // 固定位置讓 S/M/C checkbox 對齊 + const int soundCheckBoxX = 155; + const int mailCheckBoxX = 210; + const int customNotifyCheckBoxX = 265; + + foreach (var chk in standardCheckBoxes) + { + var nameInSetting = chk.NameInSetting; + + // 取得或建立設定 + if (!settings.StandardTips.TryGetValue(nameInSetting, out TipNotifyOptions options)) + { + options = new TipNotifyOptions(); + settings.SetStandardTip(nameInSetting, options); + } + + // 從 settings 讀取 Text 和 RegexPattern(如果有的話,否則使用 Designer 的預設值) + if (!string.IsNullOrEmpty(options.Text)) + { + chk.Text = options.Text; + } + else + { + // 回寫 Designer 的預設值到 settings,方便使用者編輯 settings.ini + options.Text = chk.Text; + } + + if (!string.IsNullOrEmpty(options.RegexPattern)) + { + chk.RegexPattern = options.RegexPattern; + } + else + { + // 回寫 Designer 的預設值到 settings,方便使用者編輯 settings.ini + options.RegexPattern = chk.RegexPattern; + } + + // 儲存更新後的設定(確保 Text 和 RegexPattern 被寫入 settings.ini) + settings.SetStandardTip(nameInSetting, options); + + // 設定主 checkbox + chk.Checked = options.Enabled; + chk.AutoSize = false; + chk.Width = 150; // 限制寬度,避免長文字蓋掉右邊的 🔊 ✉ C (主 checkbox X=2,🔊 X=155,可用空間 153px) + chk.CheckedChanged += (s, ev) => + { + var cb = (CgLogListenerCheckBox)s; + settings.SetStandardTipEnabled(cb.NameInSetting, cb.Checked); + }; + + // 動態建立音效 checkbox + var soundChk = new CheckBox + { + Text = "🔊", + AutoSize = true, + Location = new Point(soundCheckBoxX, chk.Top), + Checked = options.PlaySound, + Font = new Font("Segoe UI Emoji", 8) + }; + soundChk.CheckedChanged += (s, ev) => + { + settings.SetStandardTipPlaySound(nameInSetting, ((CheckBox)s).Checked); + }; + panel1.Controls.Add(soundChk); + soundCheckBoxes[nameInSetting] = soundChk; + + // 動態建立郵件 checkbox + var mailChk = new CheckBox + { + Text = "✉", + AutoSize = true, + Location = new Point(mailCheckBoxX, chk.Top), + Checked = options.SendMail, + Font = new Font("Segoe UI Emoji", 8) + }; + mailChk.CheckedChanged += (s, ev) => + { + var isChecked = ((CheckBox)s).Checked; + if (isChecked && !MailHelper.IsConfigured()) + { + MailHelper.GenerateDefaultConfig(); + MessageBox.Show(this, "請先設定 mail.ini 檔案中的 SMTP 資訊", "郵件設定", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + settings.SetStandardTipSendMail(nameInSetting, isChecked); + }; + panel1.Controls.Add(mailChk); + mailCheckBoxes[nameInSetting] = mailChk; + + // 動態建立 CustomNotify checkbox + var customNotifyChk = new CheckBox + { + Text = "C", + AutoSize = true, + Location = new Point(customNotifyCheckBoxX, chk.Top), + Checked = options.CustomNotify, + Font = new Font("微軟正黑體", 8) + }; + customNotifyChk.CheckedChanged += (s, ev) => + { + settings.SetStandardTipCustomNotify(nameInSetting, ((CheckBox)s).Checked); + }; + panel1.Controls.Add(customNotifyChk); + customNotifyCheckBoxes[nameInSetting] = customNotifyChk; + } } - private void CgLogListenerSettingCheckBox1_CheckedChanged(object sender, EventArgs e) + private void SetupCustomTips() { - var chk = (CgLogListenerCheckBox)sender; - settings.SetPlaySound(chk.Checked); + foreach (var kv in settings.CustomizeTips) + { + if (!string.IsNullOrEmpty(kv.Key)) + { + cgLogListenerListBox.Items.Add(kv.Key); + } + } + + // 雙擊修改 + cgLogListenerListBox.DoubleClick += (s, ev) => BtnEditCus_Click(s, ev); } private void CgLogListenerTrackBar_ValueChanged(object sender, EventArgs e) @@ -158,16 +264,47 @@ void BindWatcher() watcher.OnNewLog += Watcher_OnNewLog; } + private const string DefaultCookingPattern = @"恢復了\d+魔力"; + private const string DefaultCookingMessage = "時間到了,吃料理~"; + void Watcher_OnNewLog(string log) { + // 吃料理監聽:使用自定義 Regex pattern 偵測 + var cookingPattern = string.IsNullOrWhiteSpace(txtCookingPattern.Text) + ? DefaultCookingPattern + : txtCookingPattern.Text; + + bool cookingMatch = false; + try + { + cookingMatch = chkCookingReminder.Checked && Regex.IsMatch(log, cookingPattern); + } + catch { } + + if (cookingMatch) + { + Invoke((Action)delegate + { + // 重置計時器 + timerCooking.Stop(); + if (int.TryParse(txtCookingInterval.Text, out int seconds) && seconds > 0) + { + timerCooking.Interval = seconds * 1000; + timerCooking.Start(); + } + }); + } + foreach (var n in panel1.Controls.OfType()) { - if (n.Notify(log)) + var result = n.Notify(log); + if (result.IsMatch) { notifyIcon.ShowBalloonTip(1, notifyIcon.BalloonTipTitle, log, ToolTipIcon.None); + // 根據該關鍵字的設定決定是否播放音效 const string soundName = "sound.wav"; - if (settings.PlaySound && File.Exists(soundName)) + if (result.PlaySound && File.Exists(soundName)) { Invoke((Action)delegate { @@ -178,13 +315,27 @@ void Watcher_OnNewLog(string log) }); } - if (settings.CustomNotify) + // 根據該關鍵字的設定決定是否發送郵件 + if (result.SendMail) + { + try + { + MailHelper.SendMail("魔力Log監視通知", log); + } + catch { } + } + + // Custom Notifier (根據個別關鍵字設定) + if (result.CustomNotify && !string.IsNullOrEmpty(settings.CustomNotifier)) { foreach (var notifier in settings.CustomNotifier.Split(',')) { + var trimmedNotifier = notifier.Trim(); + if (string.IsNullOrEmpty(trimmedNotifier)) continue; + try { - ProcessStartInfo p = new ProcessStartInfo(notifier, $"\"{log}\"") + ProcessStartInfo p = new ProcessStartInfo(trimmedNotifier, $"\"[{settings.AppName}] {log}\"") { WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true @@ -195,7 +346,6 @@ void Watcher_OnNewLog(string log) } } - // break if was trigger break; } } @@ -203,16 +353,43 @@ void Watcher_OnNewLog(string log) private void BtnAddCus_Click(object sender, EventArgs e) { - if (FormPrompt.ShowDialog(this, out string value) != DialogResult.OK || + if (FormPrompt.ShowDialog(this, out string value, out TipNotifyOptions options) != DialogResult.OK || string.IsNullOrEmpty(value)) { return; } - settings.AddCustmizeTip(value); + settings.AddCustomizeTip(value, options); cgLogListenerListBox.Items.Add(value); } + private void BtnEditCus_Click(object sender, EventArgs e) + { + if (cgLogListenerListBox.SelectedIndex < 0) + { + MessageBox.Show(this, "請先選擇要修改的關鍵字", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + var oldKeyword = (string)cgLogListenerListBox.SelectedItem; + var oldOptions = settings.CustomizeTips[oldKeyword]; + + if (FormPrompt.ShowDialogForEdit(this, oldKeyword, oldOptions, out string newKeyword, out TipNotifyOptions newOptions) != DialogResult.OK || + string.IsNullOrEmpty(newKeyword)) + { + return; + } + + // 刪除舊的 + settings.RemoveCustomizeTip(oldKeyword); + cgLogListenerListBox.Items.Remove(oldKeyword); + + // 加入新的 + settings.AddCustomizeTip(newKeyword, newOptions); + cgLogListenerListBox.Items.Add(newKeyword); + cgLogListenerListBox.SelectedItem = newKeyword; + } + private void BtnDelCus_Click(object sender, EventArgs e) { if (cgLogListenerListBox.SelectedIndex < 0) @@ -221,22 +398,120 @@ private void BtnDelCus_Click(object sender, EventArgs e) } var selectItem = (string)cgLogListenerListBox.SelectedItem; - settings.RemoveCustmizeTip(selectItem); + settings.RemoveCustomizeTip(selectItem); cgLogListenerListBox.Items.Remove(selectItem); } - private void CheckBox1_CheckedChanged(object sender, EventArgs e) + private void TxtCustomNotifier_Leave(object sender, EventArgs e) + { + settings.SetCustomNotifier(txtCustomNotifier.Text); + } + + private void BtnSelectNotifier_Click(object sender, EventArgs e) + { + var dialog = new OpenFileDialog + { + Filter = "執行檔 (*.exe)|*.exe|所有檔案 (*.*)|*.*", + Title = "選擇 Notifier 程式" + }; + + if (dialog.ShowDialog(this) == DialogResult.OK) + { + var currentPaths = txtCustomNotifier.Text; + if (string.IsNullOrEmpty(currentPaths)) + { + txtCustomNotifier.Text = dialog.FileName; + } + else + { + txtCustomNotifier.Text = currentPaths + "," + dialog.FileName; + } + settings.SetCustomNotifier(txtCustomNotifier.Text); + } + } + + private void TxtAppName_Leave(object sender, EventArgs e) + { + // 不再自動儲存,由按鈕觸發 + } + + private void BtnSaveAppName_Click(object sender, EventArgs e) + { + var newAppName = txtAppName.Text.Trim(); + if (string.IsNullOrEmpty(newAppName)) + { + newAppName = "CgLogListener"; + txtAppName.Text = newAppName; + } + settings.SetAppName(newAppName); + UpdateAppTitle(); + } + + private void UpdateAppTitle() + { + var appTitle = $"[{settings.AppName}] 魔力Log監視"; + notifyIcon.BalloonTipTitle = appTitle; + notifyIcon.Text = appTitle; + this.Text = appTitle; + } + + private void ChkCookingReminder_CheckedChanged(object sender, EventArgs e) { - if (checkBox1.Checked) + if (chkCookingReminder.Checked) { - FormCustomNotifierPrompt.ShowDialog(this, out string value); - settings.SetCustomNotify(true); - settings.SetCustomNotifier(value); + if (!int.TryParse(txtCookingInterval.Text, out int seconds) || seconds <= 0) + { + MessageBox.Show("請輸入有效的秒數", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Warning); + chkCookingReminder.Checked = false; + return; + } + + // 驗證 Regex pattern 是否有效 + var pattern = string.IsNullOrWhiteSpace(txtCookingPattern.Text) + ? DefaultCookingPattern + : txtCookingPattern.Text; + try + { + Regex.IsMatch("test", pattern); + } + catch + { + MessageBox.Show("Regex 格式錯誤", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Warning); + chkCookingReminder.Checked = false; + return; + } } else { - settings.SetCustomNotify(false); - settings.SetCustomNotifier(string.Empty); + timerCooking.Stop(); + } + } + + private void BtnCookingDefault_Click(object sender, EventArgs e) + { + txtCookingPattern.Text = DefaultCookingPattern; + txtCookingMessage.Text = DefaultCookingMessage; + } + + private void TimerCooking_Tick(object sender, EventArgs e) + { + var message = string.IsNullOrWhiteSpace(txtCookingMessage.Text) + ? DefaultCookingMessage + : txtCookingMessage.Text; + + notifyIcon.ShowBalloonTip(3000, $"[{settings.AppName}] 吃料理通知", message, ToolTipIcon.Info); + + // 播放音效 + const string soundName = "sound.wav"; + if (File.Exists(soundName)) + { + Invoke((Action)delegate + { + mp.Stop(); + mp.Open(new Uri(new FileInfo(soundName).FullName)); + mp.Volume = settings.SoundVol / 10d; + mp.Play(); + }); } } @@ -291,6 +566,13 @@ private void FormMain_Resize(object sender, EventArgs e) private void FormMain_FormClosing(object sender, FormClosingEventArgs e) { + // 確保 AppName 在關閉前儲存 + var newAppName = txtAppName.Text.Trim(); + if (!string.IsNullOrEmpty(newAppName) && newAppName != settings.AppName) + { + settings.SetAppName(newAppName); + } + watcher?.Dispose(); notifyIcon?.Dispose(); } @@ -301,5 +583,10 @@ private void LinkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs { System.Diagnostics.Process.Start("https://github.com/WindOfNet/CgLogListener"); } + + private void txtCookingInterval_TextChanged(object sender, EventArgs e) + { + + } } } diff --git a/src/CgLogListener/FormMain.resx b/src/CgLogListener/FormMain.resx index 789ad64..bb9c6d4 100644 --- a/src/CgLogListener/FormMain.resx +++ b/src/CgLogListener/FormMain.resx @@ -118,10 +118,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 17, 17 + 173, 17 - 147, 17 + 302, 17 @@ -148,4 +148,7 @@ AADgBwAA+B8AAA== + + 17, 17 + \ No newline at end of file diff --git a/src/CgLogListener/FormPrompt.Designer.cs b/src/CgLogListener/FormPrompt.Designer.cs index e57dc3b..36ea7ee 100644 --- a/src/CgLogListener/FormPrompt.Designer.cs +++ b/src/CgLogListener/FormPrompt.Designer.cs @@ -34,6 +34,11 @@ private void InitializeComponent() this.btnCancel = new System.Windows.Forms.Button(); this.label2 = new System.Windows.Forms.Label(); this.txtExp = new System.Windows.Forms.TextBox(); + this.chkEnabled = new System.Windows.Forms.CheckBox(); + this.chkPlaySound = new System.Windows.Forms.CheckBox(); + this.chkSendMail = new System.Windows.Forms.CheckBox(); + this.chkIsRegex = new System.Windows.Forms.CheckBox(); + this.chkCustomNotify = new System.Windows.Forms.CheckBox(); this.SuspendLayout(); // // btnOk @@ -88,15 +93,74 @@ private void InitializeComponent() this.txtExp.Name = "txtExp"; this.txtExp.Size = new System.Drawing.Size(165, 21); this.txtExp.TabIndex = 2; - // + // + // chkEnabled + // + this.chkEnabled.AutoSize = true; + this.chkEnabled.Checked = true; + this.chkEnabled.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkEnabled.Location = new System.Drawing.Point(14, 61); + this.chkEnabled.Name = "chkEnabled"; + this.chkEnabled.Size = new System.Drawing.Size(52, 19); + this.chkEnabled.TabIndex = 5; + this.chkEnabled.Text = "啟用"; + this.chkEnabled.UseVisualStyleBackColor = true; + // + // chkPlaySound + // + this.chkPlaySound.AutoSize = true; + this.chkPlaySound.Checked = true; + this.chkPlaySound.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkPlaySound.Location = new System.Drawing.Point(72, 61); + this.chkPlaySound.Name = "chkPlaySound"; + this.chkPlaySound.Size = new System.Drawing.Size(52, 19); + this.chkPlaySound.TabIndex = 6; + this.chkPlaySound.Text = "音效"; + this.chkPlaySound.UseVisualStyleBackColor = true; + // + // chkSendMail + // + this.chkSendMail.AutoSize = true; + this.chkSendMail.Location = new System.Drawing.Point(130, 61); + this.chkSendMail.Name = "chkSendMail"; + this.chkSendMail.Size = new System.Drawing.Size(52, 19); + this.chkSendMail.TabIndex = 7; + this.chkSendMail.Text = "寄信"; + this.chkSendMail.UseVisualStyleBackColor = true; + // + // chkIsRegex + // + this.chkIsRegex.AutoSize = true; + this.chkIsRegex.Location = new System.Drawing.Point(188, 61); + this.chkIsRegex.Name = "chkIsRegex"; + this.chkIsRegex.Size = new System.Drawing.Size(58, 19); + this.chkIsRegex.TabIndex = 8; + this.chkIsRegex.Text = "Regex"; + this.chkIsRegex.UseVisualStyleBackColor = true; + // + // chkCustomNotify + // + this.chkCustomNotify.AutoSize = true; + this.chkCustomNotify.Location = new System.Drawing.Point(252, 61); + this.chkCustomNotify.Name = "chkCustomNotify"; + this.chkCustomNotify.Size = new System.Drawing.Size(34, 19); + this.chkCustomNotify.TabIndex = 9; + this.chkCustomNotify.Text = "C"; + this.chkCustomNotify.UseVisualStyleBackColor = true; + // // FormPrompt - // + // this.AcceptButton = this.btnOk; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; - this.ClientSize = new System.Drawing.Size(368, 64); + this.ClientSize = new System.Drawing.Size(368, 90); this.ControlBox = false; + this.Controls.Add(this.chkCustomNotify); + this.Controls.Add(this.chkIsRegex); + this.Controls.Add(this.chkSendMail); + this.Controls.Add(this.chkPlaySound); + this.Controls.Add(this.chkEnabled); this.Controls.Add(this.txtExp); this.Controls.Add(this.label2); this.Controls.Add(this.txtValue); @@ -111,7 +175,7 @@ private void InitializeComponent() this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - this.Text = " "; + this.Text = "新增/編輯自訂關鍵字"; this.ResumeLayout(false); this.PerformLayout(); @@ -125,5 +189,10 @@ private void InitializeComponent() private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Label label2; private System.Windows.Forms.TextBox txtExp; + private System.Windows.Forms.CheckBox chkEnabled; + private System.Windows.Forms.CheckBox chkPlaySound; + private System.Windows.Forms.CheckBox chkSendMail; + private System.Windows.Forms.CheckBox chkIsRegex; + private System.Windows.Forms.CheckBox chkCustomNotify; } } \ No newline at end of file diff --git a/src/CgLogListener/FormPrompt.cs b/src/CgLogListener/FormPrompt.cs index 8436e84..4be1eb8 100644 --- a/src/CgLogListener/FormPrompt.cs +++ b/src/CgLogListener/FormPrompt.cs @@ -18,6 +18,12 @@ public partial class FormPrompt : Form } public static DialogResult ShowDialog(IWin32Window owner, out string value) + { + var result = ShowDialog(owner, out value, out _); + return result; + } + + public static DialogResult ShowDialog(IWin32Window owner, out string value, out TipNotifyOptions options) { FormPrompt f = new FormPrompt(); DialogResult result = f.ShowDialog(owner); @@ -27,6 +33,55 @@ public static DialogResult ShowDialog(IWin32Window owner, out string value) value += $"|{f.txtExp.Text}"; } + options = new TipNotifyOptions( + f.chkEnabled.Checked, + f.chkPlaySound.Checked, + f.chkSendMail.Checked, + f.chkIsRegex.Checked, + f.chkCustomNotify.Checked + ); + + return result; + } + + /// + /// 編輯模式:預填已有的關鍵字和選項 + /// + public static DialogResult ShowDialogForEdit(IWin32Window owner, string existingKeyword, TipNotifyOptions existingOptions, out string value, out TipNotifyOptions options) + { + FormPrompt f = new FormPrompt(); + f.Text = "編輯自訂關鍵字"; + + // 解析 keyword 和排除詞 + var split = existingKeyword.Split('|'); + f.txtValue.Text = split[0]; + if (split.Length > 1) + { + f.txtExp.Text = split[1]; + } + + // 預填選項 + f.chkEnabled.Checked = existingOptions.Enabled; + f.chkPlaySound.Checked = existingOptions.PlaySound; + f.chkSendMail.Checked = existingOptions.SendMail; + f.chkIsRegex.Checked = existingOptions.IsRegex; + f.chkCustomNotify.Checked = existingOptions.CustomNotify; + + DialogResult result = f.ShowDialog(owner); + value = f.txtValue.Text; + if (!string.IsNullOrWhiteSpace(f.txtExp.Text)) + { + value += $"|{f.txtExp.Text}"; + } + + options = new TipNotifyOptions( + f.chkEnabled.Checked, + f.chkPlaySound.Checked, + f.chkSendMail.Checked, + f.chkIsRegex.Checked, + f.chkCustomNotify.Checked + ); + return result; } diff --git a/src/CgLogListener/INotifyMessage.cs b/src/CgLogListener/INotifyMessage.cs index ce139db..67ab6ee 100644 --- a/src/CgLogListener/INotifyMessage.cs +++ b/src/CgLogListener/INotifyMessage.cs @@ -7,6 +7,6 @@ namespace CgLogListener { public interface INotifyMessage { - bool Notify(string message); + NotifyResult Notify(string message); } } diff --git a/src/CgLogListener/MailHelper.cs b/src/CgLogListener/MailHelper.cs new file mode 100644 index 0000000..59cb411 --- /dev/null +++ b/src/CgLogListener/MailHelper.cs @@ -0,0 +1,116 @@ +using IniParser; +using System; +using System.IO; +using System.Net; +using System.Net.Mail; + +namespace CgLogListener +{ + public static class MailHelper + { + private const string MailConfigFileName = "mail.ini"; + + public static bool IsConfigured() + { + if (!File.Exists(MailConfigFileName)) + { + return false; + } + + try + { + var ini = new FileIniDataParser().ReadFile(MailConfigFileName); + return !string.IsNullOrEmpty(ini["smtp"]["host"]) && + !string.IsNullOrEmpty(ini["smtp"]["from"]) && + !string.IsNullOrEmpty(ini["smtp"]["to"]); + } + catch + { + return false; + } + } + + public static void SendMail(string subject, string body) + { + if (!File.Exists(MailConfigFileName)) + { + return; + } + + try + { + var appName = Settings.GetInstance().AppName; + var fullSubject = $"[{appName}] {subject}"; + + var ini = new FileIniDataParser().ReadFile(MailConfigFileName); + var smtpSection = ini["smtp"]; + + var host = smtpSection["host"]; + var portStr = smtpSection["port"]; + var username = smtpSection["username"]; + var password = smtpSection["password"]; + var from = smtpSection["from"]; + var to = smtpSection["to"]; + var enableSslStr = smtpSection["enableSsl"]; + + if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(from) || string.IsNullOrEmpty(to)) + { + return; + } + + int port = 587; + if (!string.IsNullOrEmpty(portStr)) + { + int.TryParse(portStr, out port); + } + + bool enableSsl = true; + if (!string.IsNullOrEmpty(enableSslStr)) + { + enableSsl = enableSslStr == "1" || enableSslStr.ToLower() == "true"; + } + + using (var client = new SmtpClient(host, port)) + { + client.EnableSsl = enableSsl; + + if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) + { + client.Credentials = new NetworkCredential(username, password); + } + + var message = new MailMessage(from, to, fullSubject, body); + client.Send(message); + } + } + catch + { + // 發送失敗時靜默處理,避免影響主程式 + } + } + + public static void GenerateDefaultConfig() + { + if (File.Exists(MailConfigFileName)) + { + return; + } + + var content = @"[smtp] +; SMTP 伺服器設定 +host=smtp.gmail.com +port=587 +enableSsl=1 + +; 認證資訊 (如果 SMTP 需要認證) +username= +password= + +; 寄件者與收件者 +from=your-email@gmail.com +to=recipient@example.com +"; + File.WriteAllText(MailConfigFileName, content); + } + } +} diff --git a/src/CgLogListener/NotifyResult.cs b/src/CgLogListener/NotifyResult.cs new file mode 100644 index 0000000..c401e7a --- /dev/null +++ b/src/CgLogListener/NotifyResult.cs @@ -0,0 +1,23 @@ +namespace CgLogListener +{ + public class NotifyResult + { + public bool IsMatch { get; set; } + public bool PlaySound { get; set; } + public bool SendMail { get; set; } + public bool CustomNotify { get; set; } + + public static NotifyResult NoMatch => new NotifyResult { IsMatch = false }; + + public static NotifyResult Match(bool playSound, bool sendMail, bool customNotify) + { + return new NotifyResult + { + IsMatch = true, + PlaySound = playSound, + SendMail = sendMail, + CustomNotify = customNotify + }; + } + } +} diff --git a/src/CgLogListener/Settings.cs b/src/CgLogListener/Settings.cs index ad0c2ec..400d12e 100644 --- a/src/CgLogListener/Settings.cs +++ b/src/CgLogListener/Settings.cs @@ -1,4 +1,4 @@ -using IniParser; +using IniParser; using IniParser.Model; using System; using System.Collections.Generic; @@ -14,15 +14,15 @@ public sealed class Settings const string settingsFileName = "settings.ini"; const string settingsBaseSection = "base"; const string settingsStandardTipsSection = "standard tips"; + const string settingsCustomTipsSection = "custom tips"; const string custmizeFileName = "custmize.dat"; - public bool CustomNotify { get; private set; } + public string AppName { get; private set; } public string CustomNotifier { get; private set; } - public bool PlaySound { get; private set; } public int SoundVol { get; private set; } public string CgLogPath { get; private set; } - public Dictionary StandardTips { get; private set; } = new Dictionary(); - public List CustomizeTips { get; private set; } = new List(); + public Dictionary StandardTips { get; private set; } = new Dictionary(); + public Dictionary CustomizeTips { get; private set; } = new Dictionary(); public static Settings GetInstance() { @@ -39,11 +39,9 @@ public void Load() { if (!File.Exists(Path.Combine(Directory.GetCurrentDirectory(), settingsFileName))) { - // gen new conf file GenConfigFile(); } - // load settings LoadSettings(); } @@ -53,24 +51,38 @@ private void LoadSettings() var iniData = fileIniDataParser.ReadFile(settingsFileName); var baseData = iniData[settingsBaseSection]; + AppName = baseData[nameof(AppName)] ?? "CgLogListener"; + if (string.IsNullOrEmpty(AppName)) AppName = "CgLogListener"; CgLogPath = baseData[nameof(CgLogPath)]; - PlaySound = baseData[nameof(PlaySound)] == "1"; - SoundVol = int.Parse(baseData[nameof(SoundVol)]); - CustomNotify = baseData[nameof(CustomNotify)] == "1"; + SoundVol = int.Parse(baseData[nameof(SoundVol)] ?? "5"); CustomNotifier = baseData[nameof(CustomNotifier)]; + // 載入標準關鍵字設定 var standardTipData = iniData[settingsStandardTipsSection]; foreach (var kd in standardTipData) { - StandardTips.Add(kd.KeyName, kd.Value == "1"); + StandardTips[kd.KeyName] = TipNotifyOptions.Parse(kd.Value); } + // 載入自訂關鍵字設定 + var customTipData = iniData[settingsCustomTipsSection]; + foreach (var kd in customTipData) + { + CustomizeTips[kd.KeyName] = TipNotifyOptions.Parse(kd.Value); + } + + // 相容舊版:讀取舊的 custmize.dat if (File.Exists(custmizeFileName)) { foreach (var s in File.ReadAllLines(custmizeFileName)) { - CustomizeTips.Add(s); + if (!string.IsNullOrEmpty(s) && !CustomizeTips.ContainsKey(s)) + { + CustomizeTips[s] = new TipNotifyOptions(true, true, false); + } } + // 遷移後刪除舊檔 + try { File.Delete(custmizeFileName); } catch { } } } @@ -78,10 +90,9 @@ private void GenConfigFile() { var iniData = new IniData(); var baseSection = iniData[settingsBaseSection]; + baseSection[nameof(AppName)] = "CgLogListener"; baseSection[nameof(CgLogPath)] = string.Empty; - baseSection[nameof(PlaySound)] = "1"; baseSection[nameof(SoundVol)] = "5"; - baseSection[nameof(CustomNotify)] = "0"; var fileIniDataParser = new FileIniDataParser(); fileIniDataParser.WriteFile(settingsFileName, iniData); @@ -93,21 +104,24 @@ private void UpdateConfig() var iniData = new IniData(); var baseSection = iniData[settingsBaseSection]; + baseSection[nameof(AppName)] = AppName; baseSection[nameof(CgLogPath)] = CgLogPath; - baseSection[nameof(PlaySound)] = PlaySound ? "1" : "0"; baseSection[nameof(SoundVol)] = SoundVol.ToString(); - baseSection[nameof(CustomNotify)] = CustomNotify ? "1" : "0"; baseSection[nameof(CustomNotifier)] = CustomNotifier; var standardTipData = iniData[settingsStandardTipsSection]; foreach (var kv in StandardTips) { - standardTipData[kv.Key] = kv.Value ? "1" : "0"; + standardTipData[kv.Key] = kv.Value.ToString(); } - fileIniDataParser.WriteFile(settingsFileName, iniData); + var customTipData = iniData[settingsCustomTipsSection]; + foreach (var kv in CustomizeTips) + { + customTipData[kv.Key] = kv.Value.ToString(); + } - File.WriteAllLines(custmizeFileName, CustomizeTips); + fileIniDataParser.WriteFile(settingsFileName, iniData); } internal void SetCgLogPath(string cgLogPath) @@ -116,15 +130,39 @@ internal void SetCgLogPath(string cgLogPath) UpdateConfig(); } - internal void SetStandardTip(string nameInSetting, bool @checked) + internal void SetStandardTip(string nameInSetting, TipNotifyOptions options) + { + StandardTips[nameInSetting] = options; + UpdateConfig(); + } + + internal void SetStandardTipEnabled(string nameInSetting, bool enabled) + { + if (!StandardTips.ContainsKey(nameInSetting)) + { + StandardTips[nameInSetting] = new TipNotifyOptions(); + } + StandardTips[nameInSetting].Enabled = enabled; + UpdateConfig(); + } + + internal void SetStandardTipPlaySound(string nameInSetting, bool playSound) { - StandardTips[nameInSetting] = @checked; + if (!StandardTips.ContainsKey(nameInSetting)) + { + StandardTips[nameInSetting] = new TipNotifyOptions(); + } + StandardTips[nameInSetting].PlaySound = playSound; UpdateConfig(); } - internal void SetPlaySound(bool @checked) + internal void SetStandardTipSendMail(string nameInSetting, bool sendMail) { - PlaySound = @checked; + if (!StandardTips.ContainsKey(nameInSetting)) + { + StandardTips[nameInSetting] = new TipNotifyOptions(); + } + StandardTips[nameInSetting].SendMail = sendMail; UpdateConfig(); } @@ -134,27 +172,73 @@ internal void SetSoundVol(int value) UpdateConfig(); } - internal void AddCustmizeTip(string value) + internal void AddCustomizeTip(string keyword, TipNotifyOptions options) { - CustomizeTips.Add(value); + CustomizeTips[keyword] = options; UpdateConfig(); } - internal void RemoveCustmizeTip(string value) + internal void RemoveCustomizeTip(string keyword) { - CustomizeTips.Remove(value); + CustomizeTips.Remove(keyword); UpdateConfig(); } + internal void SetCustomizeTipEnabled(string keyword, bool enabled) + { + if (CustomizeTips.ContainsKey(keyword)) + { + CustomizeTips[keyword].Enabled = enabled; + UpdateConfig(); + } + } + + internal void SetCustomizeTipPlaySound(string keyword, bool playSound) + { + if (CustomizeTips.ContainsKey(keyword)) + { + CustomizeTips[keyword].PlaySound = playSound; + UpdateConfig(); + } + } + + internal void SetCustomizeTipSendMail(string keyword, bool sendMail) + { + if (CustomizeTips.ContainsKey(keyword)) + { + CustomizeTips[keyword].SendMail = sendMail; + UpdateConfig(); + } + } + internal void SetCustomNotifier(string value) { CustomNotifier = value; UpdateConfig(); } - internal void SetCustomNotify(bool value) + internal void SetStandardTipCustomNotify(string nameInSetting, bool customNotify) + { + if (!StandardTips.ContainsKey(nameInSetting)) + { + StandardTips[nameInSetting] = new TipNotifyOptions(); + } + StandardTips[nameInSetting].CustomNotify = customNotify; + UpdateConfig(); + } + + internal void SetCustomizeTipCustomNotify(string keyword, bool customNotify) + { + if (CustomizeTips.ContainsKey(keyword)) + { + CustomizeTips[keyword].CustomNotify = customNotify; + UpdateConfig(); + } + } + + internal void SetAppName(string value) { - CustomNotify = value; + AppName = string.IsNullOrEmpty(value) ? "CgLogListener" : value; UpdateConfig(); } } diff --git a/src/CgLogListener/TipNotifyOptions.cs b/src/CgLogListener/TipNotifyOptions.cs new file mode 100644 index 0000000..4863b65 --- /dev/null +++ b/src/CgLogListener/TipNotifyOptions.cs @@ -0,0 +1,92 @@ +namespace CgLogListener +{ + public class TipNotifyOptions + { + public string Text { get; set; } + public string RegexPattern { get; set; } + public bool Enabled { get; set; } + public bool PlaySound { get; set; } + public bool SendMail { get; set; } + public bool IsRegex { get; set; } + public bool CustomNotify { get; set; } + + public TipNotifyOptions() + { + Enabled = false; + PlaySound = true; + SendMail = false; + IsRegex = false; + CustomNotify = false; + } + + public TipNotifyOptions(bool enabled, bool playSound, bool sendMail, bool isRegex = false, bool customNotify = false) + { + Enabled = enabled; + PlaySound = playSound; + SendMail = sendMail; + IsRegex = isRegex; + CustomNotify = customNotify; + } + + /// + /// 從 INI 字串解析 + /// 新格式: "Text|RegexPattern|enabled,playSound,sendMail,isRegex,customNotify" 如 "通知文字|regex.*pattern|1,1,0,0,1" + /// 舊格式: "enabled,playSound,sendMail,isRegex,customNotify" 如 "1,1,0,0,1" (相容) + /// + public static TipNotifyOptions Parse(string value) + { + if (string.IsNullOrEmpty(value)) + { + return new TipNotifyOptions(); + } + + // 檢查是否為新格式 (包含 |) + if (value.Contains("|")) + { + var sections = value.Split('|'); + if (sections.Length >= 3) + { + var flags = sections[2].Split(','); + return new TipNotifyOptions + { + Text = sections[0], + RegexPattern = sections[1], + Enabled = flags.Length > 0 && flags[0] == "1", + PlaySound = flags.Length > 1 && flags[1] == "1", + SendMail = flags.Length > 2 && flags[2] == "1", + IsRegex = flags.Length > 3 && flags[3] == "1", + CustomNotify = flags.Length > 4 && flags[4] == "1" + }; + } + } + + // 舊格式相容 + var parts = value.Split(','); + return new TipNotifyOptions + { + Enabled = parts.Length > 0 && parts[0] == "1", + PlaySound = parts.Length > 1 && parts[1] == "1", + SendMail = parts.Length > 2 && parts[2] == "1", + IsRegex = parts.Length > 3 && parts[3] == "1", + CustomNotify = parts.Length > 4 && parts[4] == "1" + }; + } + + /// + /// 轉成 INI 字串 + /// 如果有 Text 或 RegexPattern,使用新格式: "Text|RegexPattern|enabled,playSound,sendMail,isRegex,customNotify" + /// 否則使用舊格式: "enabled,playSound,sendMail,isRegex,customNotify" + /// + public override string ToString() + { + var flags = $"{(Enabled ? "1" : "0")},{(PlaySound ? "1" : "0")},{(SendMail ? "1" : "0")},{(IsRegex ? "1" : "0")},{(CustomNotify ? "1" : "0")}"; + + if (!string.IsNullOrEmpty(Text) || !string.IsNullOrEmpty(RegexPattern)) + { + return $"{Text ?? ""}|{RegexPattern ?? ""}|{flags}"; + } + + return flags; + } + } +} diff --git a/src/DiscordNotifier/DiscordNotifier.csproj b/src/DiscordNotifier/DiscordNotifier.csproj index 000c25d..91af799 100644 --- a/src/DiscordNotifier/DiscordNotifier.csproj +++ b/src/DiscordNotifier/DiscordNotifier.csproj @@ -1,61 +1,87 @@ - - - Debug - AnyCPU - {7F0CAD47-7583-479B-8BD2-7901CD5B81CB} - Exe - DiscordNotifier - DiscordNotifier - v4.6 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll - - - ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - + + + Debug + AnyCPU + {7F0CAD47-7583-479B-8BD2-7901CD5B81CB} + Exe + DiscordNotifier + DiscordNotifier + v4.6 + 512 + true + true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + ..\CgLogListener\bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll + + + ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + + False + .NET Framework 3.5 SP1 + false + + + \ No newline at end of file diff --git a/src/TelegramNotifier/TelegramNotifier.csproj b/src/TelegramNotifier/TelegramNotifier.csproj index 6b52c63..ca34a28 100644 --- a/src/TelegramNotifier/TelegramNotifier.csproj +++ b/src/TelegramNotifier/TelegramNotifier.csproj @@ -27,7 +27,7 @@ AnyCPU pdbonly true - bin\Release\ + ..\CgLogListener\bin\Release\ TRACE prompt 4 @@ -52,8 +52,12 @@ - + + + PreserveNewest + + \ No newline at end of file