#!/usr/bin/perl -w
use strict;

###############################################################################
#####                                                                       ###
#####    Ultimate::Opers for [x]Chat by G. Nickels <gsn@kernel-oops.de>     ###
#####                                                                       ###
###############################################################################



##### BEGIN CONFIG SECTION ####################################################

### activate / deactivate some of the scripts functions completely

my $use_snotice="true";                    # use snotice handler?
my $use_whois="true";                      # use whois replier?
my $use_akill="true";                      # use autokill commands?
my $use_rwhois="true";                     # use whois redirect?
my $use_cmode="true";                      # use chanmode announce on join

### snotice handler

my %sn_channel = (                         # which channels/tabs events go to
 '_default_' => 'b)Net_SNOTICE',
# 'Network-Info' => 'SomeTab',
# 'SomeUglyEvent' => 'SomeOtherTab',       # example line for adding own events
);

my %sn_color = (                           # define colors for events
 '_default_' => "",
 'Global' => "\00304",
 'Network-Global' => "\00307",
 'Global-Connect/Exit' => "\00308",
 'Spam-Notice' => "\00312",
 'Flood-Notice' => "\00311",
 'Server-Notice' => "\00310",
 'Realops-Notice' => "\00309",
 'Routing' => "\00313",
 'Network-Info' => "\00315",
# 'SomeUglyEvent' => "\00310",             # example line for adding events
);

my $sn_netname='bongster';                 # name of your irc network

my $sn_dowrap='true';                      # activate word wrapping?
my $sn_wordwrap=90;                        # wrap words after x chars

### whois replier

my $wi_msg_command="notice";               # message command (notice, ctcp,...)
my $wi_msg_text="Neugierling :-P";         # message text (or ctcp action e.g.)

### autokill commands

my $ak_defaultreason="go back to mama!";   # akill reason if not defined
my $ak_defaultexpires="+2d";		   # expiration time
my $ak_akill_whois="false";                # print whois info on akill?

### whois redirection

my $rw_dowrap='true';                      # activate word wrapping?
my $rw_wordwrap=70;                        # wrap words after x chars
my $rw_serverlog='false';                  # also print to server log?

my $rw_color_headfoot="\00304";            # color for whois start/end
my $rw_color_body="\00312";                # color for whois body
my $rw_color_highlight="\00303";           # color for highlighted information

### chanmode announce

my $cm_color="\00307";                     # color for chanmodes announce

##### END CONFIG SECTION ######################################################



###############################################################################
#####                                         #################################
#####   !!! DON'T CHANGE ANYTHING BELOW !!!   #################################
#####                                         #################################
###############################################################################



##### global part #############################################################

# information about script name, author, etc.
my $scriptname='Ultimate::Opers';
my $scriptversion='0.6.2';
my $description='oper script for ultimate ircd based networks';
my $year='2005';
my $author='Guido S. Nickels';
my $email='gsn@kernel-oops.de';

# script registration and onload output
my $providescommands=0;
IRC::register($scriptname,$scriptversion,$author,$description);
IRC::print("  \n");
IRC::print("Loading $scriptname $scriptversion\n");
IRC::print("  (C) $year $author <$email>\n");
IRC::print("  \n");
IRC::print("  :: Features enabled:          \n");
IRC::print("  ::                            \n");

# print enabled features

IRC::print("  :: snotice  - snotice handling \n") if($use_snotice eq "true");
IRC::print("  :: whois    - whois actions    \n") if($use_whois eq "true");
IRC::print("  :: rwhois   - whois redirect   \n") if($use_rwhois eq "true");
IRC::print("  :: cmode    - chanmode announce\n") if($use_cmode eq "true");

if($use_akill eq "true"){
 $providescommands=1;
 IRC::print("  :: akill    - akill commands  \n");
}

# if some feature provides commands...

if($providescommands==1){
 IRC::print("  ::                            \n");
 IRC::print("  :: Commands:                  \n");
 IRC::print("  ::                            \n");
}

# print available commands

if($use_akill eq "true"){
 IRC::print("  :: /autokill <nick> [<reason>]\n");
 IRC::print("  :: /ak       <nick> [<reason>]\n");
}

IRC::print("  \n");



##### snotice part ############################################################

