PDA

View Full Version : IRC Channel Bot


xanderman
08-24-2010, 11:14 AM
So this idea just poped into my head, so i put it into some code.

Lets be clear from the start here, this is not ment to be an end-user client.

This is a irc bot to manage your channel. It contains a plugin system, which makes creating features for it insanly easy.

Lets take a look at...I've placed comments in the code so im not going to go too indepth. Pay attention to the directory stucture.


This is whats calling it all (What you would run from your php executable)
Dir Location: IRC_Bot/start_bot.php
<?php
//==================================================================================================== ==//
// PHP IRC Bot v1.0 //
// Developed by James C Alexander //
// Released under the GNL Licence //
//==================================================================================================== ==//
// Instalizing the IRC Client //
// Settings: //
// $server_host (IP or DNS of the IRC Server) //
// $server_port (Port server is listening for connections on [6667 = default]) //
// $bot_nick (Name of your IRC Bot) //
// $server_channel (Channel for bot to connect to) //
// $server_timeout (Time in seconds for the bot to "give up" its attempt to connect to the server //
// $irc_Client->LogFile (Defines the path to create a log file of IRC actions) //
// Global Vars: $irc_Client (This is defined globaly so we can access class functions from a plugin //
//==================================================================================================== ==//
global $irc_Client;
require("Engine/engine.php");
$irc_Client = new IRC();

$server_host = "irc.rizon.net";
$server_port = 6667;
$bot_nick = "PHP_IRC_Bot";
$server_channel = "#yourchannel";
$server_timeout = 10;
$irc_Client->LogFile = "IRC_Log.log";

$irc_Client->Connect($server_host, $server_port, $bot_nick, $server_channel, $server_timeout);
?>


Here is the main IRC class.
Dir Location: IRC_Bot/Engine/engine.php
<?php
//==================================================================================================== =============
// PHP IRC Bot v1.0
// Developed by James C Alexander
// Released under the GNL Licence
//==================================================================================================== =============
// Functions:
//==================================================================================================== =============
// Connect($ip, $port, $nick, $chan, $timeout, $pass)
// Opens a TCP Connection to the IRC Server
// Sets the curret State to "LOGGING_IN" when connection is first opened,
// then calls SockOpened() to process the input
//==================================================================================================== =============
// SockOpened()
// Called by "Connect" if the socket connected
// This is the loop that reads the input buffer from the server
// When State is set to "LOGGING_IN" it sends our nick and user commands, and pass if applicable
// and waits for the end of the MOTD to send JOIN command, then sets the state to "LOGGED_IN"
// which then loads the plugins, and parses input from channel, notices, private messages, joins,
// parts, and quit commands.
//==================================================================================================== =============
// LoggingIn($input)
// Called by SockOpened when state is "LOGGING_IN" This function handles our NICK, USER, and JOIN
// Commands. It also cleans up some of the input, before its echo'd to the console.
//==================================================================================================== =============
// LoggedIn($input)
// Called by SockOpened when stat is "LOGGED_IN". This function instalizes, and sends data to our
// Plugins, aswell as cleaning up input for console output.
//==================================================================================================== =============
// ProcessInput($input)
// Called By LoggedIn and LogginIn, this functions responds to PING and VERSION requests.
//==================================================================================================== =============
// ParseUserData ($from)
// Splits the Nick name from the Host name.
// function SendMessage ($to, $message)
//==================================================================================================== =============
// BreakLine ($line)
// Splits the raw input from the server, into some variables we can minuplate...
// (Where it came from, what type of input is it? and what was the message
//==================================================================================================== =============
// LoadPlugins()
// Loads Plugins Located in the Plugins folder
//==================================================================================================== =============

//==================================================================================================== =============
// The Following functions may be usefull for plugin developers
//==================================================================================================== =============

