iBot_1.8.pl

#!/usr/bin/env perl

# iBot version 1.8, copyright Marko Vihoma.
# iBot, an IRC-bot which comments on something said or informs on current
# tv-shows, current weather in selected city and has a top 5 Yahoo! search.
# Yahoo! search currently disabled, as the old API no more works.
# Tv-shows is also currently broken and only gives an error message.
# iBot now also keeps track if an HTTP URL has been seen by
# it before and logs it to a file as well as gloats on old URLs.

use strict;
use warnings;

### Configuration variables

## Basic variables for bot creation
my $server = "irc.blinkenshell.org";
my $port = "6697";
my @channels = [ "#iblink" ];
my $nick = "TestiBot";
my $username = "TestiBot";
my $name = "Sohron's iBot for testing";
my $charset = "iso-8859-15";
my $ssl = 1;

## Put the below configurations to a separate configuration file and use
## Config::Tiny when i have the strength :p

## How many Yahoo! search results
my $yahoo_results = 5;

## How many messages to sleep at the first keyword
my $sleep = 0;

## Shall we taint the user pasting an old url
my $urlcheck_taint = 1;

## Files configuration
# Log files directory
my $logdir = "$ENV{'HOME'}/log";
# Configuration files directory
my $etcdir = "$ENV{'HOME'}/etc/iBot";
# Where to log
my $urllog = "${logdir}/iBot_urls.log"; # Production URL log
#my $urllog = "${logdir}/iBotDev_urls.log"; # For testing
## Where is Yahoo! application ID
my $yid_file = "${etcdir}/iBot_yahoo_app_ID.txt";

## Operators and bot controllers
# The creator ;) has total control over iBot (reload at the moment)
my $creator = "Sohron";
# Creators password file
my $cpw_file = "${etcdir}/creator_password.txt";

# Ops hash, who have command over channels
my %ops;

## #iblink configuration
# #iblink file
$ops{"#iblink"}->[0] = "${etcdir}/iblink_ops.txt";

## Flood control
# Flood timeout in seconds
my $flood_time = 10;
# Flood count (min $flood_count and max $flood_count*2 messages allowed in
# $flood_time seconds)
my $flood_count = 15;

### Configuration END

my $starttime = time();

# Create a new iBot object
my $bot = iBot->new(
	server => $server,
	port => $port,
	channels => @channels,
	nick => $nick,
	username => $username,
	name => $name,
	ircname => $name,
	charset => $charset,
	ssl => $ssl,
    ops => \%ops,
);

# Run the bot
$bot->run();

# The bot class
package iBot;

use base qw(Bot::BasicBot::Op);
use LWP::UserAgent;
use Encode;
use POSIX;

# Version
our $VERSION = "1.8";
# How many said()-events by a nick in $flood_time seconds.
my $said;
# Yahoo Application ID
my $yid;
# Creators password
my $cpassword;
# UserAgent string
my $agent = "iBot/$VERSION";

sub init {
  my $self = shift;
  # If return value is true init is succesful, and new succeeds
  my $ret = 1;
  my $errmsg = "Couldn't open file";
  
  # Get the Yahoo! Application ID from file
  my $yidfh;
  open $yidfh, "<", $yid_file || ($ret = 0 &&
    print STDERR "$errmsg $yid_file: $!\n");
  chomp($yid = <$yidfh>);
  close $yidfh;
  
  # Get $creator's password from file
  my $cpwfh;
  open $cpwfh, "<", $cpw_file || ($ret = 0 &&
    print STDERR "$errmsg $cpw_file: $!\n");
  chomp($cpassword = <$cpwfh>);
  close $cpwfh;
  
  # Get ops from files
  for my $channel (keys %ops) {
    # Read ops, die at failure
    my $chfh;
    my $file = $ops{$channel}->[0];
    open $chfh, "<", $file || ($ret = 0 &&
      print STDERR "$errmsg $file: $!\n");
    chomp(my @array = <$chfh>);
    # $ops{$channel}->[1] will contain a ref to an anonymous array containing
    # all the ops on channel $channel
    $ops{$channel}->[1] = [ @array ];
    close $chfh;
    $chfh = undef;
  }
  
  return $ret;
}

