#!/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"); }
	}