commit 347f466242c5d6fa41f5736b0f17b6c5b2447515 Author: Lucas Bortoli Date: Sat Jun 25 18:30:01 2022 -0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..103db3d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin/ +obj/ +.vs/ +packages/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ab52659 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# ntfy.sh Windows + +![Application screenshot](https://i.imgur.com/C9BFTCJ.png) + +![Notification example screenshot](https://i.imgur.com/ZmfJ8Wm.png) + +A [ntfy.sh](https://ntfy.sh/) client for Windows. \ No newline at end of file diff --git a/ntfysh_client.sln b/ntfysh_client.sln new file mode 100644 index 0000000..acd0ad7 --- /dev/null +++ b/ntfysh_client.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.32602.291 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ntfysh_client", "ntfysh_client\ntfysh_client.csproj", "{5A18D152-D620-43FE-B844-DEF30CFA50EF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5A18D152-D620-43FE-B844-DEF30CFA50EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A18D152-D620-43FE-B844-DEF30CFA50EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A18D152-D620-43FE-B844-DEF30CFA50EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A18D152-D620-43FE-B844-DEF30CFA50EF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D202877A-41B5-4879-9FFB-C5A29FC653E7} + EndGlobalSection +EndGlobal diff --git a/ntfysh_client/AboutBox.Designer.cs b/ntfysh_client/AboutBox.Designer.cs new file mode 100644 index 0000000..368151c --- /dev/null +++ b/ntfysh_client/AboutBox.Designer.cs @@ -0,0 +1,97 @@ + +namespace ntfysh_client +{ + partial class AboutBox + { + /// + /// 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.button1 = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(299, 173); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 0; + this.button1.Text = "Close"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 49); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(358, 65); + this.label1.TabIndex = 1; + this.label1.Text = "Copyright © 2022 Lucas Bortoli\r\nAll rights reserved\r\n\r\nThe icons included in this" + + " application are property of Microsoft Corporation.\r\n(Visual Studio Image Librar" + + "y)"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Font = new System.Drawing.Font("Segoe UI", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label2.Location = new System.Drawing.Point(10, 9); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(166, 30); + this.label2.TabIndex = 2; + this.label2.Text = "ntfy.sh Windows"; + // + // AboutBox + // + this.AcceptButton = this.button1; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.White; + this.ClientSize = new System.Drawing.Size(386, 208); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.button1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AboutBox"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "About"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + } +} \ No newline at end of file diff --git a/ntfysh_client/AboutBox.cs b/ntfysh_client/AboutBox.cs new file mode 100644 index 0000000..6239921 --- /dev/null +++ b/ntfysh_client/AboutBox.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace ntfysh_client +{ + public partial class AboutBox : Form + { + public AboutBox() + { + InitializeComponent(); + } + + private void button1_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.OK; + } + } +} diff --git a/ntfysh_client/AboutBox.resx b/ntfysh_client/AboutBox.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/ntfysh_client/AboutBox.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/ntfysh_client/App.config b/ntfysh_client/App.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/ntfysh_client/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ntfysh_client/Form1.Designer.cs b/ntfysh_client/Form1.Designer.cs new file mode 100644 index 0000000..e9b6d1e --- /dev/null +++ b/ntfysh_client/Form1.Designer.cs @@ -0,0 +1,237 @@ + +namespace ntfysh_client +{ + partial class Form1 + { + /// + /// 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.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1)); + this.subscribeNewTopic = new System.Windows.Forms.Button(); + this.removeSelectedTopics = new System.Windows.Forms.Button(); + this.notificationTopics = new System.Windows.Forms.ListBox(); + this.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components); + this.trayContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.showControlWindowToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.label1 = new System.Windows.Forms.Label(); + this.exitToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ntfyshWebsiteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.trayContextMenu.SuspendLayout(); + this.menuStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // subscribeNewTopic + // + this.subscribeNewTopic.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.subscribeNewTopic.Location = new System.Drawing.Point(236, 50); + this.subscribeNewTopic.Name = "subscribeNewTopic"; + this.subscribeNewTopic.Size = new System.Drawing.Size(105, 23); + this.subscribeNewTopic.TabIndex = 2; + this.subscribeNewTopic.Text = "Add"; + this.subscribeNewTopic.UseVisualStyleBackColor = true; + this.subscribeNewTopic.Click += new System.EventHandler(this.subscribeNewTopic_Click); + // + // removeSelectedTopics + // + this.removeSelectedTopics.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.removeSelectedTopics.Enabled = false; + this.removeSelectedTopics.Location = new System.Drawing.Point(236, 79); + this.removeSelectedTopics.Name = "removeSelectedTopics"; + this.removeSelectedTopics.Size = new System.Drawing.Size(105, 23); + this.removeSelectedTopics.TabIndex = 0; + this.removeSelectedTopics.Text = "Remove selected"; + this.removeSelectedTopics.UseVisualStyleBackColor = true; + this.removeSelectedTopics.Click += new System.EventHandler(this.removeSelectedTopics_Click); + // + // notificationTopics + // + this.notificationTopics.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.notificationTopics.FormattingEnabled = true; + this.notificationTopics.Location = new System.Drawing.Point(12, 50); + this.notificationTopics.Name = "notificationTopics"; + this.notificationTopics.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended; + this.notificationTopics.Size = new System.Drawing.Size(145, 173); + this.notificationTopics.TabIndex = 3; + this.notificationTopics.Click += new System.EventHandler(this.notificationTopics_Click); + this.notificationTopics.SelectedValueChanged += new System.EventHandler(this.notificationTopics_SelectedValueChanged); + // + // notifyIcon + // + this.notifyIcon.ContextMenuStrip = this.trayContextMenu; + this.notifyIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon.Icon"))); + this.notifyIcon.Text = "ntfy.sh"; + this.notifyIcon.Visible = true; + this.notifyIcon.Click += new System.EventHandler(this.notifyIcon_Click); + // + // trayContextMenu + // + this.trayContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.showControlWindowToolStripMenuItem, + this.exitToolStripMenuItem}); + this.trayContextMenu.Name = "trayContextMenu"; + this.trayContextMenu.Size = new System.Drawing.Size(190, 48); + // + // showControlWindowToolStripMenuItem + // + this.showControlWindowToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("showControlWindowToolStripMenuItem.Image"))); + this.showControlWindowToolStripMenuItem.Name = "showControlWindowToolStripMenuItem"; + this.showControlWindowToolStripMenuItem.Size = new System.Drawing.Size(189, 22); + this.showControlWindowToolStripMenuItem.Text = "Show control window"; + this.showControlWindowToolStripMenuItem.Click += new System.EventHandler(this.showControlWindowToolStripMenuItem_Click); + // + // exitToolStripMenuItem + // + this.exitToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("exitToolStripMenuItem.Image"))); + this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; + this.exitToolStripMenuItem.Size = new System.Drawing.Size(189, 22); + this.exitToolStripMenuItem.Text = "Exit"; + this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click); + // + // menuStrip1 + // + this.menuStrip1.BackColor = System.Drawing.Color.White; + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.fileToolStripMenuItem, + this.helpToolStripMenuItem}); + this.menuStrip1.Location = new System.Drawing.Point(0, 0); + this.menuStrip1.Name = "menuStrip1"; + this.menuStrip1.Size = new System.Drawing.Size(353, 24); + this.menuStrip1.TabIndex = 4; + this.menuStrip1.Text = "menuStrip1"; + // + // fileToolStripMenuItem + // + this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.exitToolStripMenuItem1}); + this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; + this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); + this.fileToolStripMenuItem.Text = "File"; + // + // helpToolStripMenuItem + // + this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.ntfyshWebsiteToolStripMenuItem, + this.toolStripMenuItem1, + this.aboutToolStripMenuItem}); + this.helpToolStripMenuItem.Name = "helpToolStripMenuItem"; + this.helpToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + this.helpToolStripMenuItem.Text = "Help"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 34); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(145, 13); + this.label1.TabIndex = 1; + this.label1.Text = "Subscribed notification topics"; + // + // exitToolStripMenuItem1 + // + this.exitToolStripMenuItem1.Image = ((System.Drawing.Image)(resources.GetObject("exitToolStripMenuItem1.Image"))); + this.exitToolStripMenuItem1.Name = "exitToolStripMenuItem1"; + this.exitToolStripMenuItem1.Size = new System.Drawing.Size(180, 22); + this.exitToolStripMenuItem1.Text = "Exit"; + // + // aboutToolStripMenuItem + // + this.aboutToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("aboutToolStripMenuItem.Image"))); + this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; + this.aboutToolStripMenuItem.Size = new System.Drawing.Size(185, 22); + this.aboutToolStripMenuItem.Text = "About"; + this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click); + // + // ntfyshWebsiteToolStripMenuItem + // + this.ntfyshWebsiteToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("ntfyshWebsiteToolStripMenuItem.Image"))); + this.ntfyshWebsiteToolStripMenuItem.Name = "ntfyshWebsiteToolStripMenuItem"; + this.ntfyshWebsiteToolStripMenuItem.Size = new System.Drawing.Size(185, 22); + this.ntfyshWebsiteToolStripMenuItem.Text = "Open ntfy.sh website"; + this.ntfyshWebsiteToolStripMenuItem.Click += new System.EventHandler(this.ntfyshWebsiteToolStripMenuItem_Click); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(182, 6); + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.White; + this.ClientSize = new System.Drawing.Size(353, 236); + this.Controls.Add(this.menuStrip1); + this.Controls.Add(this.notificationTopics); + this.Controls.Add(this.removeSelectedTopics); + this.Controls.Add(this.subscribeNewTopic); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.KeyPreview = true; + this.MainMenuStrip = this.menuStrip1; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "Form1"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "ntfy.sh"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing); + this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Form1_FormClosed); + this.Load += new System.EventHandler(this.Form1_Load); + this.trayContextMenu.ResumeLayout(false); + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.Button subscribeNewTopic; + private System.Windows.Forms.Button removeSelectedTopics; + private System.Windows.Forms.ListBox notificationTopics; + private System.Windows.Forms.NotifyIcon notifyIcon; + private System.Windows.Forms.ContextMenuStrip trayContextMenu; + private System.Windows.Forms.ToolStripMenuItem showControlWindowToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; + private System.Windows.Forms.MenuStrip menuStrip1; + private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem ntfyshWebsiteToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + } +} + diff --git a/ntfysh_client/Form1.cs b/ntfysh_client/Form1.cs new file mode 100644 index 0000000..2963b0b --- /dev/null +++ b/ntfysh_client/Form1.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace ntfysh_client +{ + public partial class Form1 : Form + { + private NotificationListener notificationListener; + + public Form1() + { + notificationListener = new NotificationListener(); + notificationListener.OnNotificationReceive += OnNotificationReceive; + InitializeComponent(); + } + + private void Form1_Load(object sender, EventArgs e) + { + this.LoadTopics(); + } + + private void subscribeNewTopic_Click(object sender, EventArgs e) + { + using (var dialog = new SubscribeDialog()) + { + var result = dialog.ShowDialog(); + + if (result == DialogResult.OK) + { + notificationListener.SubscribeToTopic(dialog.getTopicId()); + notificationTopics.Items.Add(dialog.getTopicId()); + this.SaveTopicsToFile(); + } + } + } + + private void removeSelectedTopics_Click(object sender, EventArgs e) + { + while (notificationTopics.SelectedIndex > -1) + { + var topicId = notificationTopics.Items[notificationTopics.SelectedIndex]; + notificationListener.RemoveTopic((string)topicId); + notificationTopics.Items.RemoveAt(notificationTopics.SelectedIndex); + } + + this.SaveTopicsToFile(); + } + + private void notificationTopics_SelectedValueChanged(object sender, EventArgs e) + { + removeSelectedTopics.Enabled = notificationTopics.SelectedIndices.Count > 0; + } + + private void notificationTopics_Click(object sender, EventArgs e) + { + var ev = (MouseEventArgs)e; + var clickedItemIndex = notificationTopics.IndexFromPoint(new Point(ev.X, ev.Y)); + + if (clickedItemIndex == -1) + { + notificationTopics.ClearSelected(); + } + } + + private void button1_Click(object sender, EventArgs e) + { + this.Visible = false; + } + + private void notifyIcon_Click(object sender, EventArgs e) + { + var mouseEv = (MouseEventArgs)e; + if (mouseEv.Button == MouseButtons.Left) + { + this.Visible = !this.Visible; + this.BringToFront(); + } + } + + private void showControlWindowToolStripMenuItem_Click(object sender, EventArgs e) + { + this.Visible = true; + this.BringToFront(); + } + + private string GetTopicsFilePath() + { + string binaryDirectory = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + return Path.Combine(binaryDirectory, "topics.txt"); + } + + private void SaveTopicsToFile() + { + using (StreamWriter writer = new StreamWriter(GetTopicsFilePath())) + { + foreach (string topic in notificationTopics.Items) + { + writer.WriteLine(topic); + } + } + } + + private void LoadTopics() + { + if (!File.Exists(GetTopicsFilePath())) return; + using (StreamReader reader = new StreamReader(GetTopicsFilePath())) + { + while (!reader.EndOfStream) + { + var topic = reader.ReadLine(); + notificationListener.SubscribeToTopic(topic); + notificationTopics.Items.Add(topic); + } + } + } + + private void OnNotificationReceive(object sender, NotificationReceiveEventArgs e) + { + notifyIcon.ShowBalloonTip(3000, e.Title, e.Message, ToolTipIcon.Info); + } + + private void Form1_FormClosed(object sender, FormClosedEventArgs e) + { + notifyIcon.Dispose(); + } + + private bool trueExit = false; + private void Form1_FormClosing(object sender, FormClosingEventArgs e) + { + // Let it close + if (trueExit) return; + + if (e.CloseReason == CloseReason.UserClosing) + { + this.Visible = false; + e.Cancel = true; + } + } + + private void exitToolStripMenuItem_Click(object sender, EventArgs e) + { + trueExit = true; + this.Close(); + } + + private void ntfyshWebsiteToolStripMenuItem_Click(object sender, EventArgs e) + { + System.Diagnostics.Process.Start("https://ntfy.sh/"); + } + + private void aboutToolStripMenuItem_Click(object sender, EventArgs e) + { + var d = new AboutBox(); + d.ShowDialog(); + d.Dispose(); + } + } +} diff --git a/ntfysh_client/Form1.resx b/ntfysh_client/Form1.resx new file mode 100644 index 0000000..4b4cee7 --- /dev/null +++ b/ntfysh_client/Form1.resx @@ -0,0 +1,536 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + 17, 17 + + + 123, 17 + + + + + AAABAAEAMjIAAAEAIADIKAAAFgAAACgAAAAyAAAAZAAAAAEAIAAAAAAAECcAACMuAAAjLgAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwc + HBshISEfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACEhIS4hISGsISEh+yEhIeMiIiIlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIigCEhIf8hISH/ISEh/yEhIecfHx8pAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiKAISEh/yEh + If8hISH/ISEh/yEhIeceHh4qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIiIoAhISH/ISEh/yEhIf8hISH/ISEh/yEhIegeHh4qAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIigCEhIf8hISH/ICAg5CEhIf0hISH/ISEh/yEh + IeYgICAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiKAISEh/yEh + If8hISGoIiIiaSEhIfwhISH/ISEh/yEhIegkJCQrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIiIoAhISH/ISEh/yEhIagfHx8ZICAgaCEhIfshISH/ISEh/yEhIekjIyMsAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIigCEhIf8hISH/ISEhqB8fHxkfHx8ZICAgaCEh + IfshISH/ISEh/yEhIekjIyMsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAIiIiSiIiIogiIiKIIiIiiCIiIogiIiKIIiIiiCIiIoghISHEISEh/yEh + If8hISGoHx8fGR8fHxkfHx8ZISEhbCEhIfwhISH/ISEh/yEhIekiIiKIIiIiiCIiIogiIiKIIiIiiCIi + IogiIiKIIiIiiCIiIogiIiKIIiIiiCIiIogiIiKIIiIiiCIiIogiIiKIIiIiiCIiIogiIiKIIiIiiCIi + IogiIiJKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIlMhISH6ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIagfHx8ZHx8fGR8fHxkfHx8ZICAgZyEhIfshISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIfwiIiJZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8gICD0ICAgTx8fHxkfHx8ZHx8fGR8f + HxkfHx8ZIiIiaSEhIfwhISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8hISG6ISEhoyEhIaMhISGjISEhoyEhIaMhISGjISEhoyEh + IU4fHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fSyAgIJ0hISGjISEhoyEhIaMhISGjISEhoyEh + IaMhISGjISEhoyEhIaMhISGjISEhoyEhIaMhISGjISEhoyEhIaMhISGjISEhoyEhIaMhISGjISEhuiEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxl7Rw42Hx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZrFcEhLpcAPaWTgZVHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGaxXBIe6XQD/ul0A/7pcAPaXTQZTHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8Zk00GULldAPS6XQD/ul0A/7pc + APaWTgZVHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxlTNxUlgEQOOIBEDjiARA44gEQOOIBEDjiARA44gEQOOIBEDjiARA44gEQOOIBEDjiARA44gEQOOIBE + DjiARA44pVYFcbpdAPy6XQD/ul0A/7pcAPaXTQZTHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGaRUBXC6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pd + AP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pcAPZIMBggHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZpFQFcLpdAP+6XQD/ul0A/7pd + AP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pd + AP+6XQD/ul0A/1AzFiMfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxmkVAVwul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pd + AP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+4XADrPCsaHh8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxmgUwVmul0A+7pdAP+6XQD/uVwA7IdGCEIfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZoFMFZrpdAPu6XQD/ul0A/7lc + AOyHRghCHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZVzYUJrRbAbyKSQdGHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxmlVgVxul0A/bpdAP+4XADrhUcIQR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGVc2FCa3XQHMul0A/7hcAPGPSQdJHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxmmVAVwuF0A6odGCEIfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxlXNhQmt10BzLpdAP+6XQD/ul0A/6xY + ApQfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxlTNxUlHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZVzYUJrdd + Acy6XQD/ul0A/7pdAP+wWQKkJx0dGh8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGTUjGh23XQHMul0A/7pdAP+6XQD/ul0A+7RcAcO0WwHCtFsBwrRbAcK0WwHCtFsBwrRb + AcK0WwHCtFsBwrRbAcK0WwHCtFsBwrRbAcK0WwHCtFsBwrRbAcKaUQZYHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZUDMWI7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pd + AP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/6RU + BXAfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxlLNRciul0A/7pd + AP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pd + AP+6XQD/ul0A/7pdAP+6XQD/pFQFcB8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxmrWASDul0A/7pdAP+6XQD/ul0A/axXAo2mVQR1plUEdaZVBHWmVQR1plUEdaZV + BHWmVQR1plUEdaZVBHWmVQR1plUEdaZVBHWmVQR1plUEdaZVBHWGSQ07Hx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxmqVwSBul0A/7pdAP+6XQD/t1wA3nhE + EDEfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxmrWASDul0A/7pdAP+6XQD/sFgCoh8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxmqVwSBul0A/7VaAcBNLhchHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxmgUwVmSDAYIB8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/yAgIJchISF1ISEhdSEh + IXUhISF1ISEhdSEhIXUhISF1ISEhdSEhIXUhISF1ISEhdSEhIXUhISF1ISEhdSEhIXUhISF1ISEhdSEh + IXUhISF1ISEhdSEhIXUhISF1ISEhdSEhIXUhISF1ISEhdSEhIXUhISF1ISEhdSEhIXUhISF1ISEhdSEh + IXUhISF1ISEhdSEhIXUgICCXISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISGLISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEhiwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhISGCISEhuyEhIbshISG7ISEhuyEh + IbshISG7ISEhuyEhIbshISG7ISEhuyEhIbshISG7ISEhuyEhIbshISG7ISEhuyEhIbshISG7ISEhuyEh + IbshISG7ISEhuyEhIbshISG7ISEhuyEhIbshISG7ISEhuyEhIbshISG7ISEhuyEhIbshISG7ISEhuyEh + IbshISG7ISEhuyEhIbshISG7ISEhuyEhIYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////wAD////////AAP// + /////8AA//z/////wAD/8H/////AAP/wP////8AA//Af////wAD/8A/////AAP/wB////8AA//AD//// + wAD/8AH////AAP/wAP///8AA8AAAAAADwADgAAAAAAHAAOAAAAAAAcAA4AAAAAABwADgAAAAAAHAAOAA + AAAAAcAA4AAAAAABwADgAAAAAAHAAOAAAAAAAcAA4AAAAAABwADgAAAAAAHAAOAAAAAAAcAA4AAAAAAB + wADgAAAAAAHAAOAAAAAAAcAA4AAAAAABwADgAAAAAAHAAOAAAAAAAcAA4AAAAAABwADgAAAAAAHAAOAA + AAAAAcAA4AAAAAABwADgAAAAAAHAAOAAAAAAAcAA4AAAAAABwADgAAAAAAHAAOAAAAAAAcAA4AAAAAAB + wADgAAAAAAHAAOAAAAAAAcAA4AAAAAABwADwAAAAAAPAAP///////8AA////////wAD////////AAP// + /////8AA////////wAD////////AAA== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 + JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAiklE + QVQ4T8WPQQqAIBBFvUOrXIhu3dcZOkqtu5R1ia4SdRDrxyyGGKmBIOGBDL6nmk9WCGE9yUpW0q9Ads7V + GuCQ/kHAe79joAEO6dcLFmttpQEO6c+Bvpm2oZ0zwB4zVQBiF8cIsMdMDPCb+G2vA/wgP/z6C6WAhBgo + fUFCDGi4BxIGShLpvy5jDoPes/0oNG3VAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 + JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAgUlE + QVQ4T2OgGjjOLq4AZeIFWNWBBI9yib06zC3uABXCCkDyIHVYDTnMI2pzhEvs5VFucSeoEAo4wiNii08e + DHAZQshwFICumCTNMADyK1gTl2gJiCYUNlgBSDPQ1v8gGipEPKDIBRSFAa6oIsoQQvGM1xCqpESsglgA + seroBRgYAOoOWBJbfVcRAAAAAElFTkSuQmCC + + + + 269, 17 + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 + JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABDElE + QVQ4T91RO3bCQAzUy3uhChdJnTVlCj5FcMsZsF1jmy4XycPrzl6fBLgEXMFp4srRiF0wcaBP5j0VI2m0 + 4zH9EwSq8gPP7ELPNFxtpxrbn9vVPkSsykOkqvHi2QxsWwAevhQTzG8eCb1yD7Glv2I5MlM+sLX0GqEq + 6+jVPFl6QZz7lOgdJVnzuMq/sEepbsGln2YnR3AAm0IcIE71geJsTO9mcOWAOa3zicxxxGWApXMGabaH + WDLwqhmLj8tR8SYzh2Qz5b3TUQSEF7jkLzysdB2o4tPybU8MxB9DdlFb9gNwAJv30HXQg8sAS/jmLiQD + PWPxkdabvrMzEBBeQOpI35Vw7t8V/yEQfQPXsZpY9UsnYgAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 + JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAwUlE + QVQ4T2OgClBUVHwAxP9JxA+g2sEG/JeTk5MkBYP0QLVTwQAFBYXHIAFSMEgPVDvYBfulpKSEScEgPVDt + dDIA6OTNQHX/YXysBjAxMcUxMjKuBrGB9DIgPxqmARhwOUBD3sD4WA0QERGRBHLvCwoKygLpm6KiohIw + DVBNz5HY2L0AtHkOEE8F4okgvoyMjDSSJsIGsLOzuwKFfnBycppJS0vLAJ39BUQTbQA6RnYBMkY3YAVI + gES8Aqp9QAEDAwCq9oYvtggceQAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 + JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAgUlE + QVQ4T2OgGjjOLq4AZeIFWNWBBI9yib06zC3uABXCCkDyIHVYDTnMI2pzhEvs5VFucSeoEAo4wiNii08e + DHAZQshwFICumCTNMADyK1gTl2gJiCYUNlgBSDPQ1v8gGipEPKDIBRSFAa6oIsoQQvGM1xCqpESsglgA + seroBRgYAOoOWBJbfVcRAAAAAElFTkSuQmCC + + + + + AAABAAEAMjIAAAEAIADIKAAAFgAAACgAAAAyAAAAZAAAAAEAIAAAAAAAECcAACMuAAAjLgAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwc + HBshISEfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACEhIS4hISGsISEh+yEhIeMiIiIlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIigCEhIf8hISH/ISEh/yEhIecfHx8pAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiKAISEh/yEh + If8hISH/ISEh/yEhIeceHh4qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIiIoAhISH/ISEh/yEhIf8hISH/ISEh/yEhIegeHh4qAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIigCEhIf8hISH/ICAg5CEhIf0hISH/ISEh/yEh + IeYgICAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiKAISEh/yEh + If8hISGoIiIiaSEhIfwhISH/ISEh/yEhIegkJCQrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIiIoAhISH/ISEh/yEhIagfHx8ZICAgaCEhIfshISH/ISEh/yEhIekjIyMsAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIigCEhIf8hISH/ISEhqB8fHxkfHx8ZICAgaCEh + IfshISH/ISEh/yEhIekjIyMsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAIiIiSiIiIogiIiKIIiIiiCIiIogiIiKIIiIiiCIiIoghISHEISEh/yEh + If8hISGoHx8fGR8fHxkfHx8ZISEhbCEhIfwhISH/ISEh/yEhIekiIiKIIiIiiCIiIogiIiKIIiIiiCIi + IogiIiKIIiIiiCIiIogiIiKIIiIiiCIiIogiIiKIIiIiiCIiIogiIiKIIiIiiCIiIogiIiKIIiIiiCIi + IogiIiJKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIlMhISH6ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIagfHx8ZHx8fGR8fHxkfHx8ZICAgZyEhIfshISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIfwiIiJZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8gICD0ICAgTx8fHxkfHx8ZHx8fGR8f + HxkfHx8ZIiIiaSEhIfwhISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8hISG6ISEhoyEhIaMhISGjISEhoyEhIaMhISGjISEhoyEh + IU4fHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fSyAgIJ0hISGjISEhoyEhIaMhISGjISEhoyEh + IaMhISGjISEhoyEhIaMhISGjISEhoyEhIaMhISGjISEhoyEhIaMhISGjISEhoyEhIaMhISGjISEhuiEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxl7Rw42Hx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZrFcEhLpcAPaWTgZVHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGaxXBIe6XQD/ul0A/7pcAPaXTQZTHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8Zk00GULldAPS6XQD/ul0A/7pc + APaWTgZVHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxlTNxUlgEQOOIBEDjiARA44gEQOOIBEDjiARA44gEQOOIBEDjiARA44gEQOOIBEDjiARA44gEQOOIBE + DjiARA44pVYFcbpdAPy6XQD/ul0A/7pcAPaXTQZTHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGaRUBXC6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pd + AP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pcAPZIMBggHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZpFQFcLpdAP+6XQD/ul0A/7pd + AP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pd + AP+6XQD/ul0A/1AzFiMfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxmkVAVwul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pd + AP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+4XADrPCsaHh8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxmgUwVmul0A+7pdAP+6XQD/uVwA7IdGCEIfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZoFMFZrpdAPu6XQD/ul0A/7lc + AOyHRghCHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZVzYUJrRbAbyKSQdGHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxmlVgVxul0A/bpdAP+4XADrhUcIQR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGVc2FCa3XQHMul0A/7hcAPGPSQdJHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxmmVAVwuF0A6odGCEIfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxlXNhQmt10BzLpdAP+6XQD/ul0A/6xY + ApQfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxlTNxUlHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZVzYUJrdd + Acy6XQD/ul0A/7pdAP+wWQKkJx0dGh8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGTUjGh23XQHMul0A/7pdAP+6XQD/ul0A+7RcAcO0WwHCtFsBwrRbAcK0WwHCtFsBwrRb + AcK0WwHCtFsBwrRbAcK0WwHCtFsBwrRbAcK0WwHCtFsBwrRbAcKaUQZYHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZUDMWI7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pd + AP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/6RU + BXAfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxlLNRciul0A/7pd + AP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pdAP+6XQD/ul0A/7pd + AP+6XQD/ul0A/7pdAP+6XQD/pFQFcB8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxmrWASDul0A/7pdAP+6XQD/ul0A/axXAo2mVQR1plUEdaZVBHWmVQR1plUEdaZV + BHWmVQR1plUEdaZVBHWmVQR1plUEdaZVBHWmVQR1plUEdaZVBHWGSQ07Hx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxmqVwSBul0A/7pdAP+6XQD/t1wA3nhE + EDEfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxmrWASDul0A/7pdAP+6XQD/sFgCoh8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/x8fH1MfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxmqVwSBul0A/7VaAcBNLhchHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx9TISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/Hx8fUx8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxmgUwVmSDAYIB8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fH1MhISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISHfISEh/yEhIf8fHx9THx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8f + HxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fGR8fHxkfHx8ZHx8fUyEh + If8hISH/ISEh3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhId8hISH/ISEh/yAgIJchISF1ISEhdSEh + IXUhISF1ISEhdSEhIXUhISF1ISEhdSEhIXUhISF1ISEhdSEhIXUhISF1ISEhdSEhIXUhISF1ISEhdSEh + IXUhISF1ISEhdSEhIXUhISF1ISEhdSEhIXUhISF1ISEhdSEhIXUhISF1ISEhdSEhIXUhISF1ISEhdSEh + IXUhISF1ISEhdSEhIXUgICCXISEh/yEhIf8hISHfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEh3yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhId8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISGLISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEhIf8hISH/ISEh/yEh + If8hISH/ISEhiwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhISGCISEhuyEhIbshISG7ISEhuyEh + IbshISG7ISEhuyEhIbshISG7ISEhuyEhIbshISG7ISEhuyEhIbshISG7ISEhuyEhIbshISG7ISEhuyEh + IbshISG7ISEhuyEhIbshISG7ISEhuyEhIbshISG7ISEhuyEhIbshISG7ISEhuyEhIbshISG7ISEhuyEh + IbshISG7ISEhuyEhIbshISG7ISEhuyEhIYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////wAD////////AAP// + /////8AA//z/////wAD/8H/////AAP/wP////8AA//Af////wAD/8A/////AAP/wB////8AA//AD//// + wAD/8AH////AAP/wAP///8AA8AAAAAADwADgAAAAAAHAAOAAAAAAAcAA4AAAAAABwADgAAAAAAHAAOAA + AAAAAcAA4AAAAAABwADgAAAAAAHAAOAAAAAAAcAA4AAAAAABwADgAAAAAAHAAOAAAAAAAcAA4AAAAAAB + wADgAAAAAAHAAOAAAAAAAcAA4AAAAAABwADgAAAAAAHAAOAAAAAAAcAA4AAAAAABwADgAAAAAAHAAOAA + AAAAAcAA4AAAAAABwADgAAAAAAHAAOAAAAAAAcAA4AAAAAABwADgAAAAAAHAAOAAAAAAAcAA4AAAAAAB + wADgAAAAAAHAAOAAAAAAAcAA4AAAAAABwADwAAAAAAPAAP///////8AA////////wAD////////AAP// + /////8AA////////wAD////////AAA== + + + \ No newline at end of file diff --git a/ntfysh_client/NotificationListener.cs b/ntfysh_client/NotificationListener.cs new file mode 100644 index 0000000..7a6258c --- /dev/null +++ b/ntfysh_client/NotificationListener.cs @@ -0,0 +1,146 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +namespace ntfysh_client +{ + class NotificationListener : IDisposable + { + private HttpClient httpClient; + + private bool disposedValue; + + public Dictionary subscribedTopics; + + public delegate void NotificationReceiveHandler(object sender, NotificationReceiveEventArgs e); + public event NotificationReceiveHandler OnNotificationReceive; + + public NotificationListener() + { + httpClient = new HttpClient(); + subscribedTopics = new Dictionary(); + + ServicePointManager.DefaultConnectionLimit = 100; + } + + public async Task SubscribeToTopic(string topicId) + { + var stream = await httpClient.GetStreamAsync($"https://ntfy.sh/{HttpUtility.UrlEncode(topicId)}/json"); + + using (StreamReader reader = new StreamReader(stream)) + { + subscribedTopics.Add(topicId, reader); + + try + { + // The loop will be broken when this stream is closed + while (true) + { + var line = await reader.ReadLineAsync(); + + Debug.WriteLine(line); + + NtfyEventObject nev = JsonConvert.DeserializeObject(line); + + if (nev.Event == "message") + { + if (OnNotificationReceive != null) + { + var evArgs = new NotificationReceiveEventArgs(nev.Title, nev.Message); + OnNotificationReceive(this, evArgs); + } + } + } + } catch(Exception ex) + { + Debug.WriteLine(ex); + + // If the topic is still registered, then that stream wasn't mean to be closed (maybe network failure?) + // Restart it + if (subscribedTopics.ContainsKey(topicId)) + { + SubscribeToTopic(topicId); + } + } + } + } + + public void RemoveTopic(string topicId) + { + Debug.WriteLine($"Removing topic {topicId}"); + + if (subscribedTopics.ContainsKey(topicId)) + { + // Not moronic to store it in a variable; this solves a race condition in SubscribeToTopic + var topic = subscribedTopics[topicId]; + subscribedTopics.Remove(topicId); + topic.Close(); + } + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~NotificationListener() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } + + public class NotificationReceiveEventArgs : EventArgs + { + public string Title { get; private set; } + public string Message { get; private set; } + + public NotificationReceiveEventArgs(string title, string message) + { + Title = title; + Message = message; + } + } + + public class NtfyEventObject + { + [JsonProperty("id")] + public string Id { get; set; } + [JsonProperty("time")] + public Int64 Time { get; set; } + [JsonProperty("event")] + public string Event { get; set; } + [JsonProperty("topic")] + public string Topic { get; set; } + [JsonProperty("message")] + public string Message { get; set; } + [JsonProperty("title")] + public string Title { get; set; } + } +} diff --git a/ntfysh_client/Program.cs b/ntfysh_client/Program.cs new file mode 100644 index 0000000..d9332fa --- /dev/null +++ b/ntfysh_client/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace ntfysh_client +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form1()); + } + } +} diff --git a/ntfysh_client/Properties/AssemblyInfo.cs b/ntfysh_client/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..07fb348 --- /dev/null +++ b/ntfysh_client/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ntfy.sh")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ntfy.sh")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5a18d152-d620-43fe-b844-def30cfa50ef")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ntfysh_client/Properties/Resources.Designer.cs b/ntfysh_client/Properties/Resources.Designer.cs new file mode 100644 index 0000000..8cf2c22 --- /dev/null +++ b/ntfysh_client/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ntfysh_client.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ntfysh_client.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/ntfysh_client/Properties/Resources.resx b/ntfysh_client/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/ntfysh_client/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ntfysh_client/Properties/Settings.Designer.cs b/ntfysh_client/Properties/Settings.Designer.cs new file mode 100644 index 0000000..67b3c60 --- /dev/null +++ b/ntfysh_client/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ntfysh_client.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/ntfysh_client/Properties/Settings.settings b/ntfysh_client/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/ntfysh_client/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ntfysh_client/SubscribeDialog.Designer.cs b/ntfysh_client/SubscribeDialog.Designer.cs new file mode 100644 index 0000000..e7d369e --- /dev/null +++ b/ntfysh_client/SubscribeDialog.Designer.cs @@ -0,0 +1,125 @@ + +namespace ntfysh_client +{ + partial class SubscribeDialog + { + /// + /// 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.panel1 = new System.Windows.Forms.Panel(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.topicId = new System.Windows.Forms.TextBox(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.Control; + this.panel1.Controls.Add(this.button2); + this.panel1.Controls.Add(this.button1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panel1.Location = new System.Drawing.Point(0, 81); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(297, 44); + this.panel1.TabIndex = 0; + // + // button1 + // + this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.button1.Location = new System.Drawing.Point(212, 11); + this.button1.Margin = new System.Windows.Forms.Padding(3, 10, 10, 10); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 2; + this.button1.Text = "Subscribe"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // button2 + // + this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.button2.Location = new System.Drawing.Point(131, 11); + this.button2.Margin = new System.Windows.Forms.Padding(10, 10, 3, 10); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(75, 23); + this.button2.TabIndex = 1; + this.button2.Text = "Cancel"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 23); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(51, 13); + this.label1.TabIndex = 1; + this.label1.Text = "Topic ID:"; + // + // topicId + // + this.topicId.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.topicId.Location = new System.Drawing.Point(15, 39); + this.topicId.Name = "topicId"; + this.topicId.Size = new System.Drawing.Size(273, 20); + this.topicId.TabIndex = 0; + this.topicId.KeyDown += new System.Windows.Forms.KeyEventHandler(this.topicId_KeyDown); + // + // SubscribeDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.White; + this.ClientSize = new System.Drawing.Size(297, 125); + this.Controls.Add(this.topicId); + this.Controls.Add(this.label1); + this.Controls.Add(this.panel1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "SubscribeDialog"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Subscribe to new topic"; + this.panel1.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox topicId; + } +} \ No newline at end of file diff --git a/ntfysh_client/SubscribeDialog.cs b/ntfysh_client/SubscribeDialog.cs new file mode 100644 index 0000000..7e4ded3 --- /dev/null +++ b/ntfysh_client/SubscribeDialog.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace ntfysh_client +{ + public partial class SubscribeDialog : Form + { + public SubscribeDialog() + { + InitializeComponent(); + } + + public string getTopicId() + { + return topicId.Text; + } + + private void button1_Click(object sender, EventArgs e) + { + if (topicId.Text.Length < 1) + { + MessageBox.Show("You must specify a topic name.", "Topic name not specified", MessageBoxButtons.OK, MessageBoxIcon.Error); + DialogResult = DialogResult.None; + topicId.Focus(); + return; + } + + DialogResult = DialogResult.OK; + } + + private void button2_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Cancel; + } + + private void topicId_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyData == Keys.Enter) + { + button1.PerformClick(); + e.SuppressKeyPress = true; + } + } + } +} diff --git a/ntfysh_client/SubscribeDialog.resx b/ntfysh_client/SubscribeDialog.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/ntfysh_client/SubscribeDialog.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/ntfysh_client/ntfysh_client.csproj b/ntfysh_client/ntfysh_client.csproj new file mode 100644 index 0000000..57cceb8 --- /dev/null +++ b/ntfysh_client/ntfysh_client.csproj @@ -0,0 +1,108 @@ + + + + + Debug + AnyCPU + {5A18D152-D620-43FE-B844-DEF30CFA50EF} + WinExe + ntfysh_client + ntfysh + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + Form + + + AboutBox.cs + + + Form + + + Form1.cs + + + + + + Form + + + SubscribeDialog.cs + + + AboutBox.cs + + + Form1.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + SubscribeDialog.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/ntfysh_client/packages.config b/ntfysh_client/packages.config new file mode 100644 index 0000000..5eaa239 --- /dev/null +++ b/ntfysh_client/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file