if($use_snotice eq "true")
{
 # the message handler
 IRC::add_message_handler("NOTICE","local_snotice_handler");

 sub local_snotice_handler {

  # look which net we are in
  my $sn_server = IRC::get_info(3);
  my @sn_sinfo = split /\./, $sn_server;
  my $sn_network = @sn_sinfo[1];

  # a little bit parsing of lines we get from server
  my $sn_output = "@_";
  my @sn_line = split / /,$sn_output;
  my @sn_mstring = split /:\*\*\* /,$sn_output;
  my @sn_message = split / -- /,$sn_mstring[1];
 
  # look if network is b)net - if not, do nothing
  if ($sn_network eq $sn_netname) {
   my $sn_indicator = @sn_line[3];
   my $sn_type = @sn_line[4];

   # we don't want normal notices (whois usage e.g.) in here
   if ($sn_indicator eq ":***" && $sn_type eq "Notice") {
    return 0;
   }

   # rest may go to our snotice window
   elsif ($sn_indicator eq ":***") {

    my ($sn_sendchan,$sn_msgcolor);
    #select output channel
    if (exists $sn_channel{$sn_type}) {
     $sn_sendchan=$sn_channel{$sn_type};
    }
    else {
     $sn_sendchan=$sn_channel{_default_};
    }
    IRC::command("/query $sn_sendchan");

    #select output color
    if (exists $sn_color{$sn_type}) {
     $sn_msgcolor=$sn_color{$sn_type};
    }
    else {
     $sn_msgcolor=$sn_color{_default_};
    }

    #print line
    IRC::print_with_channel("$sn_msgcolor\[$sn_type\]\n",$sn_sendchan,$sn_server);
    for (my $i=1; @sn_message[$i] ne ""; $i++) {

     # word wrapping stuff
     if ((length(@sn_message[$i]) > $sn_wordwrap) && ($sn_dowrap eq "true")) {
      my $sn_wwtext="  - @sn_message[$i]";
      my @sn_wwinput= split /\s/,$sn_wwtext;
      my ($sn_wwoutput,$sn_wwouttemp);
      my $sn_wwcount=0;    #needed for the "  - " in beginning of only 1st line

      # we have to read words into helper var and print the line if it gets
      # too long
      for (my $i=0; defined $sn_wwinput[$i]; $i++) {
       $sn_wwouttemp="$sn_wwoutput$sn_wwinput[$i] ";
       $sn_wwoutput="$sn_wwouttemp" if ((length($sn_wwouttemp) < $sn_wordwrap));
       # print output if helper var gets too big
       if (length($sn_wwouttemp) >= $sn_wordwrap) {
        if ($sn_wwcount == 0) {
         IRC::print_with_channel("$sn_msgcolor$sn_wwoutput\n",$sn_sendchan,$sn_server);
         $sn_wwcount=1;
        }
        else {
         IRC::print_with_channel("$sn_msgcolor    $sn_wwoutput\n",$sn_sendchan,$sn_server);
        }
        $sn_wwoutput="$sn_wwinput[$i] ";
        $sn_wwouttemp="";
       }
      }

      # if there is still some unprinted text left, we throw it out now
      if ($sn_wwoutput ne "") {
       if ($sn_wwcount == 0) {
        IRC::print_with_channel("$sn_msgcolor$sn_wwoutput\n",$sn_sendchan,$sn_server);
        $sn_wwcount=1;
       }
       else {
        IRC::print_with_channel("$sn_msgcolor    $sn_wwoutput\n",$sn_sendchan,$sn_server);
       }
       $sn_wwoutput="";
      }
     }

     # what to do if we don't want (or don't need) any wrapping
     else {
      IRC::print_with_channel("$sn_msgcolor  - @sn_message[$i]\n",$sn_sendchan,$sn_server);
     }
    }

    # we don't give back this stuff to xchat for further handling, because
    # we already have our nice formatted output :)
    return 1;
   }

   # just for if some input has arrived us we don't want at all, we give it
   # back to xchat for further handling now
   else {
    return 0;
   }
  }
 }
}



##### whois part ##############################################################

if($use_whois eq "true"){

 # the whois message handler
 IRC::add_message_handler("NOTICE", "local_whois_handler");

 sub local_whois_handler {
  my $wi_keyword = "/WHOIS";
  my $wi_output = "@_";
  my @wi_whois = split / /, $wi_output;
  my $wi_nick = "@wi_whois[12]";
  my $wi_command = "@wi_whois[6]";

  # if there is a whois attempt, we take action :)
  if ($wi_command eq $wi_keyword) { 
   IRC::command("/$wi_msg_command $wi_nick $wi_msg_text");
  }
  return 0;
 }
}