//==================================================================================================== =============
// SendNotice ($to, $message)
// Sends a notice ($message) to an individual ($to)
//==================================================================================================== =============
// SendMessage ($to, $message)
// Sends a PRIVMSG to an individual or channel
//==================================================================================================== =============
// SendData ($raw_data)
// Sends a Raw command to the server (example: MODE #channelName +o nickname)
//==================================================================================================== =============
// DisplayError ($type, $msg)
// Displays an error message, if $type == "CRITICAL" it will execute the die() command
//==================================================================================================== =============
// LogMessage ($from, $message)
// Logs an incomming message to the logfile and output window
//==================================================================================================== =============
// LogLine($line, $die = false)
// Logs a line to the logfile, if $die is set to true, die is called
// Normal Usage $irc_Client->LogLine("I DID SOMETHING!");
//==================================================================================================== =============
// Settings:
// Global Vars: $irc_Client (This is defined globaly so we can access class functions from a plugin
//==================================================================================================== =============
global $irc_Client;
class IRC
{
var $sAddr;
var $sPort;
var $sNick;
var $sName;
var $sChan;
var $sPass;
var $ReceiveTextHandler;
var $sqlHandler;
var $tcpSocket;
var $sockErrNo;
var $sockErrStr;
var $sockTimeout;
var $LogFile;
var $sLastCmd;
var $bInitCmdSend;
var $Version;
var $Plugins = array();
function Connect($ip, $port, $nick, $chan, $timeout, $pass = "")
{
$this->sAddr = gethostbyname($ip);
$this->sPort = $port;
$this->sNick = $nick;
$this->sPass = $pass;
$this->Version = "PHP IRC v1.0";
$this->sName = $nick . " 0 * :PHP IRC";
$this->sChan = $chan;
$this->sockTimeout = $timeout;
$this->NewLine = chr(13).chr(10);
if($this->tcpSock = @fsockopen($this->sAddr, $this->sPort, $this->sockErrNo, $this->sockErrStr, $this->sockTimeout))
{
$this->State = "LOGGING_IN";
$this->SockOpened();
}
else
$this->DisplayError("CRITICAL", "Failed to connect to ".$this->sAddr.":".$this->sPort."! The following error occured: ".$this->sockErrStr." (".$this->sockErrNo.")");
}
function SockOpened ()
{
while(!feof($this->tcpSock))
{
$receive_buffer = @fgets($this->tcpSock, 1024);
switch($this->State)
{
case "LOGGING_IN":
$this->LoggingIn($receive_buffer);
break;
case "LOGGED_IN":
$this->LoggedIn($receive_buffer);
break;
default:
}
}
}
function LoggingIn ($input)
{
if(!$this->bInitCmdSend)
{
$this->SendData("NICK ".$this->sNick);
$this->SendData("USER ".$this->sName);
if(!empty($this->sPass))
$this->SendData("PASS ".$this->sPass);
sleep(1);
$this->bInitCmdSend = true;
}
$aInput = $this->BreakLine($input);
extract($aInput);
$this->LogMessage("NOTICE", $msg);
if(is_numeric($type))
{
switch($type)
{
case "376":
$this->State = "LOGGED_IN";
$this->SendData("JOIN ".$this->sChan);
$this->bInitCmdSend = false;
break;
default:
$this->ProcessInput($msg);
break;
}
}
else
{
$this->ProcessInput($input);
}
}
function LoggedIn ($input)
{
if(!$this->bInitCmdSend)
{
$this->bInitCmdSend = true;
$this->LoadPlugins();
}
else
{
$aInput = $this->BreakLine($input);
extract($aInput);
$userInfo = $this->ParseUserData($from);
$Instagator['nick'] = $userInfo[0];
$hostParts = explode("@", $userInfo[1]);
$Instagator['host'] = array('user' => $hostParts[0], 'hostname' => $hostParts[1]);
switch($type)
{
case "JOIN":
foreach ($this->Plugins as $kids)
$kids->HandleJoin($Instagator, $msg);
case "PART":
foreach ($this->Plugins as $kids)
$kids->HandlePart($Instagator, $msg);
case "QUIT":
foreach ($this->Plugins as $kids)
$kids->HandleQuit($Instagator, $msg);
case "KICK":
foreach ($this->Plugins as $kids)
$kids->HandleKick($Instagator, $msg);
case "NOTICE":
foreach ($this->Plugins as $kids)
$kids->HandleNotice($Instagator, $msg);
case "PRIVMSG":
foreach ($this->Plugins as $kids)
$kids->HandlePRIVMsg($Instagator, $msg, !($wheretoo == $this->sChan));
default:
$this->ProcessInput($input);
}
}
}
function LoadPlugins ()
{
foreach ($this->Plugins as $loaded)
unset($loaded);
$lplugins = opendir("IRC_Bot/Plugins");
while(($plugin_name = readdir($lplugins)) !== false)
{
if(substr($plugin_name, 0, 1) != ".")
{
if(!class_exists($plugin_name))
include("IRC_Bot/Plugins/".$plugin_name."/plugin.php");
$this->Plugins[] = new $plugin_name;
}
}
closedir($lplugins);
}
function ProcessInput ($input)
{
if(substr($input, 0, 6) == "PING :")
{
$this->SendData("PONG :".substr($input, 6));
}
$aInput = $this->BreakLine($input);
extract($aInput);
if($type == "PRIVMSG")
{
if(strstr($msg, "VERSION"))
{
$userInfo = $this->ParseUserData($from);
$this->LogMessage($userInfo[0], $msg);
$this->SendMessage($userInfo[0], $this->Version);
}
}
}
function ParseUserData ($from)
{
$user = explode("!", $from);
return $user;
}
function SendMessage ($to, $message)
{
$this->SendData("PRIVMSG $to :$message");
}
function SendNotice ($to, $message)
{
$this->SendData("NOTICE $to :$message");
}
function SendData ($raw_data)
{
if(!strstr($raw_data,$this->NewLine))
{
$raw_data .= $this->NewLine;
}
$this->LogLine("[Send] ".$raw_data);
fputs($this->tcpSock, $raw_data, strlen($raw_data));
}
function BreakLine ($line)
{
$line = str_replace(chr(10), "", $line);
$line = str_replace(chr(13), "", $line);
$line = substr($line, 1, strlen($line) - 1);
$aline = explode(" ", $line);
$return['from'] = $aline[0];
$return['type'] = $aline[1];
$return['whereto'] = $aline[2];
$return['msg'] = substr(implode(" ", array_splice($aline, 3)),1);
return $return;
}
function DisplayError ($type, $msg)
{
if($type == "CRITICAL")
$this->LogLine("[".$type."] ".$msg, true);
else
$this->LogLine("[".$type."] ".$msg);
}
function LogMessage ($from, $message)
{
$this->LogLine("[$from] $message");
}
function LogLine($line, $die = false)
{
$logPointer = @fopen($this->LogFile, "a");
if(!strstr($line,$this->NewLine))
{
$line .= $this->NewLine;
}
@fwrite($logPointer, $line, strlen($line));
@fclose($logPointer);
if($die)
die($line);
else
echo($line);
}
}
?>


