#!/usr/bin/perl use strict; use warnings; use Net::MSN; use IO::Select; use POSIX; use Data::Dumper; my $handle = 'you@boxen.net'; my $password = 'yourpass'; my $D = 0; my $PIDFile = './msn-client.pid'; my $LogFile = './msn-client.log'; my $timeout = 0.01; my $s; my %admin = ( 'admin@hotmail.com' => 1 ); if (defined $ARGV[0]) { if ($ARGV[0] =~ /\-v/i) { print "Net::MSN Version: ". $Net::MSN::VERSION. "\n"; exit; } elsif ($ARGV[0] =~ /\-d/i) { $D = 1; } } if ($D == 1) { &demonize_me(); } else { $s = IO::Select->new(); $s->add(\*STDIN); } my $client = new Net::MSN( Debug => 1, Debug_Lvl => 3, Debug_STDERR => 1, Debug_LogCaller => 1, Debug_LogTime => 1, Debug_LogLvl => 1, Debug_Log => $LogFile ); $client->set_event( on_connect => \&on_connect, on_status => \&on_status, on_answer => \&on_answer, on_message => \&on_message, on_join => \&on_join, on_bye => \&on_bye, auth_add => \&auth_add ); $client->connect($handle, $password); while (1) { $client->check_event(); &checkSTDIN() unless ($D == 1); } sub checkSTDIN { if (my @r = $s->can_read($timeout)) { foreach my $fh (@r) { my $input = <$fh>; print '> '. $input; chomp($input); my ($cmd, @data) = split(/ /, $input); next unless (defined $cmd && $cmd); if ($cmd eq 'call') { if (defined $data[0]) { unless ($client->call($data[0])) { print $data[0]. " is not online or not on your contact list\n"; } } else { print "no party specified to call!\n"; } } elsif ($cmd eq 'msg') { my $calling = shift @data; my $message = join(' ', @data); my $r = $client->sendmsg($calling, $message); print $calling. " is not online or not on your contact list\n" unless (defined $r && $r); } elsif ($cmd eq 'list') { $client->send('LST', 'RL'); } elsif ($cmd eq 'quit') { $client->disconnect(); exit; } elsif ($cmd eq 'ping') { $client->sendnotrid('PNG'); } elsif ($cmd eq 'who') { my $calling = shift @data; my $response = &who(); $client->sendmsg($calling, $response); } elsif ($cmd =~ /die/) { die; } elsif ($cmd eq 'send') { my ($command, @payload) = @data; my $payload = ''; if (@payload && @payload >= 1) { $payload = join(' ', @payload); } print STDERR "SEND: ". $command. ' '. $payload. "\n"; $client->send($command, $payload); } elsif ($cmd eq 'dump') { use Data::Dumper; print '$client = '. Dumper($client). "\n"; } } } } sub on_connect { $client->{_Log}("Connected to MSN @ ". $client->{_Host}. ':'. $client->{Port}. ' as: '. $client->{ScreenName}. ' ('. $client->{Handle}. ")", 3); } sub on_status { # FIXME } sub on_message { my ($sb, $chandle, $friendly, $message) = @_; print $friendly. " says:\n ". $message. "\n" unless ($D == 1); if ($message =~ /^reply/i) { $sb->sendmsg('yes, what would you like?'); } elsif ($message =~ /^call\s*(.+?)$/i) { $client->call($1); } elsif ($message =~ /^calc([^\s])*\s*(.+?)$/) { my $sum = $2; if ($sum =~ /^[0-9 \-\+\*\/\(\)\^]*$/) { $sum = '$ans = '. $sum; my $ans; eval $sum; if (my $err = $@) { chomp($err); $sb->sendmsg('Error: '. $err); } else { $sb->sendmsg('Result: '. $ans); } } else { $sb->sendmsg('Error: Syntax Invalid'); } } elsif ($message =~ /^who$/i) { my $response = &who($chandle); $sb->sendmsg($response); } elsif ($message =~ /self\s*destruct/) { if ($admin{$chandle} == 1) { $sb->sendmsg('YAY, Ive been waiting so long!'); $sb->sendmsg('Self Destruct Sequence Initiated'); $sb->sendmsg(5); sleep 1; $sb->sendmsg(4); sleep 1; $sb->sendmsg(3); sleep 1; $sb->sendmsg(2); sleep 1; $sb->sendmsg(1); $sb->sendmsg('*BOOM*'); $client->disconnect(); sleep 1; die "Self Destructed\n"; } else { $sb->sendmsg('Your not my master!'); } } elsif ($message =~ /^msg\s+([^\s]*)\s+(.+?)$/i) { unless ($client->sendmsg($1, $chandle. '> '. $2)) { $sb->sendmsg($1. ' is either not online, or not on my contact list'); } } elsif ($message =~ /help/i) { $sb->sendmsg(&help); } else { $sb->sendmsg('I dont know, what you say?? "'. $message. '"'); } } sub help { return "msn-client's Command List\n\n". "Standard Commands:\n". " reply - msn-client will send a message to you\n". " calc - calculate an expression, ie 'calc 1 * 9'\n". " who - shows whos online on msn-client's contact list\n". " msg msnname - message someone on msn-client's list\n\n". "Admin Commands:\n". " self destruct - cause msn-client to quit\n"; } sub on_bye { my ($chandle) = @_; $client->{_Log}($chandle. " has left the conversation (switch board)", 3); } sub on_join { my ($sb, $chandle, $friendly) = @_; $client->{_Log}($chandle. " has joined the conversation (switch board)", 3); } sub on_answer { my $sb = shift; #print "Answer() called with parameters:\n"; #print " " . join(", ", @_), "\n"; } sub auth_add { my ($chandle, $friendly) = @_; $client->{_Log}('recieved authorisation request to add '. $chandle. ' ('. $friendly. ')', 3); return 1; } sub who { my ($requestor) = @_; $requestor = $requestor || ''; return 'Sorry, nobody is online :(' unless (defined $client->{Buddies} && ref $client->{Buddies} eq 'HASH'); my $response; foreach my $username (keys %{$client->{Buddies}}) { next unless ($client->{Buddies}->{$username}->{StatusCode} eq 'NLN'); #next if ($username eq $requestor); $response .= '* '. $username. ' ('. $client->{Buddies}->{$username}->{DisplayName}. ') is '. $client->{Buddies}->{$username}->{Status}. "\n"; } chomp($response); return (defined $response && $response) ? $response : 'Sorry, nobody is online :('; } sub demonize_me ($) { print "Daemonizing msn-client ...\n"; defined (my $pid = fork) or die "Can't fork: $!"; if ($pid) { # close parent process. exit; } else { # use the child process if (defined $PIDFile){ die "ERROR: I Died! Another copy of msn-client seems to be running. ". "Check ". $PIDFile. "\n" if (&is_running()); open(PIDFILE,">$PIDFile") or warn "creating $PIDFile: $!\n"; print PIDFILE "$$\n"; close PIDFILE; } POSIX::setsid or die "Can't start a new session: $!"; open (STDOUT,'>>'. $LogFile) or die "ERROR: Redirecting STDOUT to ". $LogFile. ': '. $!; open (STDERR,'>>'. $LogFile) or die "ERROR: Redirecting STDERR to ". $LogFile. ': '. $!; open (STDIN, '