public DiscordHookMessage(DiscordBot bot, string eventType, JiraUser user, JiraIssue issue, JiraIssueIcons issueIcons, JiraComment comment)
            {
                username   = bot.Name;
                avatar_url = bot.AvatarUrl;
                List <DiscordAttachmentField> embedFields = null;

                if (!eventType.Contains("comment"))
                {
                    embedFields = new List <DiscordAttachmentField>()
                    {
                        new DiscordAttachmentField()
                        {
                            name  = "Summary",
                            value = issue.Summary
                        },

                        new DiscordAttachmentField()
                        {
                            name  = "Description",
                            value = issue.Description
                        },

                        new DiscordAttachmentField()
                        {
                            name   = "Assignee",
                            value  = issue.Assignee.Name,
                            inline = true
                        },

                        new DiscordAttachmentField()
                        {
                            name   = "Status",
                            value  = issue.Status,
                            inline = true
                        },

                        new DiscordAttachmentField()
                        {
                            name   = "Resolution",
                            value  = issue.Resolution,
                            inline = true
                        }
                    };
                }
                else
                {
                    if (comment.Contents == "" || comment.Id == "")
                    {
                        return;
                    }

                    embedFields = new List <DiscordAttachmentField>()
                    {
                        new DiscordAttachmentField()
                        {
                            name  = "Summary",
                            value = issue.Summary
                        },

                        new DiscordAttachmentField()
                        {
                            name  = "Comment",
                            value = comment.Contents
                        },

                        new DiscordAttachmentField()
                        {
                            name   = "Assignee",
                            value  = issue.Assignee.Name,
                            inline = true
                        },

                        new DiscordAttachmentField()
                        {
                            name   = "Status",
                            value  = issue.Status,
                            inline = true
                        },

                        new DiscordAttachmentField()
                        {
                            name   = "Resolution",
                            value  = issue.Resolution,
                            inline = true
                        }
                    };
                }

                if (embedFields == null)
                {
                    return;
                }

                // Set the color decimal value for the issue type - values taken from Jira's default icons
                int    embedColor;
                string footerText    = eventType.Replace("Issue", issue.Type);
                string footerIconUrl = "";

                switch (issue.Type)
                {
                case "Bug":
                    embedColor    = 15026490;                           // rgb: 229, 73, 58
                    footerIconUrl = issueIcons.BugIconURL;
                    break;

                case "Task":
                    embedColor    = 4959720;                            // rgb: 75, 173, 232
                    footerIconUrl = issueIcons.TaskIconURL;
                    break;

                case "Epic":
                    embedColor    = 9457378;                            // rgb: 144, 78, 226
                    footerIconUrl = issueIcons.EpicIconURL;
                    break;

                default:
                    embedColor = 8421504;                               // rgb: 128, 128, 128
                    break;
                }

                DiscordFooterField embedFooter = new DiscordFooterField()
                {
                    text     = footerText,
                    icon_url = footerIconUrl
                };

                embeds = new List <DiscordAttachment>()
                {
                    new DiscordAttachment()
                    {
                        author = new DiscordAuthorField()
                        {
                            name     = user.Name,
                            icon_url = user.AvatarUrl
                        },

                        color = embedColor,
                        title = eventType + " – (" + issue.Key + ") " + issue.Summary,
                        url   = issue.Url,
                        //description = eventType,

                        fields = embedFields,

                        footer    = embedFooter,
                        timestamp = issue.UpdatedTimestamp
                    }
                };
            }
        public void StartListening()
        {
            if (GetServerStatus() == ServerStatus.Running)
            {
                return;
            }
            Debug.WriteLine("In StartListening");

            SetServerStatus(ServerStatus.Running);

            while (true)
            {
                try
                {
                    listener = new HttpListener();
                    listener.Prefixes.Add(prefs.LocalIP);
                    listener.Start();
                }
                catch (Exception e)
                {
                    var msg = e.Message;
                    Trace.WriteLine(msg);
                }

                Trace.WriteLine("Listening...");

                if (stopListening)
                {
                    Debug.WriteLine("Time to stop listening.");
                    listener.Stop();
                    break;
                }

                HttpListenerContext context;
                HttpListenerRequest request;

                try
                {
                    context = listener.GetContext();
                    request = context.Request;
                }
                catch (HttpListenerException e)
                {
                    var msg = e.Message + "\n\n";
                    msg += "Either run this application as administrator, which you'll have to do every time it's started, or enter the following command (all in one line and without quotes) in an elevated command prompt, which you'll only have to do once: \n\n";
                    msg += "'netsh http add urlacl url=http://LOCAL_IP:PORT/ user=COMPUTER_NAME\\WIN_ACCOUNT_NAME'\n\n";
                    msg += "Change LOCAL_IP and PORT to the local IP and port to listen on, and COMPUTER_NAME and WIN_ACCOUNT_NAME to the computer name and Windows account username.";
                    Trace.WriteLine(msg);
                    MessageBox.Show(msg);
                    return;
                }
                catch (InvalidOperationException e)
                {
                    var msg = e.Message;
                    Trace.WriteLine(msg);
                    MessageBox.Show(msg);
                    return;
                }

                if (stopListening)
                {
                    Debug.WriteLine("Time to stop listening.");
                    listener.Stop();
                    break;
                }

                HttpListenerResponse response = context.Response;
                if (request.InputStream != null)
                {
                    using (var reader = new StreamReader(request.InputStream, Encoding.UTF8))
                    {
                        var     content   = reader.ReadToEnd();
                        dynamic json      = JObject.Parse(content);
                        var     eventType = "";
                        try
                        {
                            eventType = json.issue_event_type_name.Value;
                            eventType = eventType.Replace("_", " ");
                            eventType = eventType.First().ToString().ToUpper() + eventType.Substring(1);
                            Trace.WriteLine("Issue event type name: " + eventType);

                            if (eventType == "Issue generic")
                            {
                                eventType = "Issue moved to '" + json.issue.fields.status.name.Value + "'";
                            }
                        }
                        catch (Exception ex)
                        {
                            var msg = ex.Message;
                            Trace.WriteLine(msg);

                            eventType = json.webhookEvent.Value;
                            Trace.WriteLine("Webhook event: " + eventType);
                            return;
                        }

                        JiraUser user   = new JiraUser();
                        dynamic  author = null;
                        try
                        {
                            author    = json.user;
                            user.Name = json.user.displayName.Value;
                            //prefs.BotName = user.Name + " (JIRA)";
                        }
                        catch (Exception ex)
                        {
                            var msg = ex.Message;
                            Trace.WriteLine(msg);

                            author    = json.comment.author;
                            user.Name = json.comment.author.displayName.Value;
                            //prefs.BotName = user.Name + " (JIRA)";
                        }

                        JiraIssue   issue   = new JiraIssue();
                        JiraComment comment = new JiraComment();
                        try
                        {
                            comment.Contents = json.comment.body.Value;
                            comment.Id       = json.comment.id.Value;

                            issue.Key         = json.issue.key.Value;
                            issue.Url         = prefs.JiraBaseURL + issue.Key + "?focusedCommentId=" + comment.Id;
                            issue.Type        = json.issue.fields.issuetype.name.Value;
                            issue.Summary     = json.issue.fields.summary.Value;
                            issue.Description = json.issue.fields.description.Value;
                            issue.Status      = json.issue.fields.status.name.Value;
                            issue.Resolution  = json.issue.fields.resolution.Value;
                            if (issue.Resolution == null)
                            {
                                issue.Resolution = "Unresolved";
                            }
                            issue.Reporter = new JiraUser
                            {
                                Name      = json.issue.fields.reporter.displayName.Value,
                                AvatarUrl = ((json.issue.fields.reporter.avatarUrls as IEnumerable <object>).First() as dynamic).Value.Value
                            };
                            if (json.issue.fields.assignee == null)
                            {
                                issue.Assignee = new JiraUser {
                                    Name = "*Unassigned*"
                                };
                            }
                            else
                            {
                                issue.Assignee = new JiraUser
                                {
                                    Name      = json.issue.fields.assignee.displayName.Value,
                                    AvatarUrl = ((json.issue.fields.assignee.avatarUrls as IEnumerable <object>).First() as dynamic).Value.Value
                                };
                            }
                            issue.UpdatedTimestamp = json.issue.fields.updated.Value;
                        }
                        catch (Exception ex)
                        {
                            var msg = ex.Message;
                            Trace.WriteLine(msg);

                            issue.Key         = json.issue.key.Value;
                            issue.Url         = prefs.JiraBaseURL + issue.Key;
                            issue.Type        = json.issue.fields.issuetype.name.Value;
                            issue.Summary     = json.issue.fields.summary.Value;
                            issue.Description = json.issue.fields.description.Value;
                            issue.Status      = json.issue.fields.status.name.Value;
                            issue.Resolution  = json.issue.fields.resolution.Value;
                            if (issue.Resolution == null)
                            {
                                issue.Resolution = "Unresolved";
                            }
                            issue.Reporter = new JiraUser
                            {
                                Name      = json.issue.fields.reporter.displayName.Value,
                                AvatarUrl = ((json.issue.fields.reporter.avatarUrls as IEnumerable <object>).First() as dynamic).Value.Value
                            };
                            if (json.issue.fields.assignee == null)
                            {
                                issue.Assignee = new JiraUser {
                                    Name = "*Unassigned*"
                                };
                            }
                            else
                            {
                                issue.Assignee = new JiraUser
                                {
                                    Name      = json.issue.fields.assignee.displayName.Value,
                                    AvatarUrl = ((json.issue.fields.assignee.avatarUrls as IEnumerable <object>).First() as dynamic).Value.Value
                                };
                            }
                            issue.UpdatedTimestamp = json.issue.fields.updated.Value;
                        }
                        if (issue.Description != null && issue.Description != "")
                        {
                            // Convert code tags
                            issue.Description = issue.Description.Replace("{{", "```\n");
                            issue.Description = issue.Description.Replace("}}", "\n```");
                            issue.Description = issue.Description.Replace("{code}\r\n", "```\n");
                            issue.Description = issue.Description.Replace("\r\n{code}", "\n```");

                            // Remove bold formatting
                            issue.Description = issue.Description.Replace("*", "");

                            // Trim length
                            if (issue.Description.Length > 250)
                            {
                                issue.Description = issue.Description.Substring(0, 250);

                                // Is there an odd number of code tags?
                                int numTags = Regex.Matches(issue.Description, "```").Count;
                                if (numTags % 2 != 0)
                                {
                                    // Close the last tag
                                    issue.Description += " ...\n```" + "\n([see full description](" + issue.Url + "))";
                                }
                                else
                                {
                                    issue.Description += " ... ([see full description](" + issue.Url + "))";
                                }
                            }
                        }
                        else
                        {
                            issue.Description = "*No description*";
                        }

                        try
                        {
                            user.AvatarUrl = ((author.avatarUrls as IEnumerable <object>).First() as dynamic).Value.Value;
                        }
                        catch (Exception ex)
                        {
                            var msg = ex.Message;
                            Trace.WriteLine(msg);
                        }

                        DiscordBot bot = new DiscordBot
                        {
                            Name      = prefs.BotName,
                            AvatarUrl = prefs.JiraIconURL
                        };

                        JiraIssueIcons issueIcons = new JiraIssueIcons
                        {
                            BugIconURL  = prefs.BugIconURL,
                            TaskIconURL = prefs.TaskIconURL,
                            EpicIconURL = prefs.EpicIconURL
                        };

                        Trace.WriteLine("Got webhook event '" + eventType + "' [" + issue.Summary + "]" + " by " + user.Name);

                        try
                        {
                            var discordMessage     = new DiscordHookMessage(bot, eventType, user, issue, issueIcons, comment);
                            var discordMessageJSON = JsonConvert.SerializeObject(discordMessage, Formatting.Indented);
                            var resp = client.PostAsync(prefs.DiscordURL, new StringContent(discordMessageJSON, Encoding.UTF8, "application/json")).Result;
                            Trace.WriteLine("Discord response code: " + resp.StatusCode);
                        }
                        catch (Exception ex)
                        {
                            var msg = ex.Message;
                            Trace.WriteLine(msg);
                            //MessageBox.Show(msg);
                        }
                    }
                }
                try
                {
                    using (var writer = new StreamWriter(response.OutputStream))
                    {
                        writer.Write("Hello world!");
                        writer.Close();
                    }

                    listener.Stop();
                }
                catch (IOException ex)
                {
                    var msg = ex.Message;
                    Trace.WriteLine(msg);
                }
                catch (ObjectDisposedException ex)
                {
                    var msg = ex.Message;
                    Trace.WriteLine(msg);
                }
            }
        }