using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MinecraftClient { /// /// Welcome to the Bot API file ! /// The virtual class "ChatBot" contains anything you need for creating chat bots /// Inherit from this class while adding your bot class to the namespace "Bots", below. /// Once your bot is created, simply edit the switch in Program.cs to add the corresponding command-line argument! /// /// /// The virtual class containing anything you need for creating chat bots. /// public abstract class ChatBot { public enum DisconnectReason { InGameKick, LoginRejected, ConnectionLost }; #region MinecraftCom Handler for this bot //Will be automatically set on bot loading, don't worry about this public void SetHandler(MinecraftCom handler) { this.handler = handler; } private MinecraftCom handler; #endregion /// /// Anything you want to initialize your bot, will be called on load by MinecraftCom /// public virtual void Initialize() { } /// /// Will be called every ~100ms (10fps) if loaded in MinecraftCom /// public virtual void Update() { } /// /// Any text sent by the server will be sent here by MinecraftCom /// /// Text from the server public virtual void GetText(string text) { } /// /// Is called when the client has been disconnected fom the server /// /// Disconnect Reason /// Kick message, if any /// Return TRUE if the client is about to restart public virtual bool OnDisconnect(DisconnectReason reason, string message) { return false; } #region ToolBox /// /// Send text to the server. Can be anything such as chat messages or commands /// /// Text to send to the server protected void SendText(string text) { Console.ForegroundColor = ConsoleColor.DarkGray; ConsoleIO.WriteLine("BOT:" + text); handler.SendChatMessage(text); Console.ForegroundColor = ConsoleColor.Gray; } /// /// Remove color codes ("§c") from a text message received from the server /// protected static string getVerbatim(string text) { string verbatim = ""; for (int i = 0; i < text.Length; i++) { if (text[i] == '§') { i++; //Will skip also the next char } else verbatim += text[i]; //Add the char } return verbatim; } /// /// Verify that a string contains only a-z A-Z 0-9 and _ characters. /// protected static bool isValidName(string username) { if (username == "") { return false; } string validchars = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "1234567890_"; bool passe = false; bool ok = true; for (int i = 0; i < username.Length; i++) { passe = false; for (int j = 0; j < validchars.Length; j++) { if (username[i] == validchars[j]) { passe = true; break; } } if (!passe) { ok = false; break; } } return ok; } /// /// Returns true is the text passed is a private message sent to the bot /// /// text to test /// if it's a private message, this will contain the message /// if it's a private message, this will contain the player name that sends the message /// Returns true if the text is a private message protected static bool isPrivateMessage(string text, ref string message, ref string sender) { if (text == "") { return false; } string[] tmp = text.Split(' '); try { //Detect vanilla /tell messages //Someone whispers message if (tmp.Length > 2 && tmp[1] == "whispers") { message = text.Substring(tmp[0].Length + 10); sender = tmp[0]; return isValidName(sender); } //Detect Essentials (Bukkit) /m messages //[Someone -> me] message else if (text[0] == '[' && tmp.Length > 3 && tmp[1] == "->" && (tmp[2] == "me]" || tmp[2] == "moi]")) //'me' is replaced by 'moi' in french servers { message = text.Substring(tmp[0].Length + 4 + tmp[2].Length + 1); sender = tmp[0].Substring(1); if (sender[0] == '~') { sender = sender.Substring(1); } return isValidName(sender); } else return false; } catch (IndexOutOfRangeException) { return false; } } /// /// Returns true is the text passed is a public message written by a player on the chat /// /// text to test /// if it's message, this will contain the message /// if it's message, this will contain the player name that sends the message /// Returns true if the text is a chat message protected static bool isChatMessage(string text, ref string message, ref string sender) { //Detect chat messages // message //<*Faction Someone> message //<*Faction Someone>: message //<*Faction ~Nicknamed>: message if (text == "") { return false; } if (text[0] == '<') { try { text = text.Substring(1); string[] tmp = text.Split('>'); sender = tmp[0]; message = text.Substring(sender.Length + 2); if (message.Length > 1 && message[0] == ' ') { message = message.Substring(1); } tmp = sender.Split(' '); sender = tmp[tmp.Length - 1]; if (sender[0] == '~') { sender = sender.Substring(1); } return isValidName(sender); } catch (IndexOutOfRangeException) { return false; } } else return false; } /// /// Writes some text in the console. Nothing will be sent to the server. /// /// Log text to write public static void LogToConsole(string text) { Console.ForegroundColor = ConsoleColor.DarkGray; ConsoleIO.WriteLine("[BOT] " + text); Console.ForegroundColor = ConsoleColor.Gray; } /// /// Disconnect from the server and restart the program /// It will unload & reload all the bots and then reconnect to the server /// protected void ReconnectToTheServer() { ReconnectToTheServer(3); } /// /// Disconnect from the server and restart the program /// It will unload & reload all the bots and then reconnect to the server /// /// If connection fails, the client will make X extra attempts protected void ReconnectToTheServer(int ExtraAttempts) { McTcpClient.AttemptsLeft = ExtraAttempts; Program.Restart(); } /// /// Unload the chatbot, and release associated memory. /// protected void UnloadBot() { handler.BotUnLoad(this); } #endregion } namespace Bots { /// /// Example of message receiving. /// public class TestBot : ChatBot { public override void GetText(string text) { string message = ""; string username = ""; text = getVerbatim(text); if (isPrivateMessage(text, ref message, ref username)) { ConsoleIO.WriteLine("Bot: " + username + " told me : " + message); } else if (isChatMessage(text, ref message, ref username)) { ConsoleIO.WriteLine("Bot: " + username + " said : " + message); } } } /// /// This bot sends a /ping command every 60 seconds in order to stay non-afk. /// public class AntiAFK : ChatBot { private int count; private int timeping; /// /// This bot sends a /ping command every X seconds in order to stay non-afk. /// /// Time amount between each ping (10 = 1s, 600 = 1 minute, etc.) public AntiAFK(int pingparam) { count = 0; timeping = pingparam; if (timeping < 10) { timeping = 10; } //To avoid flooding } public override void Update() { count++; if (count == timeping) { SendText("/ping"); count = 0; } } } /// /// This bot sends a /list command every X seconds and save the result. /// public class PlayerListLogger : ChatBot { private int count; private int timeping; private string file; /// /// This bot sends a /list command every X seconds and save the result. /// /// Time amount between each list ping (10 = 1s, 600 = 1 minute, etc.) public PlayerListLogger(int pingparam, string filetosavein) { count = 0; file = filetosavein; timeping = pingparam; if (timeping < 10) { timeping = 10; } //To avoid flooding } public override void Update() { count++; if (count == timeping) { SendText("/list"); count = 0; } } public override void GetText(string text) { if (text.Contains("Joueurs en ligne") || text.Contains("Connected:") || text.Contains("online:")) { LogToConsole("Saving Player List"); DateTime now = DateTime.Now; string TimeStamp = "[" + now.Year + '/' + now.Month + '/' + now.Day + ' ' + now.Hour + ':' + now.Minute + ']'; System.IO.File.AppendAllText(file, TimeStamp + "\n" + getVerbatim(text) + "\n\n"); } } } /// /// "Le jeu du Pendu" (Hangman game) /// public class Pendu : ChatBot { private int vie = 0; private int vie_param = 10; private int compteur = 0; private int compteur_param = 3000; //5 minutes private bool running = false; private bool[] discovered; private string word = ""; private string letters = ""; private string[] owners; private bool English; /// /// "Le jeu du Pendu" (Hangman Game) /// /// if true, the game will be in english. If false, the game will be in french. public Pendu(bool english) { English = english; } public override void Initialize() { owners = getowners(); } public override void Update() { if (running) { if (compteur > 0) { compteur--; } else { SendText(English ? "You took too long to try a letter." : "Temps imparti écoulé !"); SendText(English ? "Game canceled." : "Partie annulée."); running = false; } } } public override void GetText(string text) { string message = ""; string username = ""; text = getVerbatim(text); if (isPrivateMessage(text, ref message, ref username)) { if (owners.Contains(username.ToUpper())) { switch (message) { case "start": start(); break; case "stop": running = false; break; default: break; } } } else { if (running && isChatMessage(text, ref message, ref username)) { if (message.Length == 1) { char letter = message.ToUpper()[0]; if (letter >= 'A' && letter <= 'Z') { if (letters.Contains(letter)) { SendText(English ? ("Letter " + letter + " has already been tried.") : ("Le " + letter + " a déjà été proposé.")); } else { letters += letter; compteur = compteur_param; if (word.Contains(letter)) { for (int i = 0; i < word.Length; i++) { if (word[i] == letter) { discovered[i] = true; } } SendText(English ? ("Yes, the word contains a " + letter + '!') : ("Le " + letter + " figurait bien dans le mot :)")); } else { vie--; if (vie == 0) { SendText(English ? "Game Over! :]" : "Perdu ! Partie terminée :]"); SendText(English ? ("The word was: " + word) : ("Le mot était : " + word)); running = false; } else SendText(English ? ("The " + letter + "? No.") : ("Le " + letter + " ? Non.")); } if (running) { SendText(English ? ("Mysterious word: " + word_cached + " (lives : " + vie + ")") : ("Mot mystère : " + word_cached + " (vie : " + vie + ")")); } if (winner) { SendText(English ? ("Congrats, " + username + '!') : ("Félicitations, " + username + " !")); running = false; } } } } } } } private void start() { vie = vie_param; running = true; letters = ""; word = chooseword(); compteur = compteur_param; discovered = new bool[word.Length]; SendText(English ? "Hangman v1.0 - By ORelio" : "Pendu v1.0 - Par ORelio"); SendText(English ? ("Mysterious word: " + word_cached + " (lives : " + vie + ")") : ("Mot mystère : " + word_cached + " (vie : " + vie + ")")); SendText(English ? ("Try some letters ... :)") : ("Proposez une lettre ... :)")); } private string chooseword() { if (System.IO.File.Exists(English ? "config/hangman-words.txt" : "config/pendu-mots.txt")) { string[] dico = System.IO.File.ReadAllLines(English ? "words.txt" : "mots.txt"); return dico[new Random().Next(dico.Length)]; } else { LogToConsole(English ? "Cannot find words.txt in config directory !" : "Fichier mots.txt introuvable dans config/hangman dossier!"); return English ? "WORDSAREMISSING" : "DICOMANQUANT"; } } private string[] getowners() { List owners = new List(); owners.Add("CONSOLE"); if (System.IO.File.Exists("config/bot-owners.txt")) { foreach (string s in System.IO.File.ReadAllLines("config/bot-owners.txt")) { owners.Add(s.ToUpper()); } } else LogToConsole(English ? "Cannot find bot-owners.txt in config folder!" : "Fichier bot-owners.txt introuvable dans config!"); return owners.ToArray(); } private string word_cached { get { string printed = ""; for (int i = 0; i < word.Length; i++) { if (discovered[i]) { printed += word[i]; } else printed += '_'; } return printed; } } private bool winner { get { for (int i = 0; i < discovered.Length; i++) { if (!discovered[i]) { return false; } } return true; } } } /// /// This bot make the console beep on some specified words. Useful to detect when someone is talking to you, for example. /// public class Alerts : ChatBot { private string[] dictionary = new string[0]; private string[] excludelist = new string[0]; public override void Initialize() { if (System.IO.File.Exists("config/alerts.txt")) { dictionary = System.IO.File.ReadAllLines("config/alerts.txt"); for (int i = 0; i < dictionary.Length; i++) { dictionary[i] = dictionary[i].ToLower(); } } else LogToConsole("Cannot find alerts.txt in the config folder!"); if (System.IO.File.Exists("config/alerts-exclude.txt")) { excludelist = System.IO.File.ReadAllLines("config/alerts-exclude.txt"); for (int i = 0; i < excludelist.Length; i++) { excludelist[i] = excludelist[i].ToLower(); } } else LogToConsole("Cannot find alerts-exclude.txt in the config folder!"); } public override void GetText(string text) { text = getVerbatim(text); string comp = text.ToLower(); foreach (string alert in dictionary) { if (comp.Contains(alert)) { bool ok = true; foreach (string exclusion in excludelist) { if (comp.Contains(exclusion)) { ok = false; break; } } if (ok) { Console.Beep(); //Text found ! #region Displaying the text with the interesting part highlighted Console.BackgroundColor = ConsoleColor.DarkGray; Console.ForegroundColor = ConsoleColor.White; //Will be used for text displaying string[] temp = comp.Split(alert.Split(','), StringSplitOptions.RemoveEmptyEntries); int p = 0; //Special case : alert in the beginning of the text string test = ""; for (int i = 0; i < alert.Length; i++) { test += comp[i]; } if (test == alert) { Console.BackgroundColor = ConsoleColor.Yellow; Console.ForegroundColor = ConsoleColor.Red; for (int i = 0; i < alert.Length; i++) { ConsoleIO.Write(text[p]); p++; } } //Displaying the rest of the text for (int i = 0; i < temp.Length; i++) { Console.BackgroundColor = ConsoleColor.DarkGray; Console.ForegroundColor = ConsoleColor.White; for (int j = 0; j < temp[i].Length; j++) { ConsoleIO.Write(text[p]); p++; } Console.BackgroundColor = ConsoleColor.Yellow; Console.ForegroundColor = ConsoleColor.Red; try { for (int j = 0; j < alert.Length; j++) { ConsoleIO.Write(text[p]); p++; } } catch (IndexOutOfRangeException) { } } Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.Gray; ConsoleIO.Write('\n'); #endregion } } } } } /// /// This bot saves the received messages in a text file. /// public class ChatLog : ChatBot { public enum MessageFilter { AllText, AllMessages, OnlyChat, OnlyWhispers }; private bool dateandtime; private bool saveOther = true; private bool saveChat = true; private bool savePrivate = true; private string logfile; /// /// This bot saves the messages received in the specified file, with some filters and date/time tagging. /// /// The file to save the log in /// The kind of messages to save /// Add a date and time before each message public ChatLog(string file, MessageFilter filter, bool AddDateAndTime) { dateandtime = AddDateAndTime; logfile = file; switch (filter) { case MessageFilter.AllText: saveOther = true; savePrivate = true; saveChat = true; break; case MessageFilter.AllMessages: saveOther = false; savePrivate = true; saveChat = true; break; case MessageFilter.OnlyChat: saveOther = false; savePrivate = false; saveChat = true; break; case MessageFilter.OnlyWhispers: saveOther = false; savePrivate = true; saveChat = false; break; } } public override void GetText(string text) { text = getVerbatim(text); string sender = ""; string message = ""; if (saveChat && isChatMessage(text, ref message, ref sender)) { save("Chat " + sender + ": " + message); } else if (savePrivate && isPrivateMessage(text, ref message, ref sender)) { save("Private " + sender + ": " + message); } else if (saveOther) { save("Other: " + text); } } private void save(string tosave) { if (dateandtime) { int day = DateTime.Now.Day, month = DateTime.Now.Month; int hour = DateTime.Now.Hour, minute = DateTime.Now.Minute, second = DateTime.Now.Second; string D = day < 10 ? "0" + day : "" + day; string M = month < 10 ? "0" + month : "" + day; string Y = "" + DateTime.Now.Year; string h = hour < 10 ? "0" + hour : "" + hour; string m = minute < 10 ? "0" + minute : "" + minute; string s = second < 10 ? "0" + second : "" + second; tosave = "" + D + '-' + M + '-' + Y + ' ' + h + ':' + m + ':' + s + ' ' + tosave; } System.IO.FileStream stream = new System.IO.FileStream(logfile, System.IO.FileMode.OpenOrCreate); System.IO.StreamWriter writer = new System.IO.StreamWriter(stream); stream.Seek(0, System.IO.SeekOrigin.End); writer.WriteLine(tosave); writer.Dispose(); stream.Close(); } } /// /// This bot automatically re-join the server if kick message contains predefined string (Server is restarting ...) /// public class AutoRelog : ChatBot { private string[] dictionary = new string[0]; private int attempts; private int delay; /// /// This bot automatically re-join the server if kick message contains predefined string /// /// Delay before re-joining the server (in seconds) /// Number of retries if connection fails (-1 = infinite) public AutoRelog(int DelayBeforeRelog, int retries) { attempts = retries; if (attempts == -1) { attempts = int.MaxValue; } McTcpClient.AttemptsLeft = attempts; delay = DelayBeforeRelog; if (delay < 1) { delay = 1; } } public override void Initialize() { McTcpClient.AttemptsLeft = attempts; if (System.IO.File.Exists("config/kickmessages.txt")) { dictionary = System.IO.File.ReadAllLines("config/kickmessages.txt"); for (int i = 0; i < dictionary.Length; i++) { dictionary[i] = dictionary[i].ToLower(); } } else LogToConsole("Cannot find kickmessages.txt in the config directory!"); } public override bool OnDisconnect(DisconnectReason reason, string message) { message = getVerbatim(message); string comp = message.ToLower(); foreach (string msg in dictionary) { if (comp.Contains(msg)) { LogToConsole("Waiting " + delay + " seconds before reconnecting..."); System.Threading.Thread.Sleep(delay * 1000); McTcpClient.AttemptsLeft = attempts; ReconnectToTheServer(); return true; } } return false; } } /// /// Automatically send login command on servers usign the xAuth plugin /// public class xAuth : ChatBot { private string password; private int countdown = 50; public xAuth(string pass) { password = pass; } public override void Update() { countdown--; if (countdown == 0) { SendText("/login " + password); UnloadBot(); //This bot is no more needed. } } } /// /// Runs a list of commands /// Usage: bot:scripting:filename /// Script must be placed in the config directory /// public class scripting : ChatBot { private string file; private string[] lines = new string[0]; public scripting(string filename) { file = filename; } public override void Initialize() { // Loads the given file from the startup parameters if (System.IO.File.Exists("config/" + file)) { lines = System.IO.File.ReadAllLines("config/" + file); // Load the given bot text file (containing commands) for (int i = 0; i < lines.Length; i++) // Parse through each line of the bot text file { System.Threading.Thread.Sleep(100); string this_line = lines[i].Trim(); // Removes all whitespaces at start and end of current line if (this_line.Length == 0) { // Skip a completely empty line } else if (this_line.Trim().StartsWith("//")) { Console.ForegroundColor = ConsoleColor.DarkGray; Console.WriteLine("BOT:" + this_line); Console.ForegroundColor = ConsoleColor.Gray; // Don't do anything for a comment line, denoted by '//' } else if (this_line.StartsWith("send ")) { Console.ForegroundColor = ConsoleColor.Gray; SendText((lines[i].Trim().Substring(5, lines[i].Length - 5))); // Send the command } else if (this_line.StartsWith("wait ")) { Console.ForegroundColor = ConsoleColor.DarkGray; Console.WriteLine("BOT:Pausing for " + Convert.ToInt32(lines[i].Substring(5, lines[i].Length - 5)) * 100 + "ms..."); Console.ForegroundColor = ConsoleColor.Gray; System.Threading.Thread.Sleep(Convert.ToInt32(lines[i].Substring(5, lines[i].Length - 5)) * 100); // Do a wait (given in milliseconds) } else if (this_line.StartsWith("exit")) { Program.B_Client.Disconnect(); } // Optional exit only if called in bot text file, } UnloadBot(); // Otherwise continue operation of Client to normal (non-bot) usage } else { Console.WriteLine(file + " not found! Please make sure that the file is located in the config directory."); } } } } }