All pastes #904207 Raw Edit

Kon's Old IRC code

public text v1 · immutable
·published 2008-02-14 19:56 UTC
rendered paste body
////////////////////////////////////////////////
////////////////PROGRAM INFO////////////////////
// "Kon" is a "learning bot" written in C#.   //
// The bot will connect to an IRC server and  //
// "watch" the conversations.  It will then   //
// "learn" from them.  You'll then be able to //
// talk to it and hopefully it'll hold a conv-//
// ersation.                                  //
////////////////////////////////////////////////
// "Kon" is an ongoing project started by     // 
// James Iyouboushi.                          //
// Emails: jmp1139@my.gulfcoast.edu           //
//         Iyouboushi@gmail.com               //
////////////////////////////////////////////////
// This file was last updated on: 2/13/2008   //
////////////////////////////////////////////////
////////////////TO DO///////////////////////////
// Some IRC commands need to be cleaned up and//
// touched up.  Need to make it so you can set//
// a list of users that can use the bot's     //
// commands.                                  //
////////////////////////////////////////////////


using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace Kon
{

    class IRC
    {

#region variables

        // Public variables
        public        bool            canQuit             = false;
        public        bool            connected           = false;
            
        // Private variables
        private       int             ircPort             = 6667;
        private       int             numberOfErrorTries  = 0;
        private       string          ircServer           = "bots.esper.net"; 
        private       string          nickNameMain        = "Kon";
        private       string          nickNameBckup       = "KonPlushie";
        private       string          user                = "USER Kon 1 * :SPLASH !!!";
        private       string          channel             = "#KonBot";
        private       string          version             = "Kon - Version 1.0.6 - created by James Iyouboushi [iyouboushi@gmail.com]";
        private       string          rawLine             = "";
        private       string          modifiedLine        = "";
        private       bool            AIML                = false;
        private       bool            google              = false;
        public        bool            logging             = true;
        public        bool            showServerMsgs      = false;
        public        bool            showPongReply       = false;
        private       TcpClient       ircClient;
        private       NetworkStream   stream;
        private       Thread          pingSender;
        private       Thread          listenerThread;
        public static StreamWriter    writer;
        public static StreamWriter    logger;
        public static StreamReader    reader;
        public static AI              myAI;               // Kon's own brain (AI.cs)
        public static AIMLbrain       myAIML;             // Kon's AIML brain (AIML.cs)
        public static googleBrain     myGoogle;           // Kon's Google brain (google.cs)
        
        // Private constants
        private const string          CRLF                = "\r\n";
        private const string          PING                = "PING :";
        private const string          PONG                = "PONG ";
        private const string          LOG_FILE            = "KonLog.txt";

#endregion


#region constructors

        ////////////////////////////////////////////////
        //////////////CONSTRUCTORS//////////////////////
        // There are two constructors.  One will only //
        // use defaults found at the top of this file.//
        // That constructor shouldn't be called but is//
        // merely a failsafe.                         //
        ////////////////////////////////////////////////
        public IRC()
        {
            // Connect to IRC
            connect();
        }

        public IRC(String server, int port, String mainNick, String backupNick, String chan)
        {
            // Validate the incoming information.  Keep in mind that if any of these are wrong it 
            // will use defaults defined at the top of this file.
            if (server != "")
                ircServer = server;
            if (port != 0)
                ircPort = port;
            if (mainNick != "")
                nickNameMain = mainNick;
            if (backupNick != "")
                nickNameBckup = backupNick;
            if (chan != "")
                channel = chan;

            // Connect to IRC using the settings
            connect();
        }

#endregion


#region connect
        private void connect()
        {
            // Tell people we're going to connect
            System.Console.WriteLine("Connecting to " + ircServer + ":" + ircPort + " ...\n");

            try
            {
                // If logging is enabled, let's turn it on and indicate what time we started
                if (logging == true)
                {
                    logger = new StreamWriter(LOG_FILE, true);
                    logger.WriteLine("---------[STARTING: " + DateTime.Now + "]");
                }

                // Try to make a connection to the server
                ircClient = new TcpClient(ircServer, ircPort);

                stream = ircClient.GetStream();
                reader = new StreamReader(stream);
                writer = new StreamWriter(stream);

                // Start up our ping handler
                PingControl();

                // Send the ident information to the server (i.e. our user and who we are)
                writer.WriteLine(user);
                writer.Flush();

                // Need to add the ability to switch from primary to backup nick if the first is taken
                writer.WriteLine("NICK " + nickNameMain);
                writer.Flush();
             
                // We're connected, so let's set our bool to true.
                connected = true;

                // Start up our incoming data listener
                IncomingData();

                // Let's turn on the AI stuff.
                myAI = new AI();

                if (google == true)
                    myGoogle = new googleBrain();

                if (AIML == true) 
                    myAIML = new AIMLbrain();


                // Auto join the channel, if there is one
                if (channel != "")
                {
                    Thread.Sleep(500); 
                    writer.WriteLine("JOIN " + channel);
                    writer.Flush();
                }

            }
            catch (Exception e)
            {
                // Show the exception
                Console.WriteLine(e.ToString() + CRLF);

                // Sleep, before we try again
                Thread.Sleep(5000);
                connect();
            }
        }
#endregion


#region quit
        private void quit()
        {
            // Message that we're quitting
            writer.WriteLine("QUIT ");
            writer.Flush();

            // Exit out of the program.
            quit_program();
        }

        private void quit(string message)
        {
            // Message that we're quitting
            writer.WriteLine("QUIT " + ":" + message);
            writer.Flush();

            // Exit out of the program.
            quit_program();
        }

        private void quit_program()
        {

            // If logging is enabled, let's write that we're quitting.
            if (logging == true)
            {
                logger.WriteLine("---------[QUITTING: " + DateTime.Now + "]");
                logger.Close();
            }

            // Close everything down
            connected = false;
            stream.Close();
            writer.Close();
            reader.Close();
            ircClient.Close();

            // Tell the console that we can quit and release control
            canQuit = true;

            // Slow it down for a second
            Thread.Sleep(1000);
        }
#endregion


#region joinChannel
        // Join a channel, no key
        private void join(string chan)
        {
            if (channel == "")
            {
                channel = chan;
                writer.WriteLine("JOIN " + channel);
                writer.Flush();
            }

            else
            {
                Console.WriteLine("This client only supports one channel right now.\n");
            }
        }

        // Join a channel with a key
        private void join(string channel, string key)
        {
        }
#endregion


#region partChannel
        private void part(string chan)
        {

            // tell the server that we're leaving the channel.
            writer.WriteLine("PART " + chan);
            writer.Flush();

            // tell the user that we've parted.
            Console.WriteLine("LEFT " + chan);

            // let's set the channel to null.
            channel = "";
        }
#endregion


#region sendToChannel
        // Send messages something to the channel
        private void sendToChannel(string message)
        {
            // Let's pause a moment before sending the message
            Thread.Sleep(500);

            // Create the message
            writer.WriteLine("PRIVMSG " + channel + " :" + message);

            // Log it, if the logging option is on
            LogFile(">> " + channel + " : "  + message);
            
            // Flush it
            writer.Flush();
        }
#endregion


#region ConsoleCommands
        ////////////////////////////////////////////////
        /////////////CONSOLE COMMAND PARSER/////////////
        // The command parser takes the incoming text //
        // from the console and splits it up into     // 
        // tokens as well as determines what command  //
        // has been used and what function should be  //
        // called.                                    //
        ////////////////////////////////////////////////
        public void getCommand()
        {

            // Get the command string from the console
            string rawCommandString = Console.ReadLine();

            // Break the command into their tokenized parts so we can determine what to do
            string[] commandString;
            commandString = rawCommandString.Split(new char[] { ' ' });

            // Get the first element of the command string and convert it to upper case
            string command = commandString[0].ToString();
            command = command.ToUpper();


            ///////////////////////////////////////////////////
            // The QUIT command.  Used to disconnect from the//
            // server.  It'll also cause our console to stop.//
            ///////////////////////////////////////////////////
            if (command == "/QUIT")
            {
                // We need to check to see if the person has typed a quit message
                int commandLength = commandString.Length;

                if (commandLength >= 2)
                {
                    // Determine what the message is.
                    commandLength = commandString.Length - 1;
                    string message = "";

                    for (int i = 1; i <= commandLength; i++)
                    {
                        message = message + commandString[i] + " ";
                    }

                    // Let's quit with our message
                    quit(message);
                }

                else
                    quit(version);  // Quit with a default message of the version.
            }


            ///////////////////////////////////////////////////
            // The JOIN command.  Used to join an IRC channel//
            // and may have a key (if the channel is locked) //
            ///////////////////////////////////////////////////
            if (((command == "/JOIN") || (command == "/J")))
            {
                // Set our channel
                string chan = commandString[1].ToString();

                // Does the channel have the necessary #?
                if (!chan.StartsWith("#"))  
                {
                   // We'll add it here.
                    chan = "#" + commandString[1].ToString();
                }


                // Does this join command have a key for the channel?
                // Check for the max number of words in the array first.
                    int commandLength = commandString.Length;

                    if (commandLength >= 3)
                    {
                        // Is the next word a space or null?
                        if (commandString[2].ToString() != "")
                        {
                            // We have a key!  Let's get the key and join the channel.
                            string key = commandString[2].ToString();
                            join(chan, key);
                        }
                    }

                // No, it doesn't, so just join a regular channel
                    else
                        join(chan);
            }


            ///////////////////////////////////////////////////
            // The PART command.  Used to leave a channel    //
            ///////////////////////////////////////////////////
            if (((command == "/PART") || (command == "/LEAVE")))
            {
                if (channel != "")
                    part(channel);
            }

            ///////////////////////////////////////////////////
            // The "Say" command to send a message to the    //
            // active channel.                               //
            ///////////////////////////////////////////////////
            if (command == "/SAY")
            {
                if (channel != "")
                {
                    // Determine what the message is.
                    int commandLength = commandString.Length -1;
                    string message = "";

                    for (int i = 1; i <= commandLength; i++)
                    {
                        message = message + commandString[i] + " ";
                    }

                    // Send the message to the command
                    sendToChannel(message);
                }

                else
                {
                    Console.WriteLine("You must first join a channel.\n");
                }
            }

        }

#endregion


#region PingControl
        ////////////////////////////////////////////////
        ////////////////PING CONROL/////////////////////
        ////////////////////////////////////////////////
        // PING control is a set of two functions that//
        // will ping the server every 15 seconds on a //
        // seperate thread until we are no longer     //
        // connected to the server.                   //
        ////////////////////////////////////////////////
        private void PingControl() 
        {
            // Start a new thread for the Ping Control
            pingSender = new Thread (new ThreadStart(PingRun));
            pingSender.Start();

            // Begin running the control
            PingRun();
        }  


        // Send PING to irc server every 15 seconds
        private void PingRun()
        {
            try
            {

                // Is the client still running?  If so, we need to ping the server
                while ((canQuit == false) && (connected == true))
                {
                    writer.WriteLine(PING + ircServer);
                    writer.Flush();
                    Thread.Sleep(15000);
                }
            }
            catch (Exception e)
            {
                // Show the exception
                Console.WriteLine(e.ToString() + CRLF);

                // Sleep, before we try again
                Thread.Sleep(5000);
            }
        }

#endregion


#region IncomingData
        ////////////////////////////////////////////////
        /////////////INCOMING DATA//////////////////////
        ////////////////////////////////////////////////
        // These methods will handle the incoming     //
        // data and process it.                       //
        ////////////////////////////////////////////////

        private void IncomingData()
        {
            // Start a new thread for the incoming data
            listenerThread = new Thread(new ThreadStart(ReceiveData));
            listenerThread.Start();
        }

        private void ReceiveData()
        {
            while (connected == true)
            {

                try
                {
                    rawLine = reader.ReadLine();

                    // We have to take a looksie at the line and determine if it's going to be shown/logged.
                    // Break the raw line into tokenized parts so we can determine what to do
                    string[] IncomingDataString;
                    IncomingDataString = rawLine.Split(new char[] { ' ' });

                    // First priority is the PING event.  If the server is pinging us, we need to PONG back or else it'll disconnect us.
                    if (IncomingDataString[0].ToString() == "PING")
                    {
                        writer.WriteLine(PONG + IncomingDataString[1].ToString());   // it has to respond to the server sending
                        writer.Flush();
                    }
                    // Is this incoming data NOTICES, QUITs, JOINs, NICKs, or MODEs?  We need to display those.
                    if ((((((IncomingDataString[1].ToString() == "JOIN") || (IncomingDataString[1].ToString() == "NOTICE") || (IncomingDataString[1].ToString() == "QUIT") || (IncomingDataString[1].ToString() == "NICK") || (IncomingDataString[1].ToString() == "PART") || (IncomingDataString[1].ToString() == "MODE"))))))
                        DisplayData(rawLine);

                    // Is this incoming data the server info? Might be important to show it.  
                    if (((((IncomingDataString[1].ToString() == "001") || (IncomingDataString[1].ToString() == "002") || (IncomingDataString[1].ToString() == "003") || (IncomingDataString[1].ToString() == "004") || (IncomingDataString[1].ToString() == "005"))))) //&& (showServerMsgs == true))
                        DisplayData(rawLine);

                    // Is this incoming data the MOTD of the server?  If so, are we going to allow it to be shown?
                    if ((((IncomingDataString[1].ToString() == "375") || (IncomingDataString[1].ToString() == "372") || (IncomingDataString[1].ToString() == "376"))) && (showServerMsgs))
                        DisplayData(rawLine);

                    // Is this incoming data the PONG reply?  If so, are we going to allow it to be shown?
                    if ((IncomingDataString[1].ToString() == "PONG") && (showPongReply))
                        DisplayData(rawLine);

                    // For now, we'll make it a little easier to read the chat lines and not worry about deciphering commands
                    if (IncomingDataString[1].ToString() == "PRIVMSG")
                    {
                        // Determine what the message is.
                        int msgLength = IncomingDataString.Length - 1;
                        string message = "";

                        for (int i = 3; i <= msgLength; i++)
                        {
                            message = message + IncomingDataString[i] + " ";
                        }

                        // Let's chop off the ":" that starts each message, leaving us with the true message.
                        message = message.TrimStart(':');

                        // We have the message, let's get the nick.
                        string nickname = getNick(IncomingDataString[0].ToString());

                        modifiedLine = "<" + nickname + "> " + message;

                        // Okay, we're ready to display/log it.
                        DisplayData(modifiedLine);

                        // And now we're ready to see if it's a command the bot will recognize.
                        incomingCommand(message, nickname);
                    }

 //                   else
 //                   {
                        // For testing and debugging purposes, if it's a message that isn't recognized and isn't PONG, we're going to dump it into a file.
 //                       using (StreamWriter dumpFile = File.AppendText("DumpFile.txt"))
 //                       {
 //                           if (IncomingDataString[1].ToString() != "PONG")
 //                               dumpFile.Write(rawLine + CRLF);
 //                       }
 //                   }

                }
                catch (Exception e)
                {
                    // Show the exception
                    Console.WriteLine(e.ToString() + CRLF);

                    // Increase the number of error tries
                    numberOfErrorTries++;

                    // Sleep, before we try again
                    Thread.Sleep(5000);

                    // This is a way to stop the infinite loop. 
                    if (numberOfErrorTries > 3)
                        connected = false;
                }

            }
        }

        private void DisplayData(string rawLine)
        {
            // If logging is enabled, let's write it to a log file.
            LogFile(rawLine);

            // Write the line to the console, after a small throttle.
            Thread.Sleep(100);
            Console.WriteLine(rawLine);
        }


        private string getNick(string rawName)
        {
            string nickname = "";
            int nameLength = rawName.Length - 1;

            for (int i = 1; i <= nameLength; i++)
            {
                if (rawName[i].ToString() == "!")
                    break;
                else
                    nickname = nickname + rawName[i];
            }

            return nickname;
        }
#endregion


#region IncomingCommandParser
        ////////////////////////////////////////////////
        /////////////INCOMING COMMAND PARSER////////////
        // The command parser takes the incoming text //
        // from the channel and determines if it's a  // 
        // command it recognizes and will perform it, //
        // if it is.                                  //
        ////////////////////////////////////////////////
        // Available Commands:                        //
        // !splash, !quit, !reload, !reloadAIML,      //
        // !toggleAIML, !toggleGoogle, !braininfo     // 
        ////////////////////////////////////////////////


        private void incomingCommand(string message, string nick)
        {

            // Break the command into their tokenized parts so we can determine what to do
            string[] commandString;
            commandString = message.Split(new char[] { ' ' });

            // Get the first element of the command string and convert it to upper case
            string command = commandString[0].ToString();
            command = command.ToUpper();

 #region Commands
            if (command == "!SPLASH")
            {
                Thread.Sleep(1000);   // throttle it so it won't commit a flood if tons of people use it.
                sendToChannel("Kon Splash!");
                Console.WriteLine(">> " + channel + " : Kon Splash!");
            }

            if (command == "!QUIT")
            {
                // For now, the nicks allowed to use this will be hardcoded
                if (nick == "Iyouboushi")
                {
                    writer.WriteLine("QUIT " + ":" + version);
                    writer.Flush();

                    Console.WriteLine("Kon can now be closed");
                    Thread.Sleep(500);
                    quit_program();
                }
            }

            if (command == "!RELOADAIML")
            {
                // For now, the nicks allowed to use this will be hardcoded
                if (nick == "Iyouboushi")
                {
                    sendToChannel("4Reloading AIML..please wait before using any commands..");
                    Console.WriteLine(">> RELOADING AI");
                    myAI = null;
                    myAI = new AI();

                    // Let's put the thread to sleep while it's reloading.
                    Thread.Sleep(1300);
                    sendToChannel("2AI Reloaded!");
                }
            }

            if (command == "!TOGGLEAIML")
            {
                // For now, the nicks allowed to use this will be hardcoded
                if (nick == "Iyouboushi")
                {
                    if (AIML)
                    {
                        AIML = false;
                        myAIML = null;
                        sendToChannel("4No longer using AIML for replies...");
                    }
                    else
                    {
                        AIML = true;
                        google = false;  // Can't run both google and AIML.
                        sendToChannel("4Loading AIML files..please wait before using any commands..");
                        myAIML = new AIMLbrain();
                        sendToChannel("4Now using AIML in addition to my brain for replies...");
                    }
                }
            }

            if (command == "!TOGGLEGOOGLE")
            {
                // For now, the nicks allowed to use this will be hardcoded
                if (nick == "Iyouboushi")
                {
                    if (google)
                    {
                        google = false;
                        sendToChannel("4No longer using Google for replies...");
                        myGoogle = null;
                    }
                    else
                    {
                        google = true;
                        AIML = false;  // Can't run both google and AIML.
                        sendToChannel("4Now using Google in addition to my brain for replies...");
                        myGoogle = new googleBrain();
                        myAIML = null;
                    }
                }
            }

            if (command == "!BRAININFO")
            {
                sendToChannel("My brain contains " + myAI.getBrainLength() + " lines.");
            }
#endregion


            // Time to see if someone is talking to the bot.
            string addressingBot = nickNameMain + ":";
            addressingBot = addressingBot.ToUpper();

            if (command == addressingBot)
            {
                string conversation = "";
                string[] messageWithoutCommand;
                messageWithoutCommand = message.Split(new char[] { ' ' });
                int msgLength = messageWithoutCommand.Length - 1;

                for (int i = 1; i <= msgLength; i++)
                {
                    conversation = conversation + messageWithoutCommand[i] + " ";
                }


                // Check to see if it's just an emoticon.. and if so.. let's not pass it.
                if (conversation.StartsWith(":"))
                {

                    // Let's pull something from Kon's brain.
                    string reply = myAI.pullFromBrain();
                    reply = reply.Replace("un-named user", nick);
                    reply = reply.Replace("UNNAMED USER", nick);
                    sendToChannel(reply);
                    Console.WriteLine(">> " + channel + " : " + reply);
                    Thread.Sleep(500);
                }

                if (conversation.StartsWith(";"))
                {
                    // Pull something from Kon's random brain
                    string reply = myAI.pullFromBrain();
                    reply = reply.Replace("un-named user", nick);
                    reply = reply.Replace("UNNAMED USER", nick);
                    sendToChannel(reply);
                    Console.WriteLine(">> " + channel + " : " + reply);
                    Thread.Sleep(500);
                }

                if (conversation.StartsWith("kill me"))
                {
                    // Pull something from Kon's random brain
                    string reply = myAI.pullFromBrain();
                    reply = reply.Replace("un-named user", nick);
                    reply = reply.Replace("UNNAMED USER", nick);
                    sendToChannel(reply);
                    Console.WriteLine(">> " + channel + " : " + reply);
                    Thread.Sleep(500);
                }

                else
                {
                    try
                    {
                            
                        // Replace all punctuation with spaces to prevent it from going nuts
                        conversation = conversation.Replace(".", "");
                        conversation = conversation.Replace("!", "");
                        conversation = conversation.Replace(",", "");
                        conversation = conversation.Replace("?", "");

                        // Get the reply from the bot
                        
                        String reply = "";

                        // Throttle it a little bit in case there's too many people talking to it at once.
                        Thread.Sleep(500);

 #region AIMLbrain
                        // If AIML is enabled we'll use the AIML files to find a reply for the user.
                        if (AIML)
                        {
                            // Let's get the reply from the AIML structure
                            reply = AIMLbrain.getReply(reply, conversation, nick);
                        }
#endregion

 #region googleBrain
                        // If the google method is enabled, we'll turn to the web to pull out a response.
                        if (google)
                        {
                            conversation = conversation.Replace("?", "");
                            conversation.Trim();
                            reply = myGoogle.getReply(conversation);
                            reply = reply.Replace("UNNAMED USER", nick);
                        }
#endregion


                        // This is a last ditch effort to try and respond to the user. If the other methods have failed,
                        // this will reach into kon's most basic brain and pull out a reply.
                        if (reply == "")
                        {
                            reply = myAI.pullFromBrain(); 
                            reply = reply.Replace("un-named user", nick);   // this will change later.
                            reply = reply.Replace("UNNAMED USER", nick);    // so will this.
                        }

                        // Make sure the reply isn't too long so it doesn't crash the bot.
                        int replyLength = reply.Length;
                        if (replyLength >= 150)
                            reply = reply.Substring(0, 150);
                        else
                            reply = reply.Substring(0, replyLength);

                        // Display the response and log it.
                        sendToChannel(reply);
                        Console.WriteLine(">> " + channel + " : " + reply);
                    }
                    catch (Exception e)
                    {
                        // Show the exception
                        Console.WriteLine(e.ToString() + CRLF);

                        // Sleep, before we try again
                        Thread.Sleep(1000);
                    }
                }

            }

            else
            {
                // We're going to force the bot to learn something
                myAI.addToBrain(message, nick);
            }


        }
#endregion


#region logger
        ////////////////////////////////////////////////
        ///////////////////LOGGER///////////////////////
        ////////////////////////////////////////////////
        // Dump the lines into a log file for later   //
        // review.                                    //
        ////////////////////////////////////////////////
        private void LogFile(string line)
        {

            if (logging == true) 
            {
                logger.WriteLine(line);
                logger.Flush();
                return;
            }
        }
#endregion


    }
}