#!/usr/bin/perl
#
# Xchat AI script. (Using megahal.pm) By Flash_, 2007. Do what thou wilt.
# Uses the great program Megahal to simulate human intelligence. This script will 'learn' from a channel and be able to converse
# with you. Although to be fair, it's more often funny than convincing.#
#
# Features:
# o Uses a different brain/personality for each channel it's enabled for.
# o No limit for channels.
# o Be aware that these brains can grow VERY LARGE after a time and may impact disk-space and/or memory. NO housekeeping is done.
# o When turned on (seperately for each channel), replies when its own nick is mentioned.
# o Has "freespeak" mode where it will reply every N% of lines, regardless of whether called. (Global setting $freespeak_chance)
#
# Requires:
# Xchat. (2.04 or later, windows or linux)
# Perl (or ActivePerl) installed.
# AI::MegaHAL (Get it from http://search.cpan.org/~chorny/AI-MegaHAL-0.05/lib/AI/MegaHAL.pm OR "cpan AI::MegaHAL" OR "ppm" and search.
#
# Commands: botnick brain on/off/freespeak/
# Once run, use "botnick brain on" in channel from another client. (I have deliberately disabled the bot from replying to commands/chat by itself)
# If you want a reply from it, mention it's nick in a comment such as "Botnick, hello!" or "Hi there Botnick, how are you?"
# Freespeak makes the script reply to a line at 'random'. Random is weighted by the percentage in $freespeak_chance. A value of 10 means the AI will
# reply to roughly 10% of lines. (But not every tenth line, that'd be too predictable)
#
# Tips:
# If you get in a loop with another AI bot, best way is to /ignore that bot with your Xchat client.
#
# Configuration
#
my $brain_path = "c:/pengbot/brains"; # Path to brain directory. Also used for config. (No trailing slash). Forward slashes for Windows, please.
my $freespeak_chance = 5; # Chance % (out of a 100) that the AI will blurt out a comment without being named.
my $debug = 0; # Loads of logging info, 0 (off), 1 or 2
# Startup registers, don't mess below here
use strict;
use AI::MegaHAL; # The megahal AI module
my @cfg; # Internal config
my $nick = Xchat::get_info('nick'); # Get my own nick, each time.
Xchat::register( "Flashy's AI Script", "0.1a", "Flash's AI", "" );
Xchat::print "Loading Flashy's AI Script";
Xchat::hook_print('Channel Message', "ai_loop");
Xchat::hook_print('Channel Msg Hilight', "ai_loop");
load_cfg();
sub ai_loop {
my $chan = Xchat::get_info('channel'); # current channel
my $thatguy = $_[0][0];
my $original_line = $_[0][1]; # Keep it secret, keep it safe
my $input = $original_line;
my @msg_words = split(/ /,$input);
# Uncomment following line if you want AI to ignore 1-word lines.
# if (not defined $msg_words[1]) { if ($debug == 1) { Xchat::print("AI - (1 word line, aborting)"); } return Xchat::EAT_NONE; } # Don't bother with one-word lines
foreach(@cfg) {
my ($cfg_chan,$cfg_on,$cfg_free,$cfg_free_cnt) = split(/\^/);
if ($cfg_chan eq $chan) {
if ($debug > 1) { Xchat::print("AI - This channel is known to me."); }
if (($cfg_on == 1) or ($cfg_free == 1)) { # Channel turned on.
my $reply_to_this = 0;
if ((-e "$brain_path/$chan/megahal.trn") == 0) { Xchat::print("No training file for this channel, can't continue!"); return Xchat::EAT_NONE; }
my $megahal = new AI::MegaHAL('Path' => "$brain_path/$chan", 'Banner' => 0, 'Prompt' => 0, 'Wrap' => 0, 'AutoSave' => 1); # Create new brain reference
# if (($msg_words[0] =~ /$nick/i) and (lc($msg_words[1]) ne 'brain')) { # 1st word contains my nick but not command word 'brain'
if (($input =~ /$nick/i) and (lc($msg_words[1]) ne 'brain')) { # 1st word contains my nick but not command word 'brain'
if ($debug == 1) { Xchat::print("AI - (That was to me!)"); }
$reply_to_this = 1;
}
############ Below this line if "learning" ##################
# Clean input line
# Remove own nick, otherwise it repeats it several times
$input =~ s/$nick//gi;
$input =~ Xchat::strip_code($input); # Remove colours and bold, etc
my $reply = $megahal->do_reply($input);
# Megahal stored this input. Now, whether to reply or not?
if ($cfg_free == 1) {
my $random_pct = (rand(100) + 1);
if ($random_pct < $freespeak_chance) { $reply_to_this = 1; }
}
if ($reply_to_this == 1) {
Xchat::command("say $thatguy, $reply");
}
} # End known chan + turned on
} # End known chan
} # End config loop
# Check all channels for my nick and command
if (($msg_words[0] =~ /$nick/i) and ($msg_words[1] eq 'brain')) { #
my $save_this = 0;
my $cfg_save_str;
if (lc($msg_words[2]) eq 'on') {
$cfg_save_str = "$chan^1^0^$freespeak_chance^";
if ($debug == 1) { Xchat::print("AI - Set channel $chan to ON"); }
$save_this=1;
Xchat::command("say $thatguy sets AI to ON $chan");
}
if (lc($msg_words[2]) eq 'off') {
$cfg_save_str = "$chan^0^0^$freespeak_chance^";
if ($debug == 1) { Xchat::print("AI - Set channel $chan to OFF"); }
$save_this=1;
Xchat::command("say $_[0][0] sets AI to OFF in $chan");
}
if (lc($msg_words[2]) eq 'freespeak') {
$cfg_save_str = "$chan^1^1^$freespeak_chance^";
if ($debug == 1) { Xchat::print("AI - Set channel $chan to FREE"); }
$save_this=1;
Xchat::command("say $_[0][0] sets AI to Freespeak $chan");
}
if ($save_this == 1) {
Xchat::print("Here");
my @tmparr='';
open (CFG,"<$brain_path/brains.cfg");
my $fnd=0;
while (<CFG>) {
my @fields=split(/\^/);
if ($fields[0] eq $chan) { # Replace this one with new one
push (@tmparr,$cfg_save_str);
$fnd++;
} else { push (@tmparr,$_); }
}
if ($fnd == 0) { push (@tmparr,$cfg_save_str); } # In case it doesn't already exist
close(CFG);
open (CFG,">$brain_path/brains.cfg") or Xchat::print("AI: Error - Can't write $brain_path/brains.cfg");
while (<@tmparr>) { print (CFG "$_\n"); }
close (CFG);
if ($debug == 1) { Xchat::print("AI - saved config for ". scalar(@tmparr) . " channels."); }
# Make new brain directory and prime with basic tutorial
if ((mkdir("$brain_path/$chan",0777)) == 1) {
open (TUT, ">$brain_path/$chan/megahal.trn") or Xchat::print("AI: Error, Can't write training file at $brain_path/$chan/megahal.trn");
print (TUT "Hi, I'm a pretty stupid AI script. Give me time and I'll learn more from you and be smarter!"); # Prime training file with something inane
close (TUT);
}
@cfg=@tmparr; # Load new values into config
}
} # End any-chan, my nick+brain
return Xchat::EAT_NONE;
}
sub load_cfg {
open (CFG,"<$brain_path/brains.cfg") or return;
@cfg = (<CFG>);
close(CFG);
if ($debug == 1) { Xchat::print("AI: Loaded details of " . scalar(@cfg) . " channels"); }
}