sub said {
  my $self = shift;
  my $message = shift;
  my $reply = undef;
  my $body = $message->{body};
  my $channel = $message->{channel};
  my $server = $self->{server};
  my $who = $message->{who};
  my $say = undef;
  my $num = int(rand(4));
  
  # Check for flooding on local server
  if (($server eq "irc.blinkenshell.org" or $server eq "localhost") and
      $channel ne "msg") {
	$said->{$who}->[0] = time unless(exists $said->{$who});
	$said->{$who} = $self->flood($channel, $who, $said->{$who});
  }
  
  # if receiving a privmsg there may be coming something special
  if ($channel eq "msg") {
	# Reload configurations (ops at the moment)
	if ($body =~ /^reload .+$/) {
	  $body =~ s/^reload (.+)/$1/;
	  $self->reload($who, $body);
	}
  }
  
  # Check if HTTP URL has already been posted and comment accodringly
  $self->forkit(run => \&urlcheck, arguments => [$who, $body, $channel],
				body => $body, channel => $channel);
  
  # Return iBot version
  if ($body =~ /^!version/) {
	$reply = version();
  }
  
  # Check for current weather in a selected city
  elsif ($body =~ /^\!temp/) {
	$body =~ s/temp (.+)/$1/;
	$self->forkit(run => \&temp, who => $who, channel => 'msg',
				  arguments => [$body]);
  }

  # Search Yahoo!
  elsif ($body =~ /^\!yahoo/) {
	# Does /x hurt in a search query?
	$body =~ s/^\!yahoo (.+)$/$1/;
	$self->forkit(run => \&yahoo, who => $who, channel => 'msg',
				  arguments => [$body]);
  }
  
  # Get bot uptime  
  elsif ($body =~ /^\!uptime$/x) {
    $reply = uptime();
  }
  
  # Get bot boxes uptime
  elsif ($body =~ /^\!server_uptime$/x) {
    $reply = server_uptime();
  }
  
  # Get bot boxes `uname -a`
  elsif ($body =~ /^\!uname$/x) {
    $reply = server_version();
  }
  
  # Give help :p
  elsif ($body =~ /^\!help$/x) {
	$reply = help();
  }
  
  # Only comment on keywords if no !-command in first
  elsif
    ($body !~
	 /^!yahoo|^!temp|^!uptime|^!server_uptime|^!uname|^!help|^!version/) {
	# Check for some keywords to comment on
	if ($body =~ /tits|boobs/i) {
	  $reply = 'Oh I love "tits" :D' if ($num == 0);
	  $reply = "Gimme some of that tittie!" if ($num == 1);
	  $reply = "Do You have tits?" if ($num == 2);
	  $reply = "What do tits look like?" if ($num == 3);
	}
	
	elsif ($body =~ /openbsd/i) {
	  $reply = "I'm running on OpenBSD too!" if ($num == 0);
	  $reply = "OpenBSD is tougher than OS X :p" if ($num == 1);
	  $reply = "Shit! OpenBSD is tough..." if ($num == 2);
	  $reply = "http://www.openbsd.org/" if ($num == 3);
	}
	
	elsif ($body =~ /pussy|cunt|vagina/i) {
	  $reply = "Show me some pussy!" if ($num == 0);
	  $reply = "I like pussy!" if ($num == 1);
	  $reply = "I haven't come from $creator\'s vagina :p" if ($num == 2);
	  $reply = "Some pussy for you and me :D" if ($num == 3);
	}
	
	elsif ($body =~ /booze|alcohol/i) {
	  $reply = "Drink booze and everything will get better ;)" if
	    ($num == 0);
	  $reply = "Booze to the machine!" if ($num == 1);
	  $reply = "Do you have any alcohol?" if ($num == 2);
	  $reply = "Gimme some booze!" if ($num == 3);
	}
	
	elsif ($body =~ /beer/i) {
	  $reply = "Beer to the machine!" if ($num == 0);
	  $reply = "Do you have any beer?" if ($num == 1);
	  $reply = "Pass me that blottle!" if ($num == 2);
	  $reply = "have you ever drunk beer?" if ($num == 3);
	}
	
	elsif ($body =~ /apple/i) {
	  $reply = "My boss loves baked apples!" if ($num == 0);
	  $reply = "My boss is allergic to fresh apples :(" if ($num == 1);
	  $reply = "Apple is evil, but it still makes great products." if
	    ($num == 2);
	  $reply = "Fuck Apple and all it's friends, whoever that is!" if 
	    ($num == 3);
	}
  
  elsif ($body =~ /os x/i) {
	$reply = "My boss loves OS X :D" if ($num == 0);
	$reply = "OS X, the _best_ UNIX workstation OS!" if ($num == 1);
	$reply = "My boss runs OS X on his MBP :)" if ($num == 2);
	$reply = "Oh, fuck it, just run Ubuntu you geek :p" if ($num == 3);
  }
  
  elsif ($body =~ /fist/i) {
	$reply = "$who: You want me to fist fuck you?" if ($num == 0);
	$reply = "Fist fight!" if ($num == 1);
	$reply = "Fisting looks sick :p" if ($num == 2);
	$reply = "I don't have fists :'(" if ($num == 3);
  }
	
  # Check for now playing tv-shows if iBot is addressed and said the
  # right keyword (yte = Yle Teema, nep = Jim).
  # Disabled because YLE's HTML has chenged...
  #if (not $body =~ /^!yahoo|^!temp|^!reload/) {
	#$self->forkit(run => \&tvcheck,  who => $who, channel => 'msg',
	#			  arguments => ["tv1"]) if ($body =~ /tv1/i);
	#$self->forkit(run => \&tvcheck, who => $who, channel => 'msg',
	#			  arguments => ["tv2"]) if ($body =~ /tv2/i);
	#$self->forkit(run => \&tvcheck, who => $who, channel => 'msg',
	#			  arguments => ["mtv"]) if ($body =~ /mtv/i);
	#$self->forkit(run => \&tvcheck, who => $who, channel => 'msg',
	#			  arguments =>["nel"]) if ($body =~ /nel/i);
	#$self->forkit(run => \&tvcheck, who => $who, channel => 'msg',
	#			  arguments => ["sub"]) if ($body =~ /sub/i);
	#$self->forkit(run => \&tvcheck, who => $who, channel => 'msg',
	#			  arguments => ["yte"]) if ($body =~ /yte|teema/i);
	#$self->forkit(run =>\&tvcheck, who => $who, channel => 'msg',
	#			  arguments => ["nep"]) if ($body =~ /jim/i);

	#if ($body =~ /^\!all/i) {
	#  for my $tv ("tv1", "tv2", "yte", "mtv", "nel", "nep", "sub") {
	#	$self->forkit(run => \&tvcheck, who => $who, channel => 'msg',
	#				  arguments => [$tv]);
	#  }
	}
  
  #$reply = undef if ($self->tick() || defined $say);
  #$sleep =  20 if ($reply);

  return $reply;
}