Plugin Template

Dir Location: IRC_Bot/Plugins/PluginFrameWork/plugin.php
<?php
//==========================================================================//
// Plugin_FrameWork //
// Created By James C Alexander //
//==========================================================================//
// This Framework is the basis to create plugins for the //
// IRC Bot. HandleNotice, HandlePRIVMsg, HandleJoin, //
// HandlePart, HandleKick, and HandleQuit are called //
// From the Bot Engine, when the respective event //
// Takes Place on the IRC Server //
// $Instagator is an array consisting of 2 elements //
// (['nick'], and ['host']) ['host'] contains a 2 element //
// Array containing the following keys (['user'], and ['hostname']) //
// $message Contains the respective message for the Instagated action //
// Lastly, for HandlePRIVMsg $tome will be set to "true" if it was a //
// Private Message to the Bot, and not the channel //
// NOTE: The class name needs to be the same name as your plugin directory //
// IE if my plugin.php is in the "UserMode" folder my class name needs to //
// be UserMode
//==========================================================================//
class PluginFrameWork
{
function HandleNotice ($Instagator, $message) {}

function HandlePRIVMsg ($Instagator, $message, $tome = false) {}

function HandleJoin ($Instagator, $message) {}

function HandlePart ($Instagator, $message) {}

function HandleKick ($Instagator, $message) {}

function HandleQuit ($Instagator, $message) {}
}
?>

