Winify – Diff between revs 64 and 66
?pathlinks?
Rev 64 | Rev 66 | |||
---|---|---|---|---|
Line 1... | Line 1... | |||
1 | using System; |
1 | using System; |
|
- | 2 | using System.Collections.Concurrent; |
||
2 | using System.Collections.Generic; |
3 | using System.Collections.Generic; |
|
3 | using System.Diagnostics; |
4 | using System.Diagnostics; |
|
4 | using System.Drawing; |
5 | using System.Drawing; |
|
5 | using System.Globalization; |
6 | using System.Globalization; |
|
6 | using System.IO; |
7 | using System.IO; |
|
Line 16... | Line 17... | |||
16 | using System.Threading; |
17 | using System.Threading; |
|
17 | using System.Threading.Tasks; |
18 | using System.Threading.Tasks; |
|
18 | using System.Threading.Tasks.Dataflow; |
19 | using System.Threading.Tasks.Dataflow; |
|
19 | using System.Windows.Forms; |
20 | using System.Windows.Forms; |
|
20 | using Newtonsoft.Json; |
21 | using Newtonsoft.Json; |
|
- | 22 | using Org.BouncyCastle.Asn1.Pkcs; |
||
21 | using Serilog; |
23 | using Serilog; |
|
22 | using Servers; |
24 | using Servers; |
|
23 | using WebSocketSharp; |
25 | using WebSocketSharp; |
|
24 | using WebSocketSharp.Net; |
26 | using WebSocketSharp.Net; |
|
25 | using Winify.Utilities; |
27 | using Winify.Utilities; |
|
Line 42... | Line 44... | |||
42 | |
44 | |
|
Line 43... | Line 45... | |||
43 | private CancellationToken _cancellationToken; |
45 | private CancellationToken _cancellationToken; |
|
Line 44... | Line 46... | |||
44 | |
46 | |
|
Line 45... | Line 47... | |||
45 | private CancellationTokenSource _cancellationTokenSource; |
47 | private CancellationTokenSource _cancellationTokenSource; |
|
Line 46... | Line 48... | |||
46 | |
48 | |
|
Line 47... | Line 49... | |||
47 | private Task _runTask; |
49 | private Task _heartBeatTask; |
|
48 | |
50 | |
|
49 | private HttpClient _httpClient; |
51 | private HttpClient _httpClient; |
|
50 | |
- | ||
51 | private readonly Uri _webSocketsUri; |
52 | |
|
52 | |
53 | private readonly Uri _webSocketsUri; |
|
53 | private readonly Uri _httpUri; |
54 | |
|
54 | private WebSocket _webSocketSharp; |
55 | private readonly Uri _httpUri; |
|
55 | private readonly Configuration.Configuration _configuration; |
56 | private WebSocket _webSocketSharp; |
|
56 | private Task _initTask; |
57 | private readonly Configuration.Configuration _configuration; |
|
57 | private IDisposable _tplRetrievePastMessagesLink; |
58 | private IDisposable _tplRetrievePastMessagesLink; |
|
- | 59 | private IDisposable _tplWebSocketsBufferBlockTransformLink; |
||
- | 60 | private IDisposable _tplWebSocketsTransformActionLink; |
||
- | 61 | private IDisposable _tplWebSocketsTransformActionNullLink; |
||
Line 58... | Line -... | |||
58 | private IDisposable _tplWebSocketsBufferBlockTransformLink; |
- | ||
59 | private IDisposable _tplWebSocketsTransformActionLink; |
62 | private readonly BufferBlock<GotifyConnectionData> _webSocketMessageBufferBlock; |
|
Line 60... | Line 63... | |||
60 | private IDisposable _tplWebSocketsTransformActionNullLink; |
63 | private readonly Stopwatch _webSocketsClientPingStopWatch; |
|
Line 61... | Line 64... | |||
61 | private readonly BufferBlock<byte[]> _webSocketMessageBufferBlock; |
64 | private readonly ScheduledContinuation _webSocketsServerResponseScheduledContinuation; |
|
62 | private readonly Stopwatch _webSocketsClientPingStopWatch; |
65 | |
|
63 | private readonly ScheduledContinuation _webSocketsServerResponseScheduledContinuation; |
66 | private Task _retrievePastMessagesTask; |
|
64 | |
67 | private static JsonSerializer _jsonSerializer; |
|
65 | private readonly MemoryCache _applicationImageCache; |
68 | |
|
Line 66... | Line 69... | |||
66 | #endregion |
69 | #endregion |
|
- | 70 | |
||
- | 71 | #region Constructors, Destructors and Finalizers |
||
- | 72 | |
||
- | 73 | private GotifyConnection() |
||
- | 74 | { |
||
67 | |
75 | _jsonSerializer = new JsonSerializer(); |
|
68 | #region Constructors, Destructors and Finalizers |
76 | _webSocketsServerResponseScheduledContinuation = new ScheduledContinuation(); |
|
69 | |
77 | _webSocketsClientPingStopWatch = new Stopwatch(); |
|
70 | private GotifyConnection() |
78 | |
|
71 | { |
79 | _webSocketMessageBufferBlock = new BufferBlock<GotifyConnectionData>( |
|
72 | _applicationImageCache = new MemoryCache("GotifyApplicationImageCache"); |
80 | new DataflowBlockOptions |
|
Line 73... | Line -... | |||
73 | _webSocketsServerResponseScheduledContinuation = new ScheduledContinuation(); |
- | ||
74 | _webSocketsClientPingStopWatch = new Stopwatch(); |
- | ||
75 | |
81 | { |
|
Line 76... | Line 82... | |||
76 | _webSocketMessageBufferBlock = new BufferBlock<byte[]>(new DataflowBlockOptions { CancellationToken = _cancellationToken }); |
82 | CancellationToken = _cancellationToken |
|
77 | var webSocketTransformBlock = new TransformBlock<byte[], GotifyMessage>(bytes => |
83 | }); |
|
- | 84 | |
||
- | 85 | var webSocketTransformBlock = new TransformBlock<GotifyConnectionData, GotifyMessage>(data => |
||
78 | { |
86 | { |
|
79 | if (bytes.Length == 0) |
- | ||
80 | { |
- | ||
81 | return null; |
- | ||
82 | } |
- | ||
Line -... | Line 87... | |||
- | 87 | if (data.Payload == null || data.Payload.Length == 0) |
||
83 | |
88 | { |
|
- | 89 | return null; |
||
84 | var message = Encoding.UTF8.GetString(bytes, 0, bytes.Length); |
90 | } |
|
Line 85... | Line 91... | |||
85 | |
91 | |
|
- | 92 | GotifyMessage gotifyNotification; |
||
- | 93 | |
||
86 | GotifyMessage gotifyNotification; |
94 | try |
|
87 | |
95 | { |
|
Line 88... | Line 96... | |||
88 | try |
96 | var message = Encoding.UTF8.GetString(data.Payload, 0, data.Payload.Length); |
|
89 | { |
97 | |
|
Line 90... | Line 98... | |||
90 | gotifyNotification = JsonConvert.DeserializeObject<GotifyMessage>(message); |
98 | gotifyNotification = JsonConvert.DeserializeObject<GotifyMessage>(message); |
|
Line 91... | Line 99... | |||
91 | } |
99 | |
|
Line 92... | Line 100... | |||
92 | catch (JsonSerializationException exception) |
100 | if (gotifyNotification == null) |
|
93 | { |
101 | { |
|
94 | Log.Warning($"Could not deserialize notification: {exception.Message}"); |
- | ||
95 | |
- | ||
96 | return null; |
102 | throw new ArgumentNullException(); |
|
97 | } |
103 | } |
|
98 | |
104 | |
|
99 | if (gotifyNotification == null) |
- | ||
100 | { |
- | ||
101 | Log.Warning($"Could not deserialize gotify notification: {message}"); |
- | ||
102 | |
- | ||
103 | return null; |
- | ||
104 | } |
105 | gotifyNotification.Server = data.Server; |
|
Line 105... | Line 106... | |||
105 | |
106 | } |
|
106 | return gotifyNotification; |
107 | catch (Exception exception) |
|
Line 107... | Line -... | |||
107 | |
- | ||
108 | }, new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken }); |
- | ||
109 | |
- | ||
110 | var webSocketActionBlock = new ActionBlock<GotifyMessage>(async message => |
- | ||
111 | { |
- | ||
112 | message.Server = _server; |
- | ||
113 | |
- | ||
114 | var cachedImage = _applicationImageCache.Get($"{message.AppId}"); |
- | ||
115 | if (cachedImage is Stream cachedImageStream) |
- | ||
116 | { |
- | ||
117 | using var cachedImageMemoryStream = new MemoryStream(); |
- | ||
118 | await cachedImageStream.CopyToAsync(cachedImageMemoryStream); |
108 | { |
|
Line 119... | Line -... | |||
119 | |
- | ||
120 | var cachedApplicationImage = new Bitmap(cachedImageMemoryStream); |
- | ||
121 | GotifyNotification?.Invoke(this, |
- | ||
122 | new GotifyNotificationEventArgs(message, cachedApplicationImage)); |
- | ||
123 | |
- | ||
124 | return; |
- | ||
125 | } |
- | ||
126 | |
- | ||
127 | using var imageStream = await RetrieveGotifyApplicationImage(message.AppId, _cancellationToken); |
109 | Log.Warning(exception, "Could not deserialize notification."); |
|
128 | if (imageStream == null || imageStream.Length == 0) |
110 | |
|
Line 129... | Line 111... | |||
129 | { |
111 | return null; |
|
Line 130... | Line 112... | |||
130 | Log.Warning("Could not find any application image for notification"); |
112 | } |
|
Line 202... | Line 184... | |||
202 | case "HTTPS": |
184 | case "HTTPS": |
|
203 | webSocketsUriBuilder.Scheme = "wss"; |
185 | webSocketsUriBuilder.Scheme = "wss"; |
|
204 | break; |
186 | break; |
|
205 | } |
187 | } |
|
Line 206... | Line -... | |||
206 | |
- | ||
207 | try |
- | ||
208 | { |
188 | |
|
209 | webSocketsUriBuilder.Path = Path.Combine(webSocketsUriBuilder.Path, "stream"); |
- | ||
210 | } |
- | ||
211 | catch (ArgumentException exception) |
189 | if (!Uri.TryCreate(webSocketsUriBuilder.Uri, "stream", out var combinedUri)) |
|
212 | { |
- | ||
213 | Log.Error( |
190 | { |
|
214 | $"No WebSockets URL could be built from the provided URL {_server.Url} due to {exception.Message}"); |
191 | Log.Error($"No WebSockets URL could be built from the provided URL {_server.Url}."); |
|
Line 215... | Line 192... | |||
215 | } |
192 | } |
|
216 | |
193 | |
|
Line 217... | Line 194... | |||
217 | _webSocketsUri = webSocketsUriBuilder.Uri; |
194 | _webSocketsUri = combinedUri; |
|
218 | } |
195 | } |
|
219 | |
196 | |
|
Line 275... | Line 252... | |||
275 | } |
252 | } |
|
Line 276... | Line 253... | |||
276 | |
253 | |
|
277 | _cancellationTokenSource = new CancellationTokenSource(); |
254 | _cancellationTokenSource = new CancellationTokenSource(); |
|
Line 278... | Line 255... | |||
278 | _cancellationToken = _cancellationTokenSource.Token; |
255 | _cancellationToken = _cancellationTokenSource.Token; |
|
279 | |
- | ||
280 | Connect(); |
- | ||
281 | |
256 | |
|
282 | if (_configuration.RetrievePastNotificationHours != 0) |
257 | if (_webSocketSharp != null) |
|
283 | { |
258 | { |
|
Line 284... | Line -... | |||
284 | _initTask = RetrievePastMessages(_cancellationToken); |
- | ||
285 | } |
- | ||
286 | |
- | ||
287 | _runTask = HeartBeat(_cancellationToken); |
- | ||
288 | } |
- | ||
289 | |
259 | return; |
|
290 | private void Connect() |
260 | } |
|
291 | { |
261 | |
|
292 | _webSocketSharp = new WebSocket(_webSocketsUri.AbsoluteUri); |
262 | _webSocketSharp = new WebSocket(_webSocketsUri.AbsoluteUri); |
|
293 | _webSocketSharp.EmitOnPing = true; |
263 | _webSocketSharp.EmitOnPing = true; |
|
Line 313... | Line 283... | |||
313 | _webSocketSharp.OnMessage += WebSocketSharp_OnMessage; |
283 | _webSocketSharp.OnMessage += WebSocketSharp_OnMessage; |
|
314 | _webSocketSharp.OnError += WebSocketSharp_OnError; |
284 | _webSocketSharp.OnError += WebSocketSharp_OnError; |
|
315 | _webSocketSharp.OnOpen += WebSocketSharp_OnOpen; |
285 | _webSocketSharp.OnOpen += WebSocketSharp_OnOpen; |
|
316 | _webSocketSharp.OnClose += WebSocketSharp_OnClose; |
286 | _webSocketSharp.OnClose += WebSocketSharp_OnClose; |
|
Line 317... | Line 287... | |||
317 | |
287 | |
|
- | 288 | _webSocketSharp.Connect(); |
||
- | 289 | _heartBeatTask = HeartBeat(_cancellationToken); |
||
- | 290 | |
||
- | 291 | if (_configuration.RetrievePastNotificationHours != 0) |
||
- | 292 | { |
||
- | 293 | _retrievePastMessagesTask = RetrievePastMessages(_cancellationToken); |
||
318 | _webSocketSharp.ConnectAsync(); |
294 | } |
|
Line 319... | Line 295... | |||
319 | } |
295 | } |
|
320 | |
296 | |
|
321 | private void WebSocketSharp_OnClose(object sender, CloseEventArgs e) |
297 | private void WebSocketSharp_OnClose(object sender, CloseEventArgs e) |
|
Line 340... | Line 316... | |||
340 | { |
316 | { |
|
341 | Log.Error( |
317 | Log.Error( |
|
342 | $"Connection to WebSockets server {_webSocketsUri.AbsoluteUri} terminated unexpectedly with message {e.Message}", |
318 | $"Connection to WebSockets server {_webSocketsUri.AbsoluteUri} terminated unexpectedly with message {e.Message}", |
|
343 | e.Exception); |
319 | e.Exception); |
|
Line 344... | Line -... | |||
344 | |
- | ||
345 | if (_cancellationToken.IsCancellationRequested) |
- | ||
346 | { |
- | ||
347 | Stop(); |
- | ||
348 | return; |
- | ||
349 | } |
- | ||
350 | |
320 | |
|
Line 351... | Line 321... | |||
351 | await Task.Delay(TimeSpan.FromSeconds(1), _cancellationToken); |
321 | await Task.Delay(TimeSpan.FromSeconds(1), _cancellationToken); |
|
Line 352... | Line 322... | |||
352 | |
322 | |
|
353 | Log.Information($"Reconnecting to websocket server {_webSocketsUri.AbsoluteUri}"); |
323 | Log.Information($"Reconnecting to websocket server {_webSocketsUri.AbsoluteUri}"); |
|
Line 354... | Line 324... | |||
354 | |
324 | |
|
355 | Connect(); |
325 | await Stop().ContinueWith(task => Start(), CancellationToken.None); |
|
356 | } |
326 | } |
|
357 | |
327 | |
|
358 | private async void WebSocketSharp_OnMessage(object sender, MessageEventArgs e) |
328 | private async void WebSocketSharp_OnMessage(object sender, MessageEventArgs e) |
|
Line 359... | Line 329... | |||
359 | { |
329 | { |
|
360 | if (e.IsPing) |
- | ||
361 | { |
330 | if (e.IsPing) |
|
362 | Log.Information($"Server {_server} sent PING message"); |
331 | { |
|
Line 363... | Line 332... | |||
363 | |
332 | Log.Information($"Server {_server} sent PING message"); |
|
364 | _webSocketsServerResponseScheduledContinuation.Schedule(TimeSpan.FromMinutes(1), OnUnresponsiveServer, _cancellationToken); |
333 | |
|
Line 365... | Line 334... | |||
365 | |
334 | _webSocketsServerResponseScheduledContinuation.Schedule(TimeSpan.FromMinutes(1), OnUnresponsiveServer, _cancellationToken); |
|
366 | return; |
335 | return; |
|
- | 336 | } |
||
367 | } |
337 | |
|
- | 338 | await _webSocketMessageBufferBlock.SendAsync(new GotifyConnectionData(e.RawData, _server), _cancellationToken); |
||
- | 339 | } |
||
- | 340 | |
||
Line 368... | Line 341... | |||
368 | |
341 | public async Task Stop() |
|
- | 342 | { |
||
- | 343 | |
||
- | 344 | if (_webSocketSharp == null || _cancellationTokenSource == null) |
||
- | 345 | { |
||
- | 346 | return; |
||
- | 347 | } |
||
- | 348 | |
||
- | 349 | _cancellationTokenSource.Cancel(); |
||
- | 350 | |
||
- | 351 | await _heartBeatTask; |
||
- | 352 | await _retrievePastMessagesTask; |
||
- | 353 | |
||
- | 354 | if (_webSocketSharp != null) |
||
- | 355 | { |
||
- | 356 | _webSocketSharp.OnMessage -= WebSocketSharp_OnMessage; |
||
- | 357 | _webSocketSharp.OnError -= WebSocketSharp_OnError; |
||
- | 358 | _webSocketSharp.OnOpen -= WebSocketSharp_OnOpen; |
||
369 | await _webSocketMessageBufferBlock.SendAsync(e.RawData, _cancellationToken); |
359 | _webSocketSharp.OnClose -= WebSocketSharp_OnClose; |
|
Line 370... | Line 360... | |||
370 | } |
360 | |
|
Line 371... | Line 361... | |||
371 | |
361 | _webSocketSharp.Close(); |
|
Line 372... | Line 362... | |||
372 | public void Stop() |
362 | _webSocketSharp = null; |
|
373 | { |
363 | } |
|
374 | if (_cancellationTokenSource == null) return; |
- | ||
375 | |
- | ||
376 | _cancellationTokenSource.Cancel(); |
364 | |
|
377 | } |
365 | _cancellationTokenSource.Dispose(); |
|
378 | |
366 | _cancellationTokenSource = null; |
|
379 | #endregion |
- | ||
380 | |
- | ||
381 | #region Private Methods |
367 | } |
|
382 | |
- | ||
383 | private async Task RetrievePastMessages(CancellationToken cancellationToken) |
- | ||
384 | { |
- | ||
385 | var messageUriBuilder = new UriBuilder(_httpUri); |
368 | |
|
386 | |
369 | #endregion |
|
Line 387... | Line 370... | |||
387 | var gotifyApplicationBufferBlock = new BufferBlock<GotifyApplication>(new DataflowBlockOptions { CancellationToken = cancellationToken }); |
370 | |
|
388 | var gotifyApplicationActionBlock = new ActionBlock<GotifyApplication>(async application => |
371 | #region Private Methods |
|
Line 389... | Line 372... | |||
389 | { |
372 | |
|
390 | try |
373 | private async Task RetrievePastMessages(CancellationToken cancellationToken) |
|
391 | { |
374 | { |
|
392 | messageUriBuilder.Path = Path.Combine(messageUriBuilder.Path, "application", $"{application.Id}", |
375 | var gotifyApplicationBufferBlock = new BufferBlock<GotifyConnectionApplication>(new DataflowBlockOptions { CancellationToken = cancellationToken }); |
|
- | 376 | var gotifyApplicationActionBlock = new ActionBlock<GotifyConnectionApplication>(async gotifyConnectionApplication => |
||
- | 377 | { |
||
- | 378 | if (!Uri.TryCreate(_httpUri, $"application/{gotifyConnectionApplication.Application.Id}/message", out var combinedUri)) |
||
- | 379 | { |
||
393 | "message"); |
380 | Log.Error($"Could not get application message Uri {gotifyConnectionApplication.Application.Id}."); |
|
394 | } |
381 | |
|
395 | catch (ArgumentException exception) |
382 | return; |
|
396 | { |
383 | } |
|
Line 397... | Line 384... | |||
397 | Log.Error($"No application URL could be built for {_server.Url} due to {exception.Message}"); |
384 | |
|
398 | |
385 | GotifyMessageQuery gotifyMessageQuery; |
|
399 | return; |
- | ||
400 | } |
- | ||
401 | |
- | ||
402 | HttpResponseMessage messagesResponse; |
- | ||
403 | try |
- | ||
404 | { |
- | ||
405 | messagesResponse = await _httpClient.GetAsync(messageUriBuilder.Uri, cancellationToken); |
- | ||
406 | } |
- | ||
407 | catch (Exception exception) |
- | ||
408 | { |
- | ||
409 | Log.Error($"Could not get application {application.Id} due to {exception.Message}"); |
386 | try |
|
410 | |
387 | { |
|
411 | return; |
388 | using var messageStream = await _httpClient.GetStreamAsync(combinedUri); |
|
Line 412... | Line 389... | |||
412 | } |
389 | using var streamReader = new StreamReader(messageStream); |
|
413 | |
390 | using var jsonTextReader = new JsonTextReader(streamReader); |
|
Line 414... | Line 391... | |||
414 | |
391 | |
|
415 | var messages = await messagesResponse.Content.ReadAsStringAsync(); |
392 | gotifyMessageQuery = _jsonSerializer.Deserialize<GotifyMessageQuery>(jsonTextReader); |
|
416 | |
- | ||
417 | GotifyMessageQuery gotifyMessageQuery; |
- | ||
418 | try |
393 | } |
|
419 | { |
- | ||
420 | gotifyMessageQuery = |
- | ||
421 | JsonConvert.DeserializeObject<GotifyMessageQuery>(messages); |
- | ||
422 | } |
- | ||
Line 423... | Line 394... | |||
423 | catch (JsonSerializationException exception) |
394 | catch (HttpRequestException exception) |
|
424 | { |
395 | { |
|
425 | Log.Warning($"Could not deserialize the message response: {exception.Message}"); |
- | ||
Line -... | Line 396... | |||
- | 396 | Log.Warning(exception, $"Could not get application {gotifyConnectionApplication.Application.Id}."); |
||
- | 397 | |
||
- | 398 | return; |
||
- | 399 | } |
||
426 | |
400 | catch (JsonSerializationException exception) |
|
427 | return; |
401 | { |
|
428 | } |
402 | Log.Warning(exception,$"Could not deserialize the message response for application {gotifyConnectionApplication.Application.Id}."); |
|
429 | |
403 | |
|
430 | foreach (var message in gotifyMessageQuery.Messages.Where(message => message.Date >= DateTime.Now - TimeSpan.FromHours(_configuration.RetrievePastNotificationHours))) |
- | ||
431 | { |
404 | return; |
|
432 | message.Server = _server; |
405 | } |
|
433 | |
406 | |
|
- | 407 | if (gotifyMessageQuery == null || gotifyMessageQuery.Messages == null) |
||
434 | var cachedImage = _applicationImageCache.Get($"{message.AppId}"); |
408 | { |
|
435 | if (cachedImage is Stream cachedImageStream) |
409 | Log.Warning("Invalid application messages deserialized deserialized."); |
|
Line 436... | Line -... | |||
436 | { |
- | ||
437 | using var cachedImageMemoryStream = new MemoryStream(); |
- | ||
438 | await cachedImageStream.CopyToAsync(cachedImageMemoryStream); |
- | ||
439 | |
- | ||
440 | var cachedApplicationImage = new Bitmap(cachedImageMemoryStream); |
410 | |
|
441 | GotifyNotification?.Invoke(this, |
- | ||
442 | new GotifyNotificationEventArgs(message, cachedApplicationImage)); |
- | ||
443 | |
- | ||
444 | return; |
- | ||
445 | } |
- | ||
446 | |
- | ||
447 | using var imageStream = await RetrieveGotifyApplicationImage(message.AppId, _cancellationToken); |
- | ||
448 | |
411 | return; |
|
Line 449... | Line 412... | |||
449 | if (imageStream == null || imageStream.Length == 0) |
412 | } |
|
450 | { |
413 | |
|
451 | Log.Warning("Could not find any application image for notification"); |
414 | foreach (var message in gotifyMessageQuery.Messages) |
|
Line 452... | Line 415... | |||
452 | continue; |
415 | { |
|
Line 453... | Line 416... | |||
453 | } |
416 | if (message.Date < DateTime.Now - TimeSpan.FromHours(_configuration.RetrievePastNotificationHours)) |
|
454 | |
417 | { |
|
Line -... | Line 418... | |||
- | 418 | continue; |
||
455 | using var memoryStream = new MemoryStream(); |
419 | } |
|
456 | |
420 | |
|
- | 421 | using var imageStream = await RetrieveGotifyApplicationImage(message.AppId, _cancellationToken); |
||
457 | await imageStream.CopyToAsync(memoryStream); |
422 | if (imageStream == null || imageStream.Length == 0) |
|
458 | |
423 | { |
|
Line -... | Line 424... | |||
- | 424 | Log.Warning("Could not find any application image for notification."); |
||
459 | var imageBytes = memoryStream.ToArray(); |
425 | |
|
460 | |
426 | continue; |
|
Line 461... | Line 427... | |||
461 | _applicationImageCache.Add($"{message.AppId}", imageBytes, |
427 | } |
|
Line 517... | Line 483... | |||
517 | } |
483 | } |
|
518 | } |
484 | } |
|
Line 519... | Line 485... | |||
519 | |
485 | |
|
520 | private async IAsyncEnumerable<GotifyApplication> RetrieveGotifyApplications([EnumeratorCancellation] CancellationToken cancellationToken) |
486 | private async IAsyncEnumerable<GotifyApplication> RetrieveGotifyApplications([EnumeratorCancellation] CancellationToken cancellationToken) |
|
521 | { |
487 | { |
|
522 | var applicationsUriBuilder = new UriBuilder(_httpUri); |
- | ||
523 | try |
- | ||
524 | { |
- | ||
525 | applicationsUriBuilder.Path = Path.Combine(applicationsUriBuilder.Path, "application"); |
- | ||
526 | } |
- | ||
527 | catch (ArgumentException exception) |
488 | if (!Uri.TryCreate(_httpUri, "application", out var combinedUri)) |
|
528 | { |
489 | { |
|
Line 529... | Line 490... | |||
529 | Log.Error($"No application URL could be built for {_server.Url} due to {exception}"); |
490 | Log.Error($"No application URL could be built for {_server.Url}."); |
|
530 | |
491 | |
|
Line 531... | Line 492... | |||
531 | yield break; |
492 | yield break; |
|
532 | } |
- | ||
533 | |
493 | } |
|
534 | HttpResponseMessage applicationsResponse; |
494 | |
|
535 | |
495 | GotifyApplication[] gotifyApplications; |
|
536 | try |
- | ||
537 | { |
496 | try |
|
538 | applicationsResponse = await _httpClient.GetAsync(applicationsUriBuilder.Uri, cancellationToken); |
- | ||
539 | } |
497 | { |
|
Line 540... | Line 498... | |||
540 | catch (Exception exception) |
498 | using var messageStream = await _httpClient.GetStreamAsync(combinedUri); |
|
541 | { |
- | ||
Line 542... | Line -... | |||
542 | Log.Error($"Could not retrieve applications: {exception.Message}"); |
- | ||
543 | |
- | ||
544 | yield break; |
499 | using var streamReader = new StreamReader(messageStream); |
|
545 | } |
500 | using var jsonTextReader = new JsonTextReader(streamReader); |
|
546 | |
501 | |
|
547 | var applications = await applicationsResponse.Content.ReadAsStringAsync(); |
502 | gotifyApplications = _jsonSerializer.Deserialize<GotifyApplication[]>(jsonTextReader); |
|
548 | |
- | ||
549 | GotifyApplication[] gotifyApplications; |
503 | |
|
550 | try |
504 | if (gotifyApplications == null) |
|
551 | { |
505 | { |
|
552 | gotifyApplications = |
506 | throw new ArgumentNullException(); |
|
Line 553... | Line 507... | |||
553 | JsonConvert.DeserializeObject<GotifyApplication[]>(applications); |
507 | } |
|
554 | } |
508 | } |
|
Line 555... | Line 509... | |||
555 | catch (JsonSerializationException exception) |
509 | catch (Exception exception) |
|
Line 567... | Line 521... | |||
567 | |
521 | |
|
568 | private async Task<Stream> RetrieveGotifyApplicationImage(int appId, CancellationToken cancellationToken) |
522 | private async Task<Stream> RetrieveGotifyApplicationImage(int appId, CancellationToken cancellationToken) |
|
569 | { |
523 | { |
|
570 | await foreach (var application in RetrieveGotifyApplications(cancellationToken)) |
524 | await foreach (var application in RetrieveGotifyApplications(cancellationToken)) |
|
571 | { |
525 | { |
|
- | 526 | if (application.Id != appId) |
||
- | 527 | { |
||
- | 528 | continue; |
||
Line 572... | Line 529... | |||
572 | if (application.Id != appId) continue; |
529 | } |
|
573 | |
530 | |
|
574 | if (!Uri.TryCreate(Path.Combine($"{_httpUri}", $"{application.Image}"), UriKind.Absolute, out var applicationImageUri)) |
531 | if (!Uri.TryCreate(Path.Combine($"{_httpUri}", $"{application.Image}"), UriKind.Absolute, out var applicationImageUri)) |
|
- | 532 | { |
||
575 | { |
533 | Log.Warning("Could not build URL path to application icon"); |
|
576 | Log.Warning("Could not build URL path to application icon"); |
534 | |
|
Line 577... | Line -... | |||
577 | continue; |
- | ||
578 | } |
- | ||
579 | |
535 | continue; |
|
580 | HttpResponseMessage imageResponse; |
536 | } |
|
581 | |
537 | |
|
- | 538 | try |
||
- | 539 | { |
||
- | 540 | var imageResponse = await _httpClient.GetAsync(applicationImageUri, cancellationToken); |
||
- | 541 | |
||
- | 542 | var memoryStream = new MemoryStream(); |
||
- | 543 | |
||
582 | try |
544 | await imageResponse.Content.CopyToAsync(memoryStream); |
|
583 | { |
545 | |
|
584 | imageResponse = await _httpClient.GetAsync(applicationImageUri, cancellationToken); |
546 | return memoryStream; |
|
585 | } |
547 | } |
|
586 | catch (Exception exception) |
- | ||
587 | { |
- | ||
588 | Log.Error($"Could not retrieve application image: {exception.Message}"); |
548 | catch (Exception exception) |
|
- | 549 | { |
||
Line 589... | Line 550... | |||
589 | |
550 | Log.Error($"Could not retrieve application image: {exception.Message}"); |
|
- | 551 | } |
||
Line 590... | Line 552... | |||
590 | return new MemoryStream(); |
552 | } |
|
Line -... | Line 553... | |||
- | 553 | |
||
- | 554 | return new MemoryStream(); |
||
- | 555 | } |
||
591 | } |
556 | |
|
Line -... | Line 557... | |||
- | 557 | #endregion |
||
- | 558 | |
||
592 | |
559 | private class GotifyConnectionApplication |
|
- | 560 | { |
||
593 | var memoryStream = new MemoryStream(); |
561 | public GotifyApplication Application { get; } |
|
594 | |
- | ||
595 | await imageResponse.Content.CopyToAsync(memoryStream); |
- | ||
596 | |
562 | public Server Server { get; } |
|
Line -... | Line 563... | |||
- | 563 | |
||
- | 564 | public GotifyConnectionApplication(Server server, GotifyApplication application) |
||
- | 565 | { |
||
- | 566 | Server = server; |
||
- | 567 | Application = application; |
||
- | 568 | } |
||
597 | memoryStream.Position = 0L; |
569 | } |
|
- | 570 | |
||
- | 571 | private class GotifyConnectionData |
||
- | 572 | { |
||
- | 573 | public byte[] Payload { get; } |
||
598 | |
574 | public Server Server { get; } |
|
599 | return memoryStream; |
575 | |
|
600 | } |
576 | public GotifyConnectionData(byte[] payload, Server server) |