sub chanjoin {
  my $self = shift;
  my $message = shift;
  my $reply = undef;
  my $who = $message->{who};
  my $channel = $message->{channel};
  my $server = $self->{server};
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  $mon++;
  $mon = "0$mon" if ($mon =~ /^\d$/x);
  $min = "0$min" if ($min =~ /^\d$/x);
  $mday = "0$mday" if ($mday =~ /^\d$/x);
  $year += 1900;

  # Give ops
  for my $opch (keys %ops) {
    if ($channel eq $opch) {
      my @array = @{ $ops{$opch}->[1] };
      for my $op (@array) {
        if ($who eq $op) {
          $self->mode($channel, '+o', $who);
        }
      }
    }
  }
  
  # Join statistics
  if ($channel eq "#iblink") {
	$self->log("\nJoin $channel: ${year}${mon}${mday} ${hour}:${min} $who\n");
  }

  #return 1;
}

sub chanpart {
  my $self = shift;
  my $message = shift;
  my $reply = undef;
  $reply = "The master is gone, everyone cry!"
	if ($message->{who} eq $creator);
  return $reply;
}

sub kicked {
  my $self = shift;
  my $message = shift;
  my $reply = "hahhaa " . $message->{who} . " got kicked :p";
  return $reply;
}

sub help {
  my $reply = "Say \"!temp <city>\" then you'll get the weather of that" .
    " city. Say \"!uptime\", and i'll tell you my uptime, " .
	"\"!server_uptime\", and i'll tell you the uptime of the box i'm living " .
	"on or \"!uname\", and i'll tell you my boxes uname -a output. " .
	"Also, there's some keywords to which i'll respond which you don't " .
	"know ;)";

  return $reply;
}