and here is an example plugin
Dir Location IRC_Bot/Plugins/UserMode/plugin.php

<?php
global $irc_Client;
class UserMode
{
function HandleNotice ($Instagator, $message)
{
}

function HandlePRIVMsg ($Instagator, $message, $tome = false)
{
global $irc_Client;
if(substr($message, 0, 1) == "!")
{
if($this->isAdmin($Instagator['host']['hostname']))
{
$parts = $this->GetCommand($message);
$cmd = $parts[0];
$who = $parts[1];
switch ($cmd)
{
case "!op":
$irc_Client->SendData("MODE ".$irc_Client->sChan." +o ".$who);
break;
case "!deop":
$irc_Client->SendData("MODE ".$irc_Client->sChan." -o ".$who);
break;
case "!hop":
$irc_Client->SendData("MODE ".$irc_Client->sChan." +h ".$who);
break;
case "!dehop":
$irc_Client->SendData("MODE ".$irc_Client->sChan." -h ".$who);
break;
case "!voice":
$irc_Client->SendData("MODE ".$irc_Client->sChan." +v ".$who);
break;
case "!devoice":
$irc_Client->SendData("MODE ".$irc_Client->sChan." -v ".$who);
break;
case "!addadmin":
$this->AddAdmin($who);
break;
case "!removeadmin":
$this->RemoveAdmin($who);
break;
case "!adminlist":
$this->SendAdminList($Instagator['nick']);
break;
}
}
else
{
$irc_Client->SendNotice($Instagator['nick'], "Sorry, you are not on the admin list!");
}
}
}

function HandleJoin ($Instagator, $message)
{
global $irc_Client;
if($this->isAdmin($Instagator['host']['hostname']))
$irc_Client->SendData("MODE ".$irc_Client->sChan." +o ".$Instagator['nick']);
}

function HandlePart ($Instagator, $message)
{

}

function HandleKick ($Instagator, $message)
{

}

function HandleQuit ($Instagator, $message)
{

}

function AddAdmin ($host, $mode = "a")
{
global $irc_Client;
$pointer = @fopen("admin.dat", $mode);
$host .= $irc_Client->NewLine;
@fwrite($pointer, $host, strlen($host));
@fclose($pointer);
}
function RemoveAdmin ($host)
{
global $irc_Client;
$newAdmins = "";
$admins = file("admin.dat");
foreach ($admins as $admin)
{
$admin = str_replace($irc_Client->NewLine, "", $admin);
if($host != $admin)
$newAdmins .= $admin.$irc_Client->NewLine;
}
$this->AddAdmin($newAdmins, "w");
}
function SendAdminList ($who)
{
global $irc_Client;
$admins = file("admin.dat");
foreach ($admins as $admin)
{
$admin = str_replace($irc_Client->NewLine, "", $admin);
if(!empty($admin))
$irc_Client->SendNotice($who, $admin);
sleep(1);
}
$irc_Client->SendNotice($who, "==========END OF ADMIN LIST==========");
}
function isAdmin ($host)
{
global $irc_Client;
$admins = file("admin.dat");
foreach ($admins as $admin)
{
$admin = str_replace($irc_Client->NewLine, "", $admin);
if($host == $admin)
return true;
}
return false;
}
function GetCommand ($msg)
{
$temp = explode(" ", $msg);
return $temp;
}
}
?>

I've also attached a copy of it.

Lets say you have XAMPP (Or LAMPP for you linux gurus) you could just place this folder inside your php directory and make (In windows) a batch file to start the bot.

php IRC_Bot/bot_start.php

xanderman
08-24-2010, 12:38 PM
If anyone has any issues, questions or comments, please post here, you can also come to my irc channel on irc.rizon.net channel #xb for any of the above or testing purposes.