Winify – Rev 39

Subversion Repositories:
Rev:
using System;
using System.Drawing;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Serilog;
using Servers;
using ClientWebSocket = System.Net.WebSockets.Managed.ClientWebSocket;

namespace Winify.Gotify
{
    public class GotifyConnection : IDisposable
    {
        #region Public Events & Delegates

        public event EventHandler<GotifyNotificationEventArgs> GotifyNotification;

        #endregion

        #region Private Delegates, Events, Enums, Properties, Indexers and Fields

        private readonly Server _server;

        private CancellationToken _cancellationToken;

        private CancellationTokenSource _cancellationTokenSource;

        private Task _runTask;

        private HttpClient _httpClient;

        private readonly string _auth;

        private readonly Uri _webSocketsUri;

        private readonly Uri _httpUri;

        #endregion

        #region Constructors, Destructors and Finalizers

        public GotifyConnection()
        {
            _httpClient = new HttpClient();
        }

        public GotifyConnection(Server server) : this()
        {
            _server = server;

            _auth = Convert.ToBase64String(Encoding.Default.GetBytes($"{_server.Username}:{_server.Password}"));
            _httpClient.DefaultRequestHeaders.Authorization =
                new AuthenticationHeaderValue("Basic", _auth);

            if (Uri.TryCreate(_server.Url, UriKind.Absolute, out _httpUri))
            {
                // Build the web sockets URI.
                var webSocketsUriBuilder = new UriBuilder(_httpUri);
                webSocketsUriBuilder.Scheme = "ws";
                webSocketsUriBuilder.Path = Path.Combine(webSocketsUriBuilder.Path, "stream");
                _webSocketsUri = webSocketsUriBuilder.Uri;
            }
        }

        public void Dispose()
        {
            if (_cancellationTokenSource != null)
            {
                _cancellationTokenSource.Dispose();
                _cancellationTokenSource = null;
            }

            _httpClient.Dispose();
            _httpClient = null;
        }

        #endregion

        #region Public Methods

        public void Start()
        {
            _cancellationTokenSource = new CancellationTokenSource();
            _cancellationToken = _cancellationTokenSource.Token;

            _runTask = Run(_cancellationToken);
        }

        public void Stop()
        {
            if (_cancellationTokenSource != null)
            {
                _cancellationTokenSource.Cancel();
            }
        }

        #endregion

        #region Private Methods

        private async Task Run(CancellationToken cancellationToken)
        {
            try
            {
                do
                {
                    try
                    {
                        using (var webSocketClient = new ClientWebSocket())
                        {
                            webSocketClient.Options.SetRequestHeader("Authorization", $"Basic {_auth}");

                            await webSocketClient.ConnectAsync(_webSocketsUri, cancellationToken);

                            do
                            {
                                var payload = new ArraySegment<byte>(new byte[1024]);

                                var result = await webSocketClient.ReceiveAsync(payload, cancellationToken);

                                if (result.Count == 0) 
                                {
                                    continue;
                                }

                                if (payload.Array == null || payload.Count == 0) 
                                {
                                    continue;
                                }

                                var message = Encoding.UTF8.GetString(payload.Array, 0, payload.Count);

                                var gotifyNotification = JsonConvert.DeserializeObject<GotifyNotification>(message);

                                if (gotifyNotification == null)
                                {
                                    Log.Warning($"Could not deserialize gotify notification: {message}");

                                    continue;
                                }

                                gotifyNotification.Server = _server;

                                if (!Uri.TryCreate(Path.Combine($"{_httpUri}", "application"), UriKind.Absolute,
                                        out var applicationUri))
                                {
                                    continue;
                                }

                                var image = await RetrieveGotifyApplicationImage(gotifyNotification.AppId,
                                    applicationUri, cancellationToken);

                                GotifyNotification?.Invoke(this,
                                    new GotifyNotificationEventArgs(gotifyNotification, image));

                                Log.Debug($"Notification message received: {gotifyNotification.Message}");
                            } while (!cancellationToken.IsCancellationRequested);
                        }
                    }
                    catch (Exception ex) when (ex is WebSocketException || ex is HttpRequestException)
                    {
                        // Reconnect
                        Log.Warning($"Unable to connect to gotify server: {ex.Message}");

                        await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
                    }
                } while (!cancellationToken.IsCancellationRequested);
            }
            catch (Exception ex) when (ex is OperationCanceledException || ex is ObjectDisposedException)
            {
            }
            catch (Exception ex)
            {
                Log.Warning(ex, "Failure running connection loop.");
            }
        }

        private async Task<Image> RetrieveGotifyApplicationImage(int appId, Uri applicationUri,
            CancellationToken cancellationToken)
        {
            var applicationResponse = await _httpClient.GetAsync(applicationUri, cancellationToken);

            var applications = await applicationResponse.Content.ReadAsStringAsync();

            var gotifyApplications =
                JsonConvert.DeserializeObject<GotifyApplication[]>(applications);

            if (gotifyApplications == null)
            {
                return null;
            }

            foreach (var application in gotifyApplications)
            {
                if (application.Id != appId)
                {
                    continue;
                }

                if (!Uri.TryCreate(Path.Combine($"{_httpUri}", $"{application.Image}"), UriKind.Absolute,
                        out var applicationImageUri))
                {
                    continue;
                }

                var imageResponse = await _httpClient.GetAsync(applicationImageUri, cancellationToken);

                using (var memoryStream = new MemoryStream())
                {
                    await imageResponse.Content.CopyToAsync(memoryStream);

                    return Image.FromStream(memoryStream);
                }
            }

            return null;
        }

        #endregion
    }
}

Generated by GNU Enscript 1.6.5.90.