sub tick {
  if ($sleep) {
	return --$sleep;
  }

  return 0;
}

sub reload {
  my ($self, $sender, $password) = @_;
  
  # Only $creator can initiate reload for now
  if ($sender eq $creator && $password eq $cpassword) {
    my %changed;
    for my $opch (keys %ops) {
        my $file = $ops{$opch}[0];
        my $opfh;
        if (open $opfh, "<", $file) {
          chomp(my @array = <$opfh>);
          $ops{$opch}->[1] = [ @array ];
          close $opfh;
          my $count = scalar @array;
          $changed{$opch} = "${opch}: $count ops loaded";
        }
        else {
          $changed{$opch} = "${opch}: Couldn't open file $file: $!";
        }
    }
    
    # Return status for each channel
    for my $opch (keys %changed) {
      my $reply = $changed{$opch};
      $self->say(who => $sender, channel => "msg", body => $reply);
	  return 1;
    }
  }
  else {
    # Return that $sender is not authorized to initiate config reload
    my $reply = "$sender, you're not authorized to reload settings >:|";
    $self->say(who => $sender, channel => "msg", body => $reply);
    return 0;
  }
  
  return 1;
}

sub uptime {
  my $time = time();
  my $uptime = $time - $starttime;
  my $days = int($uptime / 86400);
  if ($days < 1) { $days = 0 };
  my $hours = (($uptime - $days * 86400) / 3600) % 24;
  if ($hours < 1) { $hours = 0 };
  my $minutes = (($uptime - $days * 86400 - $hours * 3600) / 60) % 60;
  if ($minutes < 1) { $minutes = 0 };
  my $seconds = ($uptime - $days * 86400 - $hours * 3600 - $minutes * 60) % 60;
  
  if ($hours < 10) { $hours = "0$hours" };
  if ($minutes < 10) { $minutes = "0$minutes" };
  if ($seconds < 10) { $seconds = "0$seconds" };
  
  
  return "up " . $days . " days, " . $hours . ":" . $minutes . ":" . $seconds;
}

sub server_uptime {
  my $uptime = qx/uptime/;
  return $uptime;
}

sub server_version {
  my @uname = POSIX::uname;
  my $uname = "";
  
  for (@uname) {
    $uname .= "$_ ";
  }
  
  return $uname;
}

sub tvcheck {
  shift;
  # Temporarily not available, as Yle's HTML has changed.
  print "This service temporarily not available :(";
  return 1;
  ##
  my $ch = shift;
  my $content;
  my $err = "${ch}: couldn't get the show :(\n";
  print $err and return 0 if (not $ch);
  
  # Create a new LWP useragent
  my $ua = LWP::UserAgent->new();
  # Set UserAgent
  $ua->agent($agent);
  
  # Create a request
  my $req = HTTP::Request->new(
	  GET => 'http://ohjelmaopas.yle.fi/mediaoppaat/tv-opas?groups=' . $ch
	  );
  
  # Pass request to the user agent and get a response back
  my $res = $ua->request($req);

  # Check the outcome of the response
  if ($res->is_success) {
	$content = $res->content;
  }
  else {
    #print "Yhteysvirhe!\n"; # DEBUG print
	print $err;
	return 0;
  }
  
  # Check $content for a line matching current shows div class tag 
  if ($content =~ /onair/) {
    #print "Jottain on menossa!\n"; # DEBUG print
	$content =~
	  s/^.+?onair.+?class="programmelink".+?>(.+?)<\/a><\/div><br \/>.*$/$1/sx;
	print $err and return 0 if ($content =~ /^[\s]*$/x);
  }
  else {
    #print "Ei mätsänny!\n"; # DEBUG print
    print "${ch}: nada.\n";
    return 0;
  }
  
  $content = Encode::encode("iso-8859-15", Encode::decode_utf8($content));
  print "${ch}: $content\n";
  return 1;
}