##### akill part ##############################################################

# have to initialize this here because it's also needed for whois redirection
# below
my $ak_akill_flag=0;
if($use_akill eq "true"){

 # akill handlers
 IRC::add_command_handler("autokill","local_ak_whois_handler");
 IRC::add_command_handler("ak","local_ak_whois_handler");
 IRC::add_message_handler("616","local_ak_akill_handler");

 # if we want silence we have to take control on ALL whois output
 if ($ak_akill_whois eq "false") {
  IRC::add_message_handler("311","local_ak_akill_handler");
  IRC::add_message_handler("307","local_ak_akill_handler");
  IRC::add_message_handler("615","local_ak_akill_handler");
  IRC::add_message_handler("319","local_ak_akill_handler");
  IRC::add_message_handler("301","local_ak_akill_handler");
  IRC::add_message_handler("312","local_ak_akill_handler");
  IRC::add_message_handler("275","local_ak_akill_handler");
  IRC::add_message_handler("313","local_ak_akill_handler");
  IRC::add_message_handler("614","local_ak_akill_handler");
  IRC::add_message_handler("317","local_ak_akill_handler");
  IRC::add_message_handler("318","local_ak_akill_handler");
 }

 my ($ak_nickname,$ak_reason);

 sub local_ak_whois_handler {
  
  # first, get commandline arguments (nick, kill reason)
  ($ak_nickname, $ak_reason)=split(/ +/, $_[0],2);
  if ($ak_reason eq "")
  {
   $ak_reason=$ak_defaultreason;
  }

  # perform whois on nick and set autokill flag
  IRC::command("/whois $ak_nickname");
  $ak_akill_flag=1;

  # we need some timeout for resetting autokill flag if we want silent
  # operation
  if ($ak_akill_whois eq "false") {
   IRC::add_timeout_handler(100,"local_ak_flag_handler");
  }
  return 1
 }

 # the timeout handler - set akill flag back to 0 after x ms
 sub local_ak_flag_handler { $ak_akill_flag=0; }

 sub local_ak_akill_handler {
 
  # only take action if the whois is performed by our whois_handler
  if ($ak_akill_flag==1) {
   my ($ak_server, $ak_code, $ak_me, $ak_nick, $ak_real, $ak_hostname, $ak_dnshost, $ak_ip) = split(/ +/, $_[0], 8);

   # if we have caught the right line of whois output, shoot the lamer ;)
   if ($ak_code==616 && $ak_nick eq $ak_nickname) {
    IRC::command("/msg operserv akill add $ak_defaultexpires *\@$ak_dnshost $ak_reason\n");
   }

   # if we don't control akill flag through timeout handler, we need to reset
   # it here
   if ($ak_akill_whois ne "false") {
    $ak_akill_flag=0;
    return 0;
   }
   else {
    return 1;
   }
  }
  else {
   return 0;
  }
 }
}


##### whois redirection ########################################################

