rendered paste bodyuse strict;
use Data::Dumper;
use Socket;
use constant { EAT => 1, PASS => 2, EAT_KILL => 3, PASS_KILL => 4 };
use Encode;
use utf8;
### --- Start Global Declarations --- ###
my %config = ();
# Okay, the idea here is that if every time we need to send something and associate the reply with something.
# We add a request entry here ( a hashref ) and then when we get stuff in then it's given to the entries in here oldest-first.
# Not sure how this will work, pass a function reference? maybe
my @requests = ();
# Used for mapping prefixes (@%+) to modes +ohv and vice versa
my %modemap = (
'~' => 'q',
'q' => '~',
'&' => 'a',
'a' => '&',
'@' => 'o',
'o' => '@',
'%' => 'h',
'h' => '%',
'+' => 'v',
'v' => '+',
);
# Used for working out if a user already has a certain access level
my %modelevels = ();
$modelevels{'o'} = 100;
$modelevels{'h'} = 90;
$modelevels{'v'} = 80;
$modelevels{''} = 0;
### --- End Global Declarations --- ###
### --- Start Configuration --- ###
$config{script} = "Xchat Utility Script";
$config{version} = "0.1";
$config{prefix} = "\cC5*\cC\cC4*\cC\cC8*\cC\cC8 ";
$config{whoistimeout} = 3;
$config{whoiswidth} = 100;
$config{identtimeout} = 5;
$config{invitetimeout} = 10;
$config{nickserv} = "NickServ";
$config{chanserv} = "ChanServ";
$config{globalstab} = "(globals)";
$config{highlighttab} = "(highlight)";
$config{globaldebug} = "Debug";
$config{networkdevs} = [ 'eth0', 'eth1', 'ra0' ];
### --- End Configuration --- ###
### --- Start Setup --- ###
Xchat::register($config{script}, $config{version}, "Om's general script for Xchat", \&on_unload);
Xchat::hook_command("WHOIS", \&handle_whois, { help_text => "/whois <nick>\n Get's information about the user" });
Xchat::hook_command("WHOWAS", \&handle_whowas, { help_text => "/whowas <nick>\n Get's information about a user that is no longer online.\n This version will use a local cache"});
Xchat::hook_command("BADWORD", \&handle_badword, { help_text => "/badword text [text] [...]\n Set chmode +g on the words given." });
Xchat::hook_command("GOODWORD", \&handle_goodword, { help_text => "/goodword text [text] [...]\n Set chmode -g on the words given." });
Xchat::hook_command("MOP", \&handle_mmode, { help_text => "/mop [channel]\n Ops all users without ops on the current channel.\n Or if a parameter is given, that channel."});
Xchat::hook_command("MHOP", \&handle_mmode, { help_text => "/mhop [channel]\n Halfops all users without halfops on the current channel.\n Or if a parameter is given, that channel."});
Xchat::hook_command("MVOP", \&handle_mmode, { help_text => "/mvop [channel]\n Voices all users without voice on the current channel.\n Or if a parameter is given, that channel."});
Xchat::hook_command("MDEOP", \&handle_mmode, { help_text => "/mdeop [channel]\n De-Ops all users with ops on the current channel.\n Or if a parameter is given, that channel."});
Xchat::hook_command("MDEHOP", \&handle_mmode, { help_text => "/mdehop [channel]\n De-Halfops all users with halfops on the current channel.\n Or if a parameter is given, that channel."});
Xchat::hook_command("MDEVOP", \&handle_mmode, { help_text => "/mdevop [channel]\n De-Voices all users with voice on the current channel.\n Or if a parameter is given, that channel."});
Xchat::hook_command("MVOPNG", \&handle_mvopng, { help_text => "/mvopng [channel]\n Voices all users without voice on the current channel, excludes users with Guest* nicks.\n Or if a parameter is given, use that channel."});
Xchat::hook_command("TKB", \&handle_timedkb, { help_text => "/tkb <nick> <duration> [reason]\n Kickbans the use for the specified duration.\n Length should be specified in 5h4m3s format for five hours, four minutes and three seconds."});
Xchat::hook_command("TKBLIST", \&handle_timedkblist, { help_text => "/tkblist\n Lists all the timed kickbans active on the current channel."});
Xchat::hook_command("TKBREM", \&handle_timedkbremove, { help_text => "/tkbrem <num>\n Remove the timed kickban with the identifier 'num', as shown after /tkb and in /tkblist."});
Xchat::hook_command("NP", \&handle_audio_nowplaying, { help_text => "/np\n Shows the currently playing song - with colours/bold/excessiveness."});
Xchat::hook_command("NPB", \&handle_audio_nowplaying, { help_text => "/np\n Shows the currently playing song - without colours/bold/excessiveness."});
Xchat::hook_command("NPL", \&handle_audio_nowplaying, { help_text => "/np\n Shows the currently playing song - with colours/bold."});
Xchat::hook_command("NPLB", \&handle_audio_nowplaying, { help_text => "/np\n Shows the currently playing song - without colours/bold."});
Xchat::hook_command("GFXINFO", \&handle_sysinfo_gfx, { help_text => "/gfxinfo\n Shows information on your system's graphics."});
Xchat::hook_command("AUDINFO", \&handle_sysinfo_aud, { help_text => "/audinfo\n Shows information on your system's audio status."});
Xchat::hook_command("MBINFO", \&handle_sysinfo_mb, { help_text => "/mbinfo\n Shows information on your system's motherboard."});
Xchat::hook_command("KERNINFO", \&handle_sysinfo_kern, { help_text => "/kerninfo\n Shows information on your system's kernel."});
Xchat::hook_command("CPUINFO", \&handle_sysinfo_cpu, { help_text => "/cpuinfo\n Shows information on your system's CPU(s)."});
Xchat::hook_command("RAMINFO", \&handle_sysinfo_ram, { help_text => "/raminfo\n Shows information on your system's RAM."});
Xchat::hook_command("DISKINFO", \&handle_sysinfo_disk, { help_text => "/diskinfo\n Shows information on your system's Disk(s)."});
Xchat::hook_command("NETINFO", \&handle_sysinfo_net, { help_text => "/netinfo\n Shows information on your system's network."});
Xchat::hook_command("TEMPINFO", \&handle_sysinfo_temp, { help_text => "/tempinfo\n Shows information on your system's temperature."});
Xchat::hook_command("SYSINFO", \&handle_sysinfo_all, { help_text => "/sysinfo\n Shows all information about your system."});
Xchat::hook_command("ADDPRERAW", \&handle_perform_preraw_add, { help_text => "/addpreraw\n Adds a raw irc string to the current network's channel autojoin list.\n To be executed immediately on connect"});
Xchat::hook_command("DELPRERAW", \&handle_perform_preraw_del, { help_text => "/delpreraw\n Deletes a raw irc string from the current network's channel autojoin list.\n To be executed immediately on connect"});
Xchat::hook_command("LSPRERAWS", \&handle_perform_preraw_list, { help_text => "/lspreraws\n Lists all the raw irc strings on the current network's autojoin list.\n To be executed immediately on connect"});
Xchat::hook_command("ADDCHAN", \&handle_perform_add, { help_text => "/addchan [channel]\n Adds the current channel, or the one specified, to the current network's channel autojoin list"});
Xchat::hook_command("DELCHAN", \&handle_perform_del, { help_text => "/delchan [channel]\n Deletes the current channel, or the one specified, from the current network's channel autojoin list"});
Xchat::hook_command("LSCHANS", \&handle_perform_list, { help_text => "/lschans\n Lists all the channels on the current network's autojoin list"});
Xchat::hook_command("ADDRAW", \&handle_perform_raw_add, { help_text => "/addraw\n Adds a raw irc string to the current network's channel autojoin list.\n To be executed after identification has timed out or succeeded"});
Xchat::hook_command("DELRAW", \&handle_perform_raw_del, { help_text => "/delraw\n Deletes a raw irc string from the current network's channel autojoin list.\n To be executed after identification has timed out or succeeded"});
Xchat::hook_command("LSRAWS", \&handle_perform_raw_list, { help_text => "/lsraws\n Lists all the raw irc strings on the current network's autojoin list.\n To be executed after identification has timed out or succeeded"});
Xchat::hook_command("SETPASS", \&handle_perform_pass_set, { help_text => "/setpass\n Set the nickserv password for this network"});
Xchat::hook_command("GETPASS", \&handle_perform_pass_get, { help_text => "/getpass\n Show the nickserv password for this network"});
Xchat::hook_command("IDENT", \&handle_identify, { help_text => "/ident\n Identify to nickserv on the current network"});
Xchat::hook_command("DOPFM", \&handle_doperform, { help_text => "/dopfm\n Execute all perform commands on the current network, 'raws' and 'preraws'"});
Xchat::hook_command("SLAP", \&handle_slap, { help_text => "/slap <target>\n Slaps target around violently with various aquatic (and not) creatures."});
Xchat::hook_command("DECODE", \&handle_decode, { help_text => "/decode <string>\n Decodes a hex-encoded string to an IP in form a.b.c.d"});
Xchat::hook_command("SHOWREQS", \&handle_showreqs, { help_text => "/showreqs\n Shows all currently active requests" });
Xchat::hook_command("SCRIPTDEBUG", \&handle_scriptdebug, { help_text => "/scriptdebug <on|off>\n Turns script debugging output on or off, 'off' is the default" });
Xchat::hook_command("TEST", \&handle_test, { help_text => "/test\n Random test command" });
Xchat::hook_command("", \&handle_enteredline);
Xchat::hook_print("Join", \&on_join); # For clonescanning
Xchat::hook_print("Channel Msg Hilight", \&on_highlight, 'msg'); # Highlight redirection
Xchat::hook_print("Channel Action Hilight", \&on_highlight, 'me'); # Highlight redirection
Xchat::hook_server("001", \&handle_connect); # For on-connect stuff ;p
Xchat::hook_server("221", \&on_raw); # Modes (from /mode)
Xchat::hook_server("301", \&on_raw); # Away status
Xchat::hook_server("307", \&on_raw); # Registered
Xchat::hook_server("310", \&on_raw); # Helpop
Xchat::hook_server("311", \&on_raw); # Displayed host
Xchat::hook_server("312", \&on_raw); # Server (whois and whowas)
Xchat::hook_server("313", \&on_raw); # Oper type
Xchat::hook_server("314", \&on_raw); # Whowas host
Xchat::hook_server("317", \&on_raw); # Idle time
Xchat::hook_server("318", \&on_raw); # End of /whois
Xchat::hook_server("319", \&on_raw); # Channels
Xchat::hook_server("320", \&on_raw); # SSL and/or swhois
Xchat::hook_server("369", \&on_raw); # End of /whowas
Xchat::hook_server("378", \&on_raw); # Real host
Xchat::hook_server("379", \&on_raw); # Modes (On Unreal, anyway)
Xchat::hook_server("401", \&on_raw); # No such nick/channel
Xchat::hook_server("402", \&null); # No such server (On Unreal, anyway)
Xchat::hook_server("406", \&on_raw); # There was no such user
Xchat::hook_server("473", \&on_invite); # For auto /cs inviting
Xchat::hook_server("671", \&on_raw); # SSL (On Unreal, retarded alert)
Xchat::hook_server("NOTICE", \&on_raw); # For catching /ns info results
Xchat::hook_server("NOTICE", \&on_notice); # For blocking annoying /ns info messages :\
Xchat::hook_server("INVITE", \&on_raw); # For catching /cs invite results
Xchat::hook_server("WALLOPS", \&on_wallops); # For redirecting wallops
on_load();
### --- End Setup --- ###
### --- Start helper code --- ###
sub xcprint
{
my($data, $chan, $net) = @_;
if($net and (!$chan or ($net eq $chan)))
{
my @chanlist = Xchat::get_list("channels");
for my $tab (@chanlist)
{
$chan = $tab->{channel} if(($tab->{network} eq $net) and ($tab->{type} == 1));
}
}
if(ref($data) eq "ARRAY")
{
Xchat::print($config{prefix} . $_, $chan, $net) for (@{$data});
}
else
{
Xchat::print($config{prefix} . $data, $chan, $net);
}
}
sub debug
{
xcprint(shift(@_), undef, $config{globaldebug});
}
sub mychomp
{
my $f = shift;
chomp $f;
return $f;
}
sub handle_scriptdebug
{
# /newserver -noconnect Debug
my $context = Xchat::find_context(undef, $config{globaldebug});
my $oldcontext = Xchat::get_context();
if($_[0][1] eq "on")
{
unless($context)
{
# XXX - Xchat bugged it seems, this should have -noconnect
Xchat::command("newserver $config{globaldebug}");
Xchat::command("settab Debug", undef, $config{globaldebug});
Xchat::command("quit", undef, $config{globaldebug});
Xchat::set_context($oldcontext);
}
}
else
{
if(defined($context))
{
Xchat::set_context($context);
Xchat::command("close");
Xchat::set_context($oldcontext);
}
}
return Xchat::EAT_ALL;
}
sub on_load
{
xcprint "Loading $config{script} version $config{version}";
$ENV{PATH} .= ":/usr/sbin:/sbin:/usr/local/sbin";
}
sub on_unload
{
xcprint "$config{script} $config{version} unloading...";
}
sub ts2str
{
my $time = shift;
my $days = int($time/(3600*24));
$time = $time % (3600*24);
my $hours = int($time/3600);
$time = $time % 3600;
my $mins = int($time/60);
$time = $time % 60;
my $out = $days . " days " if($days);
return($out . sprintf("%02d:%02d:%02d", $hours, $mins, $time));
}
sub displength
{
return length(scalar(Xchat::strip_code(shift(@_))));
}
sub request
{
# request("server", \%hashref, \&callback, $timeout);
# Add a request associated with the given server, the hashref given will be stored with the request and passed to the callback
my($server, $hashref, $callback, $timeout, $timercb, $tagstr) = @_;
debug("Error: request() server parameter was undefined") unless($server);
debug("Error: request() got a non-hashref as a parameter") unless(ref($hashref) eq "HASH");
debug("Error: request() got a non-callback as a data callback parameter") unless(ref($callback) eq "CODE");
debug("Error: request() got a non-callback as a timer callback parameter") unless(ref($timercb) eq "CODE");
debug("Error: request() timeout parameter was undefined") unless($timeout);
debug("Warning: request() hashref parameter was undefined") and ($hashref = { }) unless($hashref);
debug("Warning: request() timeout parameter was sub-zero") and ($timeout = 0) unless($timeout >= 0);
debug("Warning: request() tag parameter was undefined") and ($tagstr = "None") unless($tagstr);
debug("Warning: request() \$hashref->{_network} was already defined as '$hashref->{_network}', will be overwritten") if(defined($hashref->{_network}));
debug("Warning: request() \$hashref->{_callback} was already defined as '$hashref->{_callback}', will be overwritten") if(defined($hashref->{_callback}));
debug("Warning: request() \$hashref->{_timeout} was already defined as '$hashref->{_timeout}', will be overwritten") if(defined($hashref->{_timeout}));
debug("Warning: request() \$hashref->{_timercb} was already defined as '$hashref->{_timercb}', will be overwritten") if(defined($hashref->{_timercb}));
debug("Warning: request() \$hashref->{_start} was already defined as '$hashref->{_start}', will be overwritten") if(defined($hashref->{_start}));
$hashref->{_network} = $server;
$hashref->{_callback} = $callback;
$hashref->{_timercb} = $timercb;
$hashref->{_timeout} = $timeout;
$hashref->{_start} = time;
$hashref->{_orignet} = Xchat::get_info("network");
$hashref->{_origchan} = Xchat::get_info("channel");
$hashref->{_tag} = $tagstr;
push(@requests, $hashref);
debug("Adding request '$tagstr' on $server ($timeout secs)");
($config{_timer} = Xchat::hook_timer(1000, \&do_timeouts)) and (debug("Hooking timer...")) unless($config{_timer});
}
sub removerequest
{
my($server, $tag) = @_;
debug("Error: removerequest() server parameter was undefined") unless($server);
debug("Error: removerequest() tag parameter was undefined") unless($tag);
for(my $i = 0; $i < @requests; $i++)
{
if(($requests[$i]->{_network} eq $server) and ($requests[$i]->{_tag} =~ /$tag/i))
{
splice(@requests, $i, 1);
return 1;
}
}
return 0;
}
sub do_timeouts
{
my $time = time;
my @culls = ();
for(my $i = 0; $i < @requests; $i++)
{
my $request = @requests[$i];
if(($request->{_timeout} != 0) and ($time >= ($request->{_start} + $request->{_timeout})))
{
debug("Timing out request ($request->{_tag}) ...");
$request->{_timercb}($request);
push(@culls, $i);
}
}
splice(@requests, $_, 1) for(@culls);
if($config{_timer} and (@requests == 0))
{
debug("Unhooking timer...");
delete $config{_timer};
return Xchat::REMOVE;
}
return Xchat::KEEP;
}
sub on_raw
{
my $network = Xchat::get_info("network");
my $time = time;
my $return = Xchat::EAT_NONE;
my @culls = ();
for(my $i = 0; $i < @requests; $i++)
{
my $request = @requests[$i];
if($network eq $request->{_network})
{
my @args = @_;
$args[2] = $request;
my $r = $request->{_callback}(@args);
if(($r == EAT_KILL) or ($r == PASS_KILL))
{
# xcprint("Killing request by request, raw was: " . join(' ', @{$_[0]}));
push(@culls, $i);
}
# xcprint("Passing event onto next request: " . join(' ', @{$_[0]})) if($r == PASS);
if(($r == EAT_KILL) or ($r == EAT))
{
$return = Xchat::EAT_XCHAT;
last;
}
}
}
splice(@requests, $_, 1) for(@culls);
return $return;
}
sub handle_showreqs
{
for my $req (@requests)
{
Xchat::print(scalar(Dumper($req)));
}
return Xchat::EAT_ALL;
}
sub null
{
return Xchat::EAT_XCHAT;
}
sub get_maxmodes
{
my($channel, $network) = shift;
$channel = Xchat::get_info("channel") unless($channel);
$network = Xchat::get_info("network") unless($network);
my @list = Xchat::get_list('channels');
for my $chan (@list)
{
if(($chan->{channel} eq $channel) && ($chan->{network} eq $network))
{
return $chan->{maxmodes};
}
}
return 3;
}
sub send_massmodes
{
my($modes, $params, $chan, $net) = @_;
debug("Error: send_massmodes() didn't get an arrayref for \$modes") unless(ref($modes) eq "ARRAY");
debug("Error: send_massmodes() didn't get an arrayref for \$params") unless(ref($params) eq "ARRAY");
$chan = Xchat::get_info("channel") unless($chan);
$net = Xchat::get_info("network") unless($net);
my $maxmodes = get_maxmodes($chan, $net);
my $modestr = "";
my $paramstr = "";
my $direction = "";
my $modecount = 0;
debug("Setting massmodes on channel $chan, maxmodes is $maxmodes");
for(my $i = 0; ($i < @{$modes}) and ($i < @{$params}); $i++)
{
my $newdirection = substr($modes->[$i], 0, 1);
my $mode = substr($modes->[$i], 1);
if(($modecount >= $maxmodes) or (length($modestr)+length($paramstr)+length("MODE $chan ") > 510))
{
# Limit is 512, but there's a \r\n to go on the end yet
debug("Mode string would be over 512 characters, flushing now.");
Xchat::command("raw MODE $chan $modestr" . $paramstr, $chan, $net);
$direction = $modestr = $paramstr = "";
$modecount = 0;
}
$modestr .= $newdirection and $direction = $newdirection unless($newdirection eq $direction);
$modestr .= $mode;
$modecount++;
$paramstr .= " $params->[$i]" if(length($params->[$i]));
}
Xchat::command("raw MODE $chan $modestr" . $paramstr, $chan, $net) if(length($modestr));
}
### --- End helper code --- ###
### --- Load extra modules (now we have xcprint) --- ###
use File::Copy;
eval { require File::Slurp; };
if($@)
{
xcprint "File::Slurp not found :( Rolling our own (this is fine, but probably less efficient than if you have the File::Slurp module installed)";
sub read_file
{
my $out = "";
open(FH, "< " . shift);
$out .= $_ while(<FH>);
close(FH);
return $out;
}
}
### --- End extra modules --- ###
### --- Start /whois code --- ###
sub handle_whois
{
shift @{$_[0]}; # Lose the "whois"
my $server = Xchat::get_info("network");
xcprint "/whois on undefined server, output may be misdirected." unless($server);
for my $nick (@{$_[0]})
{
# Send out the /whois
Xchat::command("raw MODE $nick");
Xchat::command("raw WHOIS $nick $nick");
Xchat::command("raw PRIVMSG $config{nickserv} :info $nick");
# Initialise the structure we store the info in until it's printed.
my $data = {
nick => $nick,
server => $server,
};
request($server, $data, \&on_whoisraw, $config{whoistimeout}, \&do_print_whois, "Whois on $nick");
}
return Xchat::EAT_ALL;
}
sub on_whoisraw
{
my $num = $_[0][1];
my $nick = $_[0][3];
my $data = $_[2];
if($num eq "NOTICE")
{
return on_whoisnotice(@_);
}
$nick = $_[0][2] if($num eq "221");
unless(Xchat::nickcmp($nick, $data->{nick}) == 0)
{
return PASS;
}
# :rtfm.inspircd.org 221 Brain :+xrowsgi
if($num eq "221")
{
$data->{modes} = substr($_[0][3], 1);
}
elsif($num eq "301")
{
# :rtfm.inspircd.org 301 Om LaptopOm :multiword test
$data->{away} = substr($_[1][4], 1);
}
elsif($num eq "307")
{
# :irc.leachim.info 307 t LeaChim :is a registered nick
$data->{registered} = $_[1][4];
}
elsif($num eq "310")
{
# :irc.leachim.info 310 t LeaChim :is available for help.
$data->{helpop} = $_[1][4];
}
elsif($num eq "311")
{
# :rtfm.inspircd.org 311 Om LaptopOm ~om supporter.inspircd.org * :Mr. Tiddles
$data->{ident} = $_[0][4];
$data->{dhost} = $_[0][5];
$data->{gecos} = substr($_[1][7], 1);
}
elsif($num eq "312")
{
# :rtfm.inspircd.org 312 Om LaptopOm rtfm.inspircd.org :InspIRCd Support Server
$data->{servername} = $_[0][4];
$data->{serverdesc} = substr($_[1][5], 1);
}
elsif($num eq "313")
{
# :rtfm.inspircd.org 313 Om LaptopOm :is a Supporter on InspIRCd
$data->{oper} = substr($_[1][4], 1);
}
elsif($num eq "317")
{
# :rtfm.inspircd.org 317 Om LaptopOm 21215 1140968241 :seconds idle, signon time
$data->{idle} = $_[0][4];
$data->{signon} = $_[0][5];
}
elsif($num eq "318")
{
# :rtfm.inspircd.org 318 Om LaptopOm :End of /WHOIS list.
$data->{whoisdone} = 1;
if($data->{nsinfodone})
{
# xcprint "And /ns info was finished too, print 'er out";
do_print_whois($data);
return EAT_KILL;
}
}
elsif($num eq "319")
{
# :rtfm.inspircd.org 319 Om LaptopOm :@#inspircd
$data->{chans} = "" unless($data->{chans});
$data->{chans} .= substr($_[1][4], 1);
}
elsif(($num eq "320") or ($num eq "671"))
{
# :rtfm.inspircd.org 320 Om LaptopOm :is using a secure connection
if($_[1][3] =~ /secure connection/i)
{
$data->{ssl} = substr($_[1][4], 1);
}
else
{
$data->{swhois} = substr($_[1][4], 1);
}
}
elsif($num eq "378")
{
# :rtfm.inspircd.org 378 Om LaptopOm :is connecting from *@wlupton.gotadsl.co.uk 81.6.252.165
if($_[1][4] =~ /connecting\s+from\s+([^@]*)@(\S+)\s+(\S+)/)
{
$data->{host} = $2;
$data->{ip} = $3;
}
else
{
debug("Couldn't parse string into host/ip: " . $_[1][4]);
}
}
elsif($num eq "379")
{
# :irc.leachim.info 379 t LeaChim :is using modes +iowghraAsxNWztp +kcfFjveGnNqSsoJO
if($_[1][4] =~ /modes\s+(\S+)\s+(\S+)/i)
{
$data->{modes} = $1;
$data->{snomask} = $2;
}
else
{
debug("Couldn't parse string into modes/snomask: " . $_[1][4]);
}
}
elsif($num eq "401")
{
# :rtfm.inspircd.org 401 Om Blibble :No such nick/channel
$data->{offline} = substr($_[1][4], 1);
$data->{whoisdone} = 1;
if($data->{nsinfodone})
{
do_print_whois($data);
return EAT_KILL;
}
}
elsif($num eq "406")
{
# :stitch.chatspike.net 406 Omster dsfds :There was no such nickname
$data->{offline} = substr($_[1][4], 1);
$data->{whoisdone} = 1;
if($data->{nsinfodone})
{
do_print_whowas($data);
return EAT_KILL;
}
}
return EAT;
}
sub on_whoisnotice
{
my $msg = substr($_[1][3], 1);
my $data = $_[2];
# :NickServ!services@chatspike.net NOTICE Om :Om is Mr. Tiddles
# :NickServ!services@chatspike.net NOTICE Om : Last seen time: Feb 27 18:21:59 2006 UTC
# :NickServ!services@chatspike.net NOTICE Om : Time registered: May 08 23:08:36 2004 UTC
# :NickServ!services@chatspike.net NOTICE Om :Last quit message: Ping timeout
# :NickServ!services@chatspike.net NOTICE Om : URL: http://www.inspircd.org/
# :NickServ!services@chatspike.net NOTICE Om : Information: Blibble?
# :NickServ!services@chatspike.net NOTICE Om : Options: Kill protection, Security, Private
if($_[0][0] =~ /$config{nickserv}!.*/i)
{
unless($data->{nsinfodone})
{
if($msg =~ /\s*Last\s+seen\s+time:\s+(.*)/i)
{
$data->{lastseen} = $1;
}
elsif($msg =~ /\s*Last\s+quit\s+message:\s+(.*)/i)
{
$data->{lastquit} = $1;
}
elsif($msg =~ /\s*Time\s+registered:\s+(.*)/i)
{
$data->{reggedon} = $1;
}
elsif($msg =~ /\s*URL:\s+(.*)/i)
{
$data->{url} = $1;
}
elsif($msg =~ /\s*Information:\s+(.*)/i)
{
$data->{info} = $1;
}
elsif($msg =~ /\s*E-mail\s+address:\s+(\S+)/i)
{
$data->{email} = $1;
}
elsif($msg =~ /\s*Nickname\s+(\S+)\s+isn't\s+registered.*/i)
{
$data->{nsinfodone} = 1;
if($data->{whoisdone})
{
do_print_whois($data);
return EAT_KILL;
}
}
elsif($msg =~ /\s*Options:\s+(.*)/i)
{
$data->{options} = $1;
$data->{nsinfodone} = 1;
if($data->{whoisdone})
{
do_print_whois($data);
return EAT_KILL;
}
}
}
else
{
return PASS;
}
return EAT;
}
else
{
return PASS;
}
}
sub handle_enteredline
{
if(Xchat::get_info("channel") eq $config{globalstab})
{
Xchat::command("raw WALLOPS :$_[1][0]");
return Xchat::EAT_XCHAT;
}
else
{
return Xchat::EAT_NONE;
}
}
sub on_notice
{
my $msg = substr($_[1][3], 1);
my $nick = substr($_[0][0], 1, index($_[0][0], "!")-1);
my $server = Xchat::get_info("network");
if($nick =~ /Global/i)
{
Xchat::command("query $config{globalstab}");
Xchat::print("\t - \0034\002Services Global\002\003 $msg", $config{globalstab});
return Xchat::EAT_XCHAT;
}
elsif(substr($_[0][2], 0, 2) eq '$*')
{
Xchat::command("query $config{globalstab}");
Xchat::print("\t - \0034\002Global Notice\002\003 From $nick: $msg", $config{globalstab});
return Xchat::EAT_XCHAT;
}
elsif($msg =~ /\s*For\s+more\s+information,\s+type:\s+\002\/msg\s+NickServ\s+INFO\s+\S+\s+ALL.*/i)
{
return Xchat::EAT_XCHAT;
}
elsif(Xchat::nickcmp($nick, $config{nickserv}) == 0)
{
if (($msg =~ /This nickname is registered/i) or ($msg =~ /This nickname is owned by someone else./i))
{
do_identify($server);
debug("Got identify request from NickServ ($config{nickserv}) on '$server', hooking $config{identtimeout} second timer for auto-perform");
debug("Unhooked identify timeout too") if(removerequest($server, "Ident request"));
request($server, { server => $server }, sub { return PASS; }, $config{identtimeout}, \&do_perform_timeout, "Perform request");
return Xchat::EAT_XCHAT;
}
if (($msg =~ /Password accepted/i) or ($msg =~ /you are now recognized/i) or ($msg =~ /you are now identified/i))
{
do_perform($server);
debug("Got ident confirmation from NickServ ($config{nickserv}) on '$server', executed perform commands");
debug("Unhooked perform timeout too") if(removerequest($server, "Perform request"));
return EAT_KILL;
}
}
# :kohola.northshore.hawaii NOTICE Omster :*** Om removed you from #services with the message: Rawr
elsif((index($nick, '.') >= 0) and ($msg =~ /\*\*\*\s+(\S+)\s+removed\s+you\s+from\s+(\S+)\s+with\s+the\s+message:\s+(\S+)/i))
{
my($nick, $chan, $reason) = ($1, $2, $3);
xcprint("You were removed from $chan by $nick", undef, $server);
Xchat::command("join $chan");
}
return Xchat::EAT_NONE;
}
sub do_print_whowas
{
return do_print_whois(shift(@_), "whowas");
}
sub do_print_whois
{
my($data, $title) = @_;
$title = "whowas" if(!length($title) and ($data->{offline} =~ /was/i));
$title = "/whois" unless(length($title));
print_whois_start($title);
print_whois_value("Nickname", $data->{nick});
print_whois_value("Hostname", $data->{ident} . "@" . $data->{dhost}) if($data->{ident} or $data->{dhost});
print_whois_value("Real Host/IP", $data->{host} . " / " . $data->{ip}) if($data->{host} or $data->{ip});
print_whois_value("Real Name", $data->{gecos});
print_whois_value("Modes", $data->{modes});
print_whois_value("Channels", $data->{chans});
print_whois_value("Away", $data->{away});
print_whois_value("Oper", "$data->{nick} $data->{oper}") if($data->{oper});
print_whois_value("SSL", "$data->{nick} $data->{ssl}") if($data->{ssl});
print_whois_value("Extra", "$data->{nick} $data->{swhois}") if($data->{swhois});
print_whois_value("Idle", ts2str($data->{idle})) if($data->{idle});
print_whois_value("Offline", $data->{offline});
print_whois_value("Signon", scalar(localtime($data->{signon}))) if($data->{signon});
print_whois_value("Server", $data->{servername} . " (" . $data->{serverdesc} . ")") if($data->{servername} or $data->{serverdesc});
print_whois_value("Options", $data->{options});
print_whois_value("Last quit message", $data->{lastquit});
print_whois_value("Registered", $data->{reggedon});
print_whois_value("URL", $data->{url});
print_whois_value("Info", $data->{info});
print_whois_end($title);
return 0;
}
sub print_whois_start
{
my $title = shift;
my $start = "\cC6\2.\2";
my $mid = "\cC\2[\2 \cC3" . $title . "\cC \2]\2\cC6";
my $end = "\cC6\2.\2\cC";
my $glue = "";
my $onoff = 0;
for(my $i = 0; $i < (($config{whoiswidth} - displength($start) - displength($mid) - displength($end))/2); $i++)
{
$glue .= ($i % 2) ? "\cC6-" : "\cC13-";
}
Xchat::print $start . $glue . $mid . $glue . $end;
}
sub print_whois_end
{
print_whois_start(@_);
}
sub print_whois_value
{
my($key, $value) = @_;
my $line = "";
my $prefix = "";
my $postfix = "";
return unless($value);
$config{whoisdepth} = 0 unless(defined($config{whoisdepth}));
$config{whoisdepth} = 0 if($config{whoisdepth} > 3);
$prefix = ($config{whoisdepth} % 2) ? "\cC13" : "\cC6";
$prefix .= ($config{whoisdepth} < 2) ? "\2|\2\cC " : "|\cC ";
$line = $postfix = $prefix;
$config{whoisdepth}++;
for(my $i = 0; $i < $config{whoisdepth}; $i++)
{
$line .= " ";
}
$line .= "\2" . substr($key, 0, 1) . "\2" . substr($key, 1) . ": ";
# pad_and_append - append as much of possible of $value onto $line, add $postfix and print.
# return how much of $value was printed.
my $prevaluelen = displength($line);
$value = substr($value, pad_and_append($line, $value, $postfix));
$line = $prefix;
for(my $i = 0; $i < $prevaluelen; $i++)
{
$line .= " ";
}
while(length($value) > 0)
{
$value = substr($value, pad_and_append($line, $value, $postfix));
}
}
sub pad_and_append
{
my($line, $value, $postfix) = @_;
my $vlen = length($value);
my $dlen = displength($line);
my $spacefor = $config{whoiswidth} - $dlen - displength($postfix);
my $valueseg = rindex($value, " ", $spacefor);
$valueseg = $vlen if($vlen < $spacefor);
$line .= substr($value, 0, $valueseg);
$dlen = displength($line);
for(my $i = 0; $i < ($config{whoiswidth} - $dlen - displength($postfix)); $i++)
{
$line .= " ";
}
$line .= $postfix;
Xchat::print $line;
return $valueseg;
}
### --- End /whois code --- ###
### --- Start /whowas code --- ###
sub handle_whowas
{
shift @{$_[0]}; # Lose the "whowas"
my $server = Xchat::get_info("network");
xcprint "/whowas on undefined server, output may be misdirected." unless($server);
for my $nick (@{$_[0]})
{
# Send out the /whois
Xchat::command("raw WHOWAS $nick $nick");
Xchat::command("raw PRIVMSG $config{nickserv} :info $nick");
# Initialise the structure we store the info in until it's printed.
my $data = {
nick => $nick,
server => $server,
};
request($server, $data, \&on_whoisraw, $config{whoistimeout}, \&do_print_whowas, "Whowas on $nick");
}
return Xchat::EAT_ALL;
}
### --- End /whowas code --- ###
### --- Start mass mode code --- ###
sub do_massmode
{
my($chan, $char, $direction) = @_;
my $maxmodes = get_maxmodes();
my @users = Xchat::get_list("users");
my $modes = [];
my $params = [];
for my $user (@users)
{
debug("$user->{nick} has prefix $user->{prefix}");
if($direction eq '+')
{
next if($modelevels{$modemap{$user->{prefix}}} >= $modelevels{$char}); # Don't set modes on users who already have the same or higher access
}
else
{
next if($modemap{$user->{prefix}} ne $char); # Don't try and remove the mode on users we don't see as having it.
next if(Xchat::nickcmp($user->{nick}, Xchat::get_info("nick")) == 0); # Don't remove modes on ourself
}
push(@{$modes}, $direction . $char);
push(@{$params}, $user->{nick});
}
send_massmodes($modes, $params, $chan);
}
sub handle_mmode
{
my $cmd = $_[0][0];
my $chan = $_[0][1];
my @mode = ();
$chan = Xchat::get_info("channel") unless($chan);
@mode = ('o', '+') if($cmd =~ /mop/i);
@mode = ('h', '+') if($cmd =~ /mhop/i);
@mode = ('v', '+') if($cmd =~ /mvop/i);
@mode = ('o', '-') if($cmd =~ /mdeop/i);
@mode = ('h', '-') if($cmd =~ /mdehop/i);
@mode = ('v', '-') if($cmd =~ /mdevop/i);
do_massmode($chan, @mode);
return Xchat::EAT_ALL;
}
sub handle_mvopng
{
my $chan = $_[0][1];
$chan = Xchat::get_info("channel") unless($chan);
my @users = Xchat::get_list("users");
my $modes = [];
my $params = [];
for my $user (@users)
{
next if($user->{nick} =~ /Guest([0-9]+).*/i);
next if(length($modemap{$user->{prefix}}));
push(@{$modes}, '+v');
push(@{$params}, $user->{nick});
}
send_massmodes($modes, $params, $chan);
return Xchat::EAT_XCHAT;
}
### --- End mass mode code --- ###
### --- Start timed ban code --- ###
sub handle_timedkb
{
}
sub handle_timedkblist
{
}
sub handle_timedkbremove
{
}
### --- End timed ban code --- ###
### --- Start media control code --- ###
sub handle_audio_nowplaying
{
my $colourful = ($_[0][0] =~ /^np[l]?$/i) ? 1 : 0;
my $verbose = ($_[0][0] =~ /^npl[b]?$/i) ? 0 : 1;
my @dests = split(/ /, $_[1][1]);
my $player = 'audacious';
my($path, $artist, $album, $song, $title, $done, $length, $bitrate, $bps, $freq, $filename, $version, $comment, $genre, $extension, $replaygain, @meta);
sub amk
{
use URI;
my $a = shift;
if($player eq 'amarok')
{
return mychomp(qx(dcop amarok player $a));
}
elsif($player eq 'audacious')
{
my %map = ( 'currentTime' => 'current-song-output-length',
'path' => 'current-song-filename',
'artist' => 'current-song-tuple-data artist',
'album' => 'current-song-tuple-data album',
'title' => 'current-song-tuple-data title',
'totalTime' => 'current-song-length',
'sampleRate' => 'current-song-frequency-khz',
'comment' => 'current-song-tuple-data comment',
'genre' => 'current-song-tuple-data genre',
'bitrate' => 'current-song-bitrate-kbps'
);
my $out = mychomp(qx(audtool $map{$a}));
if($a eq 'path')
{
$out = URI->new($out)->file;
}
return $out;
}
else
{
return '';
}
};
sub r { my($a, @b) = @_; return "" unless($a); my $r = ((int(rand(3)) == 2) ? $a . "\017" : "\017" . $a); $r .= r(@b); return $r }
sub b { my($a, @b) = @_; return "" unless($a); my $r = "\002" . $a . "\002"; $r .= b(@b); return $r; }
$path = amk 'path';
if($path and ($path ne 'call failed'))
{
$done = amk 'currentTime';
$extension = uc substr($path, rindex($path, ".")+1);
if($extension eq 'FLAC')
{
my $str = `metaflac --show-bps --show-sample-rate --show-total-samples --show-tag=ALBUM --show-tag=ARTIST --show-tag=DESCRIPTION --show-tag=GENRE --show-tag=TITLE --show-tag=REPLAYGAIN_ALBUM_GAIN --show-tag=COMMENT "$path"`;
my($rawbps, $rawfreq, $samples, @tagdata) = split(/[\n\r]/, $str);
my %tags = (
'album' => [],
'title' => [],
'comment' => [],
'description' => [],
'genre' => [],
'title' => [],
'replaygain_album_gain' => []
);
for(@tagdata)
{
if(/^([^=]+)=(.*?)$/)
{
push(@{$tags{lc $1}}, $2);
}
}
$artist = $tags{'artist'}[0] or '';
$album = $tags{'album'}[0] or '';
$song = $tags{'title'}[0] or '';
$length = sprintf("%d:%02d", int(($samples)/(60*$rawfreq)), int(($samples%($rawfreq*60))/$rawfreq));
$bitrate = int(((-s $path)*$rawfreq)/($samples*128));
$bps = $rawbps;
$freq = $rawfreq/1000;
$comment = join('; ', @{ $tags{'comment'} }, @{ $tags{'description'} });
$genre = $tags{'genre'}[0] or '';
$replaygain = $tags{'replaygain_album_gain'}[0] or '';
}
elsif($extension eq 'MP3')
{
if(`mp3gain -s c "$path"` =~ /^Recommended "Album" dB change: (.*?)$/m)
{
$replaygain = sprintf("%.2f dB", $1);
$replaygain = '+' . $replaygain unless(substr($replaygain, 0, 1) eq '-');
}
$artist = amk 'artist';
$album = amk 'album';
$song = amk 'title';
$length = amk 'totalTime';
$freq = amk('sampleRate')/1000;
$comment = amk 'comment';
$genre = amk 'genre';
$bitrate = amk 'bitrate';
}
$title = join(' - ', $artist, $album, $song);
}
else
{
return Xchat::EAT_ALL;
}
push(@meta, "$bitrate kbit") if($bitrate > 320);
push(@meta, "$bps bps") if($bps and (int($bps) > 16));
push(@meta, "$freq kHz") if($freq != 44.1);
push(@meta, "$extension") if($extension);
push(@meta, "($replaygain ReplayGain)") if($replaygain);
push(@meta, "($genre)") if($genre);
push(@meta, "($comment)") if($comment);
my $command = join(' ', 'me', r(qw(n p :)), b('['), $title, b(']'), b('['), $done, '/', $length, b(']'));
if($verbose)
{
$command .= join(' ', '', b('['), @meta, b(']'));
}
unless($colourful)
{
$command =~ s/(\002|\017)//g;
}
# Remove possibility of executing extra commands by putting a newline in a tag field
$command =~ s/(\n|\r)//g;
if(@dests)
{
Xchat::command($command, split(/,/, $_, 2)) for(@dests);
}
else
{
Xchat::command($command);
}
# system('amsn-psm-trigger.pl "(8)' . $song . ' - ' . $artist . '(8)"');
return Xchat::EAT_ALL;
}
### --- End media control code --- ###
### --- Start abuse code --- ###
sub handle_slap
{
my $target = $_[1][1];
my $trout = undef;
my @action = ("slaps", "slashes", "murders", "attacks", "mutilates", "bitchslaps", "pokes", "stabs", "spanks", "whips", "thumps", "whacks", "wallops", "baseball-bats", "beautifies", "crushes", "crunches", "squashes", "quashes", "tickles");
my @sizes = ("a large", "a big", "a humongous", "an enormous", "a huge", "a monstrous", "a smelly", "a giant", "a large, smelly", "a bloody", "a disgusting", "a bloody, disgusting", "a large, smelly, disgusting", "a", "a dead", "a mutilated", "a wet and mouldy", "a slimy");
my @amounts = ("a bit", "a lot", "a few times", "several times", "many times", "infinitely", "repeatedly", "continuously", "forever");
my @how = ("with", "against", "on top of", "inside", "against the outside of", "underneath");
my $index = rand @amounts;
my $amount = @amounts[$index];
my $index = rand @sizes;
my $size = @sizes[$index];
my $index = rand @action;
my $action = @action[$index];
my $index = rand @how;
my $how = @how[$index];
my $rand = int(rand 10);
my $dir = Xchat::get_info("xchatdir");
my $file = '< ' . $dir . '/data/' . (($rand == 7) ? 'latin.txt' : 'fish.txt');
open(FH, $file) or debug("/me has no fish :(");
rand($.) < 1 && ($trout = $_) while <FH>;
close(FH);
chomp $trout;
Xchat::command(join(' ', "me", $action, $target, $amount, $how, $size, $trout));
return Xchat::EAT_ALL;
}
### --- End abuse code --- ###
### --- Start autojoin code --- ###
sub read_perform_file
{
my $network = shift;
my $dir = Xchat::get_info("xchatdir") . "/perform/";
$network = Xchat::get_info("network") unless($network);
$network = lc($network);
my $path = $dir . $network . ".txt";
# Nuke current data, should have called write_perform_file() if you wanted it saved :P
my $data = {};
open(FH, "< $path");
while(my $line = <FH>)
{
if($line =~ /([a-z]+)=(.*)/i)
{
my $key = lc($1);
my $value = $2;
$data->{$key} = [] unless(ref($data->{$key}) eq "ARRAY");
push(@{ $data->{$key} }, $value);
}
}
close(FH);
return $data;
}
sub write_perform_file
{
my $data = shift;
my $network = shift;
my $dir = Xchat::get_info("xchatdir") . "/perform/";
$network = Xchat::get_info("network") unless($network);
$network = lc($network);
my $path = $dir . $network . ".txt";
my $backuppath = $dir . "/backups/" . join('.', $network, time, "txt");
copy($path, $backuppath) or debug("Writing backup of perform file failed: $!");
open(FH, "> $path");
while(my($key, $value) = each(%{$data}))
{
for my $val (@{$value})
{
print FH "$key=$val\n";
}
}
close(FH);
}
sub handle_perform_pass_set
{
my $pass = $_[1][1];
(xcprint("No parameter given") and (return Xchat::EAT_ALL)) unless($pass);
my $data = read_perform_file();
$data->{password} = [ $pass ];
write_perform_file($data);
xcprint "Set password for " . Xchat::get_info("network");
return Xchat::EAT_ALL;
}
sub handle_perform_pass_get
{
my $data = read_perform_file();
my $net = Xchat::get_info("network");
xcprint(($data->{password}[0]) ? "Password for $net is $data->{password}[0]" : "No password found");
return Xchat::EAT_ALL;
}
sub handle_perform_preraw_add
{
my $raw = $_[1][1];
(xcprint("No parameter given") and (return Xchat::EAT_ALL)) unless($raw);
my $data = read_perform_file();
$data->{preraw} = [] unless(ref($data->{preraw}) eq "ARRAY");
push(@{ $data->{preraw} }, $raw);
write_perform_file($data);
xcprint "Added line to pre-autoperform list for " . Xchat::get_info("network");
return Xchat::EAT_ALL;
}
sub handle_perform_preraw_del
{
my $raw = $_[1][1];
(xcprint("No parameter given") and (return Xchat::EAT_ALL)) unless($raw);
my $data = read_perform_file();
@{ $data->{preraw} } = grep { $_ ne $raw } @{ $data->{preraw} };
write_perform_file($data);
xcprint "Removed line from pre-autoperform list for " . Xchat::get_info("network");
return Xchat::EAT_ALL;
}
sub handle_perform_preraw_list
{
my $data = read_perform_file();
xcprint "Pre-autoperform commands:";
xcprint "\t$_" for(@{ $data->{preraw} });
return Xchat::EAT_ALL;
}
sub handle_perform_add
{
my $channel = $_[0][1];
$channel = Xchat::get_info("channel") unless($channel);
my $data = read_perform_file();
$data->{chan} = [] unless(ref($data->{chan}) eq "ARRAY");
push(@{ $data->{chan} }, $channel);
write_perform_file($data);
xcprint "Added $channel to autojoin list for " . Xchat::get_info("network");
Xchat::command("join $channel");
return Xchat::EAT_ALL;
}
sub handle_perform_del
{
my $channel = $_[0][1];
$channel = Xchat::get_info("channel") unless($channel);
my $data = read_perform_file();
@{ $data->{chan} } = grep { $_ ne $channel } @{ $data->{chan} };
write_perform_file($data);
xcprint "Removed $channel from autojoin list for " . Xchat::get_info("network");
Xchat::command("part $channel Channel removed from autojoin list");
return Xchat::EAT_ALL;
}
sub handle_perform_list
{
my $data = read_perform_file();
xcprint "Autojoin channels:";
xcprint "\t$_" for(@{ $data->{chan} });
return Xchat::EAT_ALL;
}
sub handle_perform_raw_add
{
my $raw = $_[1][1];
(xcprint("No parameter given") and (return Xchat::EAT_ALL)) unless($raw);
my $data = read_perform_file();
$data->{raw} = [] unless(ref($data->{raw}) eq "ARRAY");
push(@{ $data->{raw} }, $raw);
write_perform_file($data);
xcprint "Added line to autoperform list for " . Xchat::get_info("network");
return Xchat::EAT_ALL;
}
sub handle_perform_raw_del
{
my $raw = $_[1][1];
(xcprint("No parameter given") and (return Xchat::EAT_ALL)) unless($raw);
my $data = read_perform_file();
@{ $data->{raw} } = grep { $_ ne $raw } @{ $data->{raw} };
write_perform_file($data);
xcprint "Removed line from autoperform list for " . Xchat::get_info("network");
return Xchat::EAT_ALL;
}
sub handle_perform_raw_list
{
my $data = read_perform_file();
xcprint "Autoperform commands:";
xcprint "\t$_" for(@{ $data->{raw} });
return Xchat::EAT_ALL;
}
### --- End autojoin code --- ###
### --- Start auto-identify code --- ###
sub handle_connect
{
my $server = Xchat::get_info("network");
my $data = read_perform_file();
debug("Executing first on-connect commands for '$server' and hooking ident request");
Xchat::command($_, undef, $server) for(@{ $data->{preraw} });
request($server, { server => $server }, sub { return PASS; }, $config{identtimeout}, \&do_ident_timeout, "Ident request");
return Xchat::EAT_NONE;
}
sub do_ident_timeout
{
my $server = $_[0]->{server};
debug("Ident request on '$server' timed out, identing and starting timer to execute perform commands");
do_identify($server);
request($server, { server => $server }, sub { return PASS; }, $config{identtimeout}, \&do_perform_timeout, "Perform request");
return PASS_KILL;
}
sub do_perform_timeout
{
my $server = $_[0]->{server};
debug("Ident confirmation request on '$server' timed out, executing perform commands");
do_perform($server);
return PASS_KILL;
}
sub do_identify
{
my $net = shift;
my $nick = Xchat::get_info("nick");
debug("do_identify() wasn't given a network") unless($net);
my $data = read_perform_file($net);
if((ref($data->{password}) eq "ARRAY") and @{$data->{password}})
{
xcprint("Identifying for " . $nick . "'s nickname on '$net'", $net, $net);
Xchat::command("raw PRIVMSG NickServ :identify " . $data->{password}[0], undef, $net);
}
else
{
xcprint("No password found for " . $nick . "'s nickname on '$net'", $net, $net);
}
}
sub do_perform
{
my $net = shift;
debug("do_perform() wasn't given a network") unless($net);
my $data = read_perform_file($net);
if((ref($data->{raw}) eq "ARRAY") and @{$data->{raw}})
{
xcprint("Executing raw commands for $net", $net, $net);
Xchat::command($_, undef, $net) for(@{$data->{raw}});
}
if((ref($data->{chan}) eq "ARRAY") and @{$data->{chan}})
{
# XXX - Check that the /join sent isn't over 512 characters
xcprint("Joining channels for $net", $net, $net);
Xchat::command("join " . join(',', @{$data->{chan}}), undef, $net);
}
}
sub handle_doperform
{
Xchat::command($_, undef, Xchat::get_info("network")) for(@{ read_perform_file->{preraw} });
do_perform(Xchat::get_info("network"));
return Xchat::EAT_ALL;
}
sub handle_identify
{
do_identify(Xchat::get_info("network"));
return Xchat::EAT_ALL;
}
### --- End auto-identify code --- ###
### --- Start auto-invite code --- ###
sub on_invite
{
# :megumi.chatspike.net 473 Omster #fuck :Cannot join channel (+i)
my $chan = $_[0][3];
my $net = Xchat::get_info("network");
debug("Sending /cs invite for $chan on $net");
Xchat::command("raw PRIVMSG $config{chanserv} :invite $chan", undef, $net);
request($net, { server => $net, channel => $chan, nick => Xchat::get_info("nick") }, \&on_csinvite, $config{invitetimeout}, sub { }, "/cs invite on $chan/$net" );
return Xchat::EAT_NONE;
}
sub on_csinvite
{
# :ChanServ!services@chatspike.net INVITE Omster :#fuck
if(lc($_[0][1]) eq lc("INVITE"))
{
my($nick) = split(/!/, substr($_[0][0], 1));
my $chan = substr($_[0][3], 1);
my $data = $_[2];
if((Xchat::nickcmp($nick, $config{chanserv}) == 0) and (Xchat::nickcmp($_[0][2], $data->{nick}) == 0) and ($chan eq $data->{channel}))
{
Xchat::command("JOIN $chan", undef, $data->{server});
return EAT_KILL;
}
}
return PASS;
}
### --- End auto-invite code --- ###
### --- Start message redirection code --- ###
sub on_wallops
{
my $sender = substr($_[0][0], 1);
my $message = substr($_[1][2], 1);
Xchat::command("query $config{globalstab}");
Xchat::set_context($config{globalstab}, Xchat::get_info("network"));
Xchat::emit_print("Receive Wallops", $sender, $message);
return Xchat::EAT_ALL;
}
sub on_highlight
{
my $net = Xchat::get_info("network");
my $chan = Xchat::get_info("channel");
my $nick = $_[0][0];
my $msg = $_[0][1];
Xchat::command("query $config{highlighttab}") unless(Xchat::find_context($config{highlighttab}, $net));
if($_[1] eq "msg")
{
Xchat::print("(\0034" . $nick . "/" . $chan . "\003)\t" . $msg, $config{highlighttab}, $net);
}
else
{
Xchat::print(" \0034*" . $nick . "/" . $chan . " " . $msg, $config{highlighttab}, $net);
}
return Xchat::EAT_NONE;
}
### --- End message redirection code --- ###
### --- Start +g support code --- ###
sub handle_badword
{
my(undef, @words) = @{$_[0]};
my $modes = [];
my $params = [];
foreach (@words)
{
push(@{$modes}, '+g');
push(@{$params}, $_);
}
send_massmodes($modes, $params);
return Xchat::EAT_XCHAT;
}
### --- End +g support code --- ###
sub on_join
{
my $nick = $_[0][0];
my $chan = $_[0][1];
my $host = $_[0][2];
my $flag = $_[0][3];
unless($flag)
{
Xchat::emit_print("Join", $nick, $chan, $host, 1);
my @clones = ();
for my $user (Xchat::get_list("users"))
{
my(undef, $uhost) = split(/@/, $user->{host}, 2);
my(undef, $jhost) = split(/@/, $host, 2);
if($uhost and $jhost and (lc($uhost) eq lc($jhost)))
{
push(@clones, $user->{prefix} . $user->{nick});
}
}
Xchat::print(" -\x{00BB} Clones on \cC12\002(\002\cC" . $chan . "\cC12\002)\002\cC for \cC12\002(\002\cC" . $nick . "\cC12\002)\002\cC are \cC12\002(\002\cC" . join(' ', @clones) . "\cC12\002)\002\cC") if(@clones);
return Xchat::EAT_XCHAT;
}
else
{
return Xchat::EAT_NONE;
}
}
sub handle_test { Xchat::EAT_NONE; }
sub handle_decode
{
my $coded = $_[0][1];
my $ip = join('.', hex(substr($coded,0,2)), hex(substr($coded,2,2)), hex(substr($coded,4,2)), hex(substr($coded,6,2)));
my $host = gethostbyaddr(inet_aton($ip), AF_INET);
$host = "Unresolvable" unless(length($host));
xcprint("Decoded $coded to $ip (\002$host\002)");
return Xchat::EAT_ALL;
}
sub handle_sysinfo_gfx
{
my $xinfo = `xdpyinfo`;
my $glxinfo = `glxinfo`;
my($xtitle, $xversion, $res, $card, $bits);
if($xinfo =~ /^\s*(X.*?) version:\s+(.*)$/im)
{
$xtitle = $1;
$xversion = $2;
}
else
{
$xtitle = "X11";
$xversion = "Unknown";
}
if($glxinfo and ($glxinfo =~ /^\s*OpenGL renderer string:\s+(.*)/im) and ($1 !~ /Mesa GLX Indirect/i))
{
$card = $1;
}
else
{
$card = (`lspci` =~ /^.*?VGA compatible controller:\s*(.*)$/im) ? $1 : "Unknown";
}
$res = ($xinfo =~ /^\s+dimensions:\s+(\S+).*$/im) ? $1 : "Unknown";
$bits = (`xwininfo -root` =~ /^\s+Depth: (.+)/im) ? $1 : "Unknown";
my $str = "(\002GFX\002):\002[\002 $card \002]\002 (\002$xtitle\002):\002[\002$xversion\002]\002 (\002Res\002):\002[\002$res @ $bits bits\002]\002";
return $str unless(ref($_[0]) eq 'ARRAY');
Xchat::command("say $str");
return Xchat::EAT_ALL;
}
sub handle_sysinfo_aud
{
my $lspci = `lspci`;
my $sndcard = ($lspci =~ /^.*Multimedia audio controller:\s*(.*)$/im) ? $1 : undef;
my $inpdev = ($lspci =~ /^.*?Input device controller:\s*(.*?)$/im) ? $1 : undef;
my $str = "";
$str .= "(\002SoundCard\002):\002[\002$sndcard\002]\002" if($sndcard);
$str .= " " if($sndcard and $inpdev);
$str .= "(\002Input Device Controller\002):\002[\002$inpdev\002]\002" if($inpdev);
return $str unless(ref($_[0]) eq 'ARRAY');
Xchat::command("say $str");
return Xchat::EAT_ALL;
}
sub handle_sysinfo_mb
{
my $isabrg = (`lspci` =~ /^.*ISA bridge:\s*(.*)$/im) ? $1 : "Unknown";
my $str = "";#"(\002ISA Bridge\002):\002[\002$isabrg\002]\002";
return $str unless(ref($_[0]) eq 'ARRAY');
Xchat::command("say $str");
return Xchat::EAT_ALL;
}
sub handle_sysinfo_kern
{
chomp(my $ker = `uname -srm`);
my $str = "(\002Kernel\002):\002[\002$ker\002]\002";
return $str unless(ref($_[0]) eq 'ARRAY');
Xchat::command("say $str");
return Xchat::EAT_ALL;
}
sub handle_sysinfo_cpu
{
my $cpuinfo = read_file('/proc/cpuinfo');
my $model = ($cpuinfo =~ /^\s*model name\s*:\s*(.*)$/im) ? $1 : "Unknown";
my $freq = ($cpuinfo =~ /^\s*cpu MHz\s*:\s*(.*)$/im) ? int($1) : "Unknown";
my $cache = ($cpuinfo =~ /^\s*cache size\s*:\s*(.*)$/im) ? $1 : "Unknown";
my $bogomips = ($cpuinfo =~ /^\s*bogomips\s*:\s*(.*)$/im) ? $1 : "Unknown";
my $vendor = ($cpuinfo =~ /^\s*vendor_id\s*:\s*(.*)$/im) ? $1 : "Unknown";
my $str = "(\002CPU\002):\002[\002$model @ $freq MHz\002]\002 \002Cache\002: $cache";
return $str unless(ref($_[0]) eq 'ARRAY');
Xchat::command("say $str");
return Xchat::EAT_ALL;
}
sub handle_sysinfo_disk
{
my($total, $used, $free) = (0, 0, 0);
my @pci = `lspci`;
my %ints = ();
foreach (@pci)
{
if(/^.*IDE interface:\s*(.*)$/i)
{
$ints{$1} = 1 unless($ints{$1});
$ints{$1}++;
}
}
my @tmp = ();
push(@tmp, $_ . (($ints{$_} > 1) ? " x $ints{$_}" : "")) foreach(keys(%ints));
foreach(`df`)
{
if(/(\S+)\s+(\d+)\s+(\d+)\s+(\d+).*/)
{
$total += $2;
$used += $3;
$free += $4;
}
}
my $str = "(\002Mounted Disks\002):\002[\002 Total: " . int($total/1024/1024) . " GiB, Free: " . int($free/1024/1024) . " GiB\002]\002";
return $str unless(ref($_[0]) eq 'ARRAY');
Xchat::command("say (\002Disk Controllers\002):\002[\002" . join(', ', @tmp) . "\002]\002 $str");
return Xchat::EAT_ALL;
}
sub handle_sysinfo_net
{
my($in, $out) = (0, 0);
foreach my $dev (@{$config{networkdevs}})
{
if(`ifconfig $dev` =~ /^\s*RX bytes:([0-9]+)\s+\(([0-9\.]+) Mb\)\s+TX bytes:([0-9]+)\s+\(([0-9\.]+) Mb\)/im)
{
$in += $1;
$out += $3;
}
}
my $str = "(\002Network\002):\002[\002" . int($in/1024/1024) . " MiB In, " . int($out/1024/1024) . " MiB Out\002]\002";
return $str unless(ref($_[0]) eq 'ARRAY');
Xchat::command("say $str");
return Xchat::EAT_ALL;
}
sub handle_sysinfo_temp
{
my @sens = `sensors`;
my @res = ();
foreach(@sens)
{
push(@res, "$1 Temp $2") if(/(.*?)\s+Temp:\s+\+(\S+)/i);
}
my $str = "(\002Temperature\002): \002[\002" . join(' ', @res) . "\002]\002";
return $str unless(ref($_[0]) eq 'ARRAY');
Xchat::command("say $str");
return Xchat::EAT_ALL;
}
sub handle_sysinfo_ram
{
my @tmp = ();
foreach(`free -b`)
{
if(/^\s*(Mem|Swap):\s+(\d+)\s+(\d+)\s+(\d+).*$/i)
{
my $n = ($1 eq 'Mem') ? 'RAM' : $1;
push(@tmp, "(\002$n\002):\002[\002" . int($3/1024/1024) . "/" . int($2/1024/1024) . " MiB used\002]\002");
}
}
my $str = join(' ', @tmp);
return $str unless(ref($_[0]) eq 'ARRAY');
Xchat::command("say $str");
return Xchat::EAT_ALL;
}
sub handle_sysinfo_all
{
my $header = length(Xchat::get_info("channel")) + length("PRIVMSG :\r\n");
my @parts = (handle_sysinfo_kern(), handle_sysinfo_cpu(), handle_sysinfo_misc(), handle_sysinfo_gfx(), handle_sysinfo_mb(), handle_sysinfo_ram(), handle_sysinfo_disk(), handle_sysinfo_net(), handle_sysinfo_temp());
my $out;
foreach my $part (@parts)
{
if((length($out) + length($part) + $header) < 400)
{
debug((length($out) + length($part) + $header) . " is less than 400, appending");
$out = length($out) ? "$out $part" : $part;
}
else
{
Xchat::command("say $out");
$out = $part;
}
}
Xchat::command("say $out") if(length($out));
return Xchat::EAT_ALL;
}
sub handle_sysinfo_misc
{
chomp(my $procs = `ps ax | wc -l`);
my($uptime, $load) = (`uptime` =~ /^.*?up\s+(.*?),\s+\d+\s+users,\s+load average:\s+([\d\.]+).*$/im) ? ($1, $2) : ("Unknown", "Unknown");
return "(\002Uptime\002):\002[\002$uptime\002]\002 (\002Load\002):\002[\002$load\002]\002 (\002Processes\002):\002[\002$procs\002]\002";
}