Winify – Blame information for rev 25
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | using System; |
2 | using System.Drawing; |
||
3 | using System.IO; |
||
4 | using System.Net.Http; |
||
5 | using System.Net.Http.Headers; |
||
6 | using System.Net.WebSockets; |
||
7 | using System.Text; |
||
8 | using System.Threading; |
||
9 | using System.Threading.Tasks; |
||
10 | using Newtonsoft.Json; |
||
18 | office | 11 | using Serilog; |
24 | office | 12 | using Servers; |
1 | office | 13 | using ClientWebSocket = System.Net.WebSockets.Managed.ClientWebSocket; |
14 | |||
15 | namespace Winify.Gotify |
||
16 | { |
||
17 | public class GotifyConnection : IDisposable |
||
18 | { |
||
19 | #region Public Events & Delegates |
||
20 | |||
21 | public event EventHandler<GotifyNotificationEventArgs> GotifyNotification; |
||
22 | |||
23 | #endregion |
||
24 | |||
25 | #region Private Delegates, Events, Enums, Properties, Indexers and Fields |
||
26 | |||
25 | office | 27 | private readonly Server _server; |
28 | |||
1 | office | 29 | private CancellationToken _cancellationToken; |
30 | |||
31 | private CancellationTokenSource _cancellationTokenSource; |
||
32 | |||
33 | private Task _runTask; |
||
34 | |||
25 | office | 35 | #endregion |
1 | office | 36 | |
25 | office | 37 | #region Constructors, Destructors and Finalizers |
24 | office | 38 | |
39 | public GotifyConnection(Server server) |
||
40 | { |
||
41 | _server = server; |
||
42 | } |
||
43 | |||
1 | office | 44 | public void Dispose() |
45 | { |
||
46 | if (_cancellationTokenSource != null) |
||
47 | { |
||
48 | _cancellationTokenSource.Dispose(); |
||
49 | _cancellationTokenSource = null; |
||
50 | } |
||
51 | } |
||
52 | |||
53 | #endregion |
||
54 | |||
55 | #region Public Methods |
||
56 | |||
25 | office | 57 | public void Start() |
1 | office | 58 | { |
25 | office | 59 | if (!Uri.TryCreate(_server.Url, UriKind.Absolute, out var httpUri)) |
1 | office | 60 | { |
61 | return; |
||
62 | } |
||
63 | |||
12 | office | 64 | // Build the web sockets URI. |
65 | var webSocketsUriBuilder = new UriBuilder(httpUri); |
||
66 | webSocketsUriBuilder.Scheme = "ws"; |
||
67 | webSocketsUriBuilder.Path = $"{webSocketsUriBuilder.Path}/stream"; |
||
68 | var webSocketsUri = webSocketsUriBuilder.Uri; |
||
1 | office | 69 | |
70 | _cancellationTokenSource = new CancellationTokenSource(); |
||
71 | _cancellationToken = _cancellationTokenSource.Token; |
||
72 | |||
25 | office | 73 | _runTask = Run(webSocketsUri, httpUri, _server.Username, _server.Password, _cancellationToken); |
1 | office | 74 | } |
75 | |||
76 | public void Stop() |
||
77 | { |
||
78 | if (_cancellationTokenSource != null) |
||
79 | { |
||
80 | _cancellationTokenSource.Cancel(); |
||
81 | } |
||
82 | } |
||
83 | |||
84 | #endregion |
||
85 | |||
86 | #region Private Methods |
||
87 | |||
12 | office | 88 | private async Task Run(Uri webSocketsUri, Uri httpUri, string username, string password, |
89 | CancellationToken cancellationToken) |
||
1 | office | 90 | { |
91 | try |
||
92 | { |
||
93 | do |
||
94 | { |
||
95 | try |
||
96 | { |
||
25 | office | 97 | using (var webSocketClient = new ClientWebSocket()) |
21 | office | 98 | { |
99 | var auth = Convert.ToBase64String(Encoding.Default.GetBytes($"{username}:{password}")); |
||
18 | office | 100 | |
25 | office | 101 | webSocketClient.Options.SetRequestHeader("Authorization", $"Basic {auth}"); |
18 | office | 102 | |
25 | office | 103 | await webSocketClient.ConnectAsync(webSocketsUri, cancellationToken); |
1 | office | 104 | |
21 | office | 105 | do |
1 | office | 106 | { |
21 | office | 107 | var payload = new ArraySegment<byte>(new byte[1024]); |
1 | office | 108 | |
25 | office | 109 | var result = await webSocketClient.ReceiveAsync(payload, cancellationToken); |
1 | office | 110 | |
21 | office | 111 | if (result.Count == 0) |
112 | { |
||
113 | continue; |
||
114 | } |
||
1 | office | 115 | |
21 | office | 116 | if (payload.Array == null || payload.Count == 0) |
117 | { |
||
118 | continue; |
||
119 | } |
||
1 | office | 120 | |
21 | office | 121 | var message = Encoding.UTF8.GetString(payload.Array, 0, payload.Count); |
12 | office | 122 | |
25 | office | 123 | if (!(JsonConvert.DeserializeObject<GotifyNotification>(message) is GotifyNotification |
124 | gotifyNotification)) |
||
1 | office | 125 | { |
25 | office | 126 | Log.Warning($"Could not deserialize gotify notification: {message}"); |
127 | |||
1 | office | 128 | continue; |
129 | } |
||
130 | |||
24 | office | 131 | gotifyNotification.Server = _server; |
132 | |||
25 | office | 133 | if (!Uri.TryCreate($"{httpUri}/application", UriKind.Absolute, |
134 | out var applicationUri)) |
||
12 | office | 135 | { |
136 | continue; |
||
137 | } |
||
3 | office | 138 | |
25 | office | 139 | var image = await RetrieveGotifyApplicationImage(gotifyNotification.AppId, httpUri, |
140 | applicationUri, auth, cancellationToken); |
||
12 | office | 141 | |
25 | office | 142 | GotifyNotification?.Invoke(this, |
143 | new GotifyNotificationEventArgs(gotifyNotification, image)); |
||
3 | office | 144 | |
21 | office | 145 | Log.Debug($"Notification message received: {gotifyNotification.Message}"); |
146 | } while (!cancellationToken.IsCancellationRequested); |
||
147 | } |
||
1 | office | 148 | } |
12 | office | 149 | catch (Exception ex) when (ex is WebSocketException || ex is HttpRequestException) |
1 | office | 150 | { |
25 | office | 151 | // Reconnect |
18 | office | 152 | Log.Warning($"Unable to connect to gotify server: {ex.Message}"); |
12 | office | 153 | |
1 | office | 154 | await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); |
155 | } |
||
156 | } while (!cancellationToken.IsCancellationRequested); |
||
157 | } |
||
158 | catch (Exception ex) when (ex is OperationCanceledException || ex is ObjectDisposedException) |
||
159 | { |
||
160 | } |
||
161 | catch (Exception ex) |
||
162 | { |
||
18 | office | 163 | Log.Warning(ex, "Failure running connection loop."); |
1 | office | 164 | } |
25 | office | 165 | |
166 | var e = "o"; |
||
1 | office | 167 | } |
168 | |||
25 | office | 169 | private static async Task<Image> RetrieveGotifyApplicationImage(int appId, Uri httpUri, Uri applicationUri, |
170 | string auth, |
||
171 | CancellationToken cancellationToken) |
||
172 | { |
||
173 | using (var httpClient = new HttpClient()) |
||
174 | { |
||
175 | httpClient.DefaultRequestHeaders.Authorization = |
||
176 | new AuthenticationHeaderValue("Basic", auth); |
||
177 | |||
178 | var applicationResponse = await httpClient.GetAsync(applicationUri, cancellationToken); |
||
179 | |||
180 | var applications = await applicationResponse.Content.ReadAsStringAsync(); |
||
181 | |||
182 | var gotifyApplications = |
||
183 | JsonConvert.DeserializeObject<GotifyApplication[]>(applications); |
||
184 | |||
185 | if (gotifyApplications == null) |
||
186 | { |
||
187 | return null; |
||
188 | } |
||
189 | |||
190 | foreach (var application in gotifyApplications) |
||
191 | { |
||
192 | if (application.Id != appId) |
||
193 | { |
||
194 | continue; |
||
195 | } |
||
196 | |||
197 | if (!Uri.TryCreate($"{httpUri}/{application.Image}", UriKind.Absolute, |
||
198 | out var applicationImageUri)) |
||
199 | { |
||
200 | continue; |
||
201 | } |
||
202 | |||
203 | var imageResponse = await httpClient.GetAsync(applicationImageUri, cancellationToken); |
||
204 | |||
205 | using (var memoryStream = new MemoryStream()) |
||
206 | { |
||
207 | await imageResponse.Content.CopyToAsync(memoryStream); |
||
208 | |||
209 | return Image.FromStream(memoryStream); |
||
210 | } |
||
211 | } |
||
212 | } |
||
213 | |||
214 | return null; |
||
215 | } |
||
216 | |||
1 | office | 217 | #endregion |
218 | } |
||
219 | } |