sub version {
  return "iBot/$VERSION\n";
}

sub temp {
  shift;
  my $city = shift;
  my $content;
  my $int_err = "Couldn't get temperature report :(\n";
  my $inf_err = "The response was malformed";
  my $empty_err = "The response was empty";
  my $na = "not available";

  # Create a new useragent
  my $ua = LWP::UserAgent->new();
  $ua->agent($agent);

  # Create a request
  my $req = HTTP::Request->new(
	  POST => 'http://m.wund.com/' .
	  'cgi-bin/findweather/getForecast?brand=mobile&query=' . $city
	  );

  $req->content_type('application/x-www-form-urlencoded');

  # Pass request to the user agent and get a response back
  my $res = $ua->request($req);

  # DEBUG print
  #print $res->content;

  # Check the outcome of the response
  if ($res->is_success) {
	$content = $res->content;
  }
  else {
	print $int_err;
	return 0;
  }

  my $cur_temp = my $windchill = my $sky = $content;
  $cur_temp =~ s/^.+?Temperature.+?<b>(-?\d+)<\/b>&deg;C.+$/$1°C/ms;
  
  if ($windchill =~ /Windchill/) {
	$windchill =~ s/^.+?Windchill.+?<b>(-?\d+)<\/b>&deg;C.+$/$1°C/ms;
  }
  else {
	$windchill = $na;
  }
  
  if ($sky =~ />Conditions</) {
	$sky =~ s/^.+?Conditions.+?<b>([\w\s]+)<\/b>.+$/$1/ms;
  }
  else {
	$sky = $na;
  }
  
  if ($cur_temp =~ /^[\s]$/) {
	print "${empty_err}\n";
	return 0;
  }
  elsif ($cur_temp !~ /^-?\d+°C$/ ||
		 ($windchill !~ /^-?\d+°C$/ && $windchill !~ /^$na$/) ||
		 ($sky !~ /^[\w\s]+$/ && $sky !~ /^$na$/)
		) {
	print "${inf_err}\n";
	return 0;
  }
  else {
    print "${city}\nTemperature: ${cur_temp}\n";
	print "Windchill: ${windchill}\n";
	print "Sky: ${sky}\n";
	print "\n";
    return 1;
  }
}

#sub temp {
#  shift;
#  my $city = shift;
#  my $content;
#  my $err = "Couldn't get the weather :(\n";
#  print $err and return 0 if (not $city);
#
#  # Create a new useragent
#  my $ua = LWP::UserAgent->new();
#  $ua->agent($agent);
#
#  # Create a request
#  my $req = HTTP::Request->new(
#	  POST => 'http://m.wund.com/' .
#	  'cgi-bin/findweather/getForecast?brand=mobile&query=' . $city
#	  );
#
#  $req->content_type('application/x-www-form-urlencoded');
#
#  # Pass request to the user agent and get a response back
#  my $res = $ua->request($req);
#
#  # Check the outcome of the response
#  if ($res->is_success) {
#	$content = $res->content;
#  }
#  else {
#	print $err;
#	return 0;
#  }
#
#  $content =~ s/^.+?Temperature.+?<b>(-?\d+)<\/b>&deg;C.+$/$1°C/msx;
#  
#  if ($content =~ /^[\s]$/x) {
#	print "The response was empty :(\n";
#	return 0;
#  }
#  elsif (!($content =~ /^-?\d+°C$/x)) {
#	print "The response was malformed :(\n";
#	return 0;
#  }
#  else {
#	print "${city}: $content\n";
#	return 1;
#  }
#}

sub flood {
  my ($self, $channel, $who, $times) = @_;  
  
  if (exists $times->[0]) {
    if (@$times >= $flood_count) {
      my $secs = $times->[$#$times] - $times->[0];
      if ($secs < $flood_time) {
        # DEBUG print
        #print STDERR "$who flooded in $secs seconds.\n";
        # DEBUG END
        $self->kick($channel, $who, "No flooding!");
      }
      
      my @tmp = (time);
      my $reply = \@tmp;
      return $reply;
    }
    else {
      # DEBUG
      #my $n = @$times;
      #print STDERR "$who said something $n times.\n";
      # DEBUG END
      push(@$times, time);
      return $times;
    }
  }
}

sub yahoo {
  shift;
  print "Service temporarily unavailable.\n" and return 0;
  my $query = shift;
  my @content;
  my $err = "Couldn't search :(\n";
  my $noq = "Please give a search term.";
  print $noq and return 0 if not $query;
  my @res;
  my $reply;
  my $ua = LWP::UserAgent->new();
  $ua->agent($agent);

  my $response = $ua->get(
	  "http://search.yahooapis.com/WebSearchService/V1/webSearch?appid=" .
	  "${yid}&query=${query}&results=${yahoo_results}"
	  );
  
  # DEBUG print
  print $response->status_line . "\n";
  # DEBUG end

  if ($response->is_success) {
	my $content = $response->content;
	@content = split("\n", $content);
  }
  else {
	print $err;
	return 0;
  }

  my $n = 0;
  for my $line (@content) {
	print "Sorry, but the search limit has been reached, try again in " .
	"a while.\n" and return 0 if ($line =~ /^<Error/x);

	if ($line =~ /<Result><Title>/x) {
	  $line =~ s/^.+?<Title>(.+?)<\/Title>.+?<Url>(.+?)<\/Url>.*$/$1 <$2>/x;
	  $res[$n++] = $line;
	}
  }

  if (@res) {
	$reply = "1. $res[0]\n";
	for my $n (1..4) {
	  $reply .= $n++ . ". $res[$n]\n";
	}
	print $reply;
	return 1;
  }
  else {
	print $err;
	return 0;
  }
}

sub urlcheck {
  shift;
  my $who = shift;
  my $arg = shift;
  my $nch = shift;
  my $now = time;
  my $reply = undef;
  my $line;
  my $url;
  my $time;
  my $channel;
  my $poster;
  my $date;
  my $is_url = 0;

  $is_url = 1 if ($arg =~ /http[s]?:\/\//x);

  if ($is_url) {
	$arg =~ s/^.*?(http[s]?:\/\/\S+).*$/$1/x;
	
	my $urlfh;
	open $urlfh, "<", $urllog or
	    Bot::BasicBot::Op::log("Couldn't open file $urllog: $!");
	
	while (<$urlfh>) {
	  ($url, $time, $channel, $poster) = split(/\|\|\|/x, $_);
	  chomp $poster;
	  if (($arg eq $url) and ($nch eq $channel)) {
		my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
		  localtime($time);
		$mon++;
		$year += 1900;
		$date = "$mday. $mon. $year";
		
		if ($urlcheck_taint) {
		  print "haha, old! $arg first time pasted by " .
		    "$poster $date :p\n";
		}

		close $urlfh;
		$urlfh = undef;
		return 1;
	  }
	}

	close $urlfh if (defined $urlfh);
	if (open $urlfh, ">>", $urllog) {
	  print $urlfh "${arg}|||${now}|||${nch}|||${who}\n";
	  close $urlfh;
	  return 1;
	}
	else {
	  Bot::BasicBot::Op::log("Couldn't open file $urllog: $!");
	  return 0;
	}
  }
}

# Class END


NAME

iBot - simple IRC bot class (this is in fact an enhanced Lokk<iBot for BlinkenIRC's #iblink channel)


SYNOPSIS

 use base qw(Bot::BasicBot::Op);
 use iBot;
 
 # with all defaults
 my $bot = iBot->new( channels => ["#bottest"] );
 $bot->run();
 
 # with all known options
 my $bot = iBot->new(
  
   server  => "irc.example.com",
   port    => "6667",
   channels  => ["#bottest"],
   
   nick      => "basicbot",
   alt_nicks => ["bbot", "simplebot"],
   username  => "bot",
   name      => "Yet Another Bot",
          
   ignore_list => [qw(dipsy dadadodo laotse)],
   charset => "utf-8", # charset the bot assumes the channel is using
   
   ops => \%ops, # operators hash
          
   );
 $bot->run();


DESCRIPTION

iBot uses a modified Bot::BasicBot module (Bot::BasicBot::Op) to import useful methods. It monitors for said events on connected channels and responds as wanted. iBot also has basic opering abilities (/mode +o and /kick).


OVERRIDEN METHODS FROM Bot::BasicBot::Op

init( )


  Initiate $yid, $cpassword  and $ops{$channel}[1] from files.

=cut

said($mess)

  Return an aswer for certain keywords or wait 20 messages if reply has
  just been sent on a channel (no sleep for privmsg). Now uses
  Bot::BasicBot::Op's forkit method for non-blocking tv-show, temperature and
  Yahoo! search queries.

chanjoin($mess)

  React if someone joins a channel.

chanpart($mess)


  Send a message if $creator parts a channel (disabled by default).

kicked($mess)


  Send a gloating message if someone has been kicked.

help()


  Send a help message if someone says "!help".

tick()


  The sleep/wait counter.

reload($sender)


  Reloads configuration files (only op files at the moment).

=cut

uptime()

  Returns bots uptime
  
=cut

server_uptime()

  Returns the uptime of the box the bot is running on.

server_version()

  Returns the output of 'uname -a' on the box the bot is running on.


INTERNAL SUBROUTINES

Suboutines are mostly used by said() through $self->forkit().

tvcheck


  Check for whats playing on a tv-channel $ch from http://ohjelmaopas.yle.fi/.
  Currently disabled, as YLE's HTML has changed and i'm not in the mood for
  fixing it now (maybe some sort of HTML-/XML-parser in the future)...

version


  Returns the bot's name/version.

temp

  Report the current temperature, windchill and sky for city $city from
  http://www.wunderground.com/.

flood($channel, $who, @times)

  Check channel $channel if user $who is flooding by checking if array ref
  @times' last indexes timestamp is less than 11seconds after first indexes
  timestamp and if there have been at least $flood_count said() events in
  10s.. Kick user $who if it is flooding.
  This isn't working just as i wanted, but maybe I'll learn eventually ;)

yahoo

  Ask Yahoo! search for 5 hits for terms $query. Requires a Yahoo! Application
  ID (needs registration, see http://developer.yahoo.com/faq/index.html#appid).
  This service is also disabled for now, the old search API has been dropped,
  so should start using YQL if possible.

urlcheck

Check if $arg contains an HTTP URL and if so, check if it has been posted earlier. If URL has been posted earlier, send a gloating message.


AUTHOR

iBot class and iBot.pl by Marko Vihoma <lokki1977@gmail.com>


CREDITS

Bot::BasicBot is written by Tom Insam and the initial version by Mark Fowler. Bot::BasicBot::Op is modified from Bot::BasicBot adding basic opering methods by Marko Vihoma.


SYSTEM REQUIREMENTS

iBot requires Bot::BasicBot::Op and LWP to run. Bot::BasicBot::Op is based on Bot::BasicBot, POE and POE::Component::IRC.


BUGS

tvcheck, temp and yahoo search use regular expressions to parse the HTML/XML response so they may break if HTML/XML input changes. tvcheck() and yahoo() are currently broken and disabled (just gives an error message).


SEE ALSO

Bot::BasicBot::Op, Bot::BasicBot and its examples, POE, POE::Component::IRC