/// <summary>
        /// Handles the heartbeat acknowledgement when the server asks for it.
        /// </summary>
        private void Socket_OnHeartbeatReceived(object sender, MessageEventArgs e)
        {
            var json = e.Data;

            if (json.Length <= 1)
            {
                return;
            }

            if (!DiscordMessageFactory.TryParseMessage(json, out var msg))
            {
                return;
            }

            if (msg.OpCode == GatewayOpcode.Hello)
            {
                if (heartbeatTimer != null)
                {
                    heartbeatTimer.Dispose();
                }

                heartbeatTimer          = new System.Timers.Timer(((JObject)msg.Data).Value <int>("heartbeat_interval") / 2);
                heartbeatTimer.Elapsed += (senderr, ee) =>
                {
                    Socket.Send(DiscordMessageFactory.CreateHeartbeat(GetLastSequenceNumber()));
                    if (errorCounter > 0)
                    {
                        errorCounter--;
                    }
                };
                heartbeatTimer.Start();

                Socket.Send(DiscordMessageFactory.CreateHeartbeat(GetLastSequenceNumber()));
            }
        }
        /// <summary>
        /// Parses data when Discord sends a message.
        /// </summary>
        private void Socket_OnDataReceived(object sender, MessageEventArgs e)
        {
            var json = e.Data;

            if (json == null)
            {
                return;
            }
            if (json.Length <= 1)
            {
                return;
            }

            if (debug)
            {
                Console.WriteLine("\n" + json + "\n");
            }

            if (!DiscordMessageFactory.TryParseDispatchMessage(json, out var msg))
            {
                return;
            }
            LastSequenceNumber = msg.SequenceNumber;

            var chatmsg = msg.GetChatMessageData();

            if (chatmsg != null && Channel_IDs.Contains(chatmsg.ChannelId))
            {
                if (!chatmsg.Author.IsBot)
                {
                    string msgout = chatmsg.Message;

                    // Lazy add commands until I take time to design a command service properly
                    //if (ExecuteCommand(chatmsg))
                    //    return;

                    msgout = chatParser.ConvertUserIdsToNames(msgout, chatmsg.UsersMentioned);
                    msgout = chatParser.ShortenEmojisToName(msgout);
                    msgout = $"<{chatmsg.Author.Username}> {msgout}";

                    NetHelpers.BroadcastChatMessageWithoutTCRFormattable(
                        ("[c/7489d8:Discord] - " + msgout), -1);

                    if (Channel_IDs.Count > 1)
                    {
                        messageQueue.QueueMessage(
                            Channel_IDs.Where(x => x != chatmsg.ChannelId),
                            $"**[Discord]** <{chatmsg.Author.Username}> {chatmsg.Message}");
                    }

                    Console.ForegroundColor = ConsoleColor.Blue;
                    Console.Write("[Discord] ");
                    Console.ResetColor();
                    Console.Write(msgout);
                    Console.WriteLine();
                }
            }
        }
        /// <summary>
        /// Create a new WebSocket and initiate connection with Discord servers.
        /// Utilizes BOT_TOKEN and CHANNEL_ID found in Mod Config.
        /// </summary>
        public override void Connect()
        {
            if (BOT_TOKEN == "BOT_TOKEN" || Channel_IDs.Contains(0))
            {
                PrettyPrint.Log("Discord", "Please update your Mod Config. Mod reload required.");

                if (BOT_TOKEN == "BOT_TOKEN")
                {
                    Console.WriteLine(" Invalid Token: BOT_TOKEN");
                }
                if (Channel_IDs.Contains(0))
                {
                    Console.WriteLine(" Invalid Channel Id: 0");
                }

                Console.WriteLine("  Config path: " + new Configuration().FileName);
                Console.ResetColor();
                Dispose();
                return;
            }

            errorCounter = 0;

            Socket             = new WebSocket(GATEWAY_URL);
            Socket.Compression = CompressionMethod.Deflate;
            Socket.OnOpen     += (object sender, EventArgs e) =>
            {
                PrettyPrint.Log("Discord", "Connection complete!");
                Socket.Send(DiscordMessageFactory.CreateLogin(BOT_TOKEN));
            };

            Socket.OnMessage += Socket_OnDataReceived;
            Socket.OnMessage += Socket_OnHeartbeatReceived;
            Socket.OnError   += Socket_OnError;
            if (!debug)
            {
                Socket.Log.Output = (_, __) => { }
            }
            ;
            Socket.Connect();

            if (!Main.Config.FirstTimeMessageShown ||
                Main.Config.AlwaysShowFirstTimeMessage)
            {
                messageQueue.QueueMessage(Channel_IDs,
                                          $"**This bot is powered by TerrariaChatRelay**\nUse {Main.Config.CommandPrefix}info for more commands!");
                Main.Config.FirstTimeMessageShown = true;
                Main.Config.SaveJson();
            }
        }
        public async void SendMessageToDiscordChannel(ulong channelId, string message)
        {
            message = message.Replace("\\", "\\\\");
            message = message.Replace("\"", "\\\"");
            message = message.Replace("\n", "\\n");
            string json = DiscordMessageFactory.CreateTextMessage(message);

            string response = await SimpleRequest.SendJsonDataAsync($"{API_URL}/channels/{channelId}/messages",
                                                                    new WebHeaderCollection()
            {
                { "Authorization", $"Bot {BOT_TOKEN}" }
            }, json);

            if (debug)
            {
                Console.WriteLine(response);
            }
        }