if($use_rwhois eq "true"){

 # rwhois handlers
 IRC::add_message_handler("311","local_rw_rwhois_handler");
 IRC::add_message_handler("307","local_rw_rwhois_handler");
 IRC::add_message_handler("615","local_rw_rwhois_handler");
 IRC::add_message_handler("616","local_rw_rwhois_handler");
 IRC::add_message_handler("301","local_rw_rwhois_handler");
 IRC::add_message_handler("319","local_rw_rwhois_handler");
 IRC::add_message_handler("312","local_rw_rwhois_handler");
 IRC::add_message_handler("275","local_rw_rwhois_handler");
 IRC::add_message_handler("313","local_rw_rwhois_handler");
 IRC::add_message_handler("614","local_rw_rwhois_handler");
 IRC::add_message_handler("317","local_rw_rwhois_handler");
 IRC::add_message_handler("318","local_rw_rwhois_handler");
 
 sub local_rw_rwhois_handler {

  # some preformatting of input
  my @rw_line = split / /,@_[0];

  # have to escape some chars if they appear in nicknames
  my $rw_nick1 = @rw_line[2];
  $rw_nick1 =~ s/\|/\\\|/g;
  $rw_nick1 =~ s/\^/\\\^/g;
  $rw_nick1 =~ s/\[/\\\[/g;
  $rw_nick1 =~ s/\]/\\\]/g;
  my $rw_nick2 = @rw_line[3];
  $rw_nick2 =~ s/\|/\\\|/g;
  $rw_nick2 =~ s/\^/\\\^/g;
  $rw_nick2 =~ s/\[/\\\[/g;
  $rw_nick2 =~ s/\]/\\\]/g;
  my @rw_margs = split /$rw_nick1 $rw_nick2/,@_[0];
  my @rw_mstring = split /^ +:/,@rw_margs[1];
  
  # check if whois was called from /ak or /autokill command
  if($ak_akill_flag == 0) {

   # this is the first line - we really want some reformatting for readability
   if (@rw_line[1] == 311) {
    IRC::print("$rw_color_headfoot\[ @rw_line[3] \]\n");
    my @rw_ident = split / /,@rw_mstring[0];
    my @rw_realname = split / \* +:/,@rw_mstring[0];
    shift @rw_ident;
    my $rw_id_user = @rw_ident[0];
    my $rw_id_host = @rw_ident[1];
    IRC::print("$rw_color_body  $rw_id_user\@$rw_id_host (@rw_realname[1])\n");
   }

   # convert idle time and signon date into readable values and beautify
   # output
   elsif (@rw_line[1] == 317) {
    my @rw_idlesplit = split / /,@rw_mstring[0];
    shift @rw_idlesplit;
    my ($rw_idletime,$rw_signontime)=@rw_idlesplit,2;
    my ($rw_week,$rw_day,$rw_hour,$rw_minute,$rw_sec) = &rw_idlecalc($rw_idletime);

    # some modula calc for idle time conversion
    sub rw_idlecalc {
     my ($rw_lidletime) = @_;
     my $rw_lsec = $rw_lidletime % 60;
     $rw_lidletime = ($rw_lidletime - $rw_lsec) / 60;
     my $rw_lminute = $rw_lidletime % 60;
     $rw_lidletime = ($rw_lidletime - $rw_lminute) / 60;
     my $rw_lhour = $rw_lidletime % 24;
     $rw_lidletime = ($rw_lidletime - $rw_lhour) / 24;
     my $rw_lday = $rw_lidletime % 7;
     my $rw_lweek = ($rw_lidletime - $rw_lday) / 7;

     # add leading zero if value is smaller than 10
     $rw_lsec="0$rw_lsec" if($rw_lsec<10);
     $rw_lminute="0$rw_lminute" if($rw_lminute<10);
     $rw_lhour="0$rw_lhour" if($rw_lhour<10);
     return ($rw_lweek,$rw_lday,$rw_lhour,$rw_lminute,$rw_lsec);
    }

    # convert signon date from unix time to readable values
    my ($rw_ssec, $rw_smin, $rw_shour, $rw_sdom, $rw_smon, $rw_syear, $rw_sdow, $rw_sdoy, $rw_sdls)=localtime($rw_signontime);

    # month count starts from 0 - not relly nice to read; also - again - the
    # leading zero
    $rw_smon+=1;
    $rw_smon="0$rw_smon" if ($rw_smon<10);

    # same thing here - year count starts from 1900
    $rw_syear+=1900;

    # and some more zeros
    $rw_ssec="0$rw_ssec" if ($rw_ssec<10);
    $rw_smin="0$rw_smin" if ($rw_smin<10);
    $rw_shour="0$rw_shour" if ($rw_shour<10);

    # print everything in a nice reformatted line
    IRC::print("$rw_color_body  Idle: $rw_color_highlight$rw_week\w$rw_color_body, $rw_color_highlight$rw_day\d$rw_color_body, $rw_color_highlight$rw_hour\:$rw_minute\:$rw_sec\h$rw_color_body - Signon: $rw_color_highlight$rw_syear\-$rw_smon\-$rw_sdom$rw_color_body, $rw_color_highlight$rw_shour\:$rw_smin\:$rw_ssec\h\n");
   }

   # channels line
   elsif (@rw_line[1] == 319) {

    # word wrapping stuff
    if ((length(@rw_mstring[1]) > $rw_wordwrap) && ($rw_dowrap eq "true")) {
     my $rw_wwtext="  Channels: @rw_mstring[1]";
     my @rw_wwinput= split /\s/,$rw_wwtext;
     my ($rw_wwoutput,$rw_wwouttemp);
     my $rw_wwcount=0;    #needed for the "  " in beginning of only 1st line

     # we have to read words into helper var and print the line if it gets
     # too long
     for (my $i=0; defined $rw_wwinput[$i]; $i++) {
      $rw_wwouttemp="$rw_wwoutput$rw_wwinput[$i] ";
      $rw_wwoutput="$rw_wwouttemp" if ((length($rw_wwouttemp) < $rw_wordwrap));
      # print output if helper var gets too big
      if (length($rw_wwouttemp) >= $rw_wordwrap) {
       if ($rw_wwcount == 0) {
        IRC::print("$rw_color_body$rw_wwoutput\n");
        $rw_wwcount=1;
       }
       else {
        IRC::print("$rw_color_body            $rw_wwoutput\n");
       }
       $rw_wwoutput="$rw_wwinput[$i] ";
       $rw_wwouttemp="";
      }
     }

     # if there is still some unprinted text left, we throw it out now
     if ($rw_wwoutput ne "") {
      if ($rw_wwcount == 0) {
       IRC::print("$rw_color_body$rw_wwoutput\n");
       $rw_wwcount=1;
      }
      else {
       IRC::print("$rw_color_body            $rw_wwoutput\n");
      }
      $rw_wwoutput="";
     }
    }

    # what to do if we don't want (or don't need) any wrapping
    else {
     IRC::print("$rw_color_body  Channels: @rw_mstring[1]\n");
    }
   }
   
   # away line
   elsif (@rw_line[1] == 301) {
    IRC::print("$rw_color_body  User is away: @rw_mstring[1]\n");
   }

   # the last line
   elsif (@rw_line[1] == 318) {
    IRC::print("$rw_color_headfoot\[ end of whois info \]\n");
   }

   # any line we haven't mentioned yet
   else {

    # need this ugly hack because some lines start with ":" and some don't
    if (@rw_mstring[0] ne "") {
     IRC::print("$rw_color_body @rw_mstring[0]\n");
    }
    else {
     IRC::print("$rw_color_body  @rw_mstring[1]\n");
    }
   }
   if($rw_serverlog eq "true")
   {
    return 0;
   }
   else
   {
    return 1;
   }
  }
  else {
   return 1;
  }
 }
}


##### chanmode announce ########################################################

if($use_cmode eq "true"){

 # chanmode handler
 IRC::add_message_handler("324","local_cm_cmode_handler");
 IRC::add_message_handler("329","local_cm_cmode_handler");

 my $cm_cmode_flag=0;
 my $cm_cmode_flag2=0;
 # display channel mode on event 324
 sub local_cm_cmode_handler {
  my $cm_server = IRC::get_info(3);
  my @cm_line = split / /,@_[0];
  IRC::add_timeout_handler(100,"local_cm_flag_handler");
  sub local_cm_flag_handler { $cm_cmode_flag=0; }
  IRC::add_timeout_handler(100,"local_cm_flag2_handler");
  sub local_cm_flag2_handler { $cm_cmode_flag2=0; }
  if($cm_cmode_flag==0 && @cm_line[1]==324){
   $cm_cmode_flag=1;
   IRC::print_with_channel("$cm_color@cm_line[3] modes:   @cm_line[4]",@cm_line[3],$cm_server);
   return 1;
  }
  if($cm_cmode_flag2==0 && @cm_line[1]==329){
   $cm_cmode_flag2=1;

   # convert creation date from unix time to readable values
   my ($cm_ssec, $cm_smin, $cm_shour, $cm_sdom, $cm_smon, $cm_syear, $cm_sdow, $cm_sdoy, $cm_sdls)=localtime($cm_line[4]);

   # month count starts from 0 - not relly nice to read; also - again - the
   # leading zero
   $cm_smon+=1;
   $cm_smon="0$cm_smon" if ($cm_smon<10);

   # same thing here - year count starts from 1900
   $cm_syear+=1900;

   # and some more zeros
   $cm_ssec="0$cm_ssec" if ($cm_ssec<10);
   $cm_smin="0$cm_smin" if ($cm_smin<10);
   $cm_shour="0$cm_shour" if ($cm_shour<10);

   # print to current channel
   IRC::print_with_channel("$cm_color@cm_line[3] created: $cm_syear-$cm_smon-$cm_sdom, $cm_shour:$cm_smin:$cm_ssec",@cm_line[3],$cm_server);
   return 1;
  }
  return 1;
 }
}

