////////////////////////////////////////////////
////////////////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: 06/28/2010 //
////////////////////////////////////////////////
using System;
using System.Text;
using System.Threading;
using System.Net;
using System.IO;
using System.Collections;
using System.Xml;
namespace Kon
{
partial class IRC
{
String configFile = "config.xml"; // the Configuration File
#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: //
// ADMIN LEVEL: //
// !addUser (nick), !remUser (nick), !quit //
// USER LEVEL: //
// !reloadAIML, !toggleAIML, //
// !toggleLB, !channelUsers, //
// !utter, !toggleTopic, !randomTalk [#], //
// !join #channel, !part #channel, //
// !toggleDoubleSentences //
// ALL LEVEL: //
// !splash, !konInfo, !brainInfo, !LBinfo //
/////////////////////////////////////////////////
private void incomingCommand(string message, string nick, string location)
{
// 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 CTCPCommands
// Is it a CTCP Ping?
if (command == "PING")
{
// Then let's ping back!
String pingReply = "PING " + commandString[1];
writer.WriteLine("NOTICE " + nick + " :" + pingReply);
writer.Flush();
return;
}
// Is it a CTCP Version Request?
if (command == "VERSION")
{
// Then let's respond back!
String versionReply = "VERSION " + version + "";
writer.WriteLine("NOTICE " + nick + " :" + versionReply);
writer.Flush();
return;
}
#endregion
// Let's check to see if it's a user command first. Now, if it happens to be !addUser or !remUser we
// need to send the user the admin is trying to add/remove. Else,
if (((command == "!ADDUSER") || (command == "!REMUSER") || (command == "!RANDOMTALK") || (command == "!JOIN") || (command == "!PART")))
userCommand(command, commandString[1].ToString(), nick, location);
else
userCommand(command, " ", nick, location);
// Check to see if RandomTalk is going to fire. If so, we'll change the command slightly.
//randnum
Thread.Sleep(50);
int rnd = randnum.Next(100);
if ((rnd <= randomTalk) && (rnd != 0))
{
message = nickNameMain + ":" + " " + message;
command = nickNameMain + ":";
command = command.ToUpper();
}
// Time to see if someone is talking to the bot.
string addressingBot = nickNameMain + ":";
addressingBot = addressingBot.ToUpper();
if ((command == addressingBot) || (command == "KON:"))
{
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] + " ";
}
converse(location, conversation, nick);
}
else
{
// We're going to force the bot to learn something
myAI.addToBrain(message, nick);
if (LB)
{
try
{
myLB.addToBrain(message, nick);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
#endregion
#region converse(location, conversation, channelNick)
private void converse(string location, string conversation, string channelNick)
{
if (!location.StartsWith("#"))
location = channelNick;
// Time to get the list of channel users.
writer.WriteLine("NAMES " + channel);
writer.Flush();
Thread.Sleep(10);
try
{
// Replace some punctuation with spaces to prevent it from going nuts
conversation = conversation.Replace(",", "");
// Set the reply string that will be 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, channelNick);
}
#endregion
#region LBbrain
if (LB)
{
reply = myLB.pullFromBrain(conversation, topics);
if ((doubleSentences) && (reply.Length < 16))
{
Thread.Sleep(10);
string reply2 = myLB.pullFromBrain(reply, true);
string tempReply1 = reply.Remove(reply.Length - 1);
string tempReply2 = reply2.Remove(reply2.Length - 1);
if (tempReply1.ToUpper() != tempReply2.ToUpper())
reply = reply + " " + reply2;
}
// Is the last character a comma, semi-colon or colon? If so, let's remove it.
string lastReplyCharacter = reply.Substring(reply.Length - 1, 1);
if (((lastReplyCharacter == ",") || (lastReplyCharacter == ";") || (lastReplyCharacter == ":")))
reply = reply.Remove(reply.Length - 1, 1);
// Let's clean up the reply a little bit.
if ((reply.StartsWith(" ACTION")) || (reply.StartsWith("ACTION")))
{
reply = reply.Replace("ACTION", "");
reply = reply.Replace("", "");
reply = reply.Trim();
reply = "ACTION " + reply + "";
}
else
{
reply = reply.Replace(" ACTION", " *");
reply = reply.Replace("ACTION", "*");
}
reply = reply.Trim();
}
#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();
// Let's split the reply up and try to replace the UNNAMED_USER with an actual channel user.
string[] words = reply.Split(' ');
int wordCount = words.Length - 1;
for (int i = 0; i <= wordCount; i++)
{
if (words[i].ToString() == "UNNAMED_USER:")
words[i] = channelNick + ":";
else
words[i] = words[i].Replace("UNNAMED_USER", randomChannelUser());
}
// now to rebuild the reply
reply = "";
for (int i = 0; i < words.Length; i++)
{
reply += words[i] + " ";
}
reply = reply.Trim();
// Make sure the reply isn't too long so it doesn't crash the bot.
int replyLength = reply.Length;
if (replyLength >= 440)
reply = reply.Substring(0, 440);
else
reply = reply.Substring(0, replyLength);
// Let's try to correct some grammar
reply = grammarCheck(reply);
// Display the response and log it.
sendToChannel(reply, location);
Console.WriteLine(">> " + location + " : " + reply);
}
catch (Exception e)
{
// Show the exception
Console.WriteLine(e.ToString() + CRLF);
// Sleep, before we try again
Thread.Sleep(1000);
}
}
#endregion
#region grammarCheck(reply)
private string grammarCheck(string reply)
{
reply = reply.Replace("am you ", "are you ");
reply = reply.Replace("are I ", "am I ");
reply = reply.Replace("are i ", "am I ");
reply = reply.Replace("I are ", "I am ");
reply = reply.Replace("i are ", "I am ");
reply = reply.Replace("thats", "that's");
reply = reply.Replace("do you got", "did you get ");
return reply;
}
#endregion
#region UserCommands
private void userCommand(string command, string commandParam, string nick, string location)
{
#region All Level 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!", location);
Console.WriteLine(">> " + channel + " : Kon Splash!");
}
if (command == "!BRAININFO")
{
sendToChannel("My brain contains " + myAI.getBrainLength() + " lines.", location);
}
if (command == "!LBINFO")
{
if (LB)
sendToChannel("My LB brain contains " + myLB.getBrainLength() + " lines.", location);
else
sendToChannel("4LB Brain must be turned on in order to use this command.", location);
}
if (command == "!KONINFO")
{
sendToChannel(version, location);
}
#endregion
#region User Level Commands
if (command == "!RELOADAIML")
{
if ((isUser(nick)) || (isAdmin(nick)))
{
if (AIML)
{
sendToChannel("4Reloading AIML..please wait before using any commands..", location);
Console.WriteLine(">> RELOADING AI");
AIMLbrain.unloadAIMLbrain();
myAIML = null;
myAIML = new AIMLbrain();
// Let's put the thread to sleep while it's reloading.
Thread.Sleep(1300);
sendToChannel("2AI Reloaded!", location);
}
else
sendToChannel("4The AIML brain is not currently in use.", location);
}
else
sendToChannel("4You do not have access to this command.", location);
}
if (command == "!TOGGLEAIML")
{
if ((isUser(nick)) || (isAdmin(nick)))
{
if (AIML)
{
AIML = false;
AIMLbrain.unloadAIMLbrain();
myAIML = null;
sendToChannel("4No longer using AIML for replies...", location);
}
else
{
AIML = true;
LB = false;
sendToChannel("4Loading AIML files..please wait before using any commands..", location);
myAIML = new AIMLbrain();
sendToChannel("4Now using AIML in addition to my brain for replies...", location);
myLB = null;
}
}
else
sendToChannel("4You do not have access to this command.", location);
}
if (command == "!TOGGLELB")
{
if ((isUser(nick)) || (isAdmin(nick)))
{
if (LB)
{
LB = false;
sendToChannel("4No longer using the LB Brain for replies...", location);
myLB = null;
}
else
{
LB = true;
AIML = false;
sendToChannel("4Now using the LB Brain in addition to my brain for replies...", location);
myAIML = null;
myLB = new LBbrain();
}
}
else
sendToChannel("4You do not have access to this command.", location);
}
if (command == "!CHANNELUSERS")
{
if ((isUser(nick)) || (isAdmin(nick)))
sendToChannel(channelUsers, location);
else
sendToChannel("4You do not have access to this command.", location);
}
//Private UTTER.. will send a random channel user a message
if (command == "!PUTTER")
{
if ((isUser(nick)) || (isAdmin(nick)))
converse(location, "a", randomChannelUser());
else
sendToChannel("4You do not have access to this command.", location);
}
// Regular UTTER.. will send a random message to the location the command was used
if (command == "!UTTER")
{
if ((isUser(nick)) || (isAdmin(nick)))
converse(location, "a", nick);
else
sendToChannel("4You do not have access to this command.", location);
}
if (command == "!JOIN")
{
if ((isUser(nick)) || (isAdmin(nick)))
join(commandParam);
else
sendToChannel("4You do not have access to this command.", location);
}
if (command == "!PART")
{
if ((isUser(nick)) || (isAdmin(nick)))
// Get the channel and leave
part(commandParam);
else
sendToChannel("4You do not have access to this command.", location);
}
if (command == "!TOGGLETOPIC")
{
if ((isUser(nick)) || (isAdmin(nick)))
{
if (topics)
{
topics = false;
sendToChannel("4I will no longer try to locate the topic in what you say to me.", location);
}
else
{
topics = true;
sendToChannel("4I will now try to locate the topic in what you say to me.", location);
}
}
else
sendToChannel("4You do not have access to this command.", location);
}
if (command == "!TOGGLEDOUBLESENTENCES")
{
if ((isUser(nick)) || (isAdmin(nick)))
{
if (doubleSentences)
{
doubleSentences = false;
sendToChannel("4I will no longer try to combine two sentences.", location);
}
else
{
doubleSentences = true;
sendToChannel("4I will now try to combine two sentences together.", location);
}
}
else
sendToChannel("4You do not have access to this command.", location);
}
if (command == "!RANDOMTALK")
{
if ((isUser(nick)) || (isAdmin(nick)))
{
if (commandParam == "")
{
sendToChannel("3RandomTalk is currently set to: " + randomTalk + "%", location);
Thread.Sleep(100);
sendToChannel("3To change the percent use: !randomTalk # where # is 0 to 100", location);
}
else
{
int randomTalkP;
try
{
randomTalkP = System.Convert.ToInt32(commandParam);
}
catch (Exception e)
{
sendToChannel("4!randomTalk # where # is 1 to 100", location);
randomTalkP = randomTalk;
}
randomTalk = randomTalkP;
sendToChannel("3RandomTalk now set to: " + randomTalk + "%", location);
Console.WriteLine("** Random Talk changed to: " + randomTalk + "%");
}
}
else
sendToChannel("4You do not have access to this command.", location);
}
if (command == "!DINFO")
{
// For now, the nicks allowed to use this will be hardcoded
if ((isUser(nick)) || (isAdmin(nick)))
{
sendToChannel("Reconnects: " + reconnectAttempts + "/" + retryAttempts + " :: PING control is currently: " + canPing + " :: Ping Attempt #: " + pongAttempt, location);
}
else
sendToChannel("4You do not have access to this command.", location);
}
// Returns the latest Twitter status update, if applicable.
if (command == "!TWITTER")
{
if ((isUser(nick)) || (isAdmin(nick)))
{
sendToChannel("2Current Twitter Status: " + "3" + getCurrentTwitterStatus(), location);
}
else
sendToChannel("4You do not have access to this command.", location);
}
#endregion
#region Admin Level Commands
if (command == "!QUIT")
{
// For now, the nicks allowed to use this will be hardcoded
if (isAdmin(nick))
{
writer.WriteLine("QUIT " + ":" + version);
writer.Flush();
Console.WriteLine("Kon can now be closed");
Thread.Sleep(500);
quit_program();
}
else
sendToChannel("4You do not have access to this command.", location);
}
if (command == "!ADDUSER")
{
// For now, the nicks allowed to use this will be hardcoded
if (isAdmin(nick))
{
if ((commandParam == " ") || (commandParam == ""))
sendToChannel("4A name must be attached to this command. !addUser (name)", location);
else
{
addUser(commandParam);
sendToChannel(commandParam + " added to the list of users", location);
}
}
else
sendToChannel("4You do not have access to this command.", location);
}
if (command == "!REMUSER")
{
// For now, the nicks allowed to use this will be hardcoded
if (isAdmin(nick))
{
if ((commandParam == " ") || (commandParam == ""))
sendToChannel("4A name must be attached to this command. !addUser (name)", location);
else
{
remUser(commandParam);
sendToChannel(commandParam + " removed from the list of users", location);
}
}
else
sendToChannel("4You do not have access to this command.", location);
}
if (command == "!RECONNECT")
{
// For now, the nicks allowed to use this will be hardcoded
if (isAdmin(nick))
{
writer.WriteLine("QUIT :Reconnecting");
writer.Flush();
Thread.Sleep(100);
setConnectionState(false);
}
else
sendToChannel("4You do not have access to this command.", location);
}
#endregion
}
#endregion
#region isAdmin(user)
private bool isAdmin(string user)
{
// Check for the admin and return true if the user matches, false if not.
XmlTextReader configReader = new XmlTextReader(configFile);
configReader.WhitespaceHandling = WhitespaceHandling.None;
configReader.Namespaces = false;
String data = "";
try
{
while (configReader.Read())
{
if (configReader.NodeType == XmlNodeType.Element)
{
if (configReader.LocalName.Equals("admin"))
{
data = configReader.ReadString();
break;
}
}
}
}
catch (Exception e)
{
System.Console.WriteLine("Error found: " + e.ToString() + "\n");
}
configReader.Close();
if (user.ToUpper() == data.ToUpper())
return true;
else
return false;
}
#endregion
#region isUser(user)
private bool isUser(string user)
{
ArrayList userList = new ArrayList();
// Check for the admin and return true if the user matches, false if not.
XmlTextReader configReader = new XmlTextReader(configFile);
configReader.WhitespaceHandling = WhitespaceHandling.None;
configReader.Namespaces = false;
try
{
while (configReader.Read())
{
if (configReader.NodeType == XmlNodeType.Element)
{
if (configReader.LocalName.Equals("user"))
userList.Add(configReader.ReadString());
}
}
}
catch (Exception e)
{
System.Console.WriteLine("Error found: " + e.ToString() + "\n");
}
configReader.Close();
// Now look through the arrayList
foreach (string name in userList)
{
if (name.ToString().ToUpper() == user.ToUpper())
return true;
}
return false;
}
#endregion
#region addUser(user)
private void addUser(string user)
{
// Make an XmlDocument that contains the original XML.
XmlDocument doc = new XmlDocument();
doc.Load(configFile);
// What we want to insert
string toInsert = @"" + user + "";
// Create a document fragment to contain the XML to be inserted.
XmlDocumentFragment docFrag = doc.CreateDocumentFragment();
// Set the contents of the document fragment.
docFrag.InnerXml = toInsert;
// Add the children of the document fragment to the
// original document.
doc.DocumentElement.AppendChild(docFrag);
// Save the new file.
doc.Save(configFile);
}
#endregion
#region remUser(user)
private void remUser(string user)
{
// Create an array list that holds the entire contents of the config file.
if (File.Exists(configFile))
{
ArrayList lines = new ArrayList();
using (StreamReader cFile = File.OpenText(configFile))
{
string line;
while ((line = cFile.ReadLine()) != null)
lines.Add(line);
}
// Erase the old list.
File.Delete(configFile);
// Create a new one, writing all the lines in the array except for the one that's being removed.
string toDelete = @"" + user + "";
foreach (string line in lines)
{
if (line.Trim() != toDelete)
{
using (StreamWriter cFile = File.AppendText(configFile))
{
cFile.WriteLine(line);
}
}
}
}
}
#endregion
}
}