use 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 \n Get's information about the user" }); Xchat::hook_command("WHOWAS", \&handle_whowas, { help_text => "/whowas \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 [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 \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 \n Slaps target around violently with various aquatic (and not) creatures."}); Xchat::hook_command("DECODE", \&handle_decode, { help_text => "/decode \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 \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(); 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 ; 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 = ) { 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"; }