diff --git a/App.config b/App.config index 379efdc..1da09fe 100644 --- a/App.config +++ b/App.config @@ -47,6 +47,9 @@ True + + True + diff --git a/Discord/DiscordWebHook.cs b/Discord/DiscordWebHook.cs index 5a64dbe..2c2b733 100644 --- a/Discord/DiscordWebHook.cs +++ b/Discord/DiscordWebHook.cs @@ -12,20 +12,33 @@ namespace Discord.Webhook public class DiscordWebhook { public string Url { get; set; } + public int queueRetryCount = 3; + public int sendTimeoutSeconds = 5; - private UInt64 queueNum = 0; - private readonly ConcurrentQueue<(UInt64 num, DiscordMessage message, FileInfo[] files)> _queue = new ConcurrentQueue<(UInt64 num, DiscordMessage, FileInfo[])>(); + private UInt64 totalMessages = 0; + private ConcurrentQueue<(UInt64 num, DiscordMessage message, FileInfo[] files)> _queue = new ConcurrentQueue<(UInt64 num, DiscordMessage, FileInfo[])>(); private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private bool _isProcessing; + private CancellationTokenSource _cts = new CancellationTokenSource(); + private int queueErrorCounter = 0; + private int queueSuppressedCounter = 0; + private HttpRequestException lastHpptEx = null; - public async Task SendAsync(DiscordMessage message, params FileInfo[] files) + public UInt64 TotalMessages => totalMessages; + public int QueueSize => _queue.Count; + public void CancelProcessing() => _cts.Cancel(); + public int ErrorCount => queueErrorCounter; + public bool IsProcessing => _isProcessing; + public HttpRequestException LastHpptEx => lastHpptEx; + + public async Task SendAsync(DiscordMessage message, params FileInfo[] files) { if (string.IsNullOrEmpty(Url)) throw new ArgumentNullException("Invalid Webhook URL."); string boundary = "------------------------" + DateTime.Now.Ticks.ToString("x"); - using (var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(30) }) + using (var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(sendTimeoutSeconds) }) using (var content = new MultipartFormDataContent(boundary)) { // Добавляем JSON payload @@ -54,48 +67,96 @@ namespace Discord.Webhook try { - var response = await client.PostAsync(Url, content); + var response = await client.PostAsync(Url, content, _cts.Token); response.EnsureSuccessStatusCode(); } catch (HttpRequestException ex) { - Debug.WriteLine($"Discord webhook request failed: {ex.Message}"); + Debug.WriteLine($"SendAsync: Discord webhook request failed: {ex.Message}"); + lastHpptEx = ex; + return false; + } + catch (TaskCanceledException ex) + { + Debug.WriteLine($"SendAsync: Discord webhook request cancelled: {ex.Message}"); + return false; } } + return true; } private async Task ProcessQueueAsync() { - while (_queue.TryDequeue(out var item)) + queueErrorCounter = 0; + + while (_queue.TryPeek(out var item)) { + if (_cts.Token.IsCancellationRequested) + { + Debug.WriteLine("ProcessQueueAsync: Discord queue processing cancelled."); + break; + } await _semaphore.WaitAsync(); try { - Debug.WriteLine($"Processing message {item.num}. Queue size: {_queue.Count}"); - await SendAsync(item.message, item.files); - Debug.WriteLine($"Message {item.num} sent."); + Debug.WriteLine($"ProcessQueueAsync: Processing message {item.num}. Queue size: {_queue.Count}"); + if (await SendAsync(item.message, item.files)) + { + _queue.TryDequeue(out var deqItem); + queueErrorCounter = 0; + } + else + { + queueErrorCounter++; + if (queueErrorCounter == queueRetryCount) + { + _queue.TryDequeue(out var deqItem); + queueErrorCounter = 0; + queueSuppressedCounter++; + Debug.WriteLine($"ProcessQueueAsync: Message dropped. Total messages dropped:{queueSuppressedCounter}. Queue size: {_queue.Count}."); + } + } } finally { _semaphore.Release(); } - Task.Delay(500).Wait(); + try + { + await Task.Delay(1000, _cts.Token); // Discord rate limit: 1 message per second + } + catch (TaskCanceledException) + { + Debug.WriteLine($"ProcessQueueAsync: Discord queue processing cancelled during delay. Was {_queue.Count} messages in queue. {totalMessages} messages in session. "); + break; + } } + if ( _cts.IsCancellationRequested) + { + Debug.WriteLine($"ProcessQueueAsync: Discord queue processing cancelled. Was {_queue.Count} messages in queue."); + Debug.WriteLine("Clearing queue."); + var _newqueue = new ConcurrentQueue<(UInt64 num, DiscordMessage, FileInfo[])>(); + Interlocked.Exchange(ref _queue, _newqueue); + } + Debug.WriteLine($"ProcessQueueAsync: Discord queue processing finished."); _isProcessing = false; } public void Send(DiscordMessage message, params FileInfo[] files) { - _queue.Enqueue((queueNum++, message, files)); - Debug.WriteLine($"Message {queueNum-1} added. Queue size: {_queue.Count}"); + _queue.Enqueue((totalMessages++, message, files)); + Debug.WriteLine($"Message {totalMessages-1} added. Queue size: {_queue.Count}"); if (_isProcessing) { Debug.WriteLine("Already processing queue."); return; } Debug.WriteLine("Run ProcessQueueAsync"); + _cts.Dispose(); + _cts = new CancellationTokenSource(); _isProcessing = true; Task.Run(ProcessQueueAsync); + return; } } } \ No newline at end of file diff --git a/DiscordSettings.cs b/DiscordSettings.cs index 8560bc0..8474c7e 100644 --- a/DiscordSettings.cs +++ b/DiscordSettings.cs @@ -9,15 +9,19 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; +using Process_Auto_Relaunch; using RelaunchProcess.Properties; namespace RelaunchProcess { public partial class WebhookSettings : Form { - public WebhookSettings() + private Form1 parent; + + public WebhookSettings(Form1 _parent) { InitializeComponent(); + parent = _parent; RestoreSettings(); } @@ -64,6 +68,10 @@ namespace RelaunchProcess { chbxDiscordEnabled.Checked = false; } + if (!chbxDiscordEnabled.Checked) + { + parent.dwhHook.CancelProcessing(); + } SaveSettings(); DialogResult = DialogResult.OK; Close(); diff --git a/Form1.cs b/Form1.cs index 1d9b0e8..4027fbc 100644 --- a/Form1.cs +++ b/Form1.cs @@ -15,7 +15,7 @@ using RelaunchProcess; using System.Timers; using System.Threading.Tasks; using System.Runtime.CompilerServices; - +using System.Configuration; namespace Process_Auto_Relaunch { @@ -32,7 +32,7 @@ namespace Process_Auto_Relaunch } private delegate void UpdateLogDelegate(string text, NotifyLevel level = NotifyLevel.logUpdateStatus); private readonly UpdateLogDelegate updateLogDelegate; - private DiscordWebhook dwhHook; + public DiscordWebhook dwhHook; private DiscordMessage dwhMessage; private Process WatchedProcess; private double cpuLastTime = 0; @@ -47,6 +47,24 @@ namespace Process_Auto_Relaunch public Form1() { InitializeComponent(); + if (Settings.Default.upgradeSettings) + { + Debug.WriteLine("Обновление настроек."); + try + { + Settings.Default.Upgrade(); + } + catch (ConfigurationErrorsException ex) + { + Debug.WriteLine($"Ошибка обновления настроек: {ex.Message}"); + Settings.Default.Reset(); + } + finally + { + Settings.Default.upgradeSettings = false; + Settings.Default.Save(); + } + } this.updateLogDelegate = this.UpdateStatus; this.updateLogDelegate += this.SendDiscordMessage; this.updateLogDelegate += this.HistoryLog; @@ -249,8 +267,9 @@ namespace Process_Auto_Relaunch /// private void Form1_FormClosing(object sender, FormClosingEventArgs e) { - Settings.Default.Save(); + dwhHook.CancelProcessing(); Status("Наблюдение отменено - приложение закрыто.", NotifyLevel.logAlways); + Settings.Default.Save(); } private bool ProcessByNameIsRuning(string name) @@ -476,7 +495,7 @@ namespace Process_Auto_Relaunch private void webhookDiscordToolStripMenuItem_Click(object sender, EventArgs e) { WebhookSettings discordSettings; - discordSettings = new WebhookSettings(); + discordSettings = new WebhookSettings(this); discordSettings.ShowDialog(this); discordSettings.Dispose(); } diff --git a/Process Auto Relaunch.csproj b/Process Auto Relaunch.csproj index d9789f4..4bd7346 100644 --- a/Process Auto Relaunch.csproj +++ b/Process Auto Relaunch.csproj @@ -82,14 +82,11 @@ true - - False - .\CSharpDiscordWebhook.dll - packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll + diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index a125594..fb42d28 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -29,7 +29,7 @@ using System.Runtime.InteropServices; // Номер сборки // Редакция // -[assembly: AssemblyVersion("1.6.0.0")] -[assembly: AssemblyFileVersion("1.6.0.0")] +[assembly: AssemblyVersion("1.6.1.2")] +[assembly: AssemblyFileVersion("1.6.1.2")] -[assembly: AssemblyInformationalVersion("1.6.0.0")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("1.6.1.2")] \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs index 7be96f2..b9a3b21 100644 --- a/Properties/Settings.Designer.cs +++ b/Properties/Settings.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // Этот код создан программой. // Исполняемая версия:4.0.30319.42000 @@ -12,7 +12,7 @@ namespace RelaunchProcess.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -179,5 +179,17 @@ namespace RelaunchProcess.Properties { this["closeFreezeProcess"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool upgradeSettings { + get { + return ((bool)(this["upgradeSettings"])); + } + set { + this["upgradeSettings"] = value; + } + } } } diff --git a/Properties/Settings.settings b/Properties/Settings.settings index aa69502..71525a6 100644 --- a/Properties/Settings.settings +++ b/Properties/Settings.settings @@ -41,5 +41,8 @@ True + + True + \ No newline at end of file