//This is a loop that keeps running and checks if the bot has been disconnected public StartConnectionWatchdog(SCPDiscordPlugin plugin) { while (true) { Thread.Sleep(2000); if (!plugin.clientSocket.Connected && plugin.hasConnectedOnce) { plugin.clientSocket.Close(); plugin.Info("Not connected, trying to reconnect"); if (plugin.GetConfigBool("discord_verbose")) { plugin.Warn("Discord bot connection issue detected, attempting reconnect..."); } try { plugin.clientSocket = new TcpClient(plugin.GetConfigString("discord_bot_ip"), plugin.GetConfigInt("discord_bot_port")); plugin.Info("Reconnected to Discord bot."); plugin.SendMessageToBot("default", "botmessages.reconnectedtobot"); } catch (SocketException e) { if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("Error occured while reconnecting to discord bot server."); plugin.Debug(e.ToString()); } Thread.Sleep(5000); } catch (ObjectDisposedException e) { if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("TCP client was unexpectedly closed."); plugin.Debug(e.ToString()); } Thread.Sleep(5000); } catch (ArgumentOutOfRangeException e) { if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("Invalid port."); plugin.Debug(e.ToString()); } Thread.Sleep(5000); } catch (ArgumentNullException e) { if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("IP address is null."); plugin.Debug(e.ToString()); } Thread.Sleep(5000); } } } }
//This is ran once on the first time connecting to the bot public ConnectToBot(SCPDiscordPlugin plugin) { Thread.Sleep(2000); while (!plugin.clientSocket.Connected) { if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("Attempting Bot Connection..."); } try { if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("Your Bot IP: " + plugin.GetConfigString("discord_bot_ip") + ". Your Bot Port: " + plugin.GetConfigInt("discord_bot_port") + "."); } plugin.clientSocket.Connect(plugin.GetConfigString("discord_bot_ip"), plugin.GetConfigInt("discord_bot_port")); } catch (SocketException e) { if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("Error occured while connecting to discord bot server."); plugin.Debug(e.ToString()); } Thread.Sleep(5000); } catch (ObjectDisposedException e) { if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("TCP client was unexpectedly closed."); plugin.Debug(e.ToString()); } Thread.Sleep(5000); } catch (ArgumentOutOfRangeException e) { if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("Invalid port."); plugin.Debug(e.ToString()); } Thread.Sleep(5000); } catch (ArgumentNullException e) { if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("IP address is null."); plugin.Debug(e.ToString()); } Thread.Sleep(5000); } } plugin.Info("Connected to Discord bot."); plugin.SendMessageToBot("default", "botmessages.connectedtobot"); plugin.hasConnectedOnce = true; }
//This is ran once on the first time connecting to the bot public ConnectToBot(SCPDiscordPlugin plugin) { Thread.Sleep(2000); while (!plugin.clientSocket.Connected) { if (Config.settings.verbose) { plugin.Info("Attempting Bot Connection..."); } try { if (Config.settings.verbose) { plugin.Info("Your Bot IP: " + Config.bot.ip + ". Your Bot Port: " + Config.bot.port + "."); } plugin.clientSocket.Connect(Config.bot.ip, Config.bot.port); } catch (SocketException e) { if (Config.settings.verbose) { plugin.Info("Error occured while connecting to discord bot server."); plugin.Debug(e.ToString()); } Thread.Sleep(5000); } catch (ObjectDisposedException e) { if (Config.settings.verbose) { plugin.Info("TCP client was unexpectedly closed."); plugin.Debug(e.ToString()); } Thread.Sleep(5000); } catch (ArgumentOutOfRangeException e) { if (Config.settings.verbose) { plugin.Info("Invalid port."); plugin.Debug(e.ToString()); } Thread.Sleep(5000); } catch (ArgumentNullException e) { if (Config.settings.verbose) { plugin.Info("IP address is null."); plugin.Debug(e.ToString()); } Thread.Sleep(5000); } } plugin.Info("Connected to Discord bot."); plugin.SendMessageToBot(Config.channels.onroundend, "botmessages.connectedtobot"); plugin.hasConnectedOnce = true; }
public RefreshBotActivity(SCPDiscordPlugin plugin) { var message = (plugin.pluginManager.Server.NumPlayers - 1) + " / " + plugin.GetConfigString("max_players"); if (plugin.clientSocket == null || !plugin.clientSocket.Connected) { if (plugin.hasConnectedOnce && plugin.GetConfigBool("discord_verbose")) { plugin.Warn("Error sending message '" + message + "' to bot: Not connected."); } return; } // Try to send the message to the bot try { NetworkStream serverStream = plugin.clientSocket.GetStream(); byte[] outStream = System.Text.Encoding.UTF8.GetBytes("botactivity" + message + '\0'); serverStream.Write(outStream, 0, outStream.Length); if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("Sent activity '" + message + "' to bot."); } } catch (InvalidOperationException e) { plugin.Error("Error sending activity '" + message + "' to bot."); plugin.Debug(e.ToString()); } catch (ArgumentNullException e) { plugin.Error("Error sending activity '" + message + "' to bot."); plugin.Debug(e.ToString()); } }
public static void Initialise() { plugin = SCPDiscordPlugin.instance; // Save default language files SaveDefaultLanguages(); // Read primary language file plugin.Info("Loading primary language file..."); try { LoadLanguageFile(Config.settings.language, false); } catch (Exception e) { if (e is DirectoryNotFoundException) { plugin.Error("Language directory not found."); } else if (e is UnauthorizedAccessException) { plugin.Error("Primary language file access denied."); } else if (e is FileNotFoundException) { plugin.Error("'" + Config.settings.language + ".yml' was not found."); } else if (e is JsonReaderException || e is YamlException) { plugin.Error("'" + Config.settings.language + ".yml' formatting error."); } plugin.Error("Error reading language file '" + Config.settings.language + ".yml'. Attempting to initialize backup system..."); plugin.Debug(e.ToString()); } // Read backup language file if not the same as the primary if (Config.settings.language != "english") { plugin.Info("Loading backup language file..."); try { LoadLanguageFile("english", true); } catch (Exception e) { if (e is DirectoryNotFoundException) { plugin.Error("Language directory not found."); } else if (e is UnauthorizedAccessException) { plugin.Error("Backup language file access denied."); } else if (e is FileNotFoundException) { plugin.Error("'" + Config.settings.language + ".yml' was not found."); } else if (e is JsonReaderException || e is YamlException) { plugin.Error("'" + Config.settings.language + ".yml' formatting error."); } plugin.Error("Error reading backup language file 'english.yml'."); plugin.Debug(e.ToString()); } } if (primary == null && backup == null) { plugin.Error("NO LANGUAGE FILE LOADED! DEACTIVATING SCPDISCORD."); plugin.Disable(); } ValidateLanguageStrings(); }
public Language(SCPDiscordPlugin plugin) { this.plugin = plugin; plugin.language = this; Thread.Sleep(2500); // Save default language files SaveDefaultLanguages(); // Read primary language file plugin.Info("Loading primary language file..."); try { ReadLanguage(plugin.GetConfigString("discord_language"), false); } catch (Exception e) { if (e is DirectoryNotFoundException) { plugin.Error("Language directory not found."); } else if (e is UnauthorizedAccessException) { plugin.Error("Primary language file access denied."); } else if (e is FileNotFoundException) { plugin.Error("'" + plugin.GetConfigString("discord_language") + ".yml' was not found."); } else if (e is JsonReaderException || e is YamlException) { plugin.Error("'" + plugin.GetConfigString("discord_language") + ".yml' formatting error."); } plugin.Error("Error reading language file '" + plugin.GetConfigString("discord_language") + ".yml'. Attempting to initialize backup system..."); plugin.Debug(e.ToString()); } // Read backup language file if not the same as the primary if (plugin.GetConfigString("discord_language") != "english") { plugin.Info("Loading backup language file..."); try { ReadLanguage("english", true); } catch (Exception e) { if (e is DirectoryNotFoundException) { plugin.Error("Language directory not found."); } else if (e is UnauthorizedAccessException) { plugin.Error("Backup language file access denied."); } else if (e is FileNotFoundException) { plugin.Error("'" + plugin.GetConfigString("discord_language") + ".yml' was not found."); } else if (e is JsonReaderException || e is YamlException) { plugin.Error("'" + plugin.GetConfigString("discord_language") + ".yml' formatting error."); } plugin.Error("Error reading backup language file 'english.yml'."); plugin.Debug(e.ToString()); } } if (primary == null && backup == null) { plugin.Error("NO LANGUAGE FILE LOADED! DEACTIVATING SCPDISCORD."); plugin.Disable(); } //Runs until the server has connected once Thread connectionThread = new Thread(new ThreadStart(() => new ConnectToBot(plugin))); connectionThread.Start(); //Runs the listener Thread botListenerThread = new Thread(new ThreadStart(() => new BotListener(plugin))); botListenerThread.Start(); //Keeps running to auto-reconnect if needed Thread watchdogThread = new Thread(new ThreadStart(() => new StartConnectionWatchdog(plugin))); watchdogThread.Start(); }
public RefreshChannelTopic(SCPDiscordPlugin plugin, string channelID) { Dictionary <string, string> variables = new Dictionary <string, string>(); try { Server server = plugin.pluginManager.Server; Dictionary <string, string> serverVariables; if (server != null) { serverVariables = new Dictionary <string, string> { { "players", (server.NumPlayers - 1) + "" }, { "maxplayers", plugin.GetConfigString("max_players") }, { "ip", server.IpAddress }, { "port", server.Port + "" }, { "isvisible", server.Visible + "" }, { "isverified", server.Verified + "" }, { "uptime", (plugin.serverStartTime.ElapsedMilliseconds / 1000 / 60) + "" } }; } else { serverVariables = new Dictionary <string, string> { { "players", "0" }, { "maxplayers", "0" }, { "ip", "---.---.---.---" }, { "port", "----" }, { "isvisible", "False" }, { "isverified", "False" }, { "uptime", "0" } }; } Dictionary <string, string> mapVariables; if (server != null && server.Map != null) { mapVariables = new Dictionary <string, string> { { "warheaddetonated", server.Map.WarheadDetonated + "" }, { "decontaminated", server.Map.LCZDecontaminated + "" } }; } else { mapVariables = new Dictionary <string, string> { { "warheaddetonated", "False" }, { "decontaminated", "False" } }; } Dictionary <string, string> roundVariables; if (server != null && server.Round != null) { roundVariables = new Dictionary <string, string> { { "roundduration", (server.Round.Duration / 60) + "" }, { "dclassalive", server.Round.Stats.ClassDAlive + "" }, { "dclassdead", server.Round.Stats.ClassDDead + "" }, { "dclassescaped", server.Round.Stats.ClassDEscaped + "" }, { "dclassstart", server.Round.Stats.ClassDStart + "" }, { "mtfalive", server.Round.Stats.NTFAlive + "" }, { "scientistsalive", server.Round.Stats.ScientistsAlive + "" }, { "scientistsdead", server.Round.Stats.ScientistsDead + "" }, { "scientistsescaped", server.Round.Stats.ScientistsEscaped + "" }, { "scientistsstart", server.Round.Stats.ScientistsStart + "" }, { "scpalive", server.Round.Stats.SCPAlive + "" }, { "scpdead", server.Round.Stats.SCPDead + "" }, { "scpkills", server.Round.Stats.SCPKills + "" }, { "scpstart", server.Round.Stats.SCPStart + "" }, //{ "warheaddetonated", server.Round.Stats.WarheadDetonated + "" }, { "zombies", server.Round.Stats.Zombies + "" } }; } else { roundVariables = new Dictionary <string, string> { { "roundduration", "0" }, { "dclassalive", "0" }, { "dclassdead", "0" }, { "dclassescaped", "0" }, { "dclassstart", "0" }, { "mtfalive", "0" }, { "scientistsalive", "0" }, { "scientistsdead", "0" }, { "scientistsescaped", "0" }, { "scientistsstart", "0" }, { "scpalive", "0" }, { "scpdead", "0" }, { "scpkills", "0" }, { "scpstart", "0" }, //{ "warheaddetonated", "0" }, { "zombies", "0" } }; } foreach (var entry in serverVariables) { variables.Add(entry.Key, entry.Value); } foreach (var entry in mapVariables) { variables.Add(entry.Key, entry.Value); } foreach (var entry in roundVariables) { variables.Add(entry.Key, entry.Value); } var topic = plugin.GetConfigString("discord_server_status"); topic = topic.Replace("\n", ""); // Variable insertion foreach (KeyValuePair <string, string> variable in variables) { topic = topic.Replace("<var:" + variable.Key + ">", variable.Value); } // Regex replacements Dictionary <string, string> regex = plugin.GetConfigDict("discord_server_status_regex"); // Run the regex replacements foreach (KeyValuePair <string, string> entry in regex) { topic = topic.Replace(entry.Key, entry.Value); } // Change the default keyword to the bot's representation of it if (channelID == "default") { channelID = "000000000000000000"; } // Try to send the message to the bot try { NetworkStream serverStream = plugin.clientSocket.GetStream(); byte[] outStream = System.Text.Encoding.UTF8.GetBytes("channeltopic" + channelID + topic + '\0'); serverStream.Write(outStream, 0, outStream.Length); if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("Sent channel topic '" + topic + "' to bot."); } } catch (InvalidOperationException e) { plugin.Error("Error sending channel topic '" + topic + "' to bot."); plugin.Debug(e.ToString()); } catch (ArgumentNullException e) { plugin.Error("Error sending channel topic '" + topic + "' to bot."); plugin.Debug(e.ToString()); } } catch (Exception e) { if (plugin.GetConfigBool("discord_verbose")) { plugin.Warn(e.ToString()); } } }
public SendMessageToBot(SCPDiscordPlugin plugin, string channelID, string messagePath, Dictionary <string, string> variables = null) { // Get unparsed message from config string message = ""; try { message = plugin.language.GetString(messagePath + ".message"); } catch (Exception e) { if (!(e is NullReferenceException)) { plugin.Error("Error reading base message" + e); } return; } // Abort on empty message if (message == null || message == "" || message == " " || message == ".") { plugin.Error("Tried to send empty message " + messagePath + " to discord. Verify your language file."); return; } // Abort if client is dead if (plugin.clientSocket == null || !plugin.clientSocket.Connected) { if (plugin.hasConnectedOnce && plugin.GetConfigBool("discord_verbose")) { plugin.Warn("Error sending message '" + message + "' to bot: Not connected."); } return; } // Add time stamp if (plugin.GetConfigString("discord_formatting_date") != "off") { message = "[" + DateTime.Now.ToString(plugin.GetConfigString("discord_formatting_date")) + "]: " + message; } // Change the default keyword to the bot's representation of it if (channelID == "default") { channelID = "000000000000000000"; } // Re-add newlines message = message.Replace("\\n", "\n"); // Add variables ////////////////////////////// if (variables != null) { // Variable insertion foreach (KeyValuePair <string, string> variable in variables) { // Wait until after the regex replacements to add the player names if (variable.Key == "servername" || variable.Key == "name" || variable.Key == "attackername" || variable.Key == "playername" || variable.Key == "adminname" || variable.Key == "feedback") { continue; } message = message.Replace("<var:" + variable.Key + ">", variable.Value); } } /////////////////////////////////////////////// // Global regex replacements ////////////////// Dictionary <string, string> globalRegex = new Dictionary <string, string>(); try { globalRegex = plugin.language.GetRegexDictionary("global_regex"); } catch (Exception e) { plugin.Error("Error reading global regex" + e); return; } // Run the global regex replacements foreach (KeyValuePair <string, string> entry in globalRegex) { message = message.Replace(entry.Key, entry.Value); } /////////////////////////////////////////////// // Local regex replacements /////////////////// Dictionary <string, string> localRegex = new Dictionary <string, string>(); try { localRegex = plugin.language.GetRegexDictionary(messagePath + ".regex"); } catch (Exception e) { plugin.Error("Error reading local regex" + e); return; } // Run the local regex replacements foreach (KeyValuePair <string, string> entry in localRegex) { message = message.Replace(entry.Key, entry.Value); } /////////////////////////////////////////////// if (variables != null) { // Add names/command feedback to the message // foreach (KeyValuePair <string, string> variable in variables) { if (variable.Key == "servername" || variable.Key == "name" || variable.Key == "attackername" || variable.Key == "playername" || variable.Key == "adminname" || variable.Key == "feedback") { message = message.Replace("<var:" + variable.Key + ">", EscapeDiscordFormatting(variable.Value)); } } /////////////////////////////////////////////// // Final regex replacements /////////////////// Dictionary <string, string> finalRegex = new Dictionary <string, string>(); try { finalRegex = plugin.language.GetRegexDictionary("final_regex"); } catch (Exception e) { plugin.Error("Error reading final regex" + e); return; } // Run the final regex replacements foreach (KeyValuePair <string, string> entry in finalRegex) { message = message.Replace(entry.Key, entry.Value); } /////////////////////////////////////////////// } // Try to send the message to the bot try { NetworkStream serverStream = plugin.clientSocket.GetStream(); byte[] outStream = System.Text.Encoding.UTF8.GetBytes(channelID + message + '\0'); serverStream.Write(outStream, 0, outStream.Length); if (plugin.GetConfigBool("discord_verbose")) { plugin.Info("Sent message '" + message + "' to bot."); } } catch (InvalidOperationException e) { plugin.Error("Error sending message '" + message + "' to bot."); plugin.Debug(e.ToString()); } catch (ArgumentNullException e) { plugin.Error("Error sending message '" + message + "' to bot."); plugin.Debug(e.ToString()); } }
public BotListener(SCPDiscordPlugin plugin) { this.plugin = plugin; while (true) { //Listen for connections if (plugin.clientSocket.Connected && plugin.hasConnectedOnce) { Thread.Sleep(200); try { //Discord messages can be up to 2000 chars long, UTF8 chars can be up to 4 bytes long. byte[] data = new byte[8000]; NetworkStream stream = null; try { stream = plugin.clientSocket.GetStream(); } catch (Exception ex) { if (ex is IOException) { plugin.Error("Could not get stream from socket."); } else { plugin.Error("BotListener Error: " + ex.ToString()); } } if (stream == null) { return; } string incomingData = ""; try { int lengthOfData = stream.Read(data, 0, data.Length); incomingData = Encoding.UTF8.GetString(data, 0, lengthOfData); } catch (Exception ex) { if (ex is IOException) { plugin.Error("Could not read from socket."); } else { plugin.Error("BotListener Error: " + ex.ToString()); } } List <string> messages = new List <string>(incomingData.Split('\n')); //If several messages come in at the same time, process all of them while (messages.Count > 0) { if (messages[0].Length == 0) { messages.RemoveAt(0); continue; } string[] words = messages[0].Split(' '); bool isCommand = words[0] == "command"; string command = words[1]; string[] arguments = new string[0]; if (words.Length >= 3) { arguments = words.Skip(2).ToArray(); } //A verification that message is a command and not some left over string in the socket if (isCommand) { if (command == "ban") { //Check if the command has enough arguments if (arguments.Length >= 2) { BanCommand(arguments[0], arguments[1], MergeReason(arguments.Skip(2).ToArray())); } else { Dictionary <string, string> variables = new Dictionary <string, string> { { "command", messages[0] } }; plugin.SendMessageToBot(Config.channels.statusmessages, "botresponses.missingarguments", variables); } } else if (command == "kick") { //Check if the command has enough arguments if (arguments.Length >= 1) { KickCommand(arguments[0], MergeReason(arguments.Skip(1).ToArray())); } else { Dictionary <string, string> variables = new Dictionary <string, string> { { "command", messages[0] } }; plugin.SendMessageToBot(Config.channels.statusmessages, "botresponses.missingarguments", variables); } } else if (command == "kickall") { KickallCommand(MergeReason(arguments)); } else if (command == "unban") { //Check if the command has enough arguments if (arguments.Length >= 1) { UnbanCommand(arguments[0]); } else { Dictionary <string, string> variables = new Dictionary <string, string> { { "command", messages[0] } }; plugin.SendMessageToBot(Config.channels.statusmessages, "botresponses.missingarguments", variables); } } else if (command == "list") { var message = "```md\n# Players online:\n"; foreach (Player player in plugin.Server.GetPlayers()) { string line = player.Name.PadRight(32); line += player.SteamId; line += "\n"; } message += "```"; if (plugin.clientSocket == null || !plugin.clientSocket.Connected) { if (plugin.hasConnectedOnce && Config.settings.verbose) { plugin.Warn("Error sending message '" + message + "' to bot: Not connected."); } return; } // Try to send the message to the bot try { NetworkStream serverStream = plugin.clientSocket.GetStream(); byte[] outStream = System.Text.Encoding.UTF8.GetBytes("000000000000000000" + message + '\0'); serverStream.Write(outStream, 0, outStream.Length); if (Config.settings.verbose) { plugin.Info("Sent activity '" + message + "' to bot."); } } catch (InvalidOperationException e) { plugin.Error("Error sending activity '" + message + "' to bot."); plugin.Debug(e.ToString()); } catch (ArgumentNullException e) { plugin.Error("Error sending activity '" + message + "' to bot."); plugin.Debug(e.ToString()); } } else if (command == "exit") { plugin.SendMessageToBot(Config.channels.statusmessages, "botresponses.exit"); } else if (command == "help") { plugin.SendMessageToBot(Config.channels.statusmessages, "botresponses.help"); } else if (command == "hidetag" || command == "showtag") { if (plugin.pluginManager.GetEnabledPlugin("karlofduty.toggletag") != null) { if (arguments.Length > 0) { command = "console_" + command; string response = ConsoleCommand(plugin.pluginManager.Server, command, arguments); Dictionary <string, string> variables = new Dictionary <string, string> { { "feedback", response } }; plugin.SendMessageToBot(Config.channels.statusmessages, "botresponses.consolecommandfeedback", variables); } else { Dictionary <string, string> variables = new Dictionary <string, string> { { "command", command } }; plugin.SendMessageToBot(Config.channels.statusmessages, "botresponses.missingarguments", variables); } } else { plugin.SendMessageToBot(Config.channels.statusmessages, "botresponses.toggletag.notinstalled"); } } else if (command == "vs_enable" || command == "vs_disable" || command == "vs_whitelist" || command == "vs_reload") { if (plugin.pluginManager.GetEnabledPlugin("karlofduty.vpnshield") != null) { string response = ConsoleCommand(plugin.pluginManager.Server, command, arguments); Dictionary <string, string> variables = new Dictionary <string, string> { { "feedback", response } }; plugin.SendMessageToBot(Config.channels.statusmessages, "botresponses.consolecommandfeedback", variables); } else { plugin.SendMessageToBot(Config.channels.statusmessages, "botresponses.vpnshield.notinstalled"); } } else { string response = ConsoleCommand(plugin.pluginManager.Server, command, arguments); Dictionary <string, string> variables = new Dictionary <string, string> { { "feedback", response } }; plugin.SendMessageToBot(Config.channels.statusmessages, "botresponses.consolecommandfeedback", variables); } } plugin.Info("From discord: " + messages[0]); messages.RemoveAt(0); } } catch (Exception ex) { if (ex is IOException) { plugin.Error("BotListener Error: " + ex.ToString()); } plugin.Error("BotListener Error: " + ex.ToString()); } } else { Thread.Sleep(2000); } } }