diff --git a/.gitignore b/.gitignore index 9491a2f..a39d60c 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,7 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd +/CSharpDiscordWebhook.dll +/Newtonsoft.Json.dll +/Newtonsoft.Json.xml diff --git a/App.config b/App.config index 18f8bfa..ac315de 100644 --- a/App.config +++ b/App.config @@ -32,21 +32,19 @@ 30 + + + + + False + + + Relauncher + + + https://distribution.faceit-cdn.net/images/7240adfa-6bda-43a0-abd0-8c599f176686.jpeg + - - - - - - - - - True - - - True - - diff --git a/Discord/DiscordWebHook.cs b/Discord/DiscordWebHook.cs new file mode 100644 index 0000000..d9dbe8a --- /dev/null +++ b/Discord/DiscordWebHook.cs @@ -0,0 +1,104 @@ +using System; +using System.IO; +using System.Net; +using System.Text; + +namespace Discord.Webhook +{ + public class DiscordWebhook + { + /// + /// Webhook url + /// + public string Url { get; set; } + + private void AddField(MemoryStream stream, string bound, string cDisposition, string cType, byte[] data) + { + string prefix = stream.Length > 0 ? "\r\n--" : "--"; + string fBegin = $"{prefix}{bound}\r\n"; + + byte[] fBeginBuffer = Utils.Encode(fBegin); + byte[] cDispositionBuffer = Utils.Encode(cDisposition); + byte[] cTypeBuffer = Utils.Encode(cType); + + stream.Write(fBeginBuffer, 0, fBeginBuffer.Length); + stream.Write(cDispositionBuffer, 0, cDispositionBuffer.Length); + stream.Write(cTypeBuffer, 0, cTypeBuffer.Length); + stream.Write(data, 0, data.Length); + } + + private void SetJsonPayload(MemoryStream stream, string bound, string json) + { + string cDisposition = "Content-Disposition: form-data; name=\"payload_json\"\r\n"; + string cType = "Content-Type: application/octet-stream\r\n\r\n"; + AddField(stream, bound, cDisposition, cType, Utils.Encode(json)); + } + + private void SetFile(MemoryStream stream, string bound, int index, FileInfo file) + { + string cDisposition = $"Content-Disposition: form-data; name=\"file_{index}\"; filename=\"{file.Name}\"\r\n"; + string cType = "Content-Type: application/octet-stream\r\n\r\n"; + AddField(stream, bound, cDisposition, cType, File.ReadAllBytes(file.FullName)); + } + + /// + /// Send webhook message + /// + public void Send(DiscordMessage message, params FileInfo[] files) + { + if (string.IsNullOrEmpty(Url)) + throw new ArgumentNullException("Invalid Webhook URL."); + + string bound = "------------------------" + DateTime.Now.Ticks.ToString("x"); + WebClient webhookRequest = new WebClient(); + webhookRequest.Headers.Add("Content-Type", "multipart/form-data; boundary=" + bound); + + MemoryStream stream = new MemoryStream(); + for (int i = 0; i < files.Length; i++) + SetFile(stream, bound, i, files[i]); + + string json = message.ToString(); + SetJsonPayload(stream, bound, json); + + byte[] bodyEnd = Utils.Encode($"\r\n--{bound}--"); + stream.Write(bodyEnd, 0, bodyEnd.Length); + + //byte[] beginBodyBuffer = Encoding.UTF8.GetBytes("--" + bound + "\r\n"); + //stream.Write(beginBodyBuffer, 0, beginBodyBuffer.Length); + //bool flag = file != null && file.Exists; + //if (flag) + //{ + // string fileBody = "Content-Disposition: form-data; name=\"file\"; filename=\"" + file.Name + "\"\r\nContent-Type: application/octet-stream\r\n\r\n"; + // byte[] fileBodyBuffer = Encoding.UTF8.GetBytes(fileBody); + // stream.Write(fileBodyBuffer, 0, fileBodyBuffer.Length); + // byte[] fileBuffer = File.ReadAllBytes(file.FullName); + // stream.Write(fileBuffer, 0, fileBuffer.Length); + // string fileBodyEnd = "\r\n--" + bound + "\r\n"; + // byte[] fileBodyEndBuffer = Encoding.UTF8.GetBytes(fileBodyEnd); + // stream.Write(fileBodyEndBuffer, 0, fileBodyEndBuffer.Length); + //} + //string jsonBody = string.Concat(new string[] + //{ + // "Content-Disposition: form-data; name=\"payload_json\"\r\nContent-Type: application/json\r\n\r\n", + // string.Format("{0}\r\n", message), + // "--", + // bound, + // "--" + //}); + //byte[] jsonBodyBuffer = Encoding.UTF8.GetBytes(jsonBody); + //stream.Write(jsonBodyBuffer, 0, jsonBodyBuffer.Length); + + try + { + Uri uri = new Uri(this.Url); + webhookRequest.UploadData(uri, stream.ToArray()); + } + catch (WebException ex) + { + throw new WebException(Utils.Decode(ex.Response.GetResponseStream())); + } + + stream.Dispose(); + } + } +} diff --git a/Discord/Structs.cs b/Discord/Structs.cs new file mode 100644 index 0000000..a528c88 --- /dev/null +++ b/Discord/Structs.cs @@ -0,0 +1,229 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Drawing; + +namespace Discord +{ + + /// + /// Discord message data object + /// + public struct DiscordMessage + { + /// + /// Message content + /// + public string Content; + + /// + /// Read message to everyone on the channel + /// + public bool TTS; + + /// + /// Webhook profile username to be shown + /// + public string Username; + + /// + /// Webhook profile avater to be shown + /// + public string AvatarUrl; + + /// + /// List of embeds + /// + public List Embeds; + + public override string ToString() => Utils.StructToJson(this).ToString(Formatting.None); + } + + /// + /// Discord embed data object + /// + public struct DiscordEmbed + { + /// + /// Embed title + /// + public string Title; + + /// + /// Embed description + /// + public string Description; + + /// + /// Embed url + /// + public string Url; + + /// + /// Embed timestamp + /// + public DateTime? Timestamp; + + /// + /// Embed color + /// + public Color? Color; + + /// + /// Embed footer + /// + public EmbedFooter? Footer; + + /// + /// Embed image + /// + public EmbedMedia? Image; + + /// + /// Embed thumbnail + /// + public EmbedMedia? Thumbnail; + + /// + /// Embed video + /// + public EmbedMedia? Video; + + /// + /// Embed provider + /// + public EmbedProvider? Provider; + + /// + /// Embed author + /// + public EmbedAuthor? Author; + + /// + /// Embed fields list + /// + public List Fields; + + public override string ToString() => Utils.StructToJson(this).ToString(Formatting.None); + } + + /// + /// Discord embed footer data object + /// + public struct EmbedFooter + { + /// + /// Footer text + /// + public string Text; + + /// + /// Footer icon + /// + public string IconUrl; + + /// + /// Footer icon proxy + /// + public string ProxyIconUrl; + + public override string ToString() => Utils.StructToJson(this).ToString(Formatting.None); + } + + /// + /// Discord embed media data object (images/thumbs/videos) + /// + public struct EmbedMedia + { + /// + /// Media url + /// + public string Url; + + /// + /// Media proxy url + /// + public string ProxyUrl; + + /// + /// Media height + /// + public int? Height; + + /// + /// Media width + /// + public int? Width; + + public override string ToString() => Utils.StructToJson(this).ToString(Formatting.None); + } + + /// + /// Discord embed provider data object + /// + public struct EmbedProvider + { + /// + /// Provider name + /// + public string Name; + + /// + /// Provider url + /// + public string Url; + + public override string ToString() => Utils.StructToJson(this).ToString(Formatting.None); + } + + /// + /// Discord embed author data object + /// + public struct EmbedAuthor + { + /// + /// Author name + /// + public string Name; + + /// + /// Author url + /// + public string Url; + + /// + /// Author icon + /// + public string IconUrl; + + /// + /// Author icon proxy + /// + public string ProxyIconUrl; + + public override string ToString() => Utils.StructToJson(this).ToString(Formatting.None); + } + + /// + /// Discord embed field data object + /// + public struct EmbedField + { + /// + /// Field name + /// + public string Name; + + /// + /// Field value + /// + public string Value; + + /// + /// Field align + /// + public bool InLine; + + public override string ToString() => Utils.StructToJson(this).ToString(Formatting.None); + } +} diff --git a/Discord/Utils.cs b/Discord/Utils.cs new file mode 100644 index 0000000..ce9476e --- /dev/null +++ b/Discord/Utils.cs @@ -0,0 +1,108 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Discord +{ + public static class Utils + { + /// + /// Convert Color object into hex integer + /// + /// Color to be converted + /// Converted hex integer + public static int ColorToHex(Color color) + { + string HS = + color.R.ToString("X2") + + color.G.ToString("X2") + + color.B.ToString("X2"); + + return int.Parse(HS, System.Globalization.NumberStyles.HexNumber); + } + + internal static JObject StructToJson(object @struct) + { + Type type = @struct.GetType(); + JObject json = new JObject(); + + FieldInfo[] fields = type.GetFields(); + foreach (FieldInfo field in fields) + { + string name = FieldNameToJsonName(field.Name); + object value = field.GetValue(@struct); + if (value == null) + continue; + + if (value is bool) + json.Add(name, (bool)value); + else if (value is int) + json.Add(name, (int)value); + else if (value is Color) + json.Add(name, ColorToHex((Color)value)); + else if (value is string) + json.Add(name, value as string); + else if (value is DateTime) + json.Add(name, ((DateTime)value).ToString("yyyy-MM-ddTHH\\:mm\\:ss.fffffffzzz")); + else if (value is IList && value.GetType().IsGenericType) + { + JArray array = new JArray(); + foreach (object obj in value as IList) + array.Add(StructToJson(obj)); + json.Add(name, array); + } + else json.Add(name, StructToJson(value)); + } + return json; + } + + static string[] ignore = { "InLine" }; + internal static string FieldNameToJsonName(string name) + { + if (ignore.ToList().Contains(name)) + return name.ToLower(); + + List result = new List(); + + if (IsFullUpper(name)) + result.AddRange(name.ToLower().ToCharArray()); + else + for (int i = 0; i < name.Length; i++) + { + if (i > 0 && char.IsUpper(name[i])) + result.AddRange(new[] { '_', char.ToLower(name[i]) }); + else result.Add(char.ToLower(name[i])); + } + return string.Join("", result); + } + + internal static bool IsFullUpper(string str) + { + bool upper = true; + for (int i = 0; i < str.Length; i++) + { + if (!char.IsUpper(str[i])) + { + upper = false; + break; + } + } + return upper; + } + + public static string Decode(Stream source) + { + using (StreamReader reader = new StreamReader(source)) + return reader.ReadToEnd(); + } + + public static byte[] Encode(string source, string encoding = "utf-8") + => Encoding.GetEncoding(encoding).GetBytes(source); + } +} diff --git a/DiscordSettings.Designer.cs b/DiscordSettings.Designer.cs new file mode 100644 index 0000000..4a496ba --- /dev/null +++ b/DiscordSettings.Designer.cs @@ -0,0 +1,222 @@ +namespace RelaunchProcess +{ + partial class WebhookSettings + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnOk = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.lblDwhURL = new System.Windows.Forms.Label(); + this.btnClearUrlField = new System.Windows.Forms.Button(); + this.lblDwhBotname = new System.Windows.Forms.Label(); + this.lblDwhAvatarUrl = new System.Windows.Forms.Label(); + this.textDwhAvatarUrl = new System.Windows.Forms.TextBox(); + this.textDwhBotName = new System.Windows.Forms.TextBox(); + this.textDwhURL = new System.Windows.Forms.TextBox(); + this.chbxDiscordEnabled = new System.Windows.Forms.CheckBox(); + this.btnClearAvatarUrlField = new System.Windows.Forms.Button(); + this.groupBoxSettingsDiscord = new System.Windows.Forms.GroupBox(); + this.groupBoxSettingsDiscord.SuspendLayout(); + this.SuspendLayout(); + // + // btnOk + // + this.btnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnOk.Location = new System.Drawing.Point(236, 206); + this.btnOk.Name = "btnOk"; + this.btnOk.Size = new System.Drawing.Size(75, 23); + this.btnOk.TabIndex = 0; + this.btnOk.Text = "Сохранить"; + this.btnOk.UseVisualStyleBackColor = true; + this.btnOk.Click += new System.EventHandler(this.BtnOk_Click); + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(317, 206); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 23); + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "Отменить"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.BtnCancel_Click); + // + // lblDwhURL + // + this.lblDwhURL.AutoSize = true; + this.lblDwhURL.Location = new System.Drawing.Point(6, 103); + this.lblDwhURL.Name = "lblDwhURL"; + this.lblDwhURL.Size = new System.Drawing.Size(147, 13); + this.lblDwhURL.TabIndex = 4; + this.lblDwhURL.Text = "URL-адрес Discord веб-хука"; + // + // btnClearUrlField + // + this.btnClearUrlField.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnClearUrlField.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.btnClearUrlField.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); + this.btnClearUrlField.Location = new System.Drawing.Point(350, 119); + this.btnClearUrlField.Name = "btnClearUrlField"; + this.btnClearUrlField.Size = new System.Drawing.Size(19, 19); + this.btnClearUrlField.TabIndex = 5; + this.btnClearUrlField.Text = "X"; + this.btnClearUrlField.UseVisualStyleBackColor = true; + this.btnClearUrlField.Click += new System.EventHandler(this.ClearUrl); + // + // lblDwhBotname + // + this.lblDwhBotname.AutoSize = true; + this.lblDwhBotname.Location = new System.Drawing.Point(6, 25); + this.lblDwhBotname.Name = "lblDwhBotname"; + this.lblDwhBotname.Size = new System.Drawing.Size(124, 13); + this.lblDwhBotname.TabIndex = 6; + this.lblDwhBotname.Text = "Имя бота в сообщении"; + // + // lblDwhAvatarUrl + // + this.lblDwhAvatarUrl.AutoSize = true; + this.lblDwhAvatarUrl.Location = new System.Drawing.Point(6, 64); + this.lblDwhAvatarUrl.Name = "lblDwhAvatarUrl"; + this.lblDwhAvatarUrl.Size = new System.Drawing.Size(132, 13); + this.lblDwhAvatarUrl.TabIndex = 6; + this.lblDwhAvatarUrl.Text = "URL-адрес аватара бота"; + // + // textDwhAvatarUrl + // + this.textDwhAvatarUrl.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textDwhAvatarUrl.Location = new System.Drawing.Point(6, 80); + this.textDwhAvatarUrl.Name = "textDwhAvatarUrl"; + this.textDwhAvatarUrl.Size = new System.Drawing.Size(338, 20); + this.textDwhAvatarUrl.TabIndex = 7; + // + // textDwhBotName + // + this.textDwhBotName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textDwhBotName.Location = new System.Drawing.Point(6, 41); + this.textDwhBotName.MaxLength = 40; + this.textDwhBotName.Name = "textDwhBotName"; + this.textDwhBotName.Size = new System.Drawing.Size(212, 20); + this.textDwhBotName.TabIndex = 7; + // + // textDwhURL + // + this.textDwhURL.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textDwhURL.Location = new System.Drawing.Point(6, 119); + this.textDwhURL.Name = "textDwhURL"; + this.textDwhURL.Size = new System.Drawing.Size(338, 20); + this.textDwhURL.TabIndex = 3; + // + // chbxDiscordEnabled + // + this.chbxDiscordEnabled.AutoSize = true; + this.chbxDiscordEnabled.Location = new System.Drawing.Point(12, 173); + this.chbxDiscordEnabled.Name = "chbxDiscordEnabled"; + this.chbxDiscordEnabled.Size = new System.Drawing.Size(232, 17); + this.chbxDiscordEnabled.TabIndex = 2; + this.chbxDiscordEnabled.Text = "Включить отправку сообщений в Discord"; + this.chbxDiscordEnabled.UseVisualStyleBackColor = true; + this.chbxDiscordEnabled.CheckedChanged += new System.EventHandler(this.chbxDiscordEnabled_CheckedChanged); + // + // btnClearAvatarUrlField + // + this.btnClearAvatarUrlField.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnClearAvatarUrlField.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.btnClearAvatarUrlField.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); + this.btnClearAvatarUrlField.Location = new System.Drawing.Point(350, 81); + this.btnClearAvatarUrlField.Name = "btnClearAvatarUrlField"; + this.btnClearAvatarUrlField.Size = new System.Drawing.Size(19, 19); + this.btnClearAvatarUrlField.TabIndex = 5; + this.btnClearAvatarUrlField.Text = "X"; + this.btnClearAvatarUrlField.UseVisualStyleBackColor = true; + this.btnClearAvatarUrlField.Click += new System.EventHandler(this.ClearUrl); + // + // groupBoxSettingsDiscord + // + this.groupBoxSettingsDiscord.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBoxSettingsDiscord.Controls.Add(this.lblDwhBotname); + this.groupBoxSettingsDiscord.Controls.Add(this.textDwhURL); + this.groupBoxSettingsDiscord.Controls.Add(this.textDwhAvatarUrl); + this.groupBoxSettingsDiscord.Controls.Add(this.lblDwhURL); + this.groupBoxSettingsDiscord.Controls.Add(this.textDwhBotName); + this.groupBoxSettingsDiscord.Controls.Add(this.btnClearUrlField); + this.groupBoxSettingsDiscord.Controls.Add(this.lblDwhAvatarUrl); + this.groupBoxSettingsDiscord.Controls.Add(this.btnClearAvatarUrlField); + this.groupBoxSettingsDiscord.Location = new System.Drawing.Point(12, 12); + this.groupBoxSettingsDiscord.Name = "groupBoxSettingsDiscord"; + this.groupBoxSettingsDiscord.Size = new System.Drawing.Size(379, 155); + this.groupBoxSettingsDiscord.TabIndex = 8; + this.groupBoxSettingsDiscord.TabStop = false; + this.groupBoxSettingsDiscord.Text = "Discord"; + // + // WebhookSettings + // + this.AcceptButton = this.btnOk; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(404, 241); + this.ControlBox = false; + this.Controls.Add(this.chbxDiscordEnabled); + this.Controls.Add(this.groupBoxSettingsDiscord); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOk); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(420, 280); + this.Name = "WebhookSettings"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.Text = "Настройки webhook"; + this.Load += new System.EventHandler(this.WebhookSettings_FormLoad); + this.groupBoxSettingsDiscord.ResumeLayout(false); + this.groupBoxSettingsDiscord.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button btnOk; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.CheckBox chbxDiscordEnabled; + private System.Windows.Forms.TextBox textDwhURL; + private System.Windows.Forms.Label lblDwhURL; + private System.Windows.Forms.Button btnClearUrlField; + private System.Windows.Forms.Label lblDwhBotname; + private System.Windows.Forms.TextBox textDwhBotName; + private System.Windows.Forms.Label lblDwhAvatarUrl; + private System.Windows.Forms.TextBox textDwhAvatarUrl; + private System.Windows.Forms.Button btnClearAvatarUrlField; + private System.Windows.Forms.GroupBox groupBoxSettingsDiscord; + } +} \ No newline at end of file diff --git a/DiscordSettings.cs b/DiscordSettings.cs new file mode 100644 index 0000000..2693454 --- /dev/null +++ b/DiscordSettings.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Printing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using RelaunchProcess.Properties; + +namespace RelaunchProcess +{ + public partial class WebhookSettings : Form + { + public WebhookSettings() + { + InitializeComponent(); + RestoreSettings(); + } + + private void RestoreSettings() + { + textDwhBotName.Text = Settings.Default.dwhBotname; + textDwhAvatarUrl.Text = Settings.Default.dwhAvatarURL; + textDwhURL.Text = Settings.Default.dwhURL; + chbxDiscordEnabled.Checked = Settings.Default.dwhEnabled; + } + + private void SaveSettings() + { + Settings.Default.dwhBotname = textDwhBotName.Text; + Settings.Default.dwhAvatarURL = textDwhAvatarUrl.Text; + Settings.Default.dwhURL = textDwhURL.Text; + Settings.Default.dwhEnabled = chbxDiscordEnabled.Checked; + Settings.Default.Save(); + } + + public void UpdateUI() + { + groupBoxSettingsDiscord.Enabled = !chbxDiscordEnabled.Checked; + } + + private void WebhookSettings_FormLoad(object sender, EventArgs e) + { + UpdateUI(); + } + + private void BtnCancel_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Cancel; + } + + private void BtnOk_Click(object sender, EventArgs e) + { + if ( (String.IsNullOrEmpty(textDwhURL.Text) || + Uri.IsWellFormedUriString(textDwhURL.Text, UriKind.Absolute)) && + (String.IsNullOrEmpty(textDwhAvatarUrl.Text) || + Uri.IsWellFormedUriString(textDwhAvatarUrl.Text, UriKind.Absolute)) ) + { + if (String.IsNullOrEmpty(textDwhURL.Text)) + { + chbxDiscordEnabled.Checked = false; + } + SaveSettings(); + DialogResult = DialogResult.OK; + Close(); + } + else + { + MessageBox.Show("Неверный формат URL.\rОчистите или исправьте.", "URL", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void ClearUrl(object sender, EventArgs e) + { + if ( (Button)sender == btnClearUrlField ) textDwhURL.Text = ""; + if ( (Button)sender == btnClearAvatarUrlField ) textDwhAvatarUrl.Text = ""; + } + + private void chbxDiscordEnabled_CheckedChanged(object sender, EventArgs e) + { + UpdateUI(); + } + } +} diff --git a/DiscordSettings.resx b/DiscordSettings.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/DiscordSettings.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs index b71d12f..74e4f83 100644 --- a/Form1.Designer.cs +++ b/Form1.Designer.cs @@ -48,6 +48,7 @@ namespace Process_Auto_Relaunch this.checkBoxSaveState = new System.Windows.Forms.CheckBox(); this.radioButtonEnableWathing = new System.Windows.Forms.RadioButton(); this.radioButtonDisableWathing = new System.Windows.Forms.RadioButton(); + this.btnShowDiscordSettings = new System.Windows.Forms.Button(); this.groupBoxStatus = new System.Windows.Forms.GroupBox(); this.labelStatus = new System.Windows.Forms.Label(); this.myBackgroundWorker = new System.ComponentModel.BackgroundWorker(); @@ -165,7 +166,7 @@ namespace Process_Auto_Relaunch this.buttonSetProgramStart.TabIndex = 1; this.buttonSetProgramStart.Text = "Обзор"; this.buttonSetProgramStart.UseVisualStyleBackColor = true; - this.buttonSetProgramStart.Click += new System.EventHandler(this.buttonSetProgramStart_Click); + this.buttonSetProgramStart.Click += new System.EventHandler(this.ButtonSetProgramStart_Click); // // groupBoxProgramStart // @@ -248,6 +249,7 @@ namespace Process_Auto_Relaunch this.groupBoxEnabled.Controls.Add(this.checkBoxSaveState); this.groupBoxEnabled.Controls.Add(this.radioButtonEnableWathing); this.groupBoxEnabled.Controls.Add(this.radioButtonDisableWathing); + this.groupBoxEnabled.Controls.Add(this.btnShowDiscordSettings); this.groupBoxEnabled.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204))); this.groupBoxEnabled.Location = new System.Drawing.Point(13, 345); this.groupBoxEnabled.Margin = new System.Windows.Forms.Padding(4); @@ -301,6 +303,18 @@ namespace Process_Auto_Relaunch this.radioButtonDisableWathing.UseVisualStyleBackColor = true; this.radioButtonDisableWathing.CheckedChanged += new System.EventHandler(this.radioButtonDisableWathing_CheckedChanged); // + // btnShowDiscordSettings + // + this.btnShowDiscordSettings.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.btnShowDiscordSettings.Location = new System.Drawing.Point(282, 16); + this.btnShowDiscordSettings.Margin = new System.Windows.Forms.Padding(4); + this.btnShowDiscordSettings.Name = "btnShowDiscordSettings"; + this.btnShowDiscordSettings.Size = new System.Drawing.Size(89, 34); + this.btnShowDiscordSettings.TabIndex = 1; + this.btnShowDiscordSettings.Text = "Webhook"; + this.btnShowDiscordSettings.UseVisualStyleBackColor = true; + this.btnShowDiscordSettings.Click += new System.EventHandler(this.btnShowDiscordSettings_Click); + // // groupBoxStatus // this.groupBoxStatus.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) @@ -366,6 +380,7 @@ namespace Process_Auto_Relaunch this.Controls.Add(this.groupBoxProgramStart); this.Controls.Add(this.groupBoxActions); this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4); this.MaximizeBox = false; @@ -413,5 +428,6 @@ namespace Process_Auto_Relaunch private NumericUpDown numericUpDown1; private GroupBox groupBoxHistory; private RichTextBox richTextBoxHistory; + private Button btnShowDiscordSettings; } } diff --git a/Form1.cs b/Form1.cs index 7333ca4..82311ba 100644 --- a/Form1.cs +++ b/Form1.cs @@ -7,19 +7,50 @@ using System.Threading; using System.Windows.Forms; using RelaunchProcess.Properties; using static System.Windows.Forms.VisualStyles.VisualStyleElement; +using Discord; +using Discord.Webhook; +using System.Data; +using static System.Net.Mime.MediaTypeNames; +using RelaunchProcess; + namespace Process_Auto_Relaunch { public partial class Form1 : Form { - private delegate void UpdateLogDelegate(string text, bool add_history = false); - private UpdateLogDelegate updateLogDelegate = null; + [Flags] + public enum NotifyLevel + { + logNone = 0, + logAlways = 1, // + logUpdateStatus = 2, // + logHistory = 4, // + logDiscord = 8 // + } + private delegate void UpdateLogDelegate(string text, NotifyLevel level = NotifyLevel.logUpdateStatus); + private readonly UpdateLogDelegate updateLogDelegate; + private DiscordWebhook dwhHook; + private DiscordMessage dwhMessage; public Form1() { InitializeComponent(); - this.updateLogDelegate = new UpdateLogDelegate(this.UpdateStatus); + this.updateLogDelegate = this.UpdateStatus; + this.updateLogDelegate += this.SendDiscordMessage; + this.updateLogDelegate += this.HistoryLog; myBackgroundWorker.WorkerSupportsCancellation = true; + dwhHook = new DiscordWebhook(); + /*if ( Uri.IsWellFormedUriString(Settings.Default.dwhURL,UriKind.Absolute) && Settings.Default.dwhEnabled && Settings.Default.dwhURL!="") + { + dwhHook.Url = Settings.Default.dwhURL; + } + else if (Settings.Default.dwhEnabled) { + Debug.WriteLine($" URL - ({Settings.Default.dwhURL}). Discord ."); + HistoryLog($" URL - ({Settings.Default.dwhURL}). Discord ."); + Settings.Default.dwhEnabled = false; + Settings.Default.Save(); + }*/ + } /// @@ -65,7 +96,7 @@ namespace Process_Auto_Relaunch if (myBackgroundWorker.WorkerSupportsCancellation && myBackgroundWorker.IsBusy) { myBackgroundWorker.CancelAsync(); - UpdateStatus("..."); + UpdateStatus("...",NotifyLevel.logUpdateStatus); } } @@ -112,26 +143,61 @@ namespace Process_Auto_Relaunch /// /// /// - /// - /// - public void UpdateStatus(string text, bool add_history = false) + /// / + /// + public void UpdateStatus( string text, NotifyLevel level ) { + if (!level.HasFlag(NotifyLevel.logAlways) && !level.HasFlag(NotifyLevel.logUpdateStatus)) return; labelStatus.Text = text; - - if (add_history) - { - HistoryLog(text); - } } - private void HistoryLog(string text) + /// + /// + /// + /// / + /// + private void HistoryLog( string text, NotifyLevel level ) { + if (!level.HasFlag(NotifyLevel.logAlways) && !level.HasFlag(NotifyLevel.logHistory)) return; richTextBoxHistory.Text += DateTime.Now.ToString() + ": " + text + "\n"; } - public void Status(string text, bool add_history = false) + /// + /// Discord + /// + /// / + /// + public void SendDiscordMessage( string text, NotifyLevel level ) { - Invoke(updateLogDelegate, text, add_history); + if (!level.HasFlag(NotifyLevel.logAlways) && !level.HasFlag(NotifyLevel.logDiscord)) return; + if (Settings.Default.dwhEnabled) + { + dwhHook.Url = Settings.Default.dwhURL; + dwhMessage.Username = Settings.Default.dwhBotname; + dwhMessage.AvatarUrl = Settings.Default.dwhAvatarURL; + dwhMessage.Content = ":arrows_counterclockwise: " + text; + try + { + dwhHook.Send(dwhMessage); + } + catch (Exception ex) + { + Status($" .",NotifyLevel.logHistory); + Debug.WriteLine($"Discord messaging error: {ex.Message}"); + //Settings.Default.dwhEnabled = false; + //Settings.Default.Save(); + } + } + } + + /// + /// + /// + /// / + /// + public void Status(string text, NotifyLevel level = NotifyLevel.logUpdateStatus) + { + updateLogDelegate.Invoke(text, level); } private void CheckProgramState() @@ -142,6 +208,7 @@ namespace Process_Auto_Relaunch groupBoxProcessName.Enabled = !watching; groupBoxProgramStart.Enabled = !watching; groupBoxActions.Enabled = !watching; + btnShowDiscordSettings.Enabled = !watching; // Settings.Default.enableWatching = watching; @@ -153,7 +220,7 @@ namespace Process_Auto_Relaunch /// /// /// - private void buttonSetProgramStart_Click(object sender, EventArgs e) + private void ButtonSetProgramStart_Click(object sender, EventArgs e) { OpenFileDialog openFile = new OpenFileDialog(); openFile.Filter = " (*.exe)|*.exe"; @@ -169,7 +236,6 @@ namespace Process_Auto_Relaunch textBoxProcessName.Text = textBoxProcessName.Text.Remove(textBoxProcessName.Text.Length-4); Settings.Default.startProgramPath = openFile.FileName; Settings.Default.Save(); - openFile.Dispose(); } @@ -208,7 +274,7 @@ namespace Process_Auto_Relaunch } } - Status(" .", true); + Status(" .", NotifyLevel.logAlways); Process.Start(path, args); } @@ -221,21 +287,23 @@ namespace Process_Auto_Relaunch { if (ProcessByNameIsRuning(textBoxProcessName.Text)) { - Status($" {textBoxProcessName.Text} "); + Status($" ",NotifyLevel.logUpdateStatus); + if (i < (int)numericUpDown1.Value) SendDiscordMessage($" {textBoxProcessName.Text} .",NotifyLevel.logDiscord); i = (int)numericUpDown1.Value; } else { if (radioButtonRestartTimer.Checked) { + if (i==(int)numericUpDown1.Value) Status($" {textBoxProcessName.Text} . {i} ",NotifyLevel.logDiscord); i--; - Status($" {textBoxProcessName.Text} . {i}"); + Status($" {textBoxProcessName.Text} . {i}", NotifyLevel.logUpdateStatus); } if (i <= 0 || radioButtonRestartNow.Checked) { i = (int)numericUpDown1.Value; - Status("..."); + Status($" {textBoxProcessName.Text}", NotifyLevel.logUpdateStatus|NotifyLevel.logDiscord); ProcessStart(Settings.Default.startProgramPath, textBoxArguments.Text); } } @@ -253,18 +321,26 @@ namespace Process_Auto_Relaunch { if (e.Cancelled) { - Status(" ."); + Status(" .",NotifyLevel.logUpdateStatus|NotifyLevel.logDiscord); } else if (e.Error != null) { + Status(" ! .", NotifyLevel.logUpdateStatus | NotifyLevel.logDiscord); MessageBox.Show("Error: " + e.Error.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Stop); - Status(" ! .", true); radioButtonDisableWathing.Checked = true; } else { - Status(" ."); + Status(" .", NotifyLevel.logUpdateStatus|NotifyLevel.logDiscord); } } + + private void btnShowDiscordSettings_Click(object sender, EventArgs e) + { + WebhookSettings discordSettings; + discordSettings = new WebhookSettings(); + discordSettings.ShowDialog(this); + discordSettings.Dispose(); + } } } diff --git a/Form1.resx b/Form1.resx index a2e57f8..50c97e1 100644 --- a/Form1.resx +++ b/Form1.resx @@ -121,7 +121,7 @@ 17, 17 - 131 + 40 diff --git a/Process Auto Relaunch.csproj b/Process Auto Relaunch.csproj index c45a3e1..3a337ee 100644 --- a/Process Auto Relaunch.csproj +++ b/Process Auto Relaunch.csproj @@ -63,6 +63,13 @@ bin\x64\Release\ + + False + .\CSharpDiscordWebhook.dll + + + packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll + @@ -76,13 +83,26 @@ + + + Form Form1.cs + + Form + + + DiscordSettings.cs + + + + DiscordSettings.cs + Form1.cs Designer @@ -98,6 +118,7 @@ Resources.resx True + SettingsSingleFileGenerator Settings.Designer.cs @@ -127,4 +148,8 @@ + + rem copy $(ProjectDir)\CSharpDiscordWebhook\CSharpDiscordWebhook\$(OutDir)\* $(TargetDir) +rem copy $(ProjectDir)\CSharpDiscordWebhook\CSharpDiscordWebhook\$(OutDir)\* $(ProjectDir) + \ No newline at end of file diff --git a/Process Auto Relaunch.sln b/Process Auto Relaunch.sln index cb81be4..14cd0e9 100644 --- a/Process Auto Relaunch.sln +++ b/Process Auto Relaunch.sln @@ -9,6 +9,8 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 + DebugFULL|Any CPU = DebugFULL|Any CPU + DebugFULL|x64 = DebugFULL|x64 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 EndGlobalSection @@ -17,6 +19,10 @@ Global {B48F106C-F4E2-4BFB-9BAA-42FC13C03FDD}.Debug|Any CPU.Build.0 = Debug|Any CPU {B48F106C-F4E2-4BFB-9BAA-42FC13C03FDD}.Debug|x64.ActiveCfg = Debug|x64 {B48F106C-F4E2-4BFB-9BAA-42FC13C03FDD}.Debug|x64.Build.0 = Debug|x64 + {B48F106C-F4E2-4BFB-9BAA-42FC13C03FDD}.DebugFULL|Any CPU.ActiveCfg = Debug|Any CPU + {B48F106C-F4E2-4BFB-9BAA-42FC13C03FDD}.DebugFULL|Any CPU.Build.0 = Debug|Any CPU + {B48F106C-F4E2-4BFB-9BAA-42FC13C03FDD}.DebugFULL|x64.ActiveCfg = Debug|x64 + {B48F106C-F4E2-4BFB-9BAA-42FC13C03FDD}.DebugFULL|x64.Build.0 = Debug|x64 {B48F106C-F4E2-4BFB-9BAA-42FC13C03FDD}.Release|Any CPU.ActiveCfg = Release|Any CPU {B48F106C-F4E2-4BFB-9BAA-42FC13C03FDD}.Release|Any CPU.Build.0 = Release|Any CPU {B48F106C-F4E2-4BFB-9BAA-42FC13C03FDD}.Release|x64.ActiveCfg = Release|x64 diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs index c388999..a6205d2 100644 --- a/Properties/Settings.Designer.cs +++ b/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace RelaunchProcess.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.9.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -118,5 +118,54 @@ namespace RelaunchProcess.Properties { this["timer"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string dwhURL { + get { + return ((string)(this["dwhURL"])); + } + set { + this["dwhURL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool dwhEnabled { + get { + return ((bool)(this["dwhEnabled"])); + } + set { + this["dwhEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Relauncher")] + public string dwhBotname { + get { + return ((string)(this["dwhBotname"])); + } + set { + this["dwhBotname"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("https://distribution.faceit-cdn.net/images/7240adfa-6bda-43a0-abd0-8c599f176686.j" + + "peg")] + public string dwhAvatarURL { + get { + return ((string)(this["dwhAvatarURL"])); + } + set { + this["dwhAvatarURL"] = value; + } + } } } diff --git a/Properties/Settings.settings b/Properties/Settings.settings index ad9cd31..1f40892 100644 --- a/Properties/Settings.settings +++ b/Properties/Settings.settings @@ -26,5 +26,17 @@ 30 + + + + + False + + + Relauncher + + + https://distribution.faceit-cdn.net/images/7240adfa-6bda-43a0-abd0-8c599f176686.jpeg + \ No newline at end of file diff --git a/Settings.cs b/Settings.cs new file mode 100644 index 0000000..9ba1259 --- /dev/null +++ b/Settings.cs @@ -0,0 +1,28 @@ +namespace RelaunchProcess.Properties { + + + // Этот класс позволяет обрабатывать определенные события в классе параметров: + // Событие SettingChanging возникает перед изменением значения параметра. + // Событие PropertyChanged возникает после изменения значения параметра. + // Событие SettingsLoaded возникает после загрузки значений параметров. + // Событие SettingsSaving возникает перед сохранением значений параметров. + internal sealed partial class Settings { + + public Settings() { + // // Для добавления обработчиков событий для сохранения и изменения параметров раскомментируйте приведенные ниже строки: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Добавьте здесь код для обработки события SettingChangingEvent. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Добавьте здесь код для обработки события SettingsSaving. + } + } +} diff --git a/packages.config b/packages.config new file mode 100644 index 0000000..0b14af3 --- /dev/null +++ b/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file