#!/usr/bin/env perl # Copyright (c) 2010, Douglas Haber # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * The names names of the authors may not be used to endorse or # promote products derived from this software without specific # prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS # CONTRIBUTERS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS # CONTRIBUTERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. use strict; use Net::IRC; use strict; use Expect; # ExpectBot # This program is an IRC Bot that runs line based programs # redirecting STDIN/STDOUT through an IRC Channel. It provides # no security, and so should not be run in public channels or around # people that you wouldn't want to have access to the account it is # running as. # Usage: # ./expect_bot [program] # On IRC: # !exec [cmd] [args...] - Launch the specified program # (closing the current program) # !ctrl [char] - Send a control character to the program # Settings my $NICK = "ExpectBot"; # Name of the bot my $SERVER = 'irc.---YOUR_SERVER_GOES_HERE---.net'; my $PORT = 6667; my $SSL = 1; # Use SSL? my $CHANNEL = '#expect'; my $DEBUG = 1; # Enable debugging my $FLOOD_PROTECT = 1; # Globals my $EXPECTING = 0; # Whether we are currently connect to a program my $EXP; # Expect Object sub debugmsg { if($DEBUG) { print "***** $_[0]\n"; } } sub on_connect { my ($s) = @_; debugmsg("Connected!"); $s->join($CHANNEL); } sub on_join { my ($s, $e) = @_; $s->privmsg($CHANNEL, "$NICK is here!"); if(!execcmd(join(' ', @ARGV))) { $s->privmsg($CHANNEL, "Unable to start program: " . join(' ', @ARGV)); } else { $s->privmsg($CHANNEL, "Started program: " . join(' ', @ARGV)); } read_it_all($s); } sub on_msg { my ($s, $e) = @_; $s->privmsg($CHANNEL, $e->{nick} . " is bothering me! Kick him!"); } sub execcmd { my ($cmdline) = @_; my ($cmd) = $cmdline =~ /^([^ ]*)/; my ($args) = $cmdline =~ /^[^ ]* (.*)$/; my @params = split(/ /, $args); if($EXPECTING) { $EXP->hard_close(); $EXPECTING = 0; } if(!`which $cmd`) { return(0); } $EXP = new Expect; $EXP->raw_pty(1); if($EXP->spawn($cmd, @params)) { $EXPECTING = 1; return(1); } else { $EXPECTING = 0; debugmsg("execcmd failed!: $!\n"); return(0); } } sub read_it_all { my ($s) = @_; my $out = 1; my $leading = 1; # leading text is any line before a content line my $c = 0; if(!$EXPECTING) { return; } while($out) { my $msg; $EXP->expect(1); $out = $EXP->before() . $EXP->after(); $EXP->clear_accum(); foreach $msg (split(/\n/,$out)) { if($msg eq '') { if(!$leading) { $msg = "\t "; } else { next; } } $s->privmsg($CHANNEL, $msg); if($FLOOD_PROTECT) { $c++; if($c % 6 == 0) { sleep(3); } if($c > 6) { sleep(2); } } $leading = 0; } } } sub on_public { my ($s, $e) = @_; my $txt = $e->{args}[0]; if($txt =~ /^!exec [\w\/]+/) { my ($cmd) = $txt =~ /^!exec (.*)$/; close(r); close(w); $s->privmsg($CHANNEL, "*** Request by user '" . $e->{nick} . "' to start new command: '$cmd'"); if(!execcmd($cmd)) { $s->privmsg($CHANNEL, "Unable to start program: $cmd"); } else { $s->privmsg($CHANNEL, "Started program: $cmd"); } } elsif($txt =~ /^!ctrl [a-z]$/) { my ($c) = $txt =~ /^!ctrl ([a-z])$/; my $o; if(!$EXPECTING) { $s->privmsg($CHANNEL,"*** No current program!"); return; } $c = uc($c); $o = ord($c) - ord('A') + 1; $EXP->send(chr($o)); $s->privmsg($CHANNEL,"*** Sent a Ctrl-".uc($c)." [Request by user '".$e->{nick}."]"); } else { if(!$EXPECTING) { return; } $EXP->send("$txt\n"); sleep(1); } read_it_all($s); } sub main { my $irc = new Net::IRC; my $conn = $irc->newconn( Nick => $NICK, Server => $SERVER, Port => $PORT, SSL => $SSL, Username => $NICK, Ircname => $NICK ); if(scalar(@ARGV) == 0) { die "USAGE: ./expectbot [program]\n"; } $conn->add_global_handler(376, \&on_connect); $conn->add_handler('msg', \&on_msg); $conn->add_handler('public', \&on_public); $conn->add_handler('join', \&on_join); $conn->socket->blocking(0); $irc->